Skip to content

Commit 66c6d1e

Browse files
takaswietiwai
authored andcommitted
ALSA: control: Add memory consumption limit to user controls
ALSA control interface allows users to add arbitrary control elements (called "user controls" or "user elements"), and its resource usage is limited just by the max number of control sets (currently 32). This limit, however, is quite loose: each allocation of control set may have 1028 elements, and each element may have up to 512 bytes (ILP32) or 1024 bytes (LP64) of value data. Moreover, each control set may contain the enum strings and TLV data, which can be up to 64kB and 128kB, respectively. Totally, the whole memory consumption may go over 38MB -- it's quite large, and we'd rather like to reduce the size. OTOH, there have been other requests even to increase the max number of user elements; e.g. ALSA firewire stack require the more user controls, hence we want to raise the bar, too. For satisfying both requirements, this patch changes the management of user controls: instead of setting the upper limit of the number of user controls, we check the actual memory allocation size and set the upper limit of the total allocation in bytes. As long as the memory consumption stays below the limit, more user controls are allowed than the current limit 32. At the same time, we set the lower limit (8MB) as default than the current theoretical limit, in order to lower the risk of DoS. As a compromise for lowering the default limit, now the actual memory limit is defined as a module option, 'max_user_ctl_alloc_size', so that user can increase/decrease the limit if really needed, too. Link: https://lore.kernel.org/r/[email protected] Co-developed-by: Takashi Iwai <[email protected]> Reviewed-by: Takashi Sakamoto <[email protected]> Tested-by: Takashi Sakamoto <[email protected]> Signed-off-by: Takashi Sakamoto <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Takashi Iwai <[email protected]>
1 parent 884c709 commit 66c6d1e

File tree

2 files changed

+52
-25
lines changed

2 files changed

+52
-25
lines changed

