Re: ipfw: LOR/panic with uid rules

From: Robert Watson <rwatson_at_FreeBSD.org>
Date: Wed, 24 Sep 2008 09:08:13 +0100 (BST)
On Tue, 23 Sep 2008, Robert Watson wrote:

> On Tue, 23 Sep 2008, Stefan Ehmann wrote:
>
>> Also posted about this problem recently in stable_at_. But got no replies 
>> there. So I tried on a recent CURRENT but the problem persists:
>> 
>> ipfw rules using uid are causing a deadlock. eg. allow ip from any to any 
>> uid root A simple HTTP fetch triggers this problem nearly instantly.
>> 
>> For me, this problem existed in 6.x with PREEMPTION enabled. It was fixed 
>> in 7.0. But in RELENG_7 and head it's back. This is a single processor i386 
>> machine.
>
> This is an interesting edge case -- to prevent lookup of an inpcb in the 
> output path, we normally pass the inpcb reference down to the firewall so it 
> can directly access the cred rather than looking it up.  Thus, we don't 
> recurse the global tcbinfo or inpcb locks normally on the transmit path. 
> However, it looks like we have an edge case here where we've freed the inpcb 
> but not yet unlocked the tcbinfo, and since the inpcb is freed we don't pass 
> it down--the firewall code tries to look up the inpcb and improperly 
> recurses the tcbinfo lock, boom.
>
> The uid/gid/jail code in ipfw is undesirable for a number of reasons, not 
> least because it's a layering violation.  Historically, layering violations 
> meant slightly awkward and risky recursion, but now they also mean lock 
> recursion, which has more serious consequences.  I'll investigate tomorrow 
> and see what the best solution is -- probably to drop the lock before 
> calling tcp_dropwithreset() on a NULL inpcb, which is a workaround/hack, but 
> I think our hands are forced in this case.  I'll follow up with a patch 
> then.

Here is a possible candidate patch, could you see if it resolves all of the 
issues you reported?  (Possibly I have missed other similar cases as well...)

Index: tcp_input.c
===================================================================
--- tcp_input.c	(revision 183235)
+++ tcp_input.c	(working copy)
_at__at_ -2472,12 +2472,19 _at__at_
  dropwithreset:
  	KASSERT(headlocked, ("%s: dropwithreset: head not locked", __func__));

-	tcp_dropwithreset(m, th, tp, tlen, rstreason);
-
-	if (tp != NULL)
+	/*
+	 * If tp is non-NULL, we call tcp_dropwithreset() holding both inpcb
+	 * and global locks.  However, if NULL, we must hold neither as
+	 * firewalls may acquire the global lock in order to look for a
+	 * matching inpcb.
+	 */
+	if (tp != NULL) {
+		tcp_dropwithreset(m, th, tp, tlen, rstreason);
  		INP_WUNLOCK(tp->t_inpcb);
-	if (headlocked)
-		INP_INFO_WUNLOCK(&V_tcbinfo);
+	}
+	INP_INFO_WUNLOCK(&V_tcbinfo);
+	if (tp == NULL)
+		tcp_dropwithreset(m, th, NULL, tlen, rstreason);
  	return;

  drop:
Received on Wed Sep 24 2008 - 06:08:14 UTC

This archive was generated by hypermail 2.4.0 : Wed May 19 2021 - 11:39:35 UTC