Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 14 Aug 2019 01:10:10 +0530
From:      Neeraj Pal <neerajpal09@gmail.com>
To:        freebsd-arch@freebsd.org
Subject:   Regarding the bug in FreeBSD kernel driver(s)
Message-ID:  <CANi4_RUcNt8Z0Gw1DqoOCAYt61kfhv2aoz1v9snrB_Jg38z_zQ@mail.gmail.com>

next in thread | raw e-mail | index | archive | help
Hi there,

After discussing the issue with the security-team, I have posted it
publicly.

Please find the bug information given below with workaround diff:

I have observed the "NULL pointer dereference" bug inside the FreeBSD
kernel driver code due to which kernel gets in panic (or DOS) mode and then
it has to reboot.

Actually, this vulnerability resides in lots of kernel drivers like
"uhub0", "ubt0", "umass0", "run0", "uhid0" etc.

I have tested and observed the panic for following kernel drivers:

    - usb,
    - umass (storage),
    - ubt(bluetooth),
    - run0(wifi),
    - uhid


Please find the PoC in the link:
https://www.dropbox.com/s/hd1c0a518nrw749/crash_poc_info.tar.gz?dl=0

I have observed that the devices which are using the structure
"usb_attach_arg" with function (or api) device_get_ivars(9) as mentioned
below:
"struct usb_attach_arg *uaa = device_get_ivars(dev)"
are prone to "NULL Pointer Dereference" vulnerability as there is no check
for the same and the API device_get_ivars(9) is returning NULL.

There are still lots of drivers which are lacking this NULL pointer
dereference check but due to unavailability of devices I am not able to
test the drivers. However, I am sure about others also.

I have tested the following FreeBSD kernel versions:
- FreeBSD 13-CURRENT, amd64
- FreeBSD 12-RELEASE, amd64
- FreeBSD 12-STABLE, amd64

[Problem Description]
function (or API) device_get_ivars(9) from the file
"/usr/src/sys/kern/subr_bus.c" returns a NULL pointer, which get assigned
to *uaa structure object (function "uhub_probe" from file
"/usr/src/sys/dev/usb/usb_bus.c"),
then, after that there is a if-else condition which is checking the
usb_mode from that structure and there panic occurs due to dereferencing
the NULL pointer
Same valid for other kernel drivers.

[Background]
devctl disable device: it is expected to disable the given device, and
devctl enable device: it is supposed to enable the given disabled device.

Panic occurs here, after enabling the already disabled device (but only
with usb related drivers)


[Impact]
Puts FreeBSD OS/Kernel in panic mode and then to operate it, a reboot is
required.

[Privilege]
Root privilege is required.

[Reproducibility]
Reproducibility is 100%

[Workaround/Patch]
Please find the attached patch for the file "usb_hub.c", "ng_ubt.c",
"if_run.c", "umass.c" and "uhid.c"
After appyling the patch, it first returns the "ENXIO" as mentioned in the
patch code then later invocation returns "EBUSY" as device is enabled,
which can be verified by disabling it again.


diff -ruN freebsd_orig/sys/dev/usb/input/uhid.c
freebsd_13/sys/dev/usb/input/uhid.c
--- freebsd_orig/sys/dev/usb/input/uhid.c    2019-08-05 09:46:11.170388578
+0530
+++ freebsd_13/sys/dev/usb/input/uhid.c    2019-08-05 10:28:45.305146412
+0530
@@ -677,6 +677,9 @@
     int error;
     void *buf;
     uint16_t len;
+
+    if (uaa == NULL)
+        return (ENXIO);

     DPRINTFN(11, "\n");

diff -ruN freebsd_orig/sys/dev/usb/storage/umass.c
freebsd_13/sys/dev/usb/storage/umass.c
--- freebsd_orig/sys/dev/usb/storage/umass.c    2019-08-05
10:13:33.378982245 +0530
+++ freebsd_13/sys/dev/usb/storage/umass.c    2019-08-05 09:36:58.339765496
+0530
@@ -872,6 +872,9 @@
     struct usb_attach_arg *uaa = device_get_ivars(dev);
     struct umass_probe_proto temp;

+    if (uaa == NULL)
+        return (ENXIO);
+
     if (uaa->usb_mode != USB_MODE_HOST) {
         return (ENXIO);
     }
