[frr]: Fix zebra NB RIB route lookup_next route_node lock under-run#28094
Conversation
There was a problem hiding this comment.
Pull request overview
This PR fixes an FRR zebra crash triggered during northbound oper-state RIB iteration after a bgp container restart by correcting the route_node lock/unlock contract in lookup_next via a new downstream patch in the sonic-frr patch stack.
Changes:
- Append patch
0115-zebra-keep-route_node-lock-in-NB-RIB-route-lookup_next.patchto thesrc/sonic-frr/patch/seriesstack. - Add a new FRR patch that removes an erroneous
route_unlock_node(rn)inlib_vrf_zebra_ribs_rib_route_lookup_next()and documents why the node must remain locked.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
| src/sonic-frr/patch/series | Registers the new FRR patch in the applied patch series. |
| src/sonic-frr/patch/0115-zebra-keep-route_node-lock-in-NB-RIB-route-lookup_next.patch | Removes the incorrect unlock in zebra’s NB RIB lookup_next and adds an explanatory comment. |
|
/azp run Azure.sonic-buildimage |
|
Azure Pipelines successfully started running 1 pipeline(s). |
|
@deepak-singhal0408 can you please help fix conflicts? |
e17f66f to
bbdb8db
Compare
|
/azp run Azure.sonic-buildimage |
|
Azure Pipelines successfully started running 1 pipeline(s). |
@tirupatihemanth fixed. could you help confirm if the issue
@tirupatihemanth fixed. Could you or @yizhenzha confirm if this fixes the issue as well from your side. |
|
@deepak-singhal0408 I applied this patch and tested. I confirmed this fixes the issue. |
bbdb8db to
0003e58
Compare
|
/azp run Azure.sonic-buildimage |
|
Azure Pipelines successfully started running 1 pipeline(s). |
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#27788 Signed-off-by: Deepak Singhal <deepsinghal@microsoft.com>
0003e58 to
b0de3d5
Compare
|
/azp run Azure.sonic-buildimage |
|
Azure Pipelines successfully started running 1 pipeline(s). |
Thanks @yizhenzha.. @dgsudharsan, @tirupatihemanth @yizhenzha could you help review/sign-off the PR as well. FYI, The fix has been upstreamed and merged there as well. |
|
Hi @eddieruan-alibaba @lguohan @yxieca could you help merge this PR? |
|
@GaladrielZhao will review it on Alibaba's behave. |
|
The change is not in 202605 yet. @deepak-singhal0408, please manually create the cherry pick PR for branch 202605. ---Powered by SONiC BuildBot
|
…backport to 202608) (#2629) #### Why I did it Backport of sonic-net/sonic-buildimage#28094 (fixes sonic-net/sonic-buildimage#27788) to the `202608` release branch. The master PR carries the `Cherry Pick Conflict_msft-202608` label because the auto cherry-pick did not apply, so this is the manual backport PR. The bug: the zebra northbound oper-state RIB route iterator (`lib_vrf_zebra_ribs_rib_route_lookup_next`) erroneously calls `route_unlock_node()` on the node it returns. That node 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. ##### Work item tracking - Microsoft ADO **(number only)**: N/A #### How I did it Cherry-picked the merged master commit `0361265da273e4a08d03831c1515e041e4ba3de6` (the FRR `route_node` lock fix). Conflict resolution: the only conflict was in `src/sonic-frr/patch/series`. On master the fix is registered as `0116`, right after an unrelated patch (`0115-zebra-Update-promiscuity-flag-silently...`, which touches `zebra/if_netlink.c`) that is **not** present on `202608`. Since the fix touches `zebra/zebra_nb_state.c` (a different file) and is independent of that unrelated patch, it was registered at 202608's next free slot **`0115`** (series ended at `0114`) and the unrelated promiscuity patch was not carried. The patch body is byte-identical to master's. Base parity: the FRR submodule pin is byte-identical (`4cb6d9e6bfe4ad503d1fab21e6f665804b0649ac`) on the master parent, the master fix commit, and `202608` — so the fix applies onto identical FRR source. #### How to verify it - Applied the full `202608` FRR patch series (all 84 patches, including the backported fix) in order onto the pinned FRR source `4cb6d9e6...` — every patch applies cleanly; the fix integrates at its `lookup_next` region with no overlap from preceding patches. - Behavior was A/B-validated on master in sonic-net/sonic-buildimage#28094: the zebra abort artifact is present without the fix and absent with it. Because the FRR base is byte-identical on `202608`, that validation carries over. - Full build + integration is covered by this PR's CI on the `202608` base. #### Which release branch to backport (provide reason below if selected) - [ ] 202305 - [ ] 202311 - [ ] 202405 - [ ] 202411 - [ ] 202505 - [ ] 202511 This PR **is** the release-branch backport (base = `202608`), so no further backport checkbox is selected. #### Tested branch (Please provide the tested image version) - [ ] 202608 (FRR patch series apply verified against pinned FRR `4cb6d9e6`; behavior verified on master per sonic-net/sonic-buildimage#28094) #### Description for the changelog [frr]: Fix zebra NB RIB route lookup_next route_node lock under-run #### Link to config_db schema for YANG module changes N/A Signed-off-by: Deepak Singhal <deepsinghal@microsoft.com>
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. Backport of sonic-net#28094 to 202605. Fixes: sonic-net#27788 Signed-off-by: Deepak Singhal <deepsinghal@microsoft.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…28260) 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. Backport of #28094 to 202605. Fixes: #27788 Signed-off-by: Deepak Singhal <deepsinghal@microsoft.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Why I did it
Fixes #27788.
When the
bgpcontainer restarts (e.g. critical-process SIGKILL /container_autorestart), zebra aborts during the post-restart RIB sweep with an assertion failure (node->lock > 0), wedgingvtyshand stallingbgpd.Root cause is in zebra's northbound oper-state RIB route iterator.
lib_vrf_zebra_ribs_rib_route_lookup_next()callsroute_table_get_next()(which returns the next node already locked, seeding theget_nextiteration) and then erroneously callsroute_unlock_node()on it. On resume-after-yield,get_next -> route_next()unlocks its input node again, under-running theroute_nodelock to zero and tripping the assertion that aborts zebra. The yielding oper walk fires autonomously after restart because, on FRR >= 10.5.2, VRF enable notifies the bare VRF node (/frr-vrf:lib/vrf[name="Vrf1"]) and the resulting walk descends into the whole VRF subtree (including the ~12.9k-route RIB), so it yields and resumes throughlookup_next.The point-lookup sibling (
lib_vrf_zebra_ribs_rib_route_lookup_entry) correctly keeps its unlock and is left untouched — this fix is intentionally minimal (delete the single erroneous unlock).Work item tracking
How I did it
This is the SONiC downstream backport of the upstream FRRouting fix FRRouting/frr#22490 (FRR issue FRRouting/frr#22489).
That upstream PR carries two commits:
zebra: keep route_node lock in NB RIB route lookup_next— removes the erroneousroute_unlock_node(rn)inlib_vrf_zebra_ribs_rib_route_lookup_next(). This is the crash fix (thenode->lock > 0abort).zebra: skip link-local routes without leaking node lock in NB RIB walk— folds the IPv6 link-local filter inlib_vrf_zebra_ribs_rib_route_get_next()into the empty-node skip loop, fixing a lock leak + walk truncation (missing oper-data routes). This is a correctness fix that cannot crash.Only commit (1) is backported here, as FRR patch
0116-zebra-keep-route_node-lock-in-NB-RIB-route-lookup_next.patch(registered insrc/sonic-frr/patch/series). The patch is the verbatim upstream commit — its commit message and in-code comment are unchanged from upstream commit (1); only the diff hunk offset differs (SONiC pins FRR 10.5.4, so the same change lands at@@ -399instead of upstream's@@ -466). It removes the erroneousroute_unlock_node(rn)fromzebra/zebra_nb_state.cwith a clarifying comment that the node returned byroute_table_get_next()must stay locked because it seeds theget_nextiteration.Why only one commit: SONiC carries FRR as a pinned tag plus a
patch/seriesof downstream patches; per policy we backport only what SONiC needs now to fix the observed defect (issue 27788 — the crash), and drop the patch once the FRR pin bumps to a tag that already contains the upstream commit. Commit (2) does not address the crash (it is a non-crashing oper-data correctness fix), so it is intentionally not carried as a downstream patch; it will arrive automatically via the normal FRR version bump once upstream PR 22490 merges and SONiC re-pins to a newerfrrtag. This keeps the downstream patch set minimal.How to verify it
Reproduce on a
t1-lagVS testbed with a populated Vrf1 RIB (both IPv4 and IPv6 converged, ~12.9k routes total), then SIGKILL zebra (docker exec bgp kill -9 $(pidof zebra)) so the kernel RIB is preserved and the restarted zebra reads the full RIB at startup.signal 6 ... aborting); core generated; backtraceroute_next -> _zlog_assert_failed -> abortvialib_vrf_zebra_ribs_rib_route_get_next, in threadnb_op_walk_continue scheduled from nb_op_yield();vtyshwedged.vtyshresponsive. Matched A/B over identical DUT/trigger/RIB scale: unfixed crashed at trial 2; fixed survived 5/5.tests/autorestart/test_container_autorestart.py::test_containers_autorestart[<dut>-None-bgp]PASSES on the fixed image (all critical processes restart healthy; mandatory post-test core-dump check passes), whereas the unfixed image would core during the restart sweep and fail that check.Which release branch to backport (provide reason below if selected)
Reason: 202605 only. 202605 ships FRR 10.5.4 (same as master), where the crash self-fires during the post-restart RIB sweep — the same nightly autorestart signature this PR fixes on master. The buggy
route_unlock_node(rn)line is byte-identical at the 202605 FRR pin, so the patch applies as-is.Not backported to 202511 / 202505 / 202411. The autonomous self-fire requires the bare-VRF oper-notification walk introduced in FRR 10.5.2, which these pins predate (202511 = 10.4.1, 202505 = 10.3, 202411 = 10.0.1). On those branches the lock bug is present but only reachable via an explicit oper-state GET of the RIB — and no SONiC component issues such a GET (only
vtyshcan, andshow ip route jsondoes not drive the northbound oper path), so the crash does not manifest in SONiC there. Verified empirically for 202511 (recent nightly autorestart runs: zebra healthy, 0 cores / 0 rc=124). Per the minimal/need-driven backport policy, the fix is not carried where the defect is unreachable; it will still arrive on those pins automatically once the FRR version bumps to a tag containing upstream PR 22490.Tested branch (Please provide the tested image version)
docker-fpm-frr7d46bd9139a5)Description for the changelog
zebra: keep route_node lock in NB RIB route lookup_next (fix lock under-run abort on restart)
Link to config_db schema for YANG module changes
N/A — no CONFIG_DB schema or YANG changes.
A picture of a cute animal (not mandatory but encouraged)
🦦