⬢ DragonFlyBSD Kernel Audit
DF-3481 / fix.diff
← back to finding ↓ download raw
@@ -203,6 +203,7 @@ struct ng_ppp_private {
 				frags;
 	int			qlen;			/* fraq queue length */
 	struct callout		fragTimer;		/* fraq queue check */
+	struct lwkt_token	token;			/* serialize priv (SMP) */
 };
 typedef struct ng_ppp_private *priv_p;
 
@@ -212,6 +213,10 @@ static ng_rcvmsg_t	ng_ppp_rcvmsg;
 static ng_shutdown_t	ng_ppp_rmnode;
 static ng_newhook_t	ng_ppp_newhook;
 static ng_rcvdata_t	ng_ppp_rcvdata;
+static int	ng_ppp_rcvdata_unlocked(hook_p hook,
+			struct mbuf *m, meta_p meta);
+static int	ng_ppp_newhook_unlocked(node_p node, hook_p hook,
+			const char *name);
 static ng_disconnect_t	ng_ppp_disconnect;
 
 /* Helper functions */
@@ -400,6 +405,7 @@ ng_ppp_constructor(node_p *nodep)
 	for (i = 0; i < NG_PPP_MAX_LINKS; i++)
 		priv->links[i].seq = MP_NOSEQ;
 	callout_init(&priv->fragTimer);
+	lwkt_token_init(&priv->token, "ng_ppp");
 
 	/* Done */
 	return (0);
@@ -410,6 +416,18 @@ ng_ppp_constructor(node_p *nodep)
  */
 static int
 ng_ppp_newhook(node_p node, hook_p hook, const char *name)
+{
+	const priv_p priv = node->private;
+	int ret;
+
+	lwkt_gettoken(&priv->token);
+	ret = ng_ppp_newhook_unlocked(node, hook, name);
+	lwkt_reltoken(&priv->token);
+	return (ret);
+}
+
+static int
+ng_ppp_newhook_unlocked(node_p node, hook_p hook, const char *name)
 {
 	const priv_p priv = node->private;
 	int linkNum = -1;
@@ -471,6 +489,7 @@ ng_ppp_rcvmsg(node_p node, struct ng_mesg *msg,
 	struct ng_mesg *resp = NULL;
 	int error = 0;
 
+	lwkt_gettoken(&priv->token);
 	switch (msg->header.typecookie) {
 	case NGM_PPP_COOKIE:
 		switch (msg->header.cmd) {
@@ -565,6 +584,7 @@ ng_ppp_rcvmsg(node_p node, struct ng_mesg *msg,
 			ERROUT(error);
 		ksnprintf(path, sizeof(path), "[%lx]:%s",
 		    (long)node, NG_PPP_HOOK_VJC_IP);
+		lwkt_reltoken(&priv->token);
 		return ng_send_msg(origNode, msg, path, rptr);
 	    }
 	default:
@@ -578,6 +598,7 @@ ng_ppp_rcvmsg(node_p node, struct ng_mesg *msg,
 
 done:
 	kfree(msg, M_NETGRAPH);
+	lwkt_reltoken(&priv->token);
 	return (error);
 }
 
@@ -586,6 +607,19 @@ ng_ppp_rcvmsg(node_p node, struct ng_mesg *msg,
  */
 static int
 ng_ppp_rcvdata(hook_p hook, struct mbuf *m, meta_p meta)
+{
+	const node_p node = hook->node;
+	const priv_p priv = node->private;
+	int ret;
+
+	lwkt_gettoken(&priv->token);
+	ret = ng_ppp_rcvdata_unlocked(hook, m, meta);
+	lwkt_reltoken(&priv->token);
+	return (ret);
+}
+
+static int
+ng_ppp_rcvdata_unlocked(hook_p hook, struct mbuf *m, meta_p meta)
 {
 	const node_p node = hook->node;
 	const priv_p priv = node->private;
@@ -793,6 +827,8 @@ ng_ppp_rmnode(node_p node)
 {
 	const priv_p priv = node->private;
 
+	lwkt_gettoken(&priv->token);
+
 	/* Stop fragment queue timer */
 	ng_ppp_stop_frag_timer(node);
 
@@ -801,6 +837,14 @@ ng_ppp_rmnode(node_p node)
 	ng_cutlinks(node);
 	ng_unname(node);
 	ng_ppp_frag_reset(node);
+	/*
+	 * All hooks are gone now, so no new rcvdata/rcvmsg can be
+	 * dispatched.  Drop the token before the private structure is
+	 * freed so we never touch freed memory; any synchronous caller
+	 * still spinning on the token sees valid (zeroed-by-bzero
+	 * below) memory or an invalid hook and bails.
+	 */
+	lwkt_reltoken(&priv->token);
 	bzero(priv, sizeof(*priv));
 	kfree(priv, M_NETGRAPH);
 	node->private = NULL;
@@ -818,6 +862,8 @@ ng_ppp_disconnect(hook_p hook)
 	const priv_p priv = node->private;
 	const int index = HOOK_INDEX(hook);
 
+	lwkt_gettoken(&priv->token);
+
 	/* Zero out hook pointer */
 	if (index < 0)
 		priv->links[~index].hook = NULL;
@@ -829,6 +875,7 @@ ng_ppp_disconnect(hook_p hook)
 		ng_ppp_update(node, 0);
 	else
 		ng_rmnode(node);
+	lwkt_reltoken(&priv->token);
 	return (0);
 }
 
@@ -1457,6 +1504,7 @@ ng_ppp_frag_timeout(void *arg)
 		crit_exit();
 		return;
 	}
+	lwkt_gettoken(&priv->token);
 
 	/* Reset timer state after timeout */
 	KASSERT(priv->timerActive, ("%s: !timerActive", __func__));
@@ -1469,6 +1517,7 @@ ng_ppp_frag_timeout(void *arg)
 
 	/* Scan the fragment queue */
 	ng_ppp_frag_checkstale(node);
+	lwkt_reltoken(&priv->token);
 	crit_exit();
 }