Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 25 Nov 2015 14:45:44 +0000 (UTC)
From:      Fabien Thomas <fabient@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r291301 - head/sys/netinet
Message-ID:  <201511251445.tAPEjilx016268@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: fabient
Date: Wed Nov 25 14:45:43 2015
New Revision: 291301
URL: https://svnweb.freebsd.org/changeset/base/291301

Log:
  The r241129 description was wrong that the scenario is possible
  only for read locks on pcbs. The same race can happen with write
  lock semantics as well.
  
  The race scenario:
  
  - Two threads (1 and 2) locate pcb with writer semantics (INPLOOKUP_WLOCKPCB)
   and do in_pcbref() on it.
  - 1 and 2 both drop the inp hash lock.
  - Another thread (3) grabs the inp hash lock. Then it runs in_pcbfree(),
   which wlocks the pcb. They must happen faster than 1 or 2 come INP_WLOCK()!
  - 1 and 2 congest in INP_WLOCK().
  - 3 does in_pcbremlists(), drops hash lock, and runs in_pcbrele_wlocked(),
   which doesn't free the pcb due to two references on it.
   Then it unlocks the pcb.
  - 1 (or 2) gets wlock on the pcb, runs in_pcbrele_wlocked(), which doesn't
   report inp as freed, due to 2 (or 1) still helding extra reference on it.
   The thread tries to do smth with a disconnected pcb and crashes.
  
  Submitted by:	emeric.poupon@stormshield.eu
  Reviewed by:	gleb@
  MFC after:	1 week
  Sponsored by: Stormshield
  Tested by: Cassiano Peixoto, Stormshield

Modified:
  head/sys/netinet/in_pcb.c

Modified: head/sys/netinet/in_pcb.c
==============================================================================
--- head/sys/netinet/in_pcb.c	Wed Nov 25 13:26:42 2015	(r291300)
+++ head/sys/netinet/in_pcb.c	Wed Nov 25 14:45:43 2015	(r291301)
@@ -1220,8 +1220,17 @@ in_pcbrele_wlocked(struct inpcb *inp)
 
 	INP_WLOCK_ASSERT(inp);
 
-	if (refcount_release(&inp->inp_refcount) == 0)
+	if (refcount_release(&inp->inp_refcount) == 0) {
+		/*
+		 * If the inpcb has been freed, let the caller know, even if
+		 * this isn't the last reference.
+		 */
+		if (inp->inp_flags2 & INP_FREED) {
+			INP_WUNLOCK(inp);
+			return (1);
+		}
 		return (0);
+	}
 
 	KASSERT(inp->inp_socket == NULL, ("%s: inp_socket != NULL", __func__));
 



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201511251445.tAPEjilx016268>