Skip to content

Commit ba2fa22

Browse files
[frr]: Fix zebra NB RIB route lookup_next route_node lock under-run
Add FRR patch 0115 to drop the erroneous route_unlock_node() in the zebra northbound oper-state RIB route iterator (lib_vrf_zebra_ribs_rib_route_ lookup_next). The node returned by lookup_next() seeds the get_next -> route_next() iteration, which unlocks its input; releasing the lock in lookup_next() under-runs the route_node lock to zero and aborts zebra (assertion node->lock > 0) during the post-restart RIB sweep, wedging vtysh and stalling bgpd. Fixes: sonic-net/sonic-buildimage#27788 Signed-off-by: Deepak Singhal <deepsinghal@microsoft.com> (cherry picked from commit 0361265)
1 parent c4bfa66 commit ba2fa22

2 files changed

Lines changed: 61 additions & 0 deletions

File tree

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
From 19e6c1b652a4fb7263108d3b65b9b84d2b3f2d31 Mon Sep 17 00:00:00 2001
2+
From: Deepak Singhal <deepsinghal@microsoft.com>
3+
Date: Wed, 1 Jul 2026 18:07:22 +0000
4+
Subject: [PATCH] zebra: keep route_node lock in NB RIB route lookup_next
5+
6+
The northbound oper-state iterator for the
7+
/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route list uses lookup_next()
8+
to resume a yielded walk. lookup_next() calls route_table_get_next(),
9+
which returns a route_node with the lock it acquired held, and then
10+
immediately released it with route_unlock_node() before returning the
11+
node.
12+
13+
That node is carried back into the walk as args->list_entry and passed
14+
to lib_vrf_zebra_ribs_rib_route_get_next() -> srcdest_route_next() ->
15+
route_next(), which unlocks its input node as part of advancing. Because
16+
lookup_next() had already dropped the lock, route_next() under-runs the
17+
lock count to zero and trips:
18+
19+
zebra: assertion (node->lock > 0) failed
20+
21+
aborting zebra. This is hit when the oper-state notification walk (on by
22+
default since the FRR VRF YANG state was added) traverses a large RIB
23+
and yields, then resumes through lookup_next().
24+
25+
lookup_entry() is a self-contained point lookup and correctly unlocks
26+
the node it returns. lookup_next(), by contrast, seeds an iteration
27+
whose first step (route_next) expects the carried node to still be
28+
locked. Remove the erroneous route_unlock_node() so the iterator lock
29+
contract holds.
30+
31+
Link: https://github.com/sonic-net/sonic-buildimage/issues/27788
32+
Signed-off-by: Deepak Singhal <deepsinghal@microsoft.com>
33+
---
34+
zebra/zebra_nb_state.c | 10 ++++++++--
35+
1 file changed, 8 insertions(+), 2 deletions(-)
36+
37+
diff --git a/zebra/zebra_nb_state.c b/zebra/zebra_nb_state.c
38+
index 543d4b38b3..d1d214281f 100644
39+
--- a/zebra/zebra_nb_state.c
40+
+++ b/zebra/zebra_nb_state.c
41+
@@ -399,8 +399,14 @@ lib_vrf_zebra_ribs_rib_route_lookup_next(struct nb_cb_lookup_entry_args *args)
42+
if (!rn)
43+
return NULL;
44+
45+
- route_unlock_node(rn);
46+
-
47+
+ /*
48+
+ * Unlike lib_vrf_zebra_ribs_rib_route_lookup_entry() (a self-contained
49+
+ * point lookup), this function seeds the get_next iteration: the
50+
+ * returned node is carried as args->list_entry into
51+
+ * lib_vrf_zebra_ribs_rib_route_get_next() -> srcdest_route_next() ->
52+
+ * route_next(), which unlocks its input node. The node returned by
53+
+ * route_table_get_next() must therefore keep the lock it carries.
54+
+ */
55+
return rn;
56+
}
57+
58+
--
59+
2.34.1
60+

src/sonic-frr/patch/series

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,3 +82,4 @@
8282
0112-SONiC-ONLY-lib-build-add-configure-check-for-tc_mallinfo2-and-fallback.patch
8383
0113-zebra-Refactor-SRv6-netlink-code-to-remove-duplication.patch
8484
0114-Provide-interface-when-installing-uninstalling-SRv6-uA-SIDs.patch
85+
0116-zebra-keep-route_node-lock-in-NB-RIB-route-lookup_next.patch

0 commit comments

Comments
 (0)