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>