ALSA sequencer core allows kernel-mode tasks to execute operations corresponding to ioctls by user-mode tasks. This is for compatibility layers such as ABIs for 32/64 bit and I/O operations via Open Sound System.
This design requires to handle ioctl data in kernel space. For this requirement, ALSA sequencer core temporarily changes address limit for the task by get_fs()/set_fs() macro.
Although, this way get shape of code worse, because even when an pointer has '__user' qualifier, actually it refers to kernel space, depending on the task. This easily misleads readers.
This commit is a preparation for following patches to solve this issue. Data from user space is once copied to kernel stack, then operated and copied to user space, in a consistent manner. This manner forces all ioctl operations to copy the data from/to user space, even if it's read-only or write-only. Thus, it has an overhead for simpler ioctl commands.
Signed-off-by: Takashi Sakamoto o-takashi@sakamocchi.jp --- sound/core/seq/seq_clientmgr.c | 49 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 46 insertions(+), 3 deletions(-)
diff --git a/sound/core/seq/seq_clientmgr.c b/sound/core/seq/seq_clientmgr.c index 78fd64e..de1177f 100644 --- a/sound/core/seq/seq_clientmgr.c +++ b/sound/core/seq/seq_clientmgr.c @@ -2180,7 +2180,7 @@ static int snd_seq_ioctl_query_next_port(struct snd_seq_client *client,
static const struct seq_ioctl_table { unsigned int cmd; - int (*func)(struct snd_seq_client *client, void __user * arg); + int (*func)(struct snd_seq_client *client, void *arg); } ioctl_tables[] = { { SNDRV_SEQ_IOCTL_PVERSION, seq_ioctl_pversion }, { SNDRV_SEQ_IOCTL_CLIENT_ID, seq_ioctl_client_id }, @@ -2233,14 +2233,57 @@ static int snd_seq_do_ioctl(struct snd_seq_client *client, unsigned int cmd, }
-static long snd_seq_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +static long snd_seq_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) { struct snd_seq_client *client = file->private_data; + const struct seq_ioctl_table *p; + /* To use kernel stack for ioctl data. */ + union ioctl_arg { + int pversion; + int client_id; + struct snd_seq_system_info system_info; + struct snd_seq_running_info running_info; + struct snd_seq_client_info client_info; + struct snd_seq_port_info port_info; + struct snd_seq_port_subscribe port_subscribe; + struct snd_seq_queue_info queue_info; + struct snd_seq_queue_status queue_status; + struct snd_seq_queue_tempo tempo; + struct snd_seq_queue_timer queue_timer; + struct snd_seq_queue_client queue_client; + struct snd_seq_client_pool client_pool; + struct snd_seq_remove_events remove_events; + struct snd_seq_query_subs query_subs; + } buf = {0}; + int err;
if (snd_BUG_ON(!client)) return -ENXIO; + + for (p = ioctl_tables; p->cmd > 0; ++p) { + if (p->cmd == cmd) + break; + } + if (p->cmd == 0) + return -ENOTTY; + + /* + * All of ioctl commands for ALSA sequencer get an argument of size + * within 13 bits. We can safely pick up the size from the command. + * On the other hand, some commands includes a bug of 'read' or 'write', + * thus we cannot utilize information from the access fields. + */ + if (copy_from_user(&buf, (const void __user *)arg, _IOC_SIZE(p->cmd))) + return -EFAULT; - return snd_seq_do_ioctl(client, cmd, (void __user *) arg); + err = p->func(client, &buf); + if (err >= 0) { + if (copy_to_user((void __user *)arg, &buf, _IOC_SIZE(p->cmd))) + return -EFAULT; + } + + return err; }
#ifdef CONFIG_COMPAT