include/sound/core.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ struct snd_card {
100100
struct rw_semaphore controls_rwsem; /* controls list lock */
101101
rwlock_t ctl_files_rwlock; /* ctl_files list lock */
102102
int controls_count; /* count of all controls */
103-
int user_ctl_count; /* count of all user controls */
103+
size_t user_ctl_alloc_size; // current memory allocation by user controls.
104104
struct list_head controls; /* all controls for this card */
105105
struct list_head ctl_files; /* active control files */
106106

sound/core/control.c

Lines changed: 51 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include <linux/threads.h>
88
#include <linux/interrupt.h>
99
#include <linux/module.h>
10+
#include <linux/moduleparam.h>
1011
#include <linux/slab.h>
1112
#include <linux/vmalloc.h>
1213
#include <linux/time.h>
@@ -18,8 +19,11 @@
1819
#include <sound/info.h>
1920
#include <sound/control.h>
2021

21-
/* max number of user-defined controls */
22-
#define MAX_USER_CONTROLS 32
22+
// Max allocation size for user controls.
23+
static int max_user_ctl_alloc_size = 8 * 1024 * 1024;
24+
module_param_named(max_user_ctl_alloc_size, max_user_ctl_alloc_size, int, 0444);
25+
MODULE_PARM_DESC(max_user_ctl_alloc_size, "Max allocation size for user controls");
26+
2327
#define MAX_CONTROL_COUNT 1028
2428

2529
struct snd_kctl_ioctl {
@@ -561,9 +565,6 @@ static int snd_ctl_remove_user_ctl(struct snd_ctl_file * file,
561565
goto error;
562566
}
563567
ret = snd_ctl_remove(card, kctl);
564-
if (ret < 0)
565-
goto error;
566-
card->user_ctl_count--;
567568
error:
568569
up_write(&card->controls_rwsem);
569570
return ret;
@@ -1265,6 +1266,12 @@ struct user_element {
12651266
void *priv_data; /* private data (like strings for enumerated type) */
12661267
};
12671268

1269+
// check whether the addition (in bytes) of user ctl element may overflow the limit.
1270+
static bool check_user_elem_overflow(struct snd_card *card, ssize_t add)
1271+
{
1272+
return (ssize_t)card->user_ctl_alloc_size + add > max_user_ctl_alloc_size;
1273+
}
1274+
12681275
static int snd_ctl_elem_user_info(struct snd_kcontrol *kcontrol,
12691276
struct snd_ctl_elem_info *uinfo)
12701277
{
@@ -1342,6 +1349,10 @@ static int replace_user_tlv(struct snd_kcontrol *kctl, unsigned int __user *buf,
13421349
if (size > 1024 * 128) /* sane value */
13431350
return -EINVAL;
13441351

1352+
// does the TLV size change cause overflow?
1353+
if (check_user_elem_overflow(ue->card, (ssize_t)(size - ue->tlv_data_size)))
1354+
return -ENOMEM;
1355+
13451356
container = vmemdup_user(buf, size);
13461357
if (IS_ERR(container))
13471358
return PTR_ERR(container);
@@ -1359,11 +1370,16 @@ static int replace_user_tlv(struct snd_kcontrol *kctl, unsigned int __user *buf,
13591370
for (i = 0; i < kctl->count; ++i)
13601371
kctl->vd[i].access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ;
13611372
mask = SNDRV_CTL_EVENT_MASK_INFO;
1373+
} else {
1374+
ue->card->user_ctl_alloc_size -= ue->tlv_data_size;
1375+
ue->tlv_data_size = 0;
1376+
kvfree(ue->tlv_data);
13621377
}
13631378

1364-
kvfree(ue->tlv_data);
13651379
ue->tlv_data = container;
13661380
ue->tlv_data_size = size;
1381+
// decremented at private_free.
1382+
ue->card->user_ctl_alloc_size += size;
13671383

13681384
mask |= SNDRV_CTL_EVENT_MASK_TLV;
13691385
for (i = 0; i < kctl->count; ++i)
@@ -1405,16 +1421,17 @@ static int snd_ctl_elem_init_enum_names(struct user_element *ue)
14051421
unsigned int i;
14061422
const uintptr_t user_ptrval = ue->info.value.enumerated.names_ptr;
14071423

1408-
if (ue->info.value.enumerated.names_length > 64 * 1024)
1424+
buf_len = ue->info.value.enumerated.names_length;
1425+
if (buf_len > 64 * 1024)
14091426
return -EINVAL;
14101427

1411-
names = vmemdup_user((const void __user *)user_ptrval,
1412-
ue->info.value.enumerated.names_length);
1428+
if (check_user_elem_overflow(ue->card, buf_len))
1429+
return -ENOMEM;
1430+
names = vmemdup_user((const void __user *)user_ptrval, buf_len);
14131431
if (IS_ERR(names))
14141432
return PTR_ERR(names);
14151433

14161434
/* check that there are enough valid names */
1417-
buf_len = ue->info.value.enumerated.names_length;
14181435
p = names;
14191436
for (i = 0; i < ue->info.value.enumerated.items; ++i) {
14201437
name_len = strnlen(p, buf_len);
@@ -1428,14 +1445,27 @@ static int snd_ctl_elem_init_enum_names(struct user_element *ue)
14281445

14291446
ue->priv_data = names;
14301447
ue->info.value.enumerated.names_ptr = 0;
1448+
// increment the allocation size; decremented again at private_free.
1449+
ue->card->user_ctl_alloc_size += ue->info.value.enumerated.names_length;
14311450

14321451
return 0;
14331452
}
14341453

1454+
static size_t compute_user_elem_size(size_t size, unsigned int count)
1455+
{
1456+
return sizeof(struct user_element) + size * count;
1457+
}
1458+
14351459
static void snd_ctl_elem_user_free(struct snd_kcontrol *kcontrol)
14361460
{
14371461
struct user_element *ue = kcontrol->private_data;
14381462

1463+
// decrement the allocation size.
1464+
ue->card->user_ctl_alloc_size -= compute_user_elem_size(ue->elem_data_size, kcontrol->count);
1465+
ue->card->user_ctl_alloc_size -= ue->tlv_data_size;
1466+
if (ue->priv_data)
1467+
ue->card->user_ctl_alloc_size -= ue->info.value.enumerated.names_length;
1468+
14391469
kvfree(ue->tlv_data);
14401470
kvfree(ue->priv_data);
14411471
kfree(ue);
@@ -1449,6 +1479,7 @@ static int snd_ctl_elem_add(struct snd_ctl_file *file,
14491479
unsigned int count;
14501480
unsigned int access;
14511481
long private_size;
1482+
size_t alloc_size;
14521483
struct user_element *ue;
14531484
unsigned int offset;
14541485
int err;
@@ -1466,13 +1497,6 @@ static int snd_ctl_elem_add(struct snd_ctl_file *file,
14661497
return err;
14671498
}
14681499

1469-
/*
1470-
* The number of userspace controls are counted control by control,
1471-
* not element by element.
1472-
*/
1473-
if (card->user_ctl_count + 1 > MAX_USER_CONTROLS)
1474-
return -ENOMEM;
1475-
14761500
/* Check the number of elements for this userspace control. */
14771501
count = info->owner;
14781502
if (count == 0)
@@ -1503,6 +1527,10 @@ static int snd_ctl_elem_add(struct snd_ctl_file *file,
15031527
if (info->count < 1)
15041528
return -EINVAL;
15051529
private_size = value_sizes[info->type] * info->count;
1530+
alloc_size = compute_user_elem_size(private_size, count);
1531+
1532+
if (check_user_elem_overflow(card, alloc_size))
1533+
return -ENOMEM;
15061534

15071535
/*
15081536
* Keep memory object for this userspace control. After passing this
@@ -1514,16 +1542,18 @@ static int snd_ctl_elem_add(struct snd_ctl_file *file,
15141542
if (err < 0)
15151543
return err;
15161544
memcpy(&kctl->id, &info->id, sizeof(kctl->id));
1517-
kctl->private_data = kzalloc(sizeof(struct user_element) + private_size * count,
1518-
GFP_KERNEL);
1519-
if (kctl->private_data == NULL) {
1545+
ue = kzalloc(alloc_size, GFP_KERNEL);
1546+
if (!ue) {
15201547
kfree(kctl);
15211548
return -ENOMEM;
15221549
}
1550+
kctl->private_data = ue;
15231551
kctl->private_free = snd_ctl_elem_user_free;
15241552

1553+
// increment the allocated size; decremented again at private_free.
1554+
card->user_ctl_alloc_size += alloc_size;
1555+
15251556
/* Set private data for this userspace control. */
1526-
ue = (struct user_element *)kctl->private_data;
15271557
ue->card = card;
15281558
ue->info = *info;
15291559
ue->info.access = 0;
@@ -1565,9 +1595,6 @@ static int snd_ctl_elem_add(struct snd_ctl_file *file,
15651595
* applications because the field originally means PID of a process
15661596
* which locks the element.
15671597
*/
1568-
1569-
card->user_ctl_count++;
1570-
15711598
unlock:
15721599
up_write(&card->controls_rwsem);
15731600
return err;

0 commit comments

Comments
 (0)