Linux Sound subsystem development
 help / color / mirror / Atom feed
* [PATCH v5 1/2] ALSA: control: tidy up whitespaces
@ 2026-07-06 14:14 Maciej Strozek
  2026-07-06 14:14 ` [PATCH v5 2/2] ALSA: control: add ioctl to retrieve full card components Maciej Strozek
  0 siblings, 1 reply; 3+ messages in thread
From: Maciej Strozek @ 2026-07-06 14:14 UTC (permalink / raw)
  To: Takashi Iwai, Jaroslav Kysela
  Cc: linux-sound, linux-kernel, alsa-devel, patches, Maciej Strozek

Clean up trailing whitespace in preparation for the card components
changes.

Signed-off-by: Maciej Strozek <mstrozek@opensource.cirrus.com>
---
added in v3, no changes since then
---
 sound/core/control_compat.c | 2 +-
 sound/core/init.c           | 4 ++--
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/sound/core/control_compat.c b/sound/core/control_compat.c
index 16bc80555f26..4ad571087ff5 100644
--- a/sound/core/control_compat.c
+++ b/sound/core/control_compat.c
@@ -417,7 +417,7 @@ static int snd_ctl_elem_add_compat(struct snd_ctl_file *file,
 		break;
 	}
 	return snd_ctl_elem_add(file, data, replace);
-}  
+}
 
 enum {
 	SNDRV_CTL_IOCTL_ELEM_LIST32 = _IOWR('U', 0x10, struct snd_ctl_elem_list32),
diff --git a/sound/core/init.c b/sound/core/init.c
index 56dde5bd73c4..1ff340ee6884 100644
--- a/sound/core/init.c
+++ b/sound/core/init.c
@@ -722,7 +722,7 @@ static void snd_card_set_id_no_lock(struct snd_card *card, const char *src,
 	int len, loops;
 	bool is_default = false;
 	char *id;
-	
+
 	copy_valid_id_string(card, src, nid);
 	id = card->id;
 
@@ -1031,7 +1031,7 @@ int __init snd_card_info_init(void)
  *
  *  Return: Zero otherwise a negative error code.
  */
-  
+
 int snd_component_add(struct snd_card *card, const char *component)
 {
 	char *ptr;
-- 
2.48.1


^ permalink raw reply related	[flat|nested] 3+ messages in thread

* [PATCH v5 2/2] ALSA: control: add ioctl to retrieve full card components
  2026-07-06 14:14 [PATCH v5 1/2] ALSA: control: tidy up whitespaces Maciej Strozek
@ 2026-07-06 14:14 ` Maciej Strozek
  2026-07-15 14:51   ` Takashi Iwai
  0 siblings, 1 reply; 3+ messages in thread
From: Maciej Strozek @ 2026-07-06 14:14 UTC (permalink / raw)
  To: Takashi Iwai, Jaroslav Kysela
  Cc: linux-sound, linux-kernel, alsa-devel, patches, Maciej Strozek

The fixed-size components field in SNDRV_CTL_IOCTL_CARD_INFO can be too
small on systems with many audio devices.

Keep the existing struct snd_ctl_card_info ABI intact and add a new
ioctl SNDRV_CTL_IOCTL_CARD_BYTES that carries a variable-length payload
selected by a type discriminator. The first defined type
SND_CTL_CARD_BTYPE_COMPONENTS returns the full components string. The
ioctl is designed to be reused for other variable-length card payloads
in the future.

The user-space caller may set data_allocated == 0 (or data == NULL) to
query the required length; otherwise the kernel copies the payload into
the user buffer and writes back the actual length in data_len.

When the legacy components field in struct snd_ctl_card_info is
truncated, '>' is written just before the NUL terminator to signal to
user-space that the full string is available via the new ioctl.

card->components is now dynamically allocated and grown in 32 byte
increments via krealloc(), capped at 512 bytes.

Link: https://github.com/alsa-project/alsa-lib/pull/494
Suggested-by: Jaroslav Kysela <perex@perex.cz>
Suggested-by: Takashi Iwai <tiwai@suse.com>
Signed-off-by: Maciej Strozek <mstrozek@opensource.cirrus.com>
---
Changes for v5:
 - use __u64 and u64_to_user_ptr() instead of 32bit compat layer
 - handle a NULL card->components
Changes for v4:
 - replaced snd_ctl_card_components with snd_ctl_card_bytes for possible
   future extensions
 - support query mode (data_allocated == 0 or data == NULL)
 - added compat 32-bit
 - renamed card->components_ptr / components_ptr_alloc_size to
   card->components / components_alloc_size
Changes for v3:
 - change components field to a dynamic array resizable in 32 byte
   increments
 - removed SNDRV_CTL_COMPONENTS_LEN define
 - sanity check if 'components' requests more than 512 bytes
 - added a commit to clean up trailing whitespaces
 - alsa-utils link no longer needed
Changes for v2:
 - do not modify existing card->components field
 - add a new ioctl and struct to keep the full components string
 - handle the split/trim in snd_ctl_card_info()
---
 include/sound/core.h        |  4 +--
 include/uapi/sound/asound.h | 22 ++++++++++++-
 sound/core/control.c        | 62 ++++++++++++++++++++++++++++++++++++-
 sound/core/control_compat.c |  1 +
 sound/core/init.c           | 34 +++++++++++++++++---
 5 files changed, 114 insertions(+), 9 deletions(-)

diff --git a/include/sound/core.h b/include/sound/core.h
index 404785b7d885..2ca24ac7e37f 100644
--- a/include/sound/core.h
+++ b/include/sound/core.h
@@ -108,8 +108,8 @@ struct snd_card {
 	char longname[80];		/* name of this soundcard */
 	char irq_descr[32];		/* Interrupt description */
 	char mixername[80];		/* mixer name */
-	char components[128];		/* card components delimited with
-								space */
+	char *components;		/* card components, space-delimited */
+	unsigned int components_alloc_size;	/* current allocation size of components */
 	struct module *module;		/* top-level module */
 
 	void *private_data;		/* private data for soundcard */
diff --git a/include/uapi/sound/asound.h b/include/uapi/sound/asound.h
index d3ce75ba938a..500599213f93 100644
--- a/include/uapi/sound/asound.h
+++ b/include/uapi/sound/asound.h
@@ -1058,7 +1058,7 @@ struct snd_timer_tread {
  *                                                                          *
  ****************************************************************************/
 
-#define SNDRV_CTL_VERSION		SNDRV_PROTOCOL_VERSION(2, 0, 9)
+#define SNDRV_CTL_VERSION		SNDRV_PROTOCOL_VERSION(2, 0, 10)
 
 struct snd_ctl_card_info {
 	int card;			/* card number */
@@ -1072,6 +1072,25 @@ struct snd_ctl_card_info {
 	unsigned char components[128];	/* card components / fine identification, delimited with one space (AC97 etc..) */
 };
 
+/*
+ * Card components can exceed the fixed 128 bytes in snd_ctl_card_info.
+ * Use SNDRV_CTL_IOCTL_CARD_BYTES with type SND_CTL_CARD_BTYPE_COMPONENTS
+ * to retrieve the full string.
+ */
+
+/* Type values for struct snd_ctl_card_bytes::type */
+enum {
+	SND_CTL_CARD_BTYPE_COMPONENTS	= 1,	/* full card components string */
+};
+
+struct snd_ctl_card_bytes {
+	__u32 type;				/* SND_CTL_CARD_BTYPE_* */
+	__u32 data_allocated;	/* size of @data buffer in bytes */
+	__u32 data_len;			/* in/out: actual data length in bytes */
+	__u32 reserved;			/* explicit pad */
+	__u64 data;				/* user buffer (pointer stored as __u64) */
+};
+
 typedef int __bitwise snd_ctl_elem_type_t;
 #define	SNDRV_CTL_ELEM_TYPE_NONE	((__force snd_ctl_elem_type_t) 0) /* invalid */
 #define	SNDRV_CTL_ELEM_TYPE_BOOLEAN	((__force snd_ctl_elem_type_t) 1) /* boolean type */
@@ -1198,6 +1217,7 @@ struct snd_ctl_tlv {
 
 #define SNDRV_CTL_IOCTL_PVERSION	_IOR('U', 0x00, int)
 #define SNDRV_CTL_IOCTL_CARD_INFO	_IOR('U', 0x01, struct snd_ctl_card_info)
+#define SNDRV_CTL_IOCTL_CARD_BYTES	_IOWR('U', 0x02, struct snd_ctl_card_bytes)
 #define SNDRV_CTL_IOCTL_ELEM_LIST	_IOWR('U', 0x10, struct snd_ctl_elem_list)
 #define SNDRV_CTL_IOCTL_ELEM_INFO	_IOWR('U', 0x11, struct snd_ctl_elem_info)
 #define SNDRV_CTL_IOCTL_ELEM_READ	_IOWR('U', 0x12, struct snd_ctl_elem_value)
diff --git a/sound/core/control.c b/sound/core/control.c
index 7a8dc506221e..5e9f68c96f4b 100644
--- a/sound/core/control.c
+++ b/sound/core/control.c
@@ -876,23 +876,81 @@ static int snd_ctl_card_info(struct snd_card *card, struct snd_ctl_file * ctl,
 {
 	struct snd_ctl_card_info *info __free(kfree) =
 		kzalloc(sizeof(*info), GFP_KERNEL);
+	ssize_t n;
 
 	if (! info)
 		return -ENOMEM;
+
+	static_assert(sizeof(info->components) >= 2);
+
 	scoped_guard(rwsem_read, &snd_ioctl_rwsem) {
+		const char *components = card->components;
+
+		if (!components)
+			components = "";
+
 		info->card = card->number;
 		strscpy(info->id, card->id, sizeof(info->id));
 		strscpy(info->driver, card->driver, sizeof(info->driver));
 		strscpy(info->name, card->shortname, sizeof(info->name));
 		strscpy(info->longname, card->longname, sizeof(info->longname));
 		strscpy(info->mixername, card->mixername, sizeof(info->mixername));
-		strscpy(info->components, card->components, sizeof(info->components));
+		n = strscpy(info->components, components, sizeof(info->components));
+		if (n < 0) // mark the truncation with '>' before NULL terminator
+			info->components[sizeof(info->components) - 2] = '>';
 	}
 	if (copy_to_user(arg, info, sizeof(struct snd_ctl_card_info)))
 		return -EFAULT;
 	return 0;
 }
 
+static int snd_ctl_card_bytes(struct snd_card *card,
+			      struct snd_ctl_card_bytes *info,
+			      unsigned int __user *data_len_out)
+{
+	unsigned int data_len;
+
+	switch (info->type) {
+	case SND_CTL_CARD_BTYPE_COMPONENTS:
+		scoped_guard(rwsem_read, &snd_ioctl_rwsem) {
+			const char *components = card->components;
+
+			if (!components)
+				components = "";
+
+			data_len = strlen(components) + 1;
+
+			if (!info->data || info->data_allocated == 0)
+				break;
+
+			if (info->data_allocated < data_len)
+				return -ENOMEM;
+
+			if (copy_to_user(u64_to_user_ptr(info->data), components, data_len))
+				return -EFAULT;
+		}
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	if (put_user(data_len, data_len_out))
+		return -EFAULT;
+
+	return 0;
+}
+
+static int snd_ctl_card_bytes_user(struct snd_card *card,
+				   struct snd_ctl_card_bytes __user *_info)
+{
+	struct snd_ctl_card_bytes info;
+
+	if (copy_from_user(&info, _info, sizeof(info)))
+		return -EFAULT;
+
+	return snd_ctl_card_bytes(card, &info, &_info->data_len);
+}
+
 static int snd_ctl_elem_list(struct snd_card *card,
 			     struct snd_ctl_elem_list *list)
 {
@@ -1990,6 +2048,8 @@ static long snd_ctl_ioctl(struct file *file, unsigned int cmd, unsigned long arg
 		return put_user(SNDRV_CTL_VERSION, ip) ? -EFAULT : 0;
 	case SNDRV_CTL_IOCTL_CARD_INFO:
 		return snd_ctl_card_info(card, ctl, cmd, argp);
+	case SNDRV_CTL_IOCTL_CARD_BYTES:
+		return snd_ctl_card_bytes_user(card, argp);
 	case SNDRV_CTL_IOCTL_ELEM_LIST:
 		return snd_ctl_elem_list_user(card, argp);
 	case SNDRV_CTL_IOCTL_ELEM_INFO:
diff --git a/sound/core/control_compat.c b/sound/core/control_compat.c
index 4ad571087ff5..f14d9f5e94be 100644
--- a/sound/core/control_compat.c
+++ b/sound/core/control_compat.c
@@ -446,6 +446,7 @@ static inline long snd_ctl_ioctl_compat(struct file *file, unsigned int cmd, uns
 	switch (cmd) {
 	case SNDRV_CTL_IOCTL_PVERSION:
 	case SNDRV_CTL_IOCTL_CARD_INFO:
+	case SNDRV_CTL_IOCTL_CARD_BYTES:
 	case SNDRV_CTL_IOCTL_SUBSCRIBE_EVENTS:
 	case SNDRV_CTL_IOCTL_POWER:
 	case SNDRV_CTL_IOCTL_POWER_STATE:
diff --git a/sound/core/init.c b/sound/core/init.c
index 1ff340ee6884..15a8209469c7 100644
--- a/sound/core/init.c
+++ b/sound/core/init.c
@@ -590,6 +590,9 @@ static int snd_card_do_free(struct snd_card *card)
 		snd_mixer_oss_notify_callback(card, SND_MIXER_OSS_NOTIFY_FREE);
 #endif
 	snd_device_free_all(card);
+	kfree(card->components);
+	card->components = NULL;
+	card->components_alloc_size = 0;
 	if (card->private_free)
 		card->private_free(card);
 #ifdef CONFIG_SND_CTL_DEBUG
@@ -1036,16 +1039,37 @@ int snd_component_add(struct snd_card *card, const char *component)
 {
 	char *ptr;
 	int len = strlen(component);
+	unsigned int cur_len, need_len;
 
-	ptr = strstr(card->components, component);
-	if (ptr != NULL) {
-		if (ptr[len] == '\0' || ptr[len] == ' ')	/* already there */
-			return 1;
+	if (card->components) {
+		ptr = strstr(card->components, component);
+		if (ptr) {
+			if (ptr[len] == '\0' || ptr[len] == ' ')	/* already there */
+				return 1;
+		}
+		cur_len = strlen(card->components) + 1;
+	} else {
+		cur_len = 0;
 	}
-	if (strlen(card->components) + 1 + len + 1 > sizeof(card->components)) {
+
+	need_len = cur_len + len + 1;
+	if (need_len > 512) {
 		snd_BUG();
 		return -ENOMEM;
 	}
+
+	if (need_len > card->components_alloc_size) {
+		unsigned int new_alloc = roundup(need_len, 32);
+
+		ptr = krealloc(card->components, new_alloc, GFP_KERNEL);
+		if (!ptr)
+			return -ENOMEM;
+		if (!card->components)
+			ptr[0] = '\0';
+		card->components = ptr;
+		card->components_alloc_size = new_alloc;
+	}
+
 	if (card->components[0] != '\0')
 		strcat(card->components, " ");
 	strcat(card->components, component);
-- 
2.48.1


^ permalink raw reply related	[flat|nested] 3+ messages in thread

* Re: [PATCH v5 2/2] ALSA: control: add ioctl to retrieve full card components
  2026-07-06 14:14 ` [PATCH v5 2/2] ALSA: control: add ioctl to retrieve full card components Maciej Strozek
