fix(providers): prevent TOCTOU race in _discover_providers() - #24696
Closed
wesleysimplicio wants to merge 1 commit into
Closed
wesleysimplicio wants to merge 1 commit into
wesleysimplicio wants to merge 1 commit into
Conversation
_discovered was set to True before any plugin directory was scanned or imported, so a second thread could observe _discovered=False, enter the discovery work, and race the first thread — leaving the registry partially populated or empty for callers that checked between the flag write and the actual scan. Apply double-checked locking: acquire _discover_lock, re-check the flag inside the lock, run all three discovery phases (bundled plugins, user plugins, legacy per-file modules), then set _discovered=True as the final step inside the lock. Adds a 50-thread barrier regression test and a spy test that asserts _discovered remains False while plugins are being imported. Closes NousResearch#24694 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2 tasks
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.
What does this PR do?
Fixes a time-of-check/time-of-use (TOCTOU) race condition in
providers/_discover_providers().Root cause
The detailed rationale from the original PR body is preserved below. This template update keeps the review structure consistent with #29640.
Fix
providers/__init__.py: add_discover_lock = threading.Lock(); indent all three discovery sections insidewith _discover_lock:; move_discovered = Trueto after all work completestests/providers/test_plugin_discovery.py: addTestDiscoverProvidersToctouRacewith a 50-threadthreading.Barriercontention test and a spy test asserting the flag staysFalsewhile plugins are being importedWhy this shape
This shape mirrors #29640 so reviewers can quickly compare scope, root cause, fix, tests, and related context without having to decode a custom PR description.
Tests
Original body
Related PRs / issues
Closes #24694
Original body
Summary
Fixes a time-of-check/time-of-use (TOCTOU) race condition in
providers/_discover_providers().What Changed
Fluxo
A mudança continua seguindo o fluxo original descrito na seção preservada abaixo, sem ampliar o escopo funcional deste PR.
Visão
A padronização melhora a revisão, reduz ruído e evita deriva de formatação entre PRs abertos.
Test Plan
Original body
What does this PR do?
Summary
Fixes a time-of-check/time-of-use (TOCTOU) race condition in
providers/_discover_providers().Root cause:
_discoveredwas set toTrueat the top of the function, before any plugin directory was scanned or any module was imported. A second thread entering concurrently could observe_discovered=False, pass the early-return guard, then race the first thread through the three discovery phases — resulting in a partially populated registry visible to callers.Fix: Double-checked locking pattern:
if _discovered: return) remains for the already-initialised steady state_discover_lock, re-checks the flag inside the lock, runs all three discovery phases (bundled plugins → user plugins → legacy per-file modules), then sets_discovered = Trueas the last statement inside the lockThis matches the pattern already applied to
agent/transports/__init__.py(#24692) andplugins/platforms/google_chat/adapter.py(#24679).Impact
get_provider_profile()has no retry-on-miss logic — it returnsNonedirectly if the name is absent from the registry. Under the old code, a race could cause valid provider names to returnNone, silently falling back to generic behaviour for the lifetime of the process.Changes
providers/__init__.py: add_discover_lock = threading.Lock(); indent all three discovery sections insidewith _discover_lock:; move_discovered = Trueto after all work completestests/providers/test_plugin_discovery.py: addTestDiscoverProvidersToctouRacewith a 50-threadthreading.Barriercontention test and a spy test asserting the flag staysFalsewhile plugins are being importedTest plan
uv run pytest tests/providers/— 94 passedTestDiscoverProvidersToctouRaceclass: 2 new tests (concurrent consistency + flag-ordering invariant)Closes #24694
Solution Sketch
Related Issue
Closes #24694
Type of Change
Changes Made
.github/PULL_REQUEST_TEMPLATE.mdHow to Test
Checklist
Code
fix(scope):,feat(scope):, etc.)pytest tests/ -qand all tests passDocumentation & Housekeeping
docs/, docstrings) — or N/Acli-config.yaml.exampleif I added/changed config keys — or N/ACONTRIBUTING.mdorAGENTS.mdif I changed architecture or workflows — or N/AScreenshots / Logs
Generated by Hermes Turbo
Generated by Hermes Turbo