Download raw body.
route delete ifp NULL check
On Mon, Aug 04, 2025 at 05:46:44AM +0300, Vitaliy Makkoveev wrote:
> On Sun, Aug 03, 2025 at 02:26:20PM +0200, Alexander Bluhm wrote:
> > Hi,
> >
> > Coverity complains that we check ifp NULL pointer in rtrequest_delete(),
> > but later on we dereference it unconditinally.
> >
> > The only caller that might have a NULL in ifp is nd6_free(). But
> > there we also dereference it later. I think the exclusive net lock
> > protects us there, but I want to assure it with a kassert.
> >
> > When we unlock ND6, we have to reconsider this.
> >
> > ok?
> >
>
> Exclusive netlock protects us from `rt_ifidx' change, but not from
> the interface detach from if_idxmap. So hypothetically `ifp' could be
> NULL during race condition. However, seems the nd6_free() callers own
> the `ifp' related to this `rt'. Could we pass them to the nd6_free()
> together?
Good idea, diff below.
My only concern was that sdl->sdl_index is always the same as
rt->rt_ifidx in nd6_purge(). I think we can assume that.
ok?
bluhm
Index: net/route.c
===================================================================
RCS file: /data/mirror/openbsd/cvs/src/sys/net/route.c,v
diff -u -p -r1.447 route.c
--- net/route.c 14 Jul 2025 08:48:51 -0000 1.447
+++ net/route.c 4 Aug 2025 10:19:18 -0000
@@ -907,7 +907,7 @@ rtrequest_delete(struct rt_addrinfo *inf
return (ESRCH);
/* Make sure that's the route the caller want to delete. */
- if (ifp != NULL && ifp->if_index != rt->rt_ifidx) {
+ if (ifp->if_index != rt->rt_ifidx) {
rtfree(rt);
return (ESRCH);
}
Index: netinet6/nd6.c
===================================================================
RCS file: /data/mirror/openbsd/cvs/src/sys/netinet6/nd6.c,v
diff -u -p -r1.296 nd6.c
--- netinet6/nd6.c 3 Aug 2025 11:08:40 -0000 1.296
+++ netinet6/nd6.c 4 Aug 2025 11:18:49 -0000
@@ -99,7 +99,7 @@ void nd6_slowtimo(void *);
void nd6_expire(void *);
void nd6_expire_timer(void *);
void nd6_invalidate(struct rtentry *);
-void nd6_free(struct rtentry *, int);
+void nd6_free(struct rtentry *, struct ifnet *ifp, int);
int nd6_llinfo_timer(struct rtentry *, int);
struct timeout nd6_timer_to;
@@ -329,7 +329,7 @@ nd6_llinfo_timer(struct rtentry *rt, int
} else
atomic_sub_int(&ln_hold_total, len);
- nd6_free(rt, i_am_router);
+ nd6_free(rt, ifp, i_am_router);
ln = NULL;
}
break;
@@ -345,7 +345,7 @@ nd6_llinfo_timer(struct rtentry *rt, int
case ND6_LLINFO_PURGE:
/* Garbage Collection(RFC 2461 5.3) */
if (!ND6_LLINFO_PERMANENT(ln)) {
- nd6_free(rt, i_am_router);
+ nd6_free(rt, ifp, i_am_router);
ln = NULL;
}
break;
@@ -366,7 +366,7 @@ nd6_llinfo_timer(struct rtentry *rt, int
nd6_ns_output(ifp, &dst->sin6_addr, &dst->sin6_addr,
&ln->ln_saddr6, 0);
} else {
- nd6_free(rt, i_am_router);
+ nd6_free(rt, ifp, i_am_router);
ln = NULL;
}
break;
@@ -476,7 +476,7 @@ nd6_purge(struct ifnet *ifp)
rt->rt_gateway->sa_family == AF_LINK) {
sdl = satosdl(rt->rt_gateway);
if (sdl->sdl_index == ifp->if_index)
- nd6_free(rt, i_am_router);
+ nd6_free(rt, ifp, i_am_router);
}
}
}
@@ -638,16 +638,13 @@ nd6_invalidate(struct rtentry *rt)
* Free an nd6 llinfo entry.
*/
void
-nd6_free(struct rtentry *rt, int i_am_router)
+nd6_free(struct rtentry *rt, struct ifnet *ifp, int i_am_router)
{
struct llinfo_nd6 *ln = (struct llinfo_nd6 *)rt->rt_llinfo;
struct in6_addr in6 = satosin6(rt_key(rt))->sin6_addr;
- struct ifnet *ifp;
NET_ASSERT_LOCKED_EXCLUSIVE();
- ifp = if_get(rt->rt_ifidx);
-
if (!i_am_router) {
if (ln->ln_router) {
/*
@@ -669,8 +666,6 @@ nd6_free(struct rtentry *rt, int i_am_ro
*/
if (!ISSET(rt->rt_flags, RTF_STATIC|RTF_CACHED))
rtdeletemsg(rt, ifp, ifp->if_rdomain);
-
- if_put(ifp);
}
/*
@@ -1054,7 +1049,7 @@ nd6_cache_lladdr(struct ifnet *ifp, cons
return;
if ((rt->rt_flags & (RTF_GATEWAY | RTF_LLINFO)) != RTF_LLINFO) {
fail:
- nd6_free(rt, i_am_router);
+ nd6_free(rt, ifp, i_am_router);
rtfree(rt);
return;
}
route delete ifp NULL check