[orchagent]: _exit() on graceful shutdown to avoid destructor-chain memory corruption#4699
Conversation
|
/azp run |
|
Azure Pipelines successfully started running 1 pipeline(s). |
0ea8043 to
a258478
Compare
|
/azp run |
|
Azure Pipelines successfully started running 1 pipeline(s). |
|
Reviewed the fix and the root cause in #4600. The analysis holds up: Letting Reverting to Two optional nits, neither blocking:
LGTM. |
There was a problem hiding this comment.
Pull request overview
This PR changes orchagent shutdown behavior so that when a graceful shutdown is requested it drains the async swss.rec recorder queue and terminates via _exit(0) to avoid running the ~OrchDaemon destructor chain, which the PR description notes can trigger teardown-time memory corruption due to unsafe shutdown ordering.
Changes:
- Add a post-
OrchDaemon::start()shutdown path that disables async recording (drain/flush) and calls_exit(0)instead of returning normally.
|
tagging @vpandian-nokia @prsunny to approve. |
|
/azp run |
|
Azure Pipelines successfully started running 1 pipeline(s). |
Thanks for tagging me. The proposed fix in PR #4699 looks like the right minimal approach: drain the async recorder first, then use I see the required coverage check is failing because the new shutdown branch is not covered. A small UT for the shutdown decision path would be useful if practical, but I would not expect the UT to reproduce the heap corruption itself. I am not currently on the reviewer list, so I cannot formally approve, but from my review, the fix looks reasonable. |
|
@inder-nexthop , can you fix the coverage? |
@inder-nexthop, gentle ping on this. JFYI, there are some Ci failures observed in sonic-swss-common repo without this change. |
|
@deepak-singhal0408 , suggest revert the original PR since this fix is getting delayed. @inder-nexthop , @prabhataravind , @venkit-nexthop , @vpandian-nokia for viz. |
@prsunny had a teams chat with @inder-nexthop on this today. He is working on the code coverage part. lets wait till tomorrow? thanks, |
Thanks. May I know why fixing coverage is taking more than a week? I would like to understand submitter feedback and if something can be improved on infra. @theasianpianist for viz. |
Hi @prsunny , apologies for missing your initial comment. I was on PTO, working on resolving the comments mentioned in the review. |
…emory corruption Signed-off-by: Inder Pooni <inder@nexthop.ai>
… on _exit Move the shutdown-time recorder drain and _exit() out of main() into exit_if_graceful_shutdown_requested() in orchdaemon.cpp, which is linked into the mock test binary, and add unit tests covering both branches through an injectable exit function. _exit() skips libgcov's exit hook, so in --enable-gcov builds the vs test coverage harvest (SIGTERM, then lcov inside the container) would lose all of orchagent's coverage data. Define GCOV_ENABLED in gcov builds and dump the coverage counters before _exit(); reset them afterwards so a unit test calling the function with a non-exiting exit_fn still flushes the rest of its run at exit. Signed-off-by: Inder Pooni <inder@nexthop.ai>
a258478 to
b65e524
Compare
|
/azp run |
|
Azure Pipelines successfully started running 1 pipeline(s). |
…emory corruption (sonic-net#4699) * [orchagent]: _exit() on graceful shutdown to avoid destructor-chain memory corruption What I did Made orchagent exit through _exit(0) when a graceful shutdown is requested, instead of falling through to return 0 and running the full ~OrchDaemon destructor chain. Before exiting it drains the async swss recorder so any pending records are flushed first. Why I did it Fixes sonic-net#4600. PR sonic-net#4400 added graceful shutdown handling. On SIGTERM or SIGINT the signal handler sets gOrchShutdownRequested, OrchDaemon::start() returns, and main() falls through to return 0. That runs the full ~OrchDaemon destructor chain, which is not safe to run on shutdown. During that teardown, FlexCounterManager destruction makes blocking SAI calls (stopFlexCounterPolling, which calls set_switch_attribute). Those calls go through sairedis's ZMQ channel to syncd and park the main thread in zmq_poll while several libzmq IO threads are still running. Orchs that were deleted earlier in the reverse order delete loop have freed buffers that those IO threads still reference, so the process corrupts its own heap. We saw orchagent crash in a libzmq IO thread when METADATA configuration was changed and the containers were restarted. Signed-off-by: Yogapriya Mohankumar <ymohanku@cisco.com>
…emory corruption (sonic-net#4699) * [orchagent]: _exit() on graceful shutdown to avoid destructor-chain memory corruption What I did Made orchagent exit through _exit(0) when a graceful shutdown is requested, instead of falling through to return 0 and running the full ~OrchDaemon destructor chain. Before exiting it drains the async swss recorder so any pending records are flushed first. Why I did it Fixes sonic-net#4600. PR sonic-net#4400 added graceful shutdown handling. On SIGTERM or SIGINT the signal handler sets gOrchShutdownRequested, OrchDaemon::start() returns, and main() falls through to return 0. That runs the full ~OrchDaemon destructor chain, which is not safe to run on shutdown. During that teardown, FlexCounterManager destruction makes blocking SAI calls (stopFlexCounterPolling, which calls set_switch_attribute). Those calls go through sairedis's ZMQ channel to syncd and park the main thread in zmq_poll while several libzmq IO threads are still running. Orchs that were deleted earlier in the reverse order delete loop have freed buffers that those IO threads still reference, so the process corrupts its own heap. We saw orchagent crash in a libzmq IO thread when METADATA configuration was changed and the containers were restarted.
|
This PR has the "Request for 202605 Branch" label but is missing 202605-specific test results. Please:
Master branch test results alone are not sufficient for 202605 cherry-pick approval. Comment on behalf of @vaibhavhd |
|
This PR has backport request label(s) for branch(es): 202605, but is missing required test information. Please make sure you tick the tested branch(es) in the Tested branch section and provide test evidence (e.g., 202605: <test result>) in the Test result section as well in your PR description. ---Powered by SONiC BuildBot
|
|
This is live on the 202605 nightly. We root-caused a recurring 202605 nightly failure to the same shutdown-teardown hazard as #4600, but a different casualty. Surfaces as: the pre-test sanity of Root cause (symbolized core), same enabler (#4400 →
Why the fix is right: it skips the entire destructor chain (robust to any unsafe teardown, not a per-orch patch), drains the async recorder first so nothing is lost, and |
Not necessarily related to the changes in this PR or the fix, but this statement sounds like there's an object ownership problem in orchagent, with regards to who owns the buffers and is thus responsible for freeing those buffers. |
|
@inder-nexthop @venkit-nexthop could you help provide testing result from 202605 branch proving the fix is safe/addresses the problem? we should be able to repro the issue #4699 (comment)? |
Yes, you are right that there is an object ownership problem. If orchagent had been strictly implemented with object oriented concepts, it should have had classes that strictly own some resources and object of these classes should be referenced (and held in place) by other objects that use the resources. This would have created a clean destructor order that would avoid this kind of issue. |
|
Hey, sending over a patch with the test work backing the orchagent graceful shutdown fix (sonic-swss PR 4699). What it contains:
orchagent_teardown_ut_asan_fixes.patch Test matrix: |
|
Approving for 202605 |
|
Cherry-pick PR to 202605: #4788 |
What I did
Made orchagent exit through _exit(0) when a graceful shutdown is requested, instead of falling through to return 0 and running the full ~OrchDaemon destructor chain. Before exiting it drains the async swss recorder so any pending records are flushed first.
Why I did it
Fixes #4600.
PR #4400 added graceful shutdown handling. On SIGTERM or SIGINT the signal handler sets gOrchShutdownRequested, OrchDaemon::start() returns, and main() falls through to return 0. That runs the full ~OrchDaemon destructor chain, which is not safe to run on shutdown.
During that teardown, FlexCounterManager destruction makes blocking SAI calls (stopFlexCounterPolling, which calls set_switch_attribute). Those calls go through sairedis's ZMQ channel to syncd and park the main thread in zmq_poll while several libzmq IO threads are still running. Orchs that were deleted earlier in the reverse order delete loop have freed buffers that those IO threads still reference, so the process corrupts its own heap. We saw orchagent crash in a libzmq IO thread when METADATA configuration was changed and the containers were restarted.
How I verified it
Configure the route state async publish and ZMQ options in METADATA, then restart the containers so orchagent receives SIGTERM. Before this change orchagent crashes fairly consistently during teardown. With this change orchagent shuts down cleanly and the crash no longer reproduces.
Details if related
Draining the async swss recorder through setAsync(false) joins the recorder worker so pending records are flushed, which was the reason PR #4400 wanted start() to return cleanly.