[alsa-devel] [PATCH 04/39] ALSA: seq: copy ioctl data from user space to kernel stack
Takashi Sakamoto
o-takashi at sakamocchi.jp
Sun Aug 7 11:48:40 CEST 2016
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 at sakamocchi.jp>
---
sound/core/seq/seq_clientmgr.c | 136 ++++++++++++++++++++++++++++++-----------
1 file changed, 101 insertions(+), 35 deletions(-)
diff --git a/sound/core/seq/seq_clientmgr.c b/sound/core/seq/seq_clientmgr.c
index d6c1219..17d988a 100644
--- a/sound/core/seq/seq_clientmgr.c
+++ b/sound/core/seq/seq_clientmgr.c
@@ -2182,40 +2182,70 @@ static int 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);
+ size_t arg_size;
} ioctl_tables[] = {
- { SNDRV_SEQ_IOCTL_PVERSION, seq_ioctl_pversion },
- { SNDRV_SEQ_IOCTL_CLIENT_ID, seq_ioctl_client_id },
- { SNDRV_SEQ_IOCTL_SYSTEM_INFO, seq_ioctl_system_info },
- { SNDRV_SEQ_IOCTL_RUNNING_MODE, seq_ioctl_running_mode },
- { SNDRV_SEQ_IOCTL_GET_CLIENT_INFO, seq_ioctl_get_client_info },
- { SNDRV_SEQ_IOCTL_SET_CLIENT_INFO, seq_ioctl_set_client_info },
- { SNDRV_SEQ_IOCTL_CREATE_PORT, seq_ioctl_create_port },
- { SNDRV_SEQ_IOCTL_DELETE_PORT, seq_ioctl_delete_port },
- { SNDRV_SEQ_IOCTL_GET_PORT_INFO, seq_ioctl_get_port_info },
- { SNDRV_SEQ_IOCTL_SET_PORT_INFO, seq_ioctl_set_port_info },
- { SNDRV_SEQ_IOCTL_SUBSCRIBE_PORT, seq_ioctl_subscribe_port },
- { SNDRV_SEQ_IOCTL_UNSUBSCRIBE_PORT, seq_ioctl_unsubscribe_port },
- { SNDRV_SEQ_IOCTL_CREATE_QUEUE, seq_ioctl_create_queue },
- { SNDRV_SEQ_IOCTL_DELETE_QUEUE, seq_ioctl_delete_queue },
- { SNDRV_SEQ_IOCTL_GET_QUEUE_INFO, seq_ioctl_get_queue_info },
- { SNDRV_SEQ_IOCTL_SET_QUEUE_INFO, seq_ioctl_set_queue_info },
- { SNDRV_SEQ_IOCTL_GET_NAMED_QUEUE, seq_ioctl_get_named_queue },
- { SNDRV_SEQ_IOCTL_GET_QUEUE_STATUS, seq_ioctl_get_queue_status },
- { SNDRV_SEQ_IOCTL_GET_QUEUE_TEMPO, seq_ioctl_get_queue_tempo },
- { SNDRV_SEQ_IOCTL_SET_QUEUE_TEMPO, seq_ioctl_set_queue_tempo },
- { SNDRV_SEQ_IOCTL_GET_QUEUE_TIMER, seq_ioctl_get_queue_timer },
- { SNDRV_SEQ_IOCTL_SET_QUEUE_TIMER, seq_ioctl_set_queue_timer },
- { SNDRV_SEQ_IOCTL_GET_QUEUE_CLIENT, seq_ioctl_get_queue_client },
- { SNDRV_SEQ_IOCTL_SET_QUEUE_CLIENT, seq_ioctl_set_queue_client },
- { SNDRV_SEQ_IOCTL_GET_CLIENT_POOL, seq_ioctl_get_client_pool },
- { SNDRV_SEQ_IOCTL_SET_CLIENT_POOL, seq_ioctl_set_client_pool },
- { SNDRV_SEQ_IOCTL_GET_SUBSCRIPTION, seq_ioctl_get_subscription },
- { SNDRV_SEQ_IOCTL_QUERY_NEXT_CLIENT, seq_ioctl_query_next_client },
- { SNDRV_SEQ_IOCTL_QUERY_NEXT_PORT, seq_ioctl_query_next_port },
- { SNDRV_SEQ_IOCTL_REMOVE_EVENTS, seq_ioctl_remove_events },
- { SNDRV_SEQ_IOCTL_QUERY_SUBS, seq_ioctl_query_subs },
- { 0, NULL },
+ { SNDRV_SEQ_IOCTL_PVERSION, seq_ioctl_pversion, sizeof(int) },
+ { SNDRV_SEQ_IOCTL_CLIENT_ID, seq_ioctl_client_id, sizeof(int) },
+ { SNDRV_SEQ_IOCTL_SYSTEM_INFO, seq_ioctl_system_info,
+ sizeof(struct snd_seq_system_info) },
+ { SNDRV_SEQ_IOCTL_RUNNING_MODE, seq_ioctl_running_mode,
+ sizeof(struct snd_seq_running_info) },
+ { SNDRV_SEQ_IOCTL_GET_CLIENT_INFO, seq_ioctl_get_client_info,
+ sizeof(struct snd_seq_client_info) },
+ { SNDRV_SEQ_IOCTL_SET_CLIENT_INFO, seq_ioctl_set_client_info,
+ sizeof(struct snd_seq_client_info) },
+ { SNDRV_SEQ_IOCTL_CREATE_PORT, seq_ioctl_create_port,
+ sizeof(struct snd_seq_port_info) },
+ { SNDRV_SEQ_IOCTL_DELETE_PORT, seq_ioctl_delete_port,
+ sizeof(struct snd_seq_port_info) },
+ { SNDRV_SEQ_IOCTL_GET_PORT_INFO, seq_ioctl_get_port_info,
+ sizeof(struct snd_seq_port_info) },
+ { SNDRV_SEQ_IOCTL_SET_PORT_INFO, seq_ioctl_set_port_info,
+ sizeof(struct snd_seq_port_info) },
+ { SNDRV_SEQ_IOCTL_SUBSCRIBE_PORT, seq_ioctl_subscribe_port,
+ sizeof(struct snd_seq_port_subscribe) },
+ { SNDRV_SEQ_IOCTL_UNSUBSCRIBE_PORT, seq_ioctl_unsubscribe_port,
+ sizeof(struct snd_seq_port_subscribe) },
+ { SNDRV_SEQ_IOCTL_CREATE_QUEUE, seq_ioctl_create_queue,
+ sizeof(struct snd_seq_queue_info) },
+ { SNDRV_SEQ_IOCTL_DELETE_QUEUE, seq_ioctl_delete_queue,
+ sizeof(struct snd_seq_queue_info) },
+ { SNDRV_SEQ_IOCTL_GET_QUEUE_INFO, seq_ioctl_get_queue_info,
+ sizeof(struct snd_seq_queue_info) },
+ { SNDRV_SEQ_IOCTL_SET_QUEUE_INFO, seq_ioctl_set_queue_info,
+ sizeof(struct snd_seq_queue_info) },
+ { SNDRV_SEQ_IOCTL_GET_NAMED_QUEUE, seq_ioctl_get_named_queue,
+ sizeof(struct snd_seq_queue_info) },
+ { SNDRV_SEQ_IOCTL_GET_QUEUE_STATUS, seq_ioctl_get_queue_status,
+ sizeof(struct snd_seq_queue_status) },
+ { SNDRV_SEQ_IOCTL_GET_QUEUE_TEMPO, seq_ioctl_get_queue_tempo,
+ sizeof(struct snd_seq_queue_tempo) },
+ { SNDRV_SEQ_IOCTL_SET_QUEUE_TEMPO, seq_ioctl_set_queue_tempo,
+ sizeof(struct snd_seq_queue_tempo) },
+ { SNDRV_SEQ_IOCTL_GET_QUEUE_TIMER, seq_ioctl_get_queue_timer,
+ sizeof(struct snd_seq_queue_timer) },
+ { SNDRV_SEQ_IOCTL_SET_QUEUE_TIMER, seq_ioctl_set_queue_timer,
+ sizeof(struct snd_seq_queue_timer) },
+ { SNDRV_SEQ_IOCTL_GET_QUEUE_CLIENT, seq_ioctl_get_queue_client,
+ sizeof(struct snd_seq_queue_client) },
+ { SNDRV_SEQ_IOCTL_SET_QUEUE_CLIENT, seq_ioctl_set_queue_client,
+ sizeof(struct snd_seq_queue_client) },
+ { SNDRV_SEQ_IOCTL_GET_CLIENT_POOL, seq_ioctl_get_client_pool,
+ sizeof(struct snd_seq_client_pool) },
+ { SNDRV_SEQ_IOCTL_SET_CLIENT_POOL, seq_ioctl_set_client_pool,
+ sizeof(struct snd_seq_client_pool) },
+ { SNDRV_SEQ_IOCTL_GET_SUBSCRIPTION, seq_ioctl_get_subscription,
+ sizeof(struct snd_seq_port_subscribe) },
+ { SNDRV_SEQ_IOCTL_QUERY_NEXT_CLIENT, seq_ioctl_query_next_client,
+ sizeof(struct snd_seq_client_info) },
+ { SNDRV_SEQ_IOCTL_QUERY_NEXT_PORT, seq_ioctl_query_next_port,
+ sizeof(struct snd_seq_port_info) },
+ { SNDRV_SEQ_IOCTL_REMOVE_EVENTS, seq_ioctl_remove_events,
+ sizeof(struct snd_seq_remove_events) },
+ { SNDRV_SEQ_IOCTL_QUERY_SUBS, seq_ioctl_query_subs,
+ sizeof(struct snd_seq_query_subs) },
+ { 0, NULL, 0 },
};
static int seq_do_ioctl(struct snd_seq_client *client, unsigned int cmd,
@@ -2235,14 +2265,50 @@ static int 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;
+ 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;
+
+ if (copy_from_user(&buf, (const void __user *)arg, p->arg_size))
+ return -EFAULT;
- return seq_do_ioctl(client, cmd, (void __user *) arg);
+ err = p->func(client, &buf);
+ if (err >= 0) {
+ if (copy_to_user((void __user *)arg, &buf, p->arg_size))
+ return -EFAULT;
+ }
+
+ return err;
}
#ifdef CONFIG_COMPAT
--
2.7.4
More information about the Alsa-devel
mailing list