Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3a2a5e19 authored by Takashi Iwai's avatar Takashi Iwai Committed by Greg Kroah-Hartman
Browse files

ALSA: rawmidi: Access runtime->avail always in spinlock



commit 88a06d6fd6b369d88cec46c62db3e2604a2f50d5 upstream.

The runtime->avail field may be accessed concurrently while some
places refer to it without taking the runtime->lock spinlock, as
detected by KCSAN.  Usually this isn't a big problem, but for
consistency and safety, we should take the spinlock at each place
referencing this field.

Reported-by: default avatar <syzbot+a23a6f1215c84756577c@syzkaller.appspotmail.com>
Reported-by: default avatar <syzbot+3d367d1df1d2b67f5c19@syzkaller.appspotmail.com>
Link: https://lore.kernel.org/r/20201206083527.21163-1-tiwai@suse.de


Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 8d2204a0
Loading
Loading
Loading
Loading
+35 −14
Original line number Diff line number Diff line
@@ -72,11 +72,21 @@ static inline unsigned short snd_rawmidi_file_flags(struct file *file)
	}
}

static inline int snd_rawmidi_ready(struct snd_rawmidi_substream *substream)
static inline bool __snd_rawmidi_ready(struct snd_rawmidi_runtime *runtime)
{
	return runtime->avail >= runtime->avail_min;
}

static bool snd_rawmidi_ready(struct snd_rawmidi_substream *substream)
{
	struct snd_rawmidi_runtime *runtime = substream->runtime;
	unsigned long flags;
	bool ready;

	return runtime->avail >= runtime->avail_min;
	spin_lock_irqsave(&runtime->lock, flags);
	ready = __snd_rawmidi_ready(runtime);
	spin_unlock_irqrestore(&runtime->lock, flags);
	return ready;
}

static inline int snd_rawmidi_ready_append(struct snd_rawmidi_substream *substream,
@@ -945,7 +955,7 @@ int snd_rawmidi_receive(struct snd_rawmidi_substream *substream,
	if (result > 0) {
		if (runtime->event)
			schedule_work(&runtime->event_work);
		else if (snd_rawmidi_ready(substream))
		else if (__snd_rawmidi_ready(runtime))
			wake_up(&runtime->sleep);
	}
	spin_unlock_irqrestore(&runtime->lock, flags);
@@ -1024,7 +1034,7 @@ static ssize_t snd_rawmidi_read(struct file *file, char __user *buf, size_t coun
	result = 0;
	while (count > 0) {
		spin_lock_irq(&runtime->lock);
		while (!snd_rawmidi_ready(substream)) {
		while (!__snd_rawmidi_ready(runtime)) {
			wait_queue_entry_t wait;

			if ((file->f_flags & O_NONBLOCK) != 0 || result > 0) {
@@ -1041,9 +1051,11 @@ static ssize_t snd_rawmidi_read(struct file *file, char __user *buf, size_t coun
				return -ENODEV;
			if (signal_pending(current))
				return result > 0 ? result : -ERESTARTSYS;
			if (!runtime->avail)
				return result > 0 ? result : -EIO;
			spin_lock_irq(&runtime->lock);
			if (!runtime->avail) {
				spin_unlock_irq(&runtime->lock);
				return result > 0 ? result : -EIO;
			}
		}
		spin_unlock_irq(&runtime->lock);
		count1 = snd_rawmidi_kernel_read1(substream,
@@ -1181,7 +1193,7 @@ int __snd_rawmidi_transmit_ack(struct snd_rawmidi_substream *substream, int coun
	runtime->avail += count;
	substream->bytes += count;
	if (count > 0) {
		if (runtime->drain || snd_rawmidi_ready(substream))
		if (runtime->drain || __snd_rawmidi_ready(runtime))
			wake_up(&runtime->sleep);
	}
	return count;
@@ -1370,9 +1382,11 @@ static ssize_t snd_rawmidi_write(struct file *file, const char __user *buf,
				return -ENODEV;
			if (signal_pending(current))
				return result > 0 ? result : -ERESTARTSYS;
			if (!runtime->avail && !timeout)
				return result > 0 ? result : -EIO;
			spin_lock_irq(&runtime->lock);
			if (!runtime->avail && !timeout) {
				spin_unlock_irq(&runtime->lock);
				return result > 0 ? result : -EIO;
			}
		}
		spin_unlock_irq(&runtime->lock);
		count1 = snd_rawmidi_kernel_write1(substream, buf, NULL, count);
@@ -1452,6 +1466,7 @@ static void snd_rawmidi_proc_info_read(struct snd_info_entry *entry,
	struct snd_rawmidi *rmidi;
	struct snd_rawmidi_substream *substream;
	struct snd_rawmidi_runtime *runtime;
	unsigned long buffer_size, avail, xruns;

	rmidi = entry->private_data;
	snd_iprintf(buffer, "%s\n\n", rmidi->name);
@@ -1470,13 +1485,16 @@ static void snd_rawmidi_proc_info_read(struct snd_info_entry *entry,
				    "  Owner PID    : %d\n",
				    pid_vnr(substream->pid));
				runtime = substream->runtime;
				spin_lock_irq(&runtime->lock);
				buffer_size = runtime->buffer_size;
				avail = runtime->avail;
				spin_unlock_irq(&runtime->lock);
				snd_iprintf(buffer,
				    "  Mode         : %s\n"
				    "  Buffer size  : %lu\n"
				    "  Avail        : %lu\n",
				    runtime->oss ? "OSS compatible" : "native",
				    (unsigned long) runtime->buffer_size,
				    (unsigned long) runtime->avail);
				    buffer_size, avail);
			}
		}
	}
@@ -1494,13 +1512,16 @@ static void snd_rawmidi_proc_info_read(struct snd_info_entry *entry,
					    "  Owner PID    : %d\n",
					    pid_vnr(substream->pid));
				runtime = substream->runtime;
				spin_lock_irq(&runtime->lock);
				buffer_size = runtime->buffer_size;
				avail = runtime->avail;
				xruns = runtime->xruns;
				spin_unlock_irq(&runtime->lock);
				snd_iprintf(buffer,
					    "  Buffer size  : %lu\n"
					    "  Avail        : %lu\n"
					    "  Overruns     : %lu\n",
					    (unsigned long) runtime->buffer_size,
					    (unsigned long) runtime->avail,
					    (unsigned long) runtime->xruns);
					    buffer_size, avail, xruns);
			}
		}
	}