]> Git Repo - linux.git/commitdiff
vhost/scsi: null-ptr-dereference in vhost_scsi_get_req()
authorHaoran Zhang <[email protected]>
Tue, 1 Oct 2024 20:14:15 +0000 (15:14 -0500)
committerMichael S. Tsirkin <[email protected]>
Mon, 7 Oct 2024 15:47:56 +0000 (11:47 -0400)
Since commit 3f8ca2e115e5 ("vhost/scsi: Extract common handling code
from control queue handler") a null pointer dereference bug can be
triggered when guest sends an SCSI AN request.

In vhost_scsi_ctl_handle_vq(), `vc.target` is assigned with
`&v_req.tmf.lun[1]` within a switch-case block and is then passed to
vhost_scsi_get_req() which extracts `vc->req` and `tpg`. However, for
a `VIRTIO_SCSI_T_AN_*` request, tpg is not required, so `vc.target` is
set to NULL in this branch. Later, in vhost_scsi_get_req(),
`vc->target` is dereferenced without being checked, leading to a null
pointer dereference bug. This bug can be triggered from guest.

When this bug occurs, the vhost_worker process is killed while holding
`vq->mutex` and the corresponding tpg will remain occupied
indefinitely.

Below is the KASAN report:
Oops: general protection fault, probably for non-canonical address
0xdffffc0000000000: 0000 [#1] PREEMPT SMP KASAN NOPTI
KASAN: null-ptr-deref in range [0x0000000000000000-0x0000000000000007]
CPU: 1 PID: 840 Comm: poc Not tainted 6.10.0+ #1
Hardware name: QEMU Ubuntu 24.04 PC (i440FX + PIIX, 1996), BIOS
1.16.3-debian-1.16.3-2 04/01/2014
RIP: 0010:vhost_scsi_get_req+0x165/0x3a0
Code: 00 fc ff df 48 89 fa 48 c1 ea 03 80 3c 02 00 0f 85 2b 02 00 00
48 b8 00 00 00 00 00 fc ff df 4d 8b 65 30 4c 89 e2 48 c1 ea 03 <0f> b6
04 02 4c 89 e2 83 e2 07 38 d0 7f 08 84 c0 0f 85 be 01 00 00
RSP: 0018:ffff888017affb50 EFLAGS: 00010246
RAX: dffffc0000000000 RBX: ffff88801b000000 RCX: 0000000000000000
RDX: 0000000000000000 RSI: 0000000000000000 RDI: ffff888017affcb8
RBP: ffff888017affb80 R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000000 R12: 0000000000000000
R13: ffff888017affc88 R14: ffff888017affd1c R15: ffff888017993000
FS:  000055556e076500(0000) GS:ffff88806b100000(0000) knlGS:0000000000000000
CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 00000000200027c0 CR3: 0000000010ed0004 CR4: 0000000000370ef0
Call Trace:
 <TASK>
 ? show_regs+0x86/0xa0
 ? die_addr+0x4b/0xd0
 ? exc_general_protection+0x163/0x260
 ? asm_exc_general_protection+0x27/0x30
 ? vhost_scsi_get_req+0x165/0x3a0
 vhost_scsi_ctl_handle_vq+0x2a4/0xca0
 ? __pfx_vhost_scsi_ctl_handle_vq+0x10/0x10
 ? __switch_to+0x721/0xeb0
 ? __schedule+0xda5/0x5710
 ? __kasan_check_write+0x14/0x30
 ? _raw_spin_lock+0x82/0xf0
 vhost_scsi_ctl_handle_kick+0x52/0x90
 vhost_run_work_list+0x134/0x1b0
 vhost_task_fn+0x121/0x350
...
 </TASK>
---[ end trace 0000000000000000 ]---

Let's add a check in vhost_scsi_get_req.

Fixes: 3f8ca2e115e5 ("vhost/scsi: Extract common handling code from control queue handler")
Signed-off-by: Haoran Zhang <[email protected]>
[whitespace fixes]
Signed-off-by: Mike Christie <[email protected]>
Message-Id: <b26d7ddd-b098-4361-88f8-17ca7f90adf7@oracle.com>
Signed-off-by: Michael S. Tsirkin <[email protected]>
drivers/vhost/scsi.c

index 006ffacf1c56cbecf92ca7cbd2f03ee31bde913f..c50f3fb49a668612e8e86098238309a2fc20a3e1 100644 (file)
@@ -1029,20 +1029,23 @@ vhost_scsi_get_req(struct vhost_virtqueue *vq, struct vhost_scsi_ctx *vc,
                /* virtio-scsi spec requires byte 0 of the lun to be 1 */
                vq_err(vq, "Illegal virtio-scsi lun: %u\n", *vc->lunp);
        } else {
-               struct vhost_scsi_tpg **vs_tpg, *tpg;
-
-               vs_tpg = vhost_vq_get_backend(vq);      /* validated at handler entry */
-
-               tpg = READ_ONCE(vs_tpg[*vc->target]);
-               if (unlikely(!tpg)) {
-                       vq_err(vq, "Target 0x%x does not exist\n", *vc->target);
-               } else {
-                       if (tpgp)
-                               *tpgp = tpg;
-                       ret = 0;
+               struct vhost_scsi_tpg **vs_tpg, *tpg = NULL;
+
+               if (vc->target) {
+                       /* validated at handler entry */
+                       vs_tpg = vhost_vq_get_backend(vq);
+                       tpg = READ_ONCE(vs_tpg[*vc->target]);
+                       if (unlikely(!tpg)) {
+                               vq_err(vq, "Target 0x%x does not exist\n", *vc->target);
+                               goto out;
+                       }
                }
-       }
 
+               if (tpgp)
+                       *tpgp = tpg;
+               ret = 0;
+       }
+out:
        return ret;
 }
 
This page took 0.063991 seconds and 4 git commands to generate.