Fix LedgerSecrets lock ordering - #8125
Merged
Merged
Conversation
Copilot started reviewing on behalf of
Amaury Chamayou (achamayou)
August 6, 2026 13:36
View session
Contributor
There was a problem hiding this comment.
Pull request overview
This PR removes a lock-order inversion between LedgerSecrets::lock and KV map locks by taking the encrypted-ledger-secrets KV dependency before acquiring LedgerSecrets::lock in read paths, and adds a focused TSAN test to catch regressions.
Changes:
- Reorder locking in
LedgerSecrets::get_latest(),get_latest_and_penultimate(), andget()to take the KV dependency before acquiringLedgerSecrets::lock. - Document the required lock order near dependency tracking in
LedgerSecrets. - Add
ledger_secrets_testand TSAN-specific test environment settings to detect lock-order inversions without suppressions.
Custom instructions used:
.github/copilot-instructions.md.github/instructions/reviewing.instructions.md
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| src/node/ledger_secrets.h | Moves KV dependency acquisition before LedgerSecrets::lock and documents lock ordering rationale. |
| src/node/test/ledger_secrets.cpp | Adds a regression test that exercises both lock orders (read methods + map hook rekey path). |
| CMakeLists.txt | Registers ledger_secrets_test and configures TSAN options for this test target. |
Co-authored-by: eddyashton <6000239+eddyashton@users.noreply.github.com>
Co-authored-by: eddyashton <6000239+eddyashton@users.noreply.github.com>
Co-authored-by: achamayou <4016369+achamayou@users.noreply.github.com>
Member
Author
Co-authored-by: achamayou <4016369+achamayou@users.noreply.github.com>
Eddy Ashton (eddyashton)
approved these changes
Aug 6, 2026
Eddy Ashton (eddyashton)
left a comment
Member
There was a problem hiding this comment.
Great, the comment explaining DETECT_DEADLOCKS is really what I was looking to be checked in, and having that function will let us do the same for future unit tests as we gradually unpick the suppressions. LGTM!
This was referenced Aug 10, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
LedgerSecrets::lockinget_latest(),get_latest_and_penultimate(), andget().ledger_secrets_testthat runs without TSAN suppressions in TSAN builds.Why
Local KV map hooks run while their map is still locked. The rekey path calls
LedgerSecrets::set_secret()from such a hook, establishing the order:The affected read methods previously held
LedgerSecrets::lockwhiletake_dependency_on_secrets()opened and read the encrypted-secrets KV map, establishing the inverse order:That cycle is an undesirable lock-order inversion and can deadlock when the paths overlap. Taking the KV dependency first means the KV handle operation completes before
LedgerSecrets::lockis acquired, so the inverse nested order no longer exists.This does not weaken synchronization: every access to the in-memory
ledger_secretsmap remains protected byLedgerSecrets::lock. The KV operation only records the transaction dependency. If a rekey races between dependency tracking and the in-memory read, the transaction dependency preserves conflict detection for committable transactions. The implementation performs the same operations and shortens the mutex critical section, so it introduces no additional allocation, copying, or synchronization overhead.Regression coverage
The new test calls all three affected read methods, then commits to the encrypted-secrets table with a local map hook that calls
set_secret(). This deterministically exposes both lock orders to TSAN without requiring concurrent timing.For this target only, CTest replaces the repository-wide TSAN suppression setting with
detect_deadlocks=1,halt_on_error=1, andsecond_deadlock_stack=1. A controlled run with the old ordering fails withThreadSanitizer: lock-order-inversion; the fixed ordering passes.Testing
ledger_secrets_test,node_frontend_test,encryptor_test,historical_queries_test, andsnapshotter_testin the normal build: all pass.ledger_secrets_testin a TSAN build with no suppressions: passes.git diff --check: pass.Extracted as a focused fix from the issues investigated in #7744.