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.