From 4b4eaf3b76e73c17761ce12fb99dd6ceb635bd8d Mon Sep 17 00:00:00 2001 From: Martine Lenders Date: Wed, 5 May 2021 17:48:24 +0200 Subject: [PATCH] gnrc_ipv6_nib: fix acquire race on gnrc_ipv6_nib_get_next_hop_l2addr() When two threads use `gnrc_ipv6_nib_get_next_hop_l2addr()` to determine a next hop (e.g. when there is both an IPv6 sender and a 6LoWPAN fragment forwarder), a race condition may happen, where one thread acquires the NIB and the other acquires the network interface resulting in a deadlock. By releasing the NIB (if acquired) before trying to acquire the network interface and re-acquiring the NIB after the network interface is acquired, this is fixed. --- sys/net/gnrc/network_layer/ipv6/nib/nib.c | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/sys/net/gnrc/network_layer/ipv6/nib/nib.c b/sys/net/gnrc/network_layer/ipv6/nib/nib.c index d95502be62..4575c82e0f 100644 --- a/sys/net/gnrc/network_layer/ipv6/nib/nib.c +++ b/sys/net/gnrc/network_layer/ipv6/nib/nib.c @@ -186,6 +186,18 @@ static bool _on_link(const ipv6_addr_t *dst, unsigned *iface) return ipv6_addr_is_link_local(dst); } +static gnrc_netif_t *_acquire_new_iface(unsigned iface) +{ + gnrc_netif_t *netif = gnrc_netif_get_by_pid(iface); + /* release NIB, in case other thread calls a NIB function while we wait for + * the netif */ + _nib_release(); + gnrc_netif_acquire(netif); + /* re-acquire NIB */ + _nib_acquire(); + return netif; +} + int gnrc_ipv6_nib_get_next_hop_l2addr(const ipv6_addr_t *dst, gnrc_netif_t *netif, gnrc_pktsnip_t *pkt, gnrc_ipv6_nib_nc_t *nce) @@ -208,10 +220,9 @@ int gnrc_ipv6_nib_get_next_hop_l2addr(const ipv6_addr_t *dst, ipv6_addr_to_str(addr_str, dst, sizeof(addr_str))); /* on-link prefixes return their interface */ if (!ipv6_addr_is_link_local(dst) && (iface != 0)) { - /* release preassumed interface */ + /* release pre-assumed netif */ gnrc_netif_release(netif); - netif = gnrc_netif_get_by_pid(iface); - gnrc_netif_acquire(netif); + netif = _acquire_new_iface(iface); } if ((netif == NULL) || !_resolve_addr(dst, netif, pkt, nce, node)) { @@ -259,13 +270,12 @@ int gnrc_ipv6_nib_get_next_hop_l2addr(const ipv6_addr_t *dst, } } if ((netif != NULL) && (netif->pid != (int)route.iface)) { - /* drop pre-assumed netif */ + /* release pre-assumed netif */ gnrc_netif_release(netif); } if ((netif == NULL) || (netif->pid != (int)route.iface)) { /* get actual netif */ - netif = gnrc_netif_get_by_pid(route.iface); - gnrc_netif_acquire(netif); + netif = _acquire_new_iface(route.iface); } node = _nib_onl_get(&route.next_hop, (netif != NULL) ? netif->pid : 0);