Skip to content

Commit 4019286

Browse files
author
mvs
committed
Fix the race between if_detach() and rtm_output().
When the dying network interface descriptor has if_get(9) obtained reference owned by foreign thread, the if_detach() thread will sleep just after it removed this interface from the interface index map. The data related to this interface is still in routing table, so if_get(9) called by concurrent rtm_output() thread will return NULL and the following "ifp != NULL" assertion will be triggered. So remove the "ifp != NULL" assertions from rtm_output() and try to grab `ifp' as early as possible then hold it until we finish the work. In the case we won the race and we have `ifp' non NULL, concurrent if_detach() thread will wait us. In the case we lost we just return ESRCH. The problem reported by danj@. Diff tested by danj@. ok mpi@
1 parent d782ea5 commit 4019286

File tree

1 file changed

+27
-13
lines changed

1 file changed

+27
-13
lines changed

sys/net/rtsock.c

Lines changed: 27 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/* $OpenBSD: rtsock.c,v 1.319 2021/06/23 16:10:45 cheloha Exp $ */
1+
/* $OpenBSD: rtsock.c,v 1.320 2021/09/07 09:56:00 mvs Exp $ */
22
/* $NetBSD: rtsock.c,v 1.18 1996/03/29 00:32:10 cgd Exp $ */
33

44
/*
@@ -934,8 +934,17 @@ rtm_output(struct rt_msghdr *rtm, struct rtentry **prt,
934934
* cached route because this can lead to races in the
935935
* receive path. Instead we update the L2 cache.
936936
*/
937-
if ((rt != NULL) && ISSET(rt->rt_flags, RTF_CACHED))
937+
if ((rt != NULL) && ISSET(rt->rt_flags, RTF_CACHED)) {
938+
ifp = if_get(rt->rt_ifidx);
939+
if (ifp == NULL) {
940+
rtfree(rt);
941+
rt = NULL;
942+
error = ESRCH;
943+
break;
944+
}
945+
938946
goto change;
947+
}
939948

940949
rtfree(rt);
941950
rt = NULL;
@@ -970,9 +979,13 @@ rtm_output(struct rt_msghdr *rtm, struct rtentry **prt,
970979
break;
971980
}
972981

973-
/* Detaching an interface requires the KERNEL_LOCK(). */
974982
ifp = if_get(rt->rt_ifidx);
975-
KASSERT(ifp != NULL);
983+
if (ifp == NULL) {
984+
rtfree(rt);
985+
rt = NULL;
986+
error = ESRCH;
987+
break;
988+
}
976989

977990
/*
978991
* Invalidate the cache of automagically created and
@@ -986,7 +999,6 @@ rtm_output(struct rt_msghdr *rtm, struct rtentry **prt,
986999
rtable_walk(tableid, rt_key(rt)->sa_family, NULL,
9871000
route_cleargateway, rt);
9881001
NET_UNLOCK();
989-
if_put(ifp);
9901002
break;
9911003
}
9921004

@@ -995,7 +1007,6 @@ rtm_output(struct rt_msghdr *rtm, struct rtentry **prt,
9951007
* kernel.
9961008
*/
9971009
if (ISSET(rt->rt_flags, RTF_LOCAL|RTF_BROADCAST)) {
998-
if_put(ifp);
9991010
error = EINVAL;
10001011
break;
10011012
}
@@ -1006,7 +1017,6 @@ rtm_output(struct rt_msghdr *rtm, struct rtentry **prt,
10061017
NET_LOCK();
10071018
error = rtrequest_delete(info, prio, ifp, &rt, tableid);
10081019
NET_UNLOCK();
1009-
if_put(ifp);
10101020
break;
10111021
case RTM_CHANGE:
10121022
rt = rtable_lookup(tableid, info->rti_info[RTAX_DST],
@@ -1021,6 +1031,15 @@ rtm_output(struct rt_msghdr *rtm, struct rtentry **prt,
10211031
rtfree(rt);
10221032
rt = NULL;
10231033
}
1034+
1035+
ifp = if_get(rt->rt_ifidx);
1036+
if (ifp == NULL) {
1037+
rtfree(rt);
1038+
rt = NULL;
1039+
error = ESRCH;
1040+
break;
1041+
}
1042+
10241043
/*
10251044
* If RTAX_GATEWAY is the argument we're trying to
10261045
* change, try to find a compatible route.
@@ -1083,11 +1102,8 @@ rtm_output(struct rt_msghdr *rtm, struct rtentry **prt,
10831102
}
10841103
ifa = info->rti_ifa;
10851104
if (rt->rt_ifa != ifa) {
1086-
ifp = if_get(rt->rt_ifidx);
1087-
KASSERT(ifp != NULL);
10881105
ifp->if_rtrequest(ifp, RTM_DELETE, rt);
10891106
ifafree(rt->rt_ifa);
1090-
if_put(ifp);
10911107

10921108
ifa->ifa_refcnt++;
10931109
rt->rt_ifa = ifa;
@@ -1152,10 +1168,7 @@ rtm_output(struct rt_msghdr *rtm, struct rtentry **prt,
11521168
}
11531169
rtm_setmetrics(rtm->rtm_inits, &rtm->rtm_rmx, &rt->rt_rmx);
11541170

1155-
ifp = if_get(rt->rt_ifidx);
1156-
KASSERT(ifp != NULL);
11571171
ifp->if_rtrequest(ifp, RTM_ADD, rt);
1158-
if_put(ifp);
11591172

11601173
if (info->rti_info[RTAX_LABEL] != NULL) {
11611174
char *rtlabel = ((struct sockaddr_rtlabel *)
@@ -1178,6 +1191,7 @@ rtm_output(struct rt_msghdr *rtm, struct rtentry **prt,
11781191
break;
11791192
}
11801193

1194+
if_put(ifp);
11811195
*prt = rt;
11821196
return (error);
11831197
}

0 commit comments

Comments
 (0)