[media] media: usbtv: prevent access to free'd resources
authorMatthew Leach <matthew@mattleach.net>
Fri, 8 Jul 2016 12:04:27 +0000 (09:04 -0300)
committerMauro Carvalho Chehab <mchehab@s-opensource.com>
Tue, 12 Jul 2016 16:31:08 +0000 (13:31 -0300)
When disconnecting the usbtv device, the sound card is unregistered
from ALSA and the snd member of the usbtv struct is set to NULL.  If
the usbtv snd_trigger work is running, this can cause a race condition
where the kernel will attempt to access free'd resources, shown in
[1].

This patch fixes the disconnection code by cancelling any snd_trigger
work before unregistering the sound card from ALSA and checking that
the snd member still exists in the work function.

[1]:
 usb 3-1.2: USB disconnect, device number 6
 BUG: unable to handle kernel NULL pointer dereference at 0000000000000008
 IP: [<ffffffff81093850>] process_one_work+0x30/0x480
 PGD 405bbf067 PUD 405bbe067 PMD 0
 Call Trace:
  [<ffffffff81093ce8>] worker_thread+0x48/0x4e0
  [<ffffffff81093ca0>] ? process_one_work+0x480/0x480
  [<ffffffff81093ca0>] ? process_one_work+0x480/0x480
  [<ffffffff81099998>] kthread+0xd8/0xf0
  [<ffffffff815c73c2>] ret_from_fork+0x22/0x40
  [<ffffffff810998c0>] ? kthread_worker_fn+0x170/0x170
 ---[ end trace 0f3dac5c1a38e610 ]---

Signed-off-by: Matthew Leach <matthew@mattleach.net>
Tested-by: Peter Sutton <foxxy@foxdogstudios.com>
Cc: stable@vger.kernel.org
Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@s-opensource.com>
drivers/media/usb/usbtv/usbtv-audio.c

index d4b4db302f96496dffcbccb18ec70f55a4fc7e48..1965ff1b1f12141bd7248f9eb3ea02186335eef9 100644 (file)
@@ -292,6 +292,9 @@ static void snd_usbtv_trigger(struct work_struct *work)
 {
        struct usbtv *chip = container_of(work, struct usbtv, snd_trigger);
 
+       if (!chip->snd)
+               return;
+
        if (atomic_read(&chip->snd_stream))
                usbtv_audio_start(chip);
        else
@@ -392,6 +395,8 @@ err:
 
 void usbtv_audio_free(struct usbtv *usbtv)
 {
+       cancel_work_sync(&usbtv->snd_trigger);
+
        if (usbtv->snd && usbtv->udev) {
                snd_card_free(usbtv->snd);
                usbtv->snd = NULL;