We have some races in pppac(4)
1. malloc(9) can sleep so we must check `sc' presence after malloc(9)
2. we can sleep between `sc' insertion to `sc_entry' list and
`sc_pipex_iface' initialization. Concurrent pppacioctl() can touch
this incomplete `sc'.
Index: sys/net/if_pppx.c
===================================================================
RCS file: /cvs/src/sys/net/if_pppx.c,v
retrieving revision 1.91
diff -u -p -r1.91 if_pppx.c
--- sys/net/if_pppx.c 6 Jul 2020 20:37:51 -0000 1.91
+++ sys/net/if_pppx.c 10 Jul 2020 11:04:53 -0000
@@ -1019,7 +1019,7 @@ RBT_GENERATE(pppx_ifs, pppx_if, pxi_entr
struct pppac_softc {
struct ifnet sc_if;
- unsigned int sc_dead;
+ unsigned int sc_ready;
dev_t sc_dev;
LIST_ENTRY(pppac_softc)
sc_entry;
@@ -1072,8 +1072,12 @@ pppac_lookup(dev_t dev)
struct pppac_softc *sc;
LIST_FOREACH(sc, &pppac_devs, sc_entry) {
- if (sc->sc_dev == dev)
- return (sc);
+ if (sc->sc_dev == dev) {
+ if (sc->sc_ready)
+ return (sc);
+ else
+ break;
+ }
}
return (NULL);
@@ -1088,22 +1092,25 @@ pppacattach(int n)
int
pppacopen(dev_t dev, int flags, int mode, struct proc *p)
{
- struct pppac_softc *sc;
+ struct pppac_softc *sc, *sc_tmp;
struct ifnet *ifp;
- sc = pppac_lookup(dev);
- if (sc != NULL)
- return (EBUSY);
-
sc = malloc(sizeof(*sc), M_DEVBUF, M_WAITOK|M_ZERO);
+
+ LIST_FOREACH(sc_tmp, &pppac_devs, sc_entry) {
+ if (sc_tmp->sc_dev == dev) {
+ free(sc, M_DEVBUF, sizeof(*sc));
+ return (EBUSY);
+ }
+ }
+
sc->sc_dev = dev;
+ LIST_INSERT_HEAD(&pppac_devs, sc, sc_entry);
mtx_init(&sc->sc_rsel_mtx, IPL_SOFTNET);
mtx_init(&sc->sc_wsel_mtx, IPL_SOFTNET);
mq_init(&sc->sc_mq, IFQ_MAXLEN, IPL_SOFTNET);
- LIST_INSERT_HEAD(&pppac_devs, sc, sc_entry);
-
ifp = &sc->sc_if;
snprintf(ifp->if_xname, sizeof(ifp->if_xname), "pppac%u", minor(dev));
@@ -1129,6 +1136,7 @@ pppacopen(dev_t dev, int flags, int mode
#endif
pipex_iface_init(&sc->sc_pipex_iface, ifp);
+ sc->sc_ready = 1;
return (0);
}
@@ -1136,12 +1144,14 @@ pppacopen(dev_t dev, int flags, int mode
int
pppacread(dev_t dev, struct uio *uio, int ioflag)
{
- struct pppac_softc *sc = pppac_lookup(dev);
+ struct pppac_softc *sc;
struct ifnet *ifp = &sc->sc_if;
struct mbuf *m0, *m;
int error = 0;
size_t len;
+ if ((sc = pppac_lookup(dev)) == NULL)
+ return (EBADF);
if (!ISSET(ifp->if_flags, IFF_RUNNING))
return (EHOSTDOWN);
@@ -1181,12 +1191,14 @@ pppacread(dev_t dev, struct uio *uio, in
int
pppacwrite(dev_t dev, struct uio *uio, int ioflag)
{
- struct pppac_softc *sc = pppac_lookup(dev);
+ struct pppac_softc *sc;
struct ifnet *ifp = &sc->sc_if;
uint32_t proto;
int error;
struct mbuf *m;
+ if ((sc = pppac_lookup(dev)) == NULL)
+ return (EBADF);
if (!ISSET(ifp->if_flags, IFF_RUNNING))
return (EHOSTDOWN);
@@ -1258,9 +1270,12 @@ pppacwrite(dev_t dev, struct uio *uio, i
int
pppacioctl(dev_t dev, u_long cmd, caddr_t data, int flags, struct proc *p)
{
- struct pppac_softc *sc = pppac_lookup(dev);
+ struct pppac_softc *sc;
int error = 0;
+ if ((sc = pppac_lookup(dev)) == NULL)
+ return (EBADF);
+
switch (cmd) {
case TUNSIFMODE: /* make npppd happy */
break;
@@ -1282,9 +1297,12 @@ pppacioctl(dev_t dev, u_long cmd, caddr_
int
pppacpoll(dev_t dev, int events, struct proc *p)
{
- struct pppac_softc *sc = pppac_lookup(dev);
+ struct pppac_softc *sc;
int revents = 0;
+ if ((sc = pppac_lookup(dev)) == NULL)
+ goto out;
+
if (events & (POLLIN | POLLRDNORM)) {
if (!mq_empty(&sc->sc_mq))
revents |= events & (POLLIN | POLLRDNORM);
@@ -1296,17 +1314,20 @@ pppacpoll(dev_t dev, int events, struct
if (events & (POLLIN | POLLRDNORM))
selrecord(p, &sc->sc_rsel);
}
-
+out:
return (revents);
}
int
pppackqfilter(dev_t dev, struct knote *kn)
{
- struct pppac_softc *sc = pppac_lookup(dev);
+ struct pppac_softc *sc;
struct mutex *mtx;
struct klist *klist;
+ if ((sc = pppac_lookup(dev)) == NULL)
+ return (EBADF);
+
switch (kn->kn_filter) {
case EVFILT_READ:
mtx = &sc->sc_rsel_mtx;
@@ -1373,12 +1394,15 @@ filt_pppac_write(struct knote *kn, long
int
pppacclose(dev_t dev, int flags, int mode, struct proc *p)
{
- struct pppac_softc *sc = pppac_lookup(dev);
+ struct pppac_softc *sc;
struct ifnet *ifp = &sc->sc_if;
int s;
+ if ((sc = pppac_lookup(dev)) == NULL)
+ return (EBADF);
+
+ sc->sc_ready = 0;
NET_LOCK();
- sc->sc_dead = 1;
CLR(ifp->if_flags, IFF_RUNNING);
NET_UNLOCK();
@@ -1404,7 +1428,7 @@ pppac_ioctl(struct ifnet *ifp, u_long cm
/* struct ifreq *ifr = (struct ifreq *)data; */
int error = 0;
- if (sc->sc_dead)
+ if (sc->sc_ready == 0)
return (ENXIO);
switch (cmd) {