From owner-svn-src-head@FreeBSD.ORG Tue Oct 2 12:03:03 2012 Return-Path: Delivered-To: svn-src-head@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id 2DAE71065695; Tue, 2 Oct 2012 12:03:03 +0000 (UTC) (envelope-from glebius@FreeBSD.org) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:4f8:fff6::2c]) by mx1.freebsd.org (Postfix) with ESMTP id 0E5F58FC1F; Tue, 2 Oct 2012 12:03:03 +0000 (UTC) Received: from svn.freebsd.org (localhost [127.0.0.1]) by svn.freebsd.org (8.14.4/8.14.4) with ESMTP id q92C32e5032698; Tue, 2 Oct 2012 12:03:02 GMT (envelope-from glebius@svn.freebsd.org) Received: (from glebius@localhost) by svn.freebsd.org (8.14.4/8.14.4/Submit) id q92C3282032695; Tue, 2 Oct 2012 12:03:02 GMT (envelope-from glebius@svn.freebsd.org) Message-Id: <201210021203.q92C3282032695@svn.freebsd.org> From: Gleb Smirnoff Date: Tue, 2 Oct 2012 12:03:02 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org X-SVN-Group: head MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cc: Subject: svn commit: r241129 - head/sys/netinet X-BeenThere: svn-src-head@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: SVN commit messages for the src tree for head/-current List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Tue, 02 Oct 2012 12:03:04 -0000 Author: glebius Date: Tue Oct 2 12:03:02 2012 New Revision: 241129 URL: http://svn.freebsd.org/changeset/base/241129 Log: There is a complex race in in_pcblookup_hash() and in_pcblookup_group(). Both functions need to obtain lock on the found PCB, and they can't do classic inter-lock with the PCB hash lock, due to lock order reversal. To keep the PCB stable, these functions put a reference on it and after PCB lock is acquired drop it. If the reference was the last one, this means we've raced with in_pcbfree() and the PCB is no longer valid. This approach works okay only if we are acquiring writer-lock on the PCB. In case of reader-lock, the following scenario can happen: - 2 threads locate pcb, and do in_pcbref() on it. - These 2 threads drop the inp hash lock. - Another thread comes to delete pcb via in_pcbfree(), it obtains hash lock, 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. - 2 aforementioned threads acquire reader lock on the pcb and run in_pcbrele_rlocked(). One gets 1 from in_pcbrele_rlocked() and continues, second gets 0 and considers pcb freed, returns. - The thread that got 1 continutes working with detached pcb, which later leads to panic in the underlying protocol level. To plumb that problem an additional INPCB flag introduced - INP_FREED. We check for that flag in the in_pcbrele_rlocked() and if it is set, we pretend that that was the last reference. Discussed with: rwatson, jhb Reported by: Vladimir Medvedkin Modified: head/sys/netinet/in_pcb.c head/sys/netinet/in_pcb.h Modified: head/sys/netinet/in_pcb.c ============================================================================== --- head/sys/netinet/in_pcb.c Tue Oct 2 10:09:23 2012 (r241128) +++ head/sys/netinet/in_pcb.c Tue Oct 2 12:03:02 2012 (r241129) @@ -1105,8 +1105,17 @@ in_pcbrele_rlocked(struct inpcb *inp) INP_RLOCK_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_RUNLOCK(inp); + return (1); + } return (0); + } KASSERT(inp->inp_socket == NULL, ("%s: inp_socket != NULL", __func__)); @@ -1186,6 +1195,7 @@ in_pcbfree(struct inpcb *inp) inp_freemoptions(inp->inp_moptions); #endif inp->inp_vflag = 0; + inp->inp_flags2 |= INP_FREED; crfree(inp->inp_cred); #ifdef MAC mac_inpcb_destroy(inp); Modified: head/sys/netinet/in_pcb.h ============================================================================== --- head/sys/netinet/in_pcb.h Tue Oct 2 10:09:23 2012 (r241128) +++ head/sys/netinet/in_pcb.h Tue Oct 2 12:03:02 2012 (r241129) @@ -542,6 +542,7 @@ void inp_4tuple_get(struct inpcb *inp, #define INP_RT_VALID 0x00000002 /* cached rtentry is valid */ #define INP_PCBGROUPWILD 0x00000004 /* in pcbgroup wildcard list */ #define INP_REUSEPORT 0x00000008 /* SO_REUSEPORT option is set */ +#define INP_FREED 0x00000010 /* inp itself is not valid */ /* * Flags passed to in_pcblookup*() functions.