Bug 238035 - Divide by zero in kern_fcntl_freebsd
Summary: Divide by zero in kern_fcntl_freebsd
Status: New
Alias: None
Product: Base System
Classification: Unclassified
Component: kern (show other bugs)
Version: CURRENT
Hardware: Any Any
: --- Affects Only Me
Assignee: freebsd-bugs mailing list
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2019-05-21 22:37 UTC by Andrew Turner
Modified: 2019-05-22 18:11 UTC (History)
2 users (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Andrew Turner freebsd_committer 2019-05-21 22:37:10 UTC
Syzkaller found the following divide by zero bug in kern_fcntl. It seems to be a problem with devfs as indicated by the struct statfs bsize came from.

Fatal trap 18: integer divide fault while in kernel mode
cpuid = 0; apic id = 00
instruction pointer     = 0x20:0xffffffff80fb00ea
stack pointer           = 0x28:0xfffffe001507c850
frame pointer           = 0x28:0xfffffe001507c8f0
code segment            = base rx0, limit 0xfffff, type 0x1b
                        = DPL 0, pres 1, long 1, def32 0, gran 1
processor eflags        = interrupt enabled, resume, IOPL = 0
current process         = 718 (syz-executor.3)
trap number             = 18
panic: integer divide fault
cpuid = 0
time = 1558477383
KDB: stack backtrace:
db_trace_self_wrapper() at db_trace_self_wrapper+0x47/frame 0xfffffe001507c520
vpanic() at vpanic+0x1e0/frame 0xfffffe001507c580
panic() at panic+0x43/frame 0xfffffe001507c5e0
trap_fatal() at trap_fatal+0x4c6/frame 0xfffffe001507c660
trap() at trap+0xba/frame 0xfffffe001507c780
calltrap() at calltrap+0x8/frame 0xfffffe001507c780
--- trap 0x12, rip = 0xffffffff80fb00ea, rsp = 0xfffffe001507c850, rbp = 0xfffffe001507c8f0 ---
kern_fcntl() at kern_fcntl+0x9aa/frame 0xfffffe001507c8f0
kern_fcntl_freebsd() at kern_fcntl_freebsd+0x14f/frame 0xfffffe001507c980
amd64_syscall() at amd64_syscall+0x436/frame 0xfffffe001507cab0
fast_syscall_common() at fast_syscall_common+0x101/frame 0xfffffe001507cab0
--- syscall (198, FreeBSD ELF64, nosys), rip = 0x41331a, rsp = 0x7fffdfffdf38, rbp = 0x3 ---
Uptime: 30s
netdump: overwriting mbuf zone pointers
netdump in progress. searching for server...
netdumping to 169.254.0.1 (02:82:93:04:a7:00)
Dumping 100 out of 465 MB:..16%..32%..48%..64%..80%..96%

__curthread () at /usr/home/andrew/head-git/sys/amd64/include/pcpu.h:246
246             __asm("movq %%gs:%P1,%0" : "=r" (td) : "n" (OFFSETOF_CURTHREAD));
(kgdb) bt
#0  __curthread () at /usr/home/andrew/head-git/sys/amd64/include/pcpu.h:246
#1  doadump (textdump=1) at /usr/home/andrew/head-git/sys/kern/kern_shutdown.c:383
#2  0xffffffff81032217 in kern_reboot (howto=260) at /usr/home/andrew/head-git/sys/kern/kern_shutdown.c:470
#3  0xffffffff81032825 in vpanic (fmt=<optimized out>, ap=<optimized out>) at /usr/home/andrew/head-git/sys/kern/kern_shutdown.c:896
#4  0xffffffff81032473 in panic (fmt=<unavailable>) at /usr/home/andrew/head-git/sys/kern/kern_shutdown.c:823
#5  0xffffffff816d13d6 in trap_fatal (frame=0xfffffe001507c790, eva=0) at /usr/home/andrew/head-git/sys/amd64/amd64/trap.c:946
#6  0xffffffff816d004a in trap (frame=<optimized out>) at /usr/home/andrew/head-git/sys/amd64/amd64/trap.c:218
#7  <signal handler called>
#8  0xffffffff80fb00ea in kern_fcntl (td=0xfffff80008265000, fd=<optimized out>, cmd=<optimized out>, arg=0)
    at /usr/home/andrew/head-git/sys/kern/kern_descrip.c:783
#9  0xffffffff80faf66f in kern_fcntl_freebsd (td=<optimized out>, fd=<optimized out>, cmd=15, arg=0) at /usr/home/andrew/head-git/sys/kern/kern_descrip.c:467
#10 0xffffffff816d25d6 in syscallenter (td=0xfffff80008265000) at /usr/home/andrew/head-git/sys/amd64/amd64/../../kern/subr_syscall.c:135
#11 amd64_syscall (td=0xfffff80008265000, traced=0) at /usr/home/andrew/head-git/sys/amd64/amd64/trap.c:1166
#12 <signal handler called>
#13 0x000000000041331a in ?? ()
Backtrace stopped: Cannot access memory at address 0x7fffdfffdf38
(kgdb) up 8
#8  0xffffffff80fb00ea in kern_fcntl (td=0xfffff80008265000, fd=<optimized out>, cmd=<optimized out>, arg=0)
    at /usr/home/andrew/head-git/sys/kern/kern_descrip.c:783
783                             fp->f_seqcount = (arg + bsize - 1) / bsize;
(kgdb) p bsize
$1 = 0
(kgdb)
Comment 1 Conrad Meyer freebsd_committer 2019-05-22 00:18:16 UTC
Looks like F_READAHEAD on devfs.  The problematic code is a handcoded roundup(arg, bsize).  It seems like possible solutions would be either not rounding the arg when bsize==0, or EINVALing the request.
Comment 2 Andrew Turner freebsd_committer 2019-05-22 13:44:26 UTC
The reproducer Syzkaller found is below.

// autogenerated by syzkaller (https://github.com/google/syzkaller)

#define _GNU_SOURCE

#include <pwd.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/endian.h>
#include <sys/syscall.h>
#include <unistd.h>

uint64_t r[2] = {0xffffffffffffffff, 0xffffffffffffffff};

int main(void)
{
  syscall(SYS_mmap, 0x20000000, 0x1000000, 3, 0x1012, -1, 0);
  intptr_t res = 0;
  memcpy((void*)0x20000040, "/dev///\000", 8);
  res = syscall(SYS_openat, 0xffffffffffffff9c, 0x20000040, 0, 0);
  if (res != -1)
    r[0] = res;
  res = syscall(SYS_dup2, r[0], r[0]);
  if (res != -1)
    r[1] = res;
  syscall(SYS_fcntl, r[1], 0xf, 0);
  return 0;
}
Comment 3 Conrad Meyer freebsd_committer 2019-05-22 17:41:52 UTC
0xffffffffffffff9c is just AT_FDCWD, so this is essentially just open("/dev/", O_RDONLY) and then fcntl(F_READAHEAD==0xf), I think.
Comment 4 Conrad Meyer freebsd_committer 2019-05-22 17:49:55 UTC
Smaller repro:

#include <fcntl.h>

int
main(int argc, char **argv)
{
        int fd;

        (void)argc;
        (void)argv;

        fd = open("/dev/", O_RDONLY);
        fcntl(fd, F_READAHEAD, 0);
        return (0);
}
Comment 5 Conrad Meyer freebsd_committer 2019-05-22 18:11:32 UTC
Hm, devfs_statfs() does fill f_iosize, and it looks like we did that, so how is this dividing by zero?

0xfffff8000377d000 devfs on /dev (devfs)
    mnt_flag = MULTILABEL, LOCAL
    mnt_kern_flag = EXTENDED_SHARED, LOOKUP_SHARED
    mnt_opt =
    mnt_stat = { version=538182936 type=113 flags=0x0000000000000000 bsize=512 iosize=512 blocks=2 bfree=0 bavail=0 files=0 ffree=0 syncwrites=0 asyncwrites=0 syncreads=0 asyncreads=0 namemax=255 owner=0 fsid=[1895890688, 113] }

It's definitely "Fatal trap 18: integer divide fault while in kernel mode" and
that line, though.