@ 2026-07-15 14:51   ` Takashi Iwai
  0 siblings, 0 replies; 3+ messages in thread
From: Takashi Iwai @ 2026-07-15 14:51 UTC (permalink / raw)
  To: Maciej Strozek
  Cc: Takashi Iwai, Jaroslav Kysela, linux-sound, linux-kernel,
	alsa-devel, patches

On Mon, 06 Jul 2026 16:14:57 +0200,
Maciej Strozek wrote:
> 
> The fixed-size components field in SNDRV_CTL_IOCTL_CARD_INFO can be too
> small on systems with many audio devices.
> 
> Keep the existing struct snd_ctl_card_info ABI intact and add a new
> ioctl SNDRV_CTL_IOCTL_CARD_BYTES that carries a variable-length payload
> selected by a type discriminator. The first defined type
> SND_CTL_CARD_BTYPE_COMPONENTS returns the full components string. The
> ioctl is designed to be reused for other variable-length card payloads
> in the future.
> 
> The user-space caller may set data_allocated == 0 (or data == NULL) to
> query the required length; otherwise the kernel copies the payload into
> the user buffer and writes back the actual length in data_len.
> 
> When the legacy components field in struct snd_ctl_card_info is
> truncated, '>' is written just before the NUL terminator to signal to
> user-space that the full string is available via the new ioctl.
> 
> card->components is now dynamically allocated and grown in 32 byte
> increments via krealloc(), capped at 512 bytes.
> 
> Link: https://github.com/alsa-project/alsa-lib/pull/494
> Suggested-by: Jaroslav Kysela <perex@perex.cz>
> Suggested-by: Takashi Iwai <tiwai@suse.com>
> Signed-off-by: Maciej Strozek <mstrozek@opensource.cirrus.com>
> ---
> Changes for v5:
>  - use __u64 and u64_to_user_ptr() instead of 32bit compat layer
>  - handle a NULL card->components
> Changes for v4:
>  - replaced snd_ctl_card_components with snd_ctl_card_bytes for possible
>    future extensions
>  - support query mode (data_allocated == 0 or data == NULL)
>  - added compat 32-bit
>  - renamed card->components_ptr / components_ptr_alloc_size to
>    card->components / components_alloc_size
> Changes for v3:
>  - change components field to a dynamic array resizable in 32 byte
>    increments
>  - removed SNDRV_CTL_COMPONENTS_LEN define
>  - sanity check if 'components' requests more than 512 bytes
>  - added a commit to clean up trailing whitespaces
>  - alsa-utils link no longer needed
> Changes for v2:
>  - do not modify existing card->components field
>  - add a new ioctl and struct to keep the full components string
>  - handle the split/trim in snd_ctl_card_info()
> ---
>  include/sound/core.h        |  4 +--
>  include/uapi/sound/asound.h | 22 ++++++++++++-
>  sound/core/control.c        | 62 ++++++++++++++++++++++++++++++++++++-
>  sound/core/control_compat.c |  1 +
>  sound/core/init.c           | 34 +++++++++++++++++---
>  5 files changed, 114 insertions(+), 9 deletions(-)
> 
> diff --git a/include/sound/core.h b/include/sound/core.h
> index 404785b7d885..2ca24ac7e37f 100644
> --- a/include/sound/core.h
> +++ b/include/sound/core.h
> @@ -108,8 +108,8 @@ struct snd_card {
>  	char longname[80];		/* name of this soundcard */
>  	char irq_descr[32];		/* Interrupt description */
>  	char mixername[80];		/* mixer name */
> -	char components[128];		/* card components delimited with
> -								space */
> +	char *components;		/* card components, space-delimited */
> +	unsigned int components_alloc_size;	/* current allocation size of components */
>  	struct module *module;		/* top-level module */
>  
>  	void *private_data;		/* private data for soundcard */
> diff --git a/include/uapi/sound/asound.h b/include/uapi/sound/asound.h
> index d3ce75ba938a..500599213f93 100644
> --- a/include/uapi/sound/asound.h
> +++ b/include/uapi/sound/asound.h
> @@ -1058,7 +1058,7 @@ struct snd_timer_tread {
>   *                                                                          *
>   ****************************************************************************/
>  
> -#define SNDRV_CTL_VERSION		SNDRV_PROTOCOL_VERSION(2, 0, 9)
> +#define SNDRV_CTL_VERSION		SNDRV_PROTOCOL_VERSION(2, 0, 10)
>  
>  struct snd_ctl_card_info {
>  	int card;			/* card number */
> @@ -1072,6 +1072,25 @@ struct snd_ctl_card_info {
>  	unsigned char components[128];	/* card components / fine identification, delimited with one space (AC97 etc..) */
>  };
>  
> +/*
> + * Card components can exceed the fixed 128 bytes in snd_ctl_card_info.
> + * Use SNDRV_CTL_IOCTL_CARD_BYTES with type SND_CTL_CARD_BTYPE_COMPONENTS
> + * to retrieve the full string.
> + */
> +
> +/* Type values for struct snd_ctl_card_bytes::type */
> +enum {
> +	SND_CTL_CARD_BTYPE_COMPONENTS	= 1,	/* full card components string */
> +};
> +
> +struct snd_ctl_card_bytes {
> +	__u32 type;				/* SND_CTL_CARD_BTYPE_* */
> +	__u32 data_allocated;	/* size of @data buffer in bytes */
> +	__u32 data_len;			/* in/out: actual data length in bytes */
> +	__u32 reserved;			/* explicit pad */
> +	__u64 data;				/* user buffer (pointer stored as __u64) */
> +};
> +
>  typedef int __bitwise snd_ctl_elem_type_t;
>  #define	SNDRV_CTL_ELEM_TYPE_NONE	((__force snd_ctl_elem_type_t) 0) /* invalid */
>  #define	SNDRV_CTL_ELEM_TYPE_BOOLEAN	((__force snd_ctl_elem_type_t) 1) /* boolean type */
> @@ -1198,6 +1217,7 @@ struct snd_ctl_tlv {
>  
>  #define SNDRV_CTL_IOCTL_PVERSION	_IOR('U', 0x00, int)
>  #define SNDRV_CTL_IOCTL_CARD_INFO	_IOR('U', 0x01, struct snd_ctl_card_info)
> +#define SNDRV_CTL_IOCTL_CARD_BYTES	_IOWR('U', 0x02, struct snd_ctl_card_bytes)
>  #define SNDRV_CTL_IOCTL_ELEM_LIST	_IOWR('U', 0x10, struct snd_ctl_elem_list)
>  #define SNDRV_CTL_IOCTL_ELEM_INFO	_IOWR('U', 0x11, struct snd_ctl_elem_info)
>  #define SNDRV_CTL_IOCTL_ELEM_READ	_IOWR('U', 0x12, struct snd_ctl_elem_value)
> diff --git a/sound/core/control.c b/sound/core/control.c
> index 7a8dc506221e..5e9f68c96f4b 100644
> --- a/sound/core/control.c
> +++ b/sound/core/control.c
> @@ -876,23 +876,81 @@ static int snd_ctl_card_info(struct snd_card *card, struct snd_ctl_file * ctl,
>  {
>  	struct snd_ctl_card_info *info __free(kfree) =
>  		kzalloc(sizeof(*info), GFP_KERNEL);
> +	ssize_t n;
>  
>  	if (! info)
>  		return -ENOMEM;
> +
> +	static_assert(sizeof(info->components) >= 2);
> +
>  	scoped_guard(rwsem_read, &snd_ioctl_rwsem) {
> +		const char *components = card->components;
> +
> +		if (!components)
> +			components = "";
> +
>  		info->card = card->number;
>  		strscpy(info->id, card->id, sizeof(info->id));
>  		strscpy(info->driver, card->driver, sizeof(info->driver));
>  		strscpy(info->name, card->shortname, sizeof(info->name));
>  		strscpy(info->longname, card->longname, sizeof(info->longname));
>  		strscpy(info->mixername, card->mixername, sizeof(info->mixername));
> -		strscpy(info->components, card->components, sizeof(info->components));
> +		n = strscpy(info->components, components, sizeof(info->components));
> +		if (n < 0) // mark the truncation with '>' before NULL terminator
> +			info->components[sizeof(info->components) - 2] = '>';
>  	}
>  	if (copy_to_user(arg, info, sizeof(struct snd_ctl_card_info)))
>  		return -EFAULT;
>  	return 0;
>  }
>  
> +static int snd_ctl_card_bytes(struct snd_card *card,
> +			      struct snd_ctl_card_bytes *info,
> +			      unsigned int __user *data_len_out)
> +{
> +	unsigned int data_len;
> +
> +	switch (info->type) {
> +	case SND_CTL_CARD_BTYPE_COMPONENTS:
> +		scoped_guard(rwsem_read, &snd_ioctl_rwsem) {
> +			const char *components = card->components;
> +
> +			if (!components)
> +				components = "";
> +
> +			data_len = strlen(components) + 1;
> +
> +			if (!info->data || info->data_allocated == 0)
> +				break;
> +
> +			if (info->data_allocated < data_len)
> +				return -ENOMEM;
> +
> +			if (copy_to_user(u64_to_user_ptr(info->data), components, data_len))
> +				return -EFAULT;
> +		}
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	if (put_user(data_len, data_len_out))
> +		return -EFAULT;
> +
> +	return 0;
> +}
> +
> +static int snd_ctl_card_bytes_user(struct snd_card *card,
> +				   struct snd_ctl_card_bytes __user *_info)
> +{
> +	struct snd_ctl_card_bytes info;
> +
> +	if (copy_from_user(&info, _info, sizeof(info)))
> +		return -EFAULT;
> +
> +	return snd_ctl_card_bytes(card, &info, &_info->data_len);
> +}
> +
>  static int snd_ctl_elem_list(struct snd_card *card,
>  			     struct snd_ctl_elem_list *list)
>  {
> @@ -1990,6 +2048,8 @@ static long snd_ctl_ioctl(struct file *file, unsigned int cmd, unsigned long arg
>  		return put_user(SNDRV_CTL_VERSION, ip) ? -EFAULT : 0;
>  	case SNDRV_CTL_IOCTL_CARD_INFO:
>  		return snd_ctl_card_info(card, ctl, cmd, argp);
> +	case SNDRV_CTL_IOCTL_CARD_BYTES:
> +		return snd_ctl_card_bytes_user(card, argp);
>  	case SNDRV_CTL_IOCTL_ELEM_LIST:
>  		return snd_ctl_elem_list_user(card, argp);
>  	case SNDRV_CTL_IOCTL_ELEM_INFO:
> diff --git a/sound/core/control_compat.c b/sound/core/control_compat.c
> index 4ad571087ff5..f14d9f5e94be 100644
> --- a/sound/core/control_compat.c
> +++ b/sound/core/control_compat.c
> @@ -446,6 +446,7 @@ static inline long snd_ctl_ioctl_compat(struct file *file, unsigned int cmd, uns
>  	switch (cmd) {
>  	case SNDRV_CTL_IOCTL_PVERSION:
>  	case SNDRV_CTL_IOCTL_CARD_INFO:
> +	case SNDRV_CTL_IOCTL_CARD_BYTES:
>  	case SNDRV_CTL_IOCTL_SUBSCRIBE_EVENTS:
>  	case SNDRV_CTL_IOCTL_POWER:
>  	case SNDRV_CTL_IOCTL_POWER_STATE:
> diff --git a/sound/core/init.c b/sound/core/init.c
> index 1ff340ee6884..15a8209469c7 100644
> --- a/sound/core/init.c
> +++ b/sound/core/init.c
> @@ -590,6 +590,9 @@ static int snd_card_do_free(struct snd_card *card)
>  		snd_mixer_oss_notify_callback(card, SND_MIXER_OSS_NOTIFY_FREE);
>  #endif
>  	snd_device_free_all(card);
> +	kfree(card->components);
> +	card->components = NULL;
> +	card->components_alloc_size = 0;
>  	if (card->private_free)
>  		card->private_free(card);
>  #ifdef CONFIG_SND_CTL_DEBUG
> @@ -1036,16 +1039,37 @@ int snd_component_add(struct snd_card *card, const char *component)
>  {
>  	char *ptr;
>  	int len = strlen(component);
> +	unsigned int cur_len, need_len;
>  
> -	ptr = strstr(card->components, component);
> -	if (ptr != NULL) {
> -		if (ptr[len] == '\0' || ptr[len] == ' ')	/* already there */
> -			return 1;
> +	if (card->components) {
> +		ptr = strstr(card->components, component);
> +		if (ptr) {
> +			if (ptr[len] == '\0' || ptr[len] == ' ')	/* already there */
> +				return 1;
> +		}
> +		cur_len = strlen(card->components) + 1;
> +	} else {
> +		cur_len = 0;
>  	}
> -	if (strlen(card->components) + 1 + len + 1 > sizeof(card->components)) {
> +
> +	need_len = cur_len + len + 1;
> +	if (need_len > 512) {
>  		snd_BUG();
>  		return -ENOMEM;
>  	}
> +
> +	if (need_len > card->components_alloc_size) {
> +		unsigned int new_alloc = roundup(need_len, 32);
> +
> +		ptr = krealloc(card->components, new_alloc, GFP_KERNEL);
> +		if (!ptr)
> +			return -ENOMEM;
> +		if (!card->components)
> +			ptr[0] = '\0';
> +		card->components = ptr;
> +		card->components_alloc_size = new_alloc;
> +	}
> +
>  	if (card->components[0] != '\0')
>  		strcat(card->components, " ");
>  	strcat(card->components, component);

I believe we need to protect snd_component_add() against the racy
ioctl?  Otherwise the existing guard in your patch makes no sense.


thanks,

Takashi


^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2026-07-15 14:52 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-06 14:14 [PATCH v5 1/2] ALSA: control: tidy up whitespaces Maciej Strozek
2026-07-06 14:14 ` [PATCH v5 2/2] ALSA: control: add ioctl to retrieve full card components Maciej Strozek
2026-07-15 14:51   ` Takashi Iwai

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox