TCP socket shutdown race condition

Ed Maste emaste at sandvine.com
Wed Aug 20 15:41:52 PDT 2003


>Well, I guess the spl() fix is probably going to be the quickest here
>then, please send it to me once you've pounded on it, Ed.

So my spl() fix seems to eliminate the problem for me but while I'm looking
at this stuff I want to make sure there aren't any related cases left in.
My current patch is at the end of this message.

One potential problem is a crfree() interrupting code that's incrementing a
ucred reference.  For example, uipc_socket.c:socreate():

	so->so_cred = p->p_ucred;
	crhold(so->so_cred);

However, I don't think a crfree() interrupting between these should cause a
problem.  The refcount would always be at least 2 to begin with, so the
ucred wouldn't be free()d.

Another possible problem is a crfree() interrupting a crfree() for the same
ucred.  This would result in a double free, leading to who knows what
corruption.  I did try the test against a kernel with invariants on, but
nothing happened; presumably the timing changed enough that the race
condition wouldn't occur.

I protected the if (--cr->cr_ref == 0) in crfree() with splhigh() but left
the actuall free() out of splhigh.

The cr->cr_ref++ in crhold() assembles to an incl (%eax) which will be
atomic on a single processor.  However I don't think anything guarantees gcc
will emit that code for that source instruction, so I put a splhigh around
it too.  Probably atomic_add_int would be better -- I'll try that out too.

Our stress test is running against the patch below, and I'll report any
findings.  If you have any comments on this, please let me know.



Index: kern_prot.c
===================================================================
RCS file: /cvs/src/sys/kern/kern_prot.c,v
retrieving revision 1.53.2.9
diff -c -3 -r1.53.2.9 kern_prot.c
*** kern_prot.c 9 Mar 2002 05:20:26 -0000       1.53.2.9
--- kern_prot.c 20 Aug 2003 22:12:29 -0000
***************
*** 997,1003 ****
--- 997,1005 ----
  crhold(cr) 
          struct ucred *cr;
  {
+         int s = splhigh();
        cr->cr_ref++;
+         splx(s);
  }
  
  /*
***************
*** 1008,1017 ****
--- 1010,1021 ----
  crfree(cr)
        struct ucred *cr;
  {
+         int s = splhigh();
        if (cr->cr_ref == 0)
                panic("Freeing already free credential! %p", cr);
        
        if (--cr->cr_ref == 0) {
+                 splx(s);
                /*
                 * Some callers of crget(), such as nfs_statfs(),
                 * allocate a temporary credential, but don't
***************
*** 1020,1026 ****
--- 1024,1032 ----
                if (cr->cr_uidinfo != NULL)
                        uifree(cr->cr_uidinfo);
                FREE((caddr_t)cr, M_CRED);
+                 return;
        }
+         splx(s);
  }
  
  /*


More information about the freebsd-net mailing list