[PATCH 3/6] ALSA: timer: fix lockless tu->tread read in snd_timer_user_read()
Omer Cohen <[email protected]> Fri, 26 Jun 2026 16:47:06 +0300
| Newsgroups | org.alsa-project.alsa-devel,org.kernel.vger.stable |
|---|---|
| Message-ID | <[email protected]> |
snd_timer_user_read() reads tu->tread without holding ioctl_lock to
determine the entry size (unit), then reads it again under ioctl_lock
to select the copy format. A concurrent SNDRV_TIMER_IOCTL_TREAD64
can change tu->tread between the two reads, causing a mismatch between
the entry size used for the read loop and the format used for the
actual copy.
Move the initial tu->tread read inside the ioctl_lock and cache the
value in a local variable for consistent use throughout the function.
Fixes: d11662f4f798 ("ALSA: timer: Fix race between read and ioctl")
Cc: [email protected]
Reported-by: Omer Cohen <[email protected]>
Signed-off-by: Omer Cohen <[email protected]>
---
sound/core/timer.c | 11 +++++++----
1 file changed, 7 insertions(+), 4 deletions(-)
diff --git a/sound/core/timer.c b/sound/core/timer.c
index 51c6ac4df9f4..XXXXXXXXXXXX 100644
--- a/sound/core/timer.c
+++ b/sound/core/timer.c
@@ -2390,12 +2390,15 @@ static ssize_t snd_timer_user_read(struct file *file, char __user *buffer,
struct snd_timer_tread32 tread32;
struct snd_timer_user *tu;
long result = 0, unit;
+ int tread_format;
int qhead;
int err = 0;
tu = file->private_data;
- switch (tu->tread) {
- case TREAD_FORMAT_TIME64:
+ mutex_lock(&tu->ioctl_lock);
+ tread_format = tu->tread;
+ switch (tread_format) {
+ case TREAD_FORMAT_TIME64:
unit = sizeof(struct snd_timer_tread64);
break;
case TREAD_FORMAT_TIME32:
@@ -2407,8 +2410,8 @@ static ssize_t snd_timer_user_read(struct file *file, char __user *buffer,
default:
WARN_ONCE(1, "Corrupt snd_timer_user\n");
+ mutex_unlock(&tu->ioctl_lock);
return -ENOTSUPP;
}
- mutex_lock(&tu->ioctl_lock);
spin_lock_irq(&tu->qlock);
while ((long)count - result >= unit) {
@@ -2453,7 +2456,7 @@ static ssize_t snd_timer_user_read(struct file *file, char __user *buffer,
tread = &tu->tqueue[qhead];
- switch (tu->tread) {
+ switch (tread_format) {
case TREAD_FORMAT_TIME64:
if (copy_to_user(buffer, tread,
sizeof(struct snd_timer_tread64)))
--
2.43.0