Module Name:    src
Committed By:   martin
Date:           Thu Jul 12 15:11:56 UTC 2018

Modified Files:
        src/sys/net [netbsd-8]: if_pppoe.c

Log Message:
Pull up following revision(s) (requested by yamaguchi in ticket #890):
        sys/net/if_pppoe.c: revision 1.137
        sys/net/if_pppoe.c: revision 1.139
        sys/net/if_pppoe.c: revision 1.140
Drop early if there's no PPPoE interface. Otherwise it is easy for someone
to flood dmesg over the local subnet.
Fix not to use PPPOE_UNLOCK before acccess to pppoe_softc
to avoid a race condition
According to the locking order of pppoe(4), the access to
pppoe_softc has to follow 5 steps as below.
1. aquire pppoe_softc_list_lock
2. aquire pppoe_softc lock
3. release pppoe_softc_list_lock
4. access to pppoe_softc
5. release pppoe_softc lock
However, pppoe_dispatch_disc_pkt() releases the lock of pppoe_softc
temporarily, and then re-aquires it before step 4 of the adove. So,
it is possible for other contexts to destroy a pppoe_softc in the
interim.
To fix this condition, avoid PPPOE_UNLOCK with the problem.
ok by knakahara@n.o
Fix to aquire pppoe_softc_list_lock before read and write the list
ok by knakahara@n.o


To generate a diff of this commit:
cvs rdiff -u -r1.125.6.8 -r1.125.6.9 src/sys/net/if_pppoe.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sys/net/if_pppoe.c
diff -u src/sys/net/if_pppoe.c:1.125.6.8 src/sys/net/if_pppoe.c:1.125.6.9
--- src/sys/net/if_pppoe.c:1.125.6.8	Thu Jun  7 17:42:25 2018
+++ src/sys/net/if_pppoe.c	Thu Jul 12 15:11:56 2018
@@ -1,4 +1,4 @@
-/* $NetBSD: if_pppoe.c,v 1.125.6.8 2018/06/07 17:42:25 martin Exp $ */
+/* $NetBSD: if_pppoe.c,v 1.125.6.9 2018/07/12 15:11:56 martin Exp $ */
 
 /*-
  * Copyright (c) 2002, 2008 The NetBSD Foundation, Inc.
@@ -30,7 +30,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: if_pppoe.c,v 1.125.6.8 2018/06/07 17:42:25 martin Exp $");
+__KERNEL_RCSID(0, "$NetBSD: if_pppoe.c,v 1.125.6.9 2018/07/12 15:11:56 martin Exp $");
 
 #ifdef _KERNEL_OPT
 #include "pppoe.h"
@@ -277,8 +277,11 @@ pppoedetach(void)
 {
 	int error = 0;
 
-	if (!LIST_EMPTY(&pppoe_softc_list))
+	rw_enter(&pppoe_softc_list_lock, RW_READER);
+	if (!LIST_EMPTY(&pppoe_softc_list)) {
+		rw_exit(&pppoe_softc_list_lock);
 		error = EBUSY;
+	}
 
 	if (error == 0) {
 		if_clone_detach(&pppoe_cloner);
@@ -338,9 +341,12 @@ pppoe_clone_create(struct if_clone *ifc,
 	sppp_attach(&sc->sc_sppp.pp_if);
 
 	bpf_attach(&sc->sc_sppp.pp_if, DLT_PPP_ETHER, 0);
+	rw_enter(&pppoe_softc_list_lock, RW_READER);
 	if (LIST_EMPTY(&pppoe_softc_list)) {
 		pfil_add_ihook(pppoe_ifattach_hook, NULL, PFIL_IFNET, if_pfil);
 	}
+	rw_exit(&pppoe_softc_list_lock);
+
 	if_register(&sc->sc_sppp.pp_if);
 
 	rw_init(&sc->sc_lock);
@@ -424,14 +430,18 @@ pppoe_find_softc_by_hunique(uint8_t *tok
 {
 	struct pppoe_softc *sc, *t;
 
-	if (LIST_EMPTY(&pppoe_softc_list))
+	rw_enter(&pppoe_softc_list_lock, RW_READER);
+	if (LIST_EMPTY(&pppoe_softc_list)) {
+		rw_exit(&pppoe_softc_list_lock);
 		return NULL;
+	}
 
-	if (len != sizeof sc)
+	if (len != sizeof sc) {
+		rw_exit(&pppoe_softc_list_lock);
 		return NULL;
+	}
 	memcpy(&t, token, len);
 
-	rw_enter(&pppoe_softc_list_lock, RW_READER);
 	LIST_FOREACH(sc, &pppoe_softc_list, sc_list) {
 		if (sc == t) {
 			PPPOE_LOCK(sc, lock);
@@ -518,15 +528,15 @@ pppoe_dispatch_disc_pkt(struct mbuf *m, 
 	size_t ac_cookie_len;
 	uint8_t *relay_sid;
 	size_t relay_sid_len;
-#ifdef PPPOE_SERVER
 	uint8_t *hunique;
 	size_t hunique_len;
-#endif
 	struct pppoehdr *ph;
 	struct pppoetag *pt;
 	struct mbuf *n;
 	int noff, err, errortag;
 	struct ether_header *eh;
+	struct ifnet *rcvif;
+	struct psref psref;
 
 	/* as long as we don't know which instance */
 	strlcpy(devname, "pppoe", sizeof(devname));
@@ -545,10 +555,8 @@ pppoe_dispatch_disc_pkt(struct mbuf *m, 
 	ac_cookie_len = 0;
 	relay_sid = NULL;
 	relay_sid_len = 0;
-#ifdef PPPOE_SERVER
 	hunique = NULL;
 	hunique_len = 0;
-#endif
 	session = 0;
 	if (m->m_pkthdr.len - off <= PPPOE_HEADERLEN) {
 		printf("pppoe: packet too short: %d\n", m->m_pkthdr.len);
@@ -601,8 +609,7 @@ pppoe_dispatch_disc_pkt(struct mbuf *m, 
 		case PPPOE_TAG_SNAME:
 			break;	/* ignored */
 		case PPPOE_TAG_ACNAME:
-			error = NULL;
-			if (sc != NULL && len > 0) {
+			if (len > 0) {
 				error = malloc(len + 1, M_TEMP, M_NOWAIT);
 				if (error == NULL)
 					break;
@@ -616,40 +623,24 @@ pppoe_dispatch_disc_pkt(struct mbuf *m, 
 				}
 
 				strlcpy(error, mtod(n, char*) + noff, len + 1);
-				printf("%s: connected to %s\n", devname, error);
+				printf("pppoe: connected to %s\n", error);
 				free(error, M_TEMP);
 			}
 			break;	/* ignored */
-		case PPPOE_TAG_HUNIQUE: {
-			struct ifnet *rcvif;
-			struct psref psref;
+		case PPPOE_TAG_HUNIQUE:
+			if (hunique == NULL) {
+				n = m_pulldown(m, off + sizeof(*pt), len,
+				    &noff);
+				if (!n) {
+					m = NULL;
+					err_msg = "TAG HUNIQUE ERROR";
+					break;
+				}
 
-			if (sc != NULL)
-				break;
-			n = m_pulldown(m, off + sizeof(*pt), len, &noff);
-			if (!n) {
-				m = NULL;
-				err_msg = "TAG HUNIQUE ERROR";
-				break;
-			}
-#ifdef PPPOE_SERVER
-			hunique = mtod(n, uint8_t *) + noff;
-			hunique_len = len;
-#endif
-			rcvif = m_get_rcvif_psref(m, &psref);
-			if (rcvif != NULL) {
-				sc = pppoe_find_softc_by_hunique(
-					mtod(n, char *) + noff, len, rcvif,
-					RW_READER);
-			}
-			m_put_rcvif_psref(rcvif, &psref);
-			if (sc != NULL) {
-				strlcpy(devname, sc->sc_sppp.pp_if.if_xname,
-				    sizeof(devname));
-				PPPOE_UNLOCK(sc);
+				hunique = mtod(n, uint8_t *) + noff;
+				hunique_len = len;
 			}
 			break;
-		}
 		case PPPOE_TAG_ACCOOKIE:
 			if (ac_cookie == NULL) {
 				n = m_pulldown(m, off + sizeof(*pt), len,
@@ -722,9 +713,12 @@ breakbreak:;
 		 * got service name, concentrator name, and/or host unique.
 		 * ignore if we have no interfaces with IFF_PASSIVE|IFF_UP.
 		 */
-		if (LIST_EMPTY(&pppoe_softc_list))
-			goto done;
 		rw_enter(&pppoe_softc_list_lock, RW_READER);
+		if (LIST_EMPTY(&pppoe_softc_list)) {
+			rw_exit(&pppoe_softc_list_lock);
+			goto done;
+		}
+
 		LIST_FOREACH(sc, &pppoe_softc_list, sc_list) {
 			PPPOE_LOCK(sc, RW_WRITER);
 			if (!(sc->sc_sppp.pp_if.if_flags & IFF_UP)) {
@@ -768,9 +762,6 @@ breakbreak:;
 #endif /* PPPOE_SERVER */
 	case PPPOE_CODE_PADR:
 #ifdef PPPOE_SERVER
-	    {
-		struct ifnet *rcvif;
-		struct psref psref;
 		/*
 		 * get sc from ac_cookie if IFF_PASSIVE
 		 */
@@ -790,8 +781,12 @@ breakbreak:;
 		m_put_rcvif_psref(rcvif, &psref);
 		if (sc == NULL) {
 			/* be quiet if there is not a single pppoe instance */
-			if (!LIST_EMPTY(&pppoe_softc_list))
-				printf("pppoe: received PADR but could not find request for it\n");
+			rw_enter(&pppoe_softc_list_lock, RW_READER);
+			if (!LIST_EMPTY(&pppoe_softc_list)) {
+				printf("pppoe: received PADR"
+				    " but could not find request for it\n");
+			}
+			rw_exit(&pppoe_softc_list_lock);
 			goto done;
 		}
 
@@ -820,21 +815,35 @@ breakbreak:;
 
 		sc->sc_sppp.pp_up(&sc->sc_sppp);
 		break;
-	    }
 #else
 		/* ignore, we are no access concentrator */
 		goto done;
 #endif /* PPPOE_SERVER */
 	case PPPOE_CODE_PADO:
+		rcvif = m_get_rcvif_psref(m, &psref);
+		if (__predict_false(rcvif == NULL))
+			goto done;
+
+		if (hunique != NULL) {
+			sc = pppoe_find_softc_by_hunique(hunique,
+							 hunique_len,
+							 rcvif,
+							 RW_WRITER);
+		}
+
+		m_put_rcvif_psref(rcvif, &psref);
+
 		if (sc == NULL) {
 			/* be quiet if there is not a single pppoe instance */
-			if (!LIST_EMPTY(&pppoe_softc_list))
-				printf("pppoe: received PADO but could not find request for it\n");
+			rw_enter(&pppoe_softc_list_lock, RW_READER);
+			if (!LIST_EMPTY(&pppoe_softc_list)) {
+				printf("pppoe: received PADO"
+				    " but could not find request for it\n");
+			}
+			rw_exit(&pppoe_softc_list_lock);
 			goto done;
 		}
 
-		PPPOE_LOCK(sc, RW_WRITER);
-
 		if (sc->sc_state != PPPOE_STATE_PADI_SENT) {
 			printf("%s: received unexpected PADO\n",
 			    sc->sc_sppp.pp_if.if_xname);
@@ -889,10 +898,21 @@ breakbreak:;
 		PPPOE_UNLOCK(sc);
 		break;
 	case PPPOE_CODE_PADS:
-		if (sc == NULL)
+		rcvif = m_get_rcvif_psref(m, &psref);
+		if (__predict_false(rcvif == NULL))
 			goto done;
 
-		PPPOE_LOCK(sc, RW_WRITER);
+		if (hunique != NULL) {
+			sc = pppoe_find_softc_by_hunique(hunique,
+							 hunique_len,
+							 rcvif,
+							 RW_WRITER);
+		}
+
+		m_put_rcvif_psref(rcvif, &psref);
+
+		if (sc == NULL)
+			goto done;
 
 		sc->sc_session = session;
 		callout_stop(&sc->sc_timeout);
@@ -904,26 +924,37 @@ breakbreak:;
 
 		sc->sc_sppp.pp_up(&sc->sc_sppp);	/* notify upper layers */
 		break;
-	case PPPOE_CODE_PADT: {
-		struct ifnet *rcvif;
-		struct psref psref;
-
+	case PPPOE_CODE_PADT:
 		rcvif = m_get_rcvif_psref(m, &psref);
-		if (__predict_true(rcvif != NULL)) {
-			sc = pppoe_find_softc_by_session(session, rcvif,
-			    RW_WRITER);
-		}
+		if (__predict_false(rcvif == NULL))
+			goto done;
+
+		sc = pppoe_find_softc_by_session(session, rcvif,
+		    RW_WRITER);
+
 		m_put_rcvif_psref(rcvif, &psref);
+
 		if (sc == NULL)
 			goto done;
 
 		pppoe_clear_softc(sc, "received PADT");
 		PPPOE_UNLOCK(sc);
 		break;
-	}
 	default:
+		rcvif = m_get_rcvif_psref(m, &psref);
+		if (__predict_false(rcvif == NULL))
+			goto done;
+
+		if (hunique != NULL) {
+			sc = pppoe_find_softc_by_hunique(hunique,
+							 hunique_len,
+							 rcvif,
+							 RW_READER);
+		}
+
+		m_put_rcvif_psref(rcvif, &psref);
+
 		if (sc != NULL) {
-			PPPOE_LOCK(sc, RW_READER);
 			strlcpy(devname, sc->sc_sppp.pp_if.if_xname,
 			    sizeof(devname));
 			PPPOE_UNLOCK(sc);
@@ -944,13 +975,19 @@ done:
 static void
 pppoe_disc_input(struct mbuf *m)
 {
+	KASSERT(m->m_flags & M_PKTHDR);
 
-	/* avoid error messages if there is not a single pppoe instance */
+	/*
+	 * Avoid error messages if there is not a single PPPoE instance.
+	 */
+	rw_enter(&pppoe_softc_list_lock, RW_READER);
 	if (!LIST_EMPTY(&pppoe_softc_list)) {
-		KASSERT(m->m_flags & M_PKTHDR);
+		rw_exit(&pppoe_softc_list_lock);
 		pppoe_dispatch_disc_pkt(m, 0);
-	} else
+	} else {
+		rw_exit(&pppoe_softc_list_lock);
 		m_freem(m);
+	}
 }
 
 static bool
@@ -977,6 +1014,16 @@ pppoe_data_input(struct mbuf *m)
 
 	KASSERT(m->m_flags & M_PKTHDR);
 
+	/*
+	 * Avoid error messages if there is not a single PPPoE instance.
+	 */
+	rw_enter(&pppoe_softc_list_lock, RW_READER);
+	if (LIST_EMPTY(&pppoe_softc_list)) {
+		rw_exit(&pppoe_softc_list_lock);
+		goto drop;
+	}
+	rw_exit(&pppoe_softc_list_lock);
+
 	if (term_unknown) {
 		memcpy(shost, mtod(m, struct ether_header*)->ether_shost,
 		    ETHER_ADDR_LEN);

Reply via email to