Skip to content

Commit 81f5775

Browse files
committed
ALSA: rawmidi: Fix race at copying & updating the position
The rawmidi read and write functions manage runtime stream status such as runtime->appl_ptr and runtime->avail. These point where to copy the new data and how many bytes have been copied (or to be read). The problem is that rawmidi read/write call copy_from_user() or copy_to_user(), and the runtime spinlock is temporarily unlocked and relocked while copying user-space. Since the current code advances and updates the runtime status after the spin unlock/relock, the copy and the update may be asynchronous, and eventually runtime->avail might go to a negative value when many concurrent accesses are done. This may lead to memory corruption in the end. For fixing this race, in this patch, the status update code is performed in the same lock before the temporary unlock. Also, the spinlock is now taken more widely in snd_rawmidi_kernel_read1() for protecting more properly during the whole operation. BugLink: http://lkml.kernel.org/r/CACT4Y+b-dCmNf1GpgPKfDO0ih+uZCL2JV4__j-r1kdhPLSgQCQ@mail.gmail.com Reported-by: Dmitry Vyukov <[email protected]> Tested-by: Dmitry Vyukov <[email protected]> Cc: <[email protected]> Signed-off-by: Takashi Iwai <[email protected]>
1 parent 06ab300 commit 81f5775

File tree

1 file changed

+22
-12
lines changed

1 file changed

+22
-12
lines changed

sound/core/rawmidi.c

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -942,31 +942,36 @@ static long snd_rawmidi_kernel_read1(struct snd_rawmidi_substream *substream,
942942
unsigned long flags;
943943
long result = 0, count1;
944944
struct snd_rawmidi_runtime *runtime = substream->runtime;
945+
unsigned long appl_ptr;
945946

947+
spin_lock_irqsave(&runtime->lock, flags);
946948
while (count > 0 && runtime->avail) {
947949
count1 = runtime->buffer_size - runtime->appl_ptr;
948950
if (count1 > count)
949951
count1 = count;
950-
spin_lock_irqsave(&runtime->lock, flags);
951952
if (count1 > (int)runtime->avail)
952953
count1 = runtime->avail;
954+
955+
/* update runtime->appl_ptr before unlocking for userbuf */
956+
appl_ptr = runtime->appl_ptr;
957+
runtime->appl_ptr += count1;
958+
runtime->appl_ptr %= runtime->buffer_size;
959+
runtime->avail -= count1;
960+
953961
if (kernelbuf)
954-
memcpy(kernelbuf + result, runtime->buffer + runtime->appl_ptr, count1);
962+
memcpy(kernelbuf + result, runtime->buffer + appl_ptr, count1);
955963
if (userbuf) {
956964
spin_unlock_irqrestore(&runtime->lock, flags);
957965
if (copy_to_user(userbuf + result,
958-
runtime->buffer + runtime->appl_ptr, count1)) {
966+
runtime->buffer + appl_ptr, count1)) {
959967
return result > 0 ? result : -EFAULT;
960968
}
961969
spin_lock_irqsave(&runtime->lock, flags);
962970
}
963-
runtime->appl_ptr += count1;
964-
runtime->appl_ptr %= runtime->buffer_size;
965-
runtime->avail -= count1;
966-
spin_unlock_irqrestore(&runtime->lock, flags);
967971
result += count1;
968972
count -= count1;
969973
}
974+
spin_unlock_irqrestore(&runtime->lock, flags);
970975
return result;
971976
}
972977

@@ -1223,6 +1228,7 @@ static long snd_rawmidi_kernel_write1(struct snd_rawmidi_substream *substream,
12231228
unsigned long flags;
12241229
long count1, result;
12251230
struct snd_rawmidi_runtime *runtime = substream->runtime;
1231+
unsigned long appl_ptr;
12261232

12271233
if (!kernelbuf && !userbuf)
12281234
return -EINVAL;
@@ -1243,22 +1249,26 @@ static long snd_rawmidi_kernel_write1(struct snd_rawmidi_substream *substream,
12431249
count1 = count;
12441250
if (count1 > (long)runtime->avail)
12451251
count1 = runtime->avail;
1252+
1253+
/* update runtime->appl_ptr before unlocking for userbuf */
1254+
appl_ptr = runtime->appl_ptr;
1255+
runtime->appl_ptr += count1;
1256+
runtime->appl_ptr %= runtime->buffer_size;
1257+
runtime->avail -= count1;
1258+
12461259
if (kernelbuf)
1247-
memcpy(runtime->buffer + runtime->appl_ptr,
1260+
memcpy(runtime->buffer + appl_ptr,
12481261
kernelbuf + result, count1);
12491262
else if (userbuf) {
12501263
spin_unlock_irqrestore(&runtime->lock, flags);
1251-
if (copy_from_user(runtime->buffer + runtime->appl_ptr,
1264+
if (copy_from_user(runtime->buffer + appl_ptr,
12521265
userbuf + result, count1)) {
12531266
spin_lock_irqsave(&runtime->lock, flags);
12541267
result = result > 0 ? result : -EFAULT;
12551268
goto __end;
12561269
}
12571270
spin_lock_irqsave(&runtime->lock, flags);
12581271
}
1259-
runtime->appl_ptr += count1;
1260-
runtime->appl_ptr %= runtime->buffer_size;
1261-
runtime->avail -= count1;
12621272
result += count1;
12631273
count -= count1;
12641274
}

0 commit comments

Comments
 (0)