diff options
author | Alexander Bluhm <bluhm@cvs.openbsd.org> | 2022-08-29 07:51:46 +0000 |
---|---|---|
committer | Alexander Bluhm <bluhm@cvs.openbsd.org> | 2022-08-29 07:51:46 +0000 |
commit | 7af2afb19278351638c297087b8ffc5e6602cdfd (patch) | |
tree | a89c527374c1de0aa8c86938f7366ec77a25f91e /sys/net/route.c | |
parent | 8eb0469369fd4bcdf15084a38130f428d6ab5824 (diff) |
Use struct refcnt for interface address reference counting.
There was a crash due to use after free of the ifa although it is
ref counted. As ifa_refcnt was a simple integer increment, there
may be a path where multiple CPUs access it concurrently. So change
to struct refcnt which is MP safe and provides dt(4) leak debugging.
Link level address for IPsec enc(4) and various MPLS interfaces is
special. There ifa is part of struct sc. Use refcount anyway and
add a panic to detect use after free.
bug report stsp@; OK mvs@
Diffstat (limited to 'sys/net/route.c')
-rw-r--r-- | sys/net/route.c | 27 |
1 files changed, 14 insertions, 13 deletions
diff --git a/sys/net/route.c b/sys/net/route.c index 7d1aeeca26a..6f5cb089aec 100644 --- a/sys/net/route.c +++ b/sys/net/route.c @@ -1,4 +1,4 @@ -/* $OpenBSD: route.c,v 1.413 2022/07/28 22:19:09 bluhm Exp $ */ +/* $OpenBSD: route.c,v 1.414 2022/08/29 07:51:45 bluhm Exp $ */ /* $NetBSD: route.c,v 1.14 1996/02/13 22:00:46 christos Exp $ */ /* @@ -146,7 +146,6 @@ extern unsigned int rtmap_limit; struct cpumem * rtcounters; int rttrash; /* routes not in table but not freed */ -int ifatrash; /* ifas not in ifp list but not free */ struct pool rtentry_pool; /* pool for rtentry structures */ struct pool rttimer_pool; /* pool for rttimer structures */ @@ -512,16 +511,19 @@ rtfree(struct rtentry *rt) pool_put(&rtentry_pool, rt); } +struct ifaddr * +ifaref(struct ifaddr *ifa) +{ + refcnt_take(&ifa->ifa_refcnt); + return ifa; +} + void ifafree(struct ifaddr *ifa) { - if (ifa == NULL) - panic("ifafree"); - if (ifa->ifa_refcnt == 0) { - ifatrash--; - free(ifa, M_IFADDR, 0); - } else - ifa->ifa_refcnt--; + if (refcnt_rele(&ifa->ifa_refcnt) == 0) + return; + free(ifa, M_IFADDR, 0); } /* @@ -901,8 +903,7 @@ rtrequest(int req, struct rt_addrinfo *info, u_int8_t prio, rt_mpls_clear(rt); #endif - ifa->ifa_refcnt++; - rt->rt_ifa = ifa; + rt->rt_ifa = ifaref(ifa); rt->rt_ifidx = ifp->if_index; /* * Copy metrics and a back pointer from the cloned @@ -1857,8 +1858,8 @@ db_print_ifa(struct ifaddr *ifa) db_print_sa(ifa->ifa_dstaddr); db_printf(" ifa_mask="); db_print_sa(ifa->ifa_netmask); - db_printf(" flags=0x%x, refcnt=%d, metric=%d\n", - ifa->ifa_flags, ifa->ifa_refcnt, ifa->ifa_metric); + db_printf(" flags=0x%x, refcnt=%u, metric=%d\n", + ifa->ifa_flags, ifa->ifa_refcnt.r_refs, ifa->ifa_metric); } /* |