diff -ruN freebsd_orig/sys/dev/usb/usb_hub.c
freebsd_13/sys/dev/usb/usb_hub.c
--- freebsd_orig/sys/dev/usb/usb_hub.c    2019-08-05 10:25:57.276874204
+0530
+++ freebsd_13/sys/dev/usb/usb_hub.c    2019-08-05 08:18:08.537617812 +0530
@@ -1111,6 +1111,9 @@
 {
     struct usb_attach_arg *uaa = device_get_ivars(dev);

+    if (uaa == NULL)
+        return (ENXIO);
+
     if (uaa->usb_mode != USB_MODE_HOST)
         return (ENXIO);

diff -ruN freebsd_orig/sys/dev/usb/wlan/if_run.c
freebsd_13/sys/dev/usb/wlan/if_run.c
--- freebsd_orig/sys/dev/usb/wlan/if_run.c    2019-08-05 10:13:33.398982487
+0530
+++ freebsd_13/sys/dev/usb/wlan/if_run.c    2019-08-05 09:36:08.982716789
+0530
@@ -720,6 +720,8 @@
 {
     struct usb_attach_arg *uaa = device_get_ivars(self);

+    if (uaa == NULL)
+        return (ENXIO);
     if (uaa->usb_mode != USB_MODE_HOST)
         return (ENXIO);
     if (uaa->info.bConfigIndex != 0)
diff -ruN freebsd_orig/sys/netgraph/bluetooth/drivers/ubt/ng_ubt.c
freebsd_13/sys/netgraph/bluetooth/drivers/ubt/ng_ubt.c
--- freebsd_orig/sys/netgraph/bluetooth/drivers/ubt/ng_ubt.c    2019-08-05
10:13:33.318981516 +0530
+++ freebsd_13/sys/netgraph/bluetooth/drivers/ubt/ng_ubt.c    2019-08-05
09:33:46.941575792 +0530
@@ -513,6 +513,9 @@
     struct usb_attach_arg    *uaa = device_get_ivars(dev);
     int error;

+    if (uaa == NULL)
+        return (ENXIO);
+
     if (uaa->usb_mode != USB_MODE_HOST)
         return (ENXIO);

I have manually applied necessary checks for this bug to 5 device driver
code (names are mentioned above)

I have also tried to directly check for "NULL pointer" in the function
"device_get_ivars(dev)" but it is returning directly to the structure as
mentioned above but still a check is required to be at driver code for
structure pointer, that is, *uaa. So, it wasn't working.

Now, either we have to manually patch every driver files which are using
the above structure with function "device_get_ivars(dev)" or we have to
come up with the one line solution for all drivers.

[PoC/Log File/binary]
Please find the attached tar file in the link (
https://www.dropbox.com/s/hd1c0a518nrw749/crash_poc_info.tar.gz?dl=0) for
PoC code, Log Files and PoC binary


[Steps to Reproduce]
- untar the file "crash_poc_info.tar.gz"
- directly load binary from the directory "crash_poc/" or "make" it then
load it
- usage: ./crash_poc_bin <device_name>
-- For example: ./crash_bin_poc uhub0 or ./crash_bin_poc ubt0


[Attached Files]
- Log crash info: "crash_13-current_info/"
- PoC src and binary: "crash_poc_codeWith_binary/"
- patch: "patch_info/"


[Actual results]
Panic Log as follows:

freebsd dumped core - see ./vmcore.0

Sat Aug  3 17:31:43 UTC 2019

FreeBSD freebsd 13.0-CURRENT FreeBSD 13.0-CURRENT r350103 GENERIC  amd64

panic: page fault

GNU gdb (GDB) 8.3 [GDB v8.3 for FreeBSD]
Copyright (C) 2019 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html
>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-portbld-freebsd13.0".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
    <http://www.gnu.org/software/gdb/documentation/>.

For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from /boot/kernel/kernel...
Reading symbols from /usr/lib/debug//boot/kernel/kernel.debug...

Unread portion of the kernel message buffer:

Fatal trap 12: page fault while in kernel mode
cpuid = 3; apic id = 03
fault virtual address   = 0x38
fault code              = supervisor read data, page not present
instruction pointer     = 0x20:0xffffffff80a0ace1
stack pointer           = 0x28:0xfffffe0017f97510
frame pointer           = 0x28:0xfffffe0017f97510
code segment            = base 0x0, limit 0xfffff, type 0x1b
                        = DPL 0, pres 1, long 1, def32 0, gran 1
processor eflags        = interrupt enabled, resume, IOPL = 0
current process         = 1005 (devctl)
trap number             = 12
panic: page fault
cpuid = 3
time = 1564812037
KDB: stack backtrace:
db_trace_self_wrapper() at db_trace_self_wrapper+0x2b/frame
0xfffffe0017f971d0
vpanic() at vpanic+0x19d/frame 0xfffffe0017f97220
panic() at panic+0x43/frame 0xfffffe0017f97280
trap_fatal() at trap_fatal+0x39c/frame 0xfffffe0017f972e0
trap_pfault() at trap_pfault+0x62/frame 0xfffffe0017f97330
trap() at trap+0x2b4/frame 0xfffffe0017f97440
calltrap() at calltrap+0x8/frame 0xfffffe0017f97440
--- trap 0xc, rip = 0xffffffff80a0ace1, rsp = 0xfffffe0017f97510, rbp =
0xfffffe0017f97510 ---
uhub_probe() at uhub_probe+0x11/frame 0xfffffe0017f97510
device_probe_child() at device_probe_child+0x194/frame 0xfffffe0017f97570
device_probe() at device_probe+0x98/frame 0xfffffe0017f975a0
device_probe_and_attach() at device_probe_and_attach+0x32/frame
0xfffffe0017f975d0
devctl2_ioctl() at devctl2_ioctl+0x5e2/frame 0xfffffe0017f976a0
devfs_ioctl() at devfs_ioctl+0xca/frame 0xfffffe0017f976f0
VOP_IOCTL_APV() at VOP_IOCTL_APV+0x63/frame 0xfffffe0017f97710
vn_ioctl() at vn_ioctl+0x13d/frame 0xfffffe0017f97820
devfs_ioctl_f() at devfs_ioctl_f+0x1f/frame 0xfffffe0017f97840
kern_ioctl() at kern_ioctl+0x28a/frame 0xfffffe0017f978b0
sys_ioctl() at sys_ioctl+0x15d/frame 0xfffffe0017f97980
amd64_syscall() at amd64_syscall+0x2bb/frame 0xfffffe0017f97ab0
fast_syscall_common() at fast_syscall_common+0x101/frame 0xfffffe0017f97ab0
--- syscall (54, FreeBSD ELF64, sys_ioctl), rip = 0x80041a31a, rsp =
0x7fffffffea38, rbp = 0x7fffffffeaf0 ---
KDB: enter: panic

__curthread () at /usr/src/sys/amd64/include/pcpu.h:246
warning: Source file is more recent than executable.
246             __asm("movq %%gs:%P1,%0" : "=r" (td) : "n"
(OFFSETOF_CURTHREAD));
(kgdb) #0  __curthread () at /usr/src/sys/amd64/include/pcpu.h:246
#1  doadump (textdump=0) at /usr/src/sys/kern/kern_shutdown.c:392
#2  0xffffffff8049d27b in db_dump (dummy=<optimized out>,
    dummy2=<optimized out>, dummy3=<unavailable>, dummy4=<unavailable>)
    at /usr/src/sys/ddb/db_command.c:575
#3  0xffffffff8049d049 in db_command (last_cmdp=<optimized out>,
    cmd_table=<optimized out>, dopager=1) at
/usr/src/sys/ddb/db_command.c:482
#4  0xffffffff8049cdc4 in db_command_loop ()
    at /usr/src/sys/ddb/db_command.c:535
#5  0xffffffff8049ff6f in db_trap (type=<optimized out>, code=<optimized
out>)
    at /usr/src/sys/ddb/db_main.c:252
