Bug 144874 - [if_bridge] [patch] if_bridge frees mbuf after pfil hooks returns non-zero
Summary: [if_bridge] [patch] if_bridge frees mbuf after pfil hooks returns non-zero
Status: Open
Alias: None
Product: Base System
Classification: Unclassified
Component: kern (show other bugs)
Version: unspecified
Hardware: Any Any
: Normal Affects Only Me
Assignee: freebsd-bugs mailing list
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2010-03-19 15:30 UTC by Jake Montogmery
Modified: 2017-12-31 22:34 UTC (History)
0 users

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Jake Montogmery 2010-03-19 15:30:02 UTC
he man pages for ipfil state:

"The filter returns an error (errno) if the packet processing is to stop, or 0 if the processing is to continue.  If the packet processing is to stop, it is the responsibility of the filter to free the packet."

When ip_input() is being used to process the hooks, then this is the case [see ip_input.c rev 199625 line 517.] However, when the ip_bridge diver is being used, then bridge_pfil() [if_bridge.c rev 199625 line 2932] will call m_freem() [at line 3214] on the buffer that was passed to the pfil hook even if the hook returns a non-zero value. This is due to "faulty" logic at lines 3189-3192. As stated in the documentation a non-zero return from the pfil hook should indicate that the filter has freed the mbuf. This results in "double freeing" of the mbuf, which causes various panics down the line.

If the pfil hook sets the mbuf to NULL before returning, then the if_bridge filter behaves properly. The problem is that if a filter is written based on the documentation, and tested on a system that is not running as a bridge, it will behave fine. Once a bridge is introduced the system will panic at apparently "random" times.

The problem exists in at least in 7.1, 7.2 and 8.0, though I suspect it is older than that.

Fix: 

It would be nice to see the bridge_pfil() code fixed. However, it could be high risk, since there may be some incorrectly written filters out there that rely on this behavior. If the if_bridge code is not fixed, then it would be very helpful to developers to document that the mbuf pointer should be set to NULL if a non-zero value is returned from a pfil hook. This could be added as part of the pfil specification, or at least in the BUGS section of the pfil manpage documentation. 

The code could be fixed by changing [if_bridge.c rev 199625 line 3189]
-  if (*mp == NULL)
-    return (error);
-  if (error != 0)
-    goto bad;

+  if (*mp == NULL || error !=0)
+    return (error);
How-To-Repeat: Create a simple pfil hook and install it with pfil_add_hook(PFIL_IN). The hook should drop (some) packets by returning a non-zero value. The hook should free the mbuf on dropped packets by calling m_freem(*mp). The filter should _not_ modify the mbuf pointer (mp). Install a if_bridge on the system, and pass traffic through the bridge, such that at least one packet gets dropped by the pfil hook. At some point shortly after that the system will panic. The panic is usually occurs in sbflush_internal(), though there are other ways that the corruption can manifest.
Comment 1 Mark Linimon freebsd_committer freebsd_triage 2010-03-19 23:53:11 UTC
Responsible Changed
From-To: freebsd-bugs->freebsd-net

Over to maintainer(s).
Comment 2 Gleb Kurtsou 2010-03-20 01:50:04 UTC
[...]
> Create a simple pfil hook and install it with pfil_add_hook(PFIL_IN).
> The hook should drop (some) packets by returning a non-zero value. The
> hook should free the mbuf on dropped packets by calling m_freem(*mp).
> The filter should _not_ modify the mbuf pointer (mp). Install a
                  ^^^^^^^^^ documentation is wrong here.
As far as I can see all firewalls in the tree zero mp after free,
something like:
	if (chk && *m) {
		m_freem(*m);
		*m = NULL;
	}

Correct fix would be to update documentation and add KASSERT to
pfil_run_hooks checking *mp == 0 if hook returned non-zero result.

> if_bridge on the system, and pass traffic through the bridge, such
> that at least one packet gets dropped by the pfil hook. At some point
> shortly after that the system will panic. The panic is usually occurs
> in sbflush_internal(), though there are other ways that the corruption
> can manifest.
Comment 3 Eitan Adler freebsd_committer freebsd_triage 2017-12-31 08:00:02 UTC
For bugs matching the following criteria:

Status: In Progress Changed: (is less than) 2014-06-01

Reset to default assignee and clear in-progress tags.

Mail being skipped