Casinos Not On GamstopNon Gamstop CasinosCasinos Not On GamstopOnline Casinos UKNon Gamstop Casino
2nd Jun 2001 [SBWID-223]
COMMAND
	    VFS
SYSTEMS AFFECTED
	    OpenBSD
PROBLEM
	    Alexander Viro  found following.   Let's start  with the  trivial:
	    good old aliasing bugs.
	    Example 1:
	
	        dup2() vs. close(). Relevant file: kern/kern_descrip.c
	        sys_dup2(p, v, retval)
	                struct proc *p;
	                void *v;
	                register_t *retval;
	        {
	        [snip]
	                if ((u_int)old >= fdp->fd_nfiles || fdp->fd_ofiles[old] == NULL ||
	                    (u_int)new >= p->p_rlimit[RLIMIT_NOFILE].rlim_cur ||
	                    (u_int)new >= maxfiles)
	                        return (EBADF);
	
	    OK, we've checked (among other things) that old is indeed opened.
	
	        [snip]
	                if (new >= fdp->fd_nfiles) {
	
	    We either expand a descriptor table
	
	        [snip]
	                } else {
	
	    Or reuse existing descriptor, closing file if it's opened.
	
	                        (void) fdrelease(p, new);
	
	    Which is the blocking operation, BTW.
	
	                }
	                return (finishdup(fdp, old, new, retval));
	        }
	        [snip]
	        finishdup(fdp, old, new, retval)
	                register struct filedesc *fdp;
	                register int old, new;
	                register_t *retval;
	        {
	                register struct file *fp;
	                fp = fdp->fd_ofiles[old];
	
	    Got the struct sile of the file we are trying to dup...
	
	                if (fp->f_count == LONG_MAX-2)
	
	    ... and dereference it. We had checked that it's non-NULL, right?
	    Wrong.  Another thread might be sharing our descriptor table  (man
	    rfork).  IOW, fdp points to shared data structure.  So we had done
	    the equivalent of
	
		        if (global_var) {
			        blocking_call();
			        if (global_var->f_count)
				        ...
		        }
	
	    We have a  nice shiny race  between dup2(0,1); and  close(0).  And
	    it's a wide one.  Turning that into full-blown exploit is left  as
	    an exercise for readers.
	    Example 2:
	
	        pipe() vs. close() (kern/sys_pipe.c)
	        sys_opipe(p, v, retval)
	        [snip]
	                error = falloc(p, &rf, &fd);
	                if (error)
	                        goto free2;
	        [snip]
	                retval[0] = fd;
	                error = falloc(p, &wf, &fd);
	                if (error)
	                        goto free3;
	        [snip]
	                return (0);
	        free3:
	                ffree(rf);
	                fdremove(fdp, retval[0]);
	        free2:
	        [snip]
	
	    Think  what  happens  if  the  second  allocation  fails.  It is a
	    blocking  call.   During  that  time  another  thread  had  a nice
	    possibility to  call close(retval[0]);  since that  value is  very
	    easy  to  predict  -  it's  the  first  available file descriptor.
	    close() would
	        * remove pointer from fdp[retval[0]]
	        * call ffree() on it.
	    Now, we  come back  and do  _another_ ffree()  on the  poor beast.
	    Welcome to kernel panic...  Code is equivalent to
	
	        global_var = p = alloc_foo();
	        blocking_call();
	        release_foo(p);
	        global_var = NULL;
	
	    It's obviously broken - obviously for anyone with half of clue.
	    One can easily provide more examples  of the same crap and so  can
	    anyone who would bother to RTFS the descriptor handling in kern/*.
	    Apparently that had never happened during the last 5 years or so.
	    Not talking about the bugs that would require anything  nontrivial
	    to find and understand.  Just follow the yello^Wpiles of sloppy  C
	    and nearly every  one will turn  out to be  exploitable.  And  no,
	    it's  not  limited  to  descriptor   handling  -  same  goes   for
	    sys_pipe.c, etc.
SOLUTION
	    Nothing yet.
	

Internet highlights