#6  0xffffffff80c1522c in kdb_trap (type=3, code=0, tf=<optimized out>)
    at /usr/src/sys/kern/subr_kdb.c:692
#7  0xffffffff81099ba1 in trap (frame=0xfffffe0017f97100)
    at /usr/src/sys/amd64/amd64/trap.c:621
#8  <signal handler called>
#9  kdb_enter (why=0xffffffff8132cf49 "panic", msg=<optimized out>)
    at /usr/src/sys/kern/subr_kdb.c:479
#10 0xffffffff80bcad6a in vpanic (fmt=<optimized out>, ap=<optimized out>)
    at /usr/src/sys/kern/kern_shutdown.c:894
#11 0xffffffff80bcaae3 in panic (
    fmt=0xffffffff81e88898 <cnputs_mtx> "\233\036/\201\377\377\377\377")
    at /usr/src/sys/kern/kern_shutdown.c:832
#12 0xffffffff81099ffc in trap_fatal (frame=0xfffffe0017f97450, eva=56)
    at /usr/src/sys/amd64/amd64/trap.c:943
#13 0xffffffff8109a062 in trap_pfault (frame=0xfffffe0017f97450,
    usermode=<optimized out>) at /usr/src/sys/amd64/amd64/trap.c:767
#14 0xffffffff81099644 in trap (frame=0xfffffe0017f97450)
    at /usr/src/sys/amd64/amd64/trap.c:443
