Thanks to awolk@ hacking on USB wireless we found a lot of new races in
the network stack.
Passing and ``ifp'' pointer to a task is *not* safe. If the task
sleeps, then another thread my start executing if_detach() freeing the
memory associated to ``ifp''. And task do sleep, that's why we use
them in the first place.
So we should always pass and ifp index like I did with the
if_input_process() task. Diff attached fix awolk@'s first panic.
ok?
Index: net/if.c
===================================================================
RCS file: /cvs/src/sys/net/if.c,v
retrieving revision 1.441
diff -u -p -r1.441 if.c
--- net/if.c 4 Sep 2016 10:32:01 -0000 1.441
+++ net/if.c 4 Sep 2016 11:03:37 -0000
@@ -400,6 +400,8 @@ if_map_dtor(void *null, void *m)
void
if_attachsetup(struct ifnet *ifp)
{
+ unsigned long ifidx;
+
TAILQ_INIT(&ifp->if_groups);
if_addgroup(ifp, IFG_ALL);
@@ -409,18 +411,19 @@ if_attachsetup(struct ifnet *ifp)
pfi_attach_ifnet(ifp);
#endif
- task_set(ifp->if_watchdogtask, if_watchdog_task, ifp);
timeout_set(ifp->if_slowtimo, if_slowtimo, ifp);
if_slowtimo(ifp);
- task_set(ifp->if_linkstatetask, if_linkstate, ifp);
-
if_idxmap_insert(ifp);
KASSERT(if_get(0) == NULL);
+ ifidx = ifp->if_index;
+
mq_init(&ifp->if_inputqueue, 8192, IPL_NET);
- task_set(ifp->if_inputtask, if_input_process,
- (void *)(unsigned long)ifp->if_index);
+ task_set(ifp->if_inputtask, if_input_process, (void *)ifidx);
+ task_set(ifp->if_watchdogtask, if_watchdog_task, (void *)ifidx);
+ task_set(ifp->if_linkstatetask, if_linkstate, (void *)ifidx);
+
/* Announce the interface. */
rt_ifannouncemsg(ifp, IFAN_ARRIVAL);
@@ -1482,11 +1485,16 @@ if_up(struct ifnet *ifp)
* a link-state transition.
*/
void
-if_linkstate(void *xifp)
+if_linkstate(void *xifidx)
{
- struct ifnet *ifp = xifp;
+ unsigned int ifidx = (unsigned long)xifidx;
+ struct ifnet *ifp;
int s;
+ ifp = if_get(ifidx);
+ if (ifp == NULL)
+ return;
+
s = splsoftnet();
rt_ifmsg(ifp);
#ifndef SMALL_KERNEL
@@ -1494,6 +1502,8 @@ if_linkstate(void *xifp)
#endif
dohooks(ifp->if_linkstatehooks, 0);
splx(s);
+
+ if_put(ifp);
}
/*
@@ -1525,15 +1535,22 @@ if_slowtimo(void *arg)
}
void
-if_watchdog_task(void *arg)
+if_watchdog_task(void *xifidx)
{
- struct ifnet *ifp = arg;
+ unsigned int ifidx = (unsigned long)xifidx;
+ struct ifnet *ifp;
int s;
+ ifp = if_get(ifidx);
+ if (ifp == NULL)
+ return;
+
s = splnet();
if (ifp->if_watchdog)
(*ifp->if_watchdog)(ifp);
splx(s);
+
+ if_put(ifp);
}
/*