ALSA: seq: More protection for concurrent write and ioctl races
[pandora-kernel.git] / sound / core / seq / seq_clientmgr.c
index dd9da62..12f80c5 100644 (file)
@@ -907,7 +907,8 @@ int snd_seq_dispatch_event(struct snd_seq_event_cell *cell, int atomic, int hop)
 static int snd_seq_client_enqueue_event(struct snd_seq_client *client,
                                        struct snd_seq_event *event,
                                        struct file *file, int blocking,
-                                       int atomic, int hop)
+                                       int atomic, int hop,
+                                       struct mutex *mutexp)
 {
        struct snd_seq_event_cell *cell;
        int err;
@@ -945,7 +946,8 @@ static int snd_seq_client_enqueue_event(struct snd_seq_client *client,
                return -ENXIO; /* queue is not allocated */
 
        /* allocate an event cell */
-       err = snd_seq_event_dup(client->pool, event, &cell, !blocking || atomic, file);
+       err = snd_seq_event_dup(client->pool, event, &cell, !blocking || atomic,
+                               file, mutexp);
        if (err < 0)
                return err;
 
@@ -1000,7 +1002,7 @@ static ssize_t snd_seq_write(struct file *file, const char __user *buf,
 {
        struct snd_seq_client *client = file->private_data;
        int written = 0, len;
-       int err = -EINVAL;
+       int err;
        struct snd_seq_event event;
 
        if (!(snd_seq_file_flags(file) & SNDRV_SEQ_LFLG_OUTPUT))
@@ -1014,12 +1016,15 @@ static ssize_t snd_seq_write(struct file *file, const char __user *buf,
                return -ENXIO;
 
        /* allocate the pool now if the pool is not allocated yet */ 
+       mutex_lock(&client->ioctl_mutex);
        if (client->pool->size > 0 && !snd_seq_write_pool_allocated(client)) {
-               if (snd_seq_pool_init(client->pool) < 0)
-                       return -ENOMEM;
+               err = snd_seq_pool_init(client->pool);
+               if (err < 0)
+                       goto out;
        }
 
        /* only process whole events */
+       err = -EINVAL;
        while (count >= sizeof(struct snd_seq_event)) {
                /* Read in the event header from the user */
                len = sizeof(event);
@@ -1066,7 +1071,7 @@ static ssize_t snd_seq_write(struct file *file, const char __user *buf,
                /* ok, enqueue it */
                err = snd_seq_client_enqueue_event(client, &event, file,
                                                   !(file->f_flags & O_NONBLOCK),
-                                                  0, 0);
+                                                  0, 0, &client->ioctl_mutex);
                if (err < 0)
                        break;
 
@@ -1077,6 +1082,8 @@ static ssize_t snd_seq_write(struct file *file, const char __user *buf,
                written += len;
        }
 
+ out:
+       mutex_unlock(&client->ioctl_mutex);
        return written ? written : err;
 }
 
@@ -1913,6 +1920,9 @@ static int snd_seq_ioctl_set_client_pool(struct snd_seq_client *client,
            (! snd_seq_write_pool_allocated(client) ||
             info.output_pool != client->pool->size)) {
                if (snd_seq_write_pool_allocated(client)) {
+                       /* is the pool in use? */
+                       if (atomic_read(&client->pool->counter))
+                               return -EBUSY;
                        /* remove all existing cells */
                        snd_seq_pool_mark_closing(client->pool);
                        snd_seq_queue_client_leave_cells(client->number);
@@ -2189,7 +2199,6 @@ static int snd_seq_do_ioctl(struct snd_seq_client *client, unsigned int cmd,
                            void __user *arg)
 {
        struct seq_ioctl_table *p;
-       int ret;
 
        switch (cmd) {
        case SNDRV_SEQ_IOCTL_PVERSION:
@@ -2203,12 +2212,8 @@ static int snd_seq_do_ioctl(struct snd_seq_client *client, unsigned int cmd,
        if (! arg)
                return -EFAULT;
        for (p = ioctl_tables; p->cmd; p++) {
-               if (p->cmd == cmd) {
-                       mutex_lock(&client->ioctl_mutex);
-                       ret = p->func(client, arg);
-                       mutex_unlock(&client->ioctl_mutex);
-                       return ret;
-               }
+               if (p->cmd == cmd)
+                       return p->func(client, arg);
        }
        snd_printd("seq unknown ioctl() 0x%x (type='%c', number=0x%02x)\n",
                   cmd, _IOC_TYPE(cmd), _IOC_NR(cmd));
@@ -2219,11 +2224,15 @@ 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)
 {
        struct snd_seq_client *client = file->private_data;
+       long ret;
 
        if (snd_BUG_ON(!client))
                return -ENXIO;
                
-       return snd_seq_do_ioctl(client, cmd, (void __user *) arg);
+       mutex_lock(&client->ioctl_mutex);
+       ret = snd_seq_do_ioctl(client, cmd, (void __user *) arg);
+       mutex_unlock(&client->ioctl_mutex);
+       return ret;
 }
 
 #ifdef CONFIG_COMPAT
@@ -2337,7 +2346,8 @@ static int kernel_client_enqueue(int client, struct snd_seq_event *ev,
        if (! cptr->accept_output)
                result = -EPERM;
        else /* send it */
-               result = snd_seq_client_enqueue_event(cptr, ev, file, blocking, atomic, hop);
+               result = snd_seq_client_enqueue_event(cptr, ev, file, blocking,
+                                                     atomic, hop, NULL);
 
        snd_seq_client_unlock(cptr);
        return result;