#15 <signal handler called>
#16 uhub_probe (dev=<optimized out>) at /usr/src/sys/dev/usb/usb_hub.c:1114
#17 0xffffffff80c02574 in DEVICE_PROBE (dev=<optimized out>)
    at ./device_if.h:115
#18 device_probe_child (dev=0xfffff80003242b00, child=0xfffff800031ff400)
    at /usr/src/sys/kern/subr_bus.c:2150
#19 0xffffffff80c03388 in device_probe (dev=0xfffff800031ff400)
    at /usr/src/sys/kern/subr_bus.c:2897
#20 0xffffffff80c03442 in device_probe_and_attach (dev=0xfffff800031ff400)
    at /usr/src/sys/kern/subr_bus.c:2921
#21 0xffffffff80c08f22 in devctl2_ioctl (cdev=<optimized out>,
    cmd=<optimized out>, data=0xfffff8002bf80200 "uhub0",
    fflag=<optimized out>, td=<optimized out>)
    at /usr/src/sys/kern/subr_bus.c:1776
#22 0xffffffff80a8622a in devfs_ioctl (ap=0xfffffe0017f97728)
    at /usr/src/sys/fs/devfs/devfs_vnops.c:834
#23 0xffffffff81220263 in VOP_IOCTL_APV (
    vop=0xffffffff81aeb458 <devfs_specops>, a=0xfffffe0017f97728)
    at vnode_if.c:1052
#24 0xffffffff80cb062d in vn_ioctl (fp=0xfffff80003cce960,
    com=<optimized out>, data=0xfffff8002bf80200,
    active_cred=0xfffff800032ba500, td=0x246)
    at /usr/src/sys/kern/vfs_vnops.c:1492
#25 0xffffffff80a868bf in devfs_ioctl_f (fp=0xfffff800031ff400,
    com=18446744071590345336, data=0x18, cred=0x0, td=0xfffff8002b3d1000)
    at /usr/src/sys/fs/devfs/devfs_vnops.c:766
#26 0xffffffff80c3ae2a in fo_ioctl (fp=<optimized out>, com=<optimized out>,
    data=0xffffffff81fd09d0 <w_locklistdata+265744>, active_cred=0x0,
    td=<optimized out>) at /usr/src/sys/sys/file.h:333
#27 kern_ioctl (td=<optimized out>, fd=<optimized out>, com=2157462531,
    data=0xffffffff81fd09d0 <w_locklistdata+265744> "")
    at /usr/src/sys/kern/sys_generic.c:800
#28 0xffffffff80c3ab2d in sys_ioctl (td=0xfffff8002b3d1000,
    uap=0xfffff8002b3d13c8) at /usr/src/sys/kern/sys_generic.c:712
#29 0xffffffff8109ab2b in syscallenter (td=0xfffff8002b3d1000)
    at /usr/src/sys/amd64/amd64/../../kern/subr_syscall.c:144
#30 amd64_syscall (td=0xfffff8002b3d1000, traced=0)
    at /usr/src/sys/amd64/amd64/trap.c:1180
#31 <signal handler called>
#32 0x000000080041a31a in ?? ()
Backtrace stopped: Cannot access memory at address 0x7fffffffea38
(kgdb)

Please confirm and let me know if any other info required.

-- 

Thank you!
Sincere regards,
Neeraj Pal



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?CANi4_RUcNt8Z0Gw1DqoOCAYt61kfhv2aoz1v9snrB_Jg38z_zQ>