Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 21 Sep 2017 20:16:11 +0000 (UTC)
From:      Eugene Grosbein <eugen@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r323873 - head/sys/netgraph
Message-ID:  <201709212016.v8LKGBMi024412@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: eugen (ports committer)
Date: Thu Sep 21 20:16:10 2017
New Revision: 323873
URL: https://svnweb.freebsd.org/changeset/base/323873

Log:
  Unprotected modification of ng_iface(4) private data leads to kernel panic.
  Fix a race with per-node read-mostly lock and refcounting for a hook.
  
  PR:			220076
  Tested by:		peixoto.cassiano
  Approved by:		avg (mentor), mav (mentor)
  MFC after:		1 week
  Relnotes:		yes
  Differential Revision:	https://reviews.freebsd.org/D12435

Modified:
  head/sys/netgraph/ng_iface.c

Modified: head/sys/netgraph/ng_iface.c
==============================================================================
--- head/sys/netgraph/ng_iface.c	Thu Sep 21 20:13:03 2017	(r323872)
+++ head/sys/netgraph/ng_iface.c	Thu Sep 21 20:16:10 2017	(r323873)
@@ -64,6 +64,7 @@
 #include <sys/errno.h>
 #include <sys/proc.h>
 #include <sys/random.h>
+#include <sys/rmlock.h>
 #include <sys/sockio.h>
 #include <sys/socket.h>
 #include <sys/syslog.h>
@@ -112,9 +113,15 @@ struct ng_iface_private {
 	int	unit;			/* Interface unit number */
 	node_p	node;			/* Our netgraph node */
 	hook_p	hooks[NUM_FAMILIES];	/* Hook for each address family */
+	struct rmlock	lock;		/* Protect private data changes */
 };
 typedef struct ng_iface_private *priv_p;
 
+#define	PRIV_RLOCK(priv, t)	rm_rlock(&priv->lock, t)
+#define	PRIV_RUNLOCK(priv, t)	rm_runlock(&priv->lock, t)
+#define	PRIV_WLOCK(priv)	rm_wlock(&priv->lock)
+#define	PRIV_WUNLOCK(priv)	rm_wunlock(&priv->lock)
+
 /* Interface methods */
 static void	ng_iface_start(struct ifnet *ifp);
 static int	ng_iface_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data);
@@ -431,8 +438,10 @@ ng_iface_bpftap(struct ifnet *ifp, struct mbuf *m, sa_
 static int
 ng_iface_send(struct ifnet *ifp, struct mbuf *m, sa_family_t sa)
 {
+	struct rm_priotracker priv_tracker;
 	const priv_p priv = (priv_p) ifp->if_softc;
 	const iffam_p iffam = get_iffam_from_af(sa);
+	hook_p hook;
 	int error;
 	int len;
 
@@ -446,10 +455,20 @@ ng_iface_send(struct ifnet *ifp, struct mbuf *m, sa_fa
 	/* Copy length before the mbuf gets invalidated. */
 	len = m->m_pkthdr.len;
 
-	/* Send packet. If hook is not connected, mbuf will get freed. */
+	PRIV_RLOCK(priv, &priv_tracker);
+	hook = *get_hook_from_iffam(priv, iffam);
+	if (hook == NULL) {
+		NG_FREE_M(m);
+		PRIV_RUNLOCK(priv, &priv_tracker);
+		return ENETDOWN;
+	}
+	NG_HOOK_REF(hook);
+	PRIV_RUNLOCK(priv, &priv_tracker);
+
 	NG_OUTBOUND_THREAD_REF();
-	NG_SEND_DATA_ONLY(error, *get_hook_from_iffam(priv, iffam), m);
+	NG_SEND_DATA_ONLY(error, hook, m);
 	NG_OUTBOUND_THREAD_UNREF();
+	NG_HOOK_UNREF(hook);
 
 	/* Update stats. */
 	if (error == 0) {
@@ -516,6 +535,8 @@ ng_iface_constructor(node_p node)
 		return (ENOMEM);
 	}
 
+	rm_init(&priv->lock, "ng_iface private rmlock");
+
 	/* Link them together */
 	ifp->if_softc = priv;
 	priv->ifp = ifp;
@@ -562,16 +583,21 @@ static int
 ng_iface_newhook(node_p node, hook_p hook, const char *name)
 {
 	const iffam_p iffam = get_iffam_from_name(name);
+	const priv_p priv = NG_NODE_PRIVATE(node);
 	hook_p *hookptr;
 
 	if (iffam == NULL)
 		return (EPFNOSUPPORT);
-	hookptr = get_hook_from_iffam(NG_NODE_PRIVATE(node), iffam);
-	if (*hookptr != NULL)
+	PRIV_WLOCK(priv);
+	hookptr = get_hook_from_iffam(priv, iffam);
+	if (*hookptr != NULL) {
+		PRIV_WUNLOCK(priv);
 		return (EISCONN);
+	}
 	*hookptr = hook;
 	NG_HOOK_HI_STACK(hook);
 	NG_HOOK_SET_TO_INBOUND(hook);
+	PRIV_WUNLOCK(priv);
 	return (0);
 }
 
@@ -730,6 +756,7 @@ ng_iface_shutdown(node_p node)
 	CURVNET_RESTORE();
 	priv->ifp = NULL;
 	free_unr(V_ng_iface_unit, priv->unit);
+	rm_destroy(&priv->lock);
 	free(priv, M_NETGRAPH_IFACE);
 	NG_NODE_SET_PRIVATE(node, NULL);
 	NG_NODE_UNREF(node);
@@ -748,7 +775,9 @@ ng_iface_disconnect(hook_p hook)
 
 	if (iffam == NULL)
 		panic("%s", __func__);
+	PRIV_WLOCK(priv);
 	*get_hook_from_iffam(priv, iffam) = NULL;
+	PRIV_WUNLOCK(priv);
 	return (0);
 }
 



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