media: si470x: Fix use-after-free in si470x_int_in_callback()
authorShigeru Yoshida <syoshida@redhat.com>
Tue, 22 Nov 2022 18:51:59 +0000 (03:51 +0900)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sat, 7 Jan 2023 11:07:33 +0000 (12:07 +0100)
[ Upstream commit 7d21e0b1b41b21d628bf2afce777727bd4479aa5 ]

syzbot reported use-after-free in si470x_int_in_callback() [1].  This
indicates that urb->context, which contains struct si470x_device
object, is freed when si470x_int_in_callback() is called.

The cause of this issue is that si470x_int_in_callback() is called for
freed urb.

si470x_usb_driver_probe() calls si470x_start_usb(), which then calls
usb_submit_urb() and si470x_start().  If si470x_start_usb() fails,
si470x_usb_driver_probe() doesn't kill urb, but it just frees struct
si470x_device object, as depicted below:

si470x_usb_driver_probe()
  ...
  si470x_start_usb()
    ...
    usb_submit_urb()
    retval = si470x_start()
    return retval
  if (retval < 0)
    free struct si470x_device object, but don't kill urb

This patch fixes this issue by killing urb when si470x_start_usb()
fails and urb is submitted.  If si470x_start_usb() fails and urb is
not submitted, i.e. submitting usb fails, it just frees struct
si470x_device object.

Reported-by: syzbot+9ca7a12fd736d93e0232@syzkaller.appspotmail.com
Link: https://syzkaller.appspot.com/bug?id=94ed6dddd5a55e90fd4bab942aa4bb297741d977
Signed-off-by: Shigeru Yoshida <syoshida@redhat.com>
Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
Signed-off-by: Sasha Levin <sashal@kernel.org>
drivers/media/radio/si470x/radio-si470x-usb.c

index a8a0ff9a1f83844eee94d3615be0c3ad3d4798a8..6724c5287cc37bb8597ae27708359e992fe15780 100644 (file)
@@ -741,8 +741,10 @@ static int si470x_usb_driver_probe(struct usb_interface *intf,
 
        /* start radio */
        retval = si470x_start_usb(radio);
-       if (retval < 0)
+       if (retval < 0 && !radio->int_in_running)
                goto err_buf;
+       else if (retval < 0)    /* in case of radio->int_in_running == 1 */
+               goto err_all;
 
        /* set initial frequency */
        si470x_set_freq(radio, 87.5 * FREQ_MUL); /* available in all regions */