ALSA: control: Use scoped cleanup for user control buffers

User-defined control TLV data and enum names are copied from user space
with vmemdup_user() before being installed in the user_element. Until
ownership is transferred, these temporary buffers have to be released on
every validation exit.

Use __free(kvfree) for the temporary buffers and no_free_ptr() when
ownership is transferred to the user_element. This removes the manual
kvfree() calls from the unchanged-TLV and enum-name validation paths,
makes the ownership hand-off explicit, and keeps the existing allocation
accounting and ABI unchanged.

No functional change is intended.

Signed-off-by: Cássio Gabriel <cassiogabrielcontato@gmail.com>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Link: https://patch.msgid.link/20260604-alsa-scoped-cleanups-v1-1-10c43152a728@gmail.com
This commit is contained in:
Cássio Gabriel 2026-06-04 01:48:12 -03:00 committed by Takashi Iwai
parent 3580bc5352
commit 83615ff7c6

View File

@ -1550,7 +1550,6 @@ static int replace_user_tlv(struct snd_kcontrol *kctl, unsigned int __user *buf,
unsigned int size)
{
struct user_element *ue = snd_kcontrol_chip(kctl);
unsigned int *container;
unsigned int mask = 0;
int i;
int change;
@ -1564,17 +1563,16 @@ static int replace_user_tlv(struct snd_kcontrol *kctl, unsigned int __user *buf,
if (check_user_elem_overflow(ue->card, (ssize_t)(size - ue->tlv_data_size)))
return -ENOMEM;
container = vmemdup_user(buf, size);
unsigned int *container __free(kvfree) = vmemdup_user(buf, size);
if (IS_ERR(container))
return PTR_ERR(container);
change = ue->tlv_data_size != size;
if (!change)
change = memcmp(ue->tlv_data, container, size) != 0;
if (!change) {
kvfree(container);
if (!change)
return 0;
}
if (ue->tlv_data == NULL) {
/* Now TLV data is available. */
@ -1587,7 +1585,7 @@ static int replace_user_tlv(struct snd_kcontrol *kctl, unsigned int __user *buf,
kvfree(ue->tlv_data);
}
ue->tlv_data = container;
ue->tlv_data = no_free_ptr(container);
ue->tlv_data_size = size;
// decremented at private_free.
ue->card->user_ctl_alloc_size += size;
@ -1628,7 +1626,6 @@ static int snd_ctl_elem_user_tlv(struct snd_kcontrol *kctl, int op_flag,
/* called in controls_rwsem write lock */
static int snd_ctl_elem_init_enum_names(struct user_element *ue)
{
char *names, *p;
size_t buf_len, name_len;
unsigned int i;
const uintptr_t user_ptrval = ue->info.value.enumerated.names_ptr;
@ -1641,27 +1638,28 @@ static int snd_ctl_elem_init_enum_names(struct user_element *ue)
if (check_user_elem_overflow(ue->card, buf_len))
return -ENOMEM;
names = vmemdup_user((const void __user *)user_ptrval, buf_len);
char *names __free(kvfree) = vmemdup_user((const void __user *)user_ptrval,
buf_len);
if (IS_ERR(names))
return PTR_ERR(names);
/* check that there are enough valid names */
p = names;
char *p = names;
for (i = 0; i < ue->info.value.enumerated.items; ++i) {
if (buf_len == 0) {
kvfree(names);
if (buf_len == 0)
return -EINVAL;
}
name_len = strnlen(p, buf_len);
if (name_len == 0 || name_len >= 64 || name_len == buf_len) {
kvfree(names);
if (name_len == 0 || name_len >= 64 || name_len == buf_len)
return -EINVAL;
}
p += name_len + 1;
buf_len -= name_len + 1;
}
ue->priv_data = names;
ue->priv_data = no_free_ptr(names);
ue->info.value.enumerated.names_ptr = 0;
// increment the allocation size; decremented again at private_free.
ue->card->user_ctl_alloc_size += ue->info.value.enumerated.names_length;