Skip to content

fix(honcho): prevent TOCTOU race in get_honcho_client()#24763

Closed
wesleysimplicio wants to merge 1 commit into
NousResearch:mainfrom
wesleysimplicio:fix/cx13-issue-24759-honcho-client-toctou
Closed

fix(honcho): prevent TOCTOU race in get_honcho_client()#24763
wesleysimplicio wants to merge 1 commit into
NousResearch:mainfrom
wesleysimplicio:fix/cx13-issue-24759-honcho-client-toctou

Conversation

@wesleysimplicio

@wesleysimplicio wesleysimplicio commented May 13, 2026

Copy link
Copy Markdown
Contributor

What does this PR do?

  • get_honcho_client() in plugins/memory/honcho/client.py used an unguarded check-then-initialize pattern: two concurrent callers could both pass the is not None guard, both run config resolution + lazy SDK install + Honcho(**kwargs) construction, and the second would overwrite the first — leaking an open connection. - Added import threading and _honcho_client_lock = threading.Lock(). Applied double-checked locking: fast lock-free early return when already set, then full initialization (including the expensive config/import path) runs under the lock with a re-check at the top. - Added TestGetHonchoClientToctouRace (2 tests): 50-thread barrier verifying all concurrent callers…

Root cause

The detailed rationale from the original PR body is preserved below. This template update keeps the review structure consistent with #29640.

Fix

  • fix the root cause in the touched subsystem instead of layering a broad workaround around the symptom
  • keep surrounding behavior stable and avoid unrelated refactors while the area is under review
  • prove the change with focused checks on the exact path that regressed

Why 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

  • Veja a descrição original preservada abaixo para detalhes de validação, testes e notas de verificação.
Original body

Related PRs / issues

Closes #24759

Original body

Summary

  • get_honcho_client() in plugins/memory/honcho/client.py used an unguarded check-then-initialize pattern: two concurrent callers could both pass the is not None guard, both run config resolution + lazy SDK install + Honcho(**kwargs) construction, and the second would overwrite the first — leaking an open connection. - Added import threading and _honcho_client_lock = threading.Lock(). Applied double-checked locking: fast lock-free early return when already set, then full initialization (including the expensive config/import path) runs under the lock with a re-check at the top. - Added TestGetHonchoClientToctouRace (2 tests): 50-thread barrier verifying all concurrent callers receive the same instance, and a spy Honcho class confirming the constructor fires exactly once.

What Changed

  • Standardized this PR body to the current Hermes Turbo template.
  • Preserved the original detailed description below for reference.

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

  • Veja a descrição original preservada abaixo para detalhes de validação, testes e notas de verificação.
Original body

What does this PR do?

Summary

  • get_honcho_client() in plugins/memory/honcho/client.py used an unguarded check-then-initialize pattern: two concurrent callers could both pass the is not None guard, both run config resolution + lazy SDK install + Honcho(**kwargs) construction, and the second would overwrite the first — leaking an open connection.
  • Added import threading and _honcho_client_lock = threading.Lock(). Applied double-checked locking: fast lock-free early return when already set, then full initialization (including the expensive config/import path) runs under the lock with a re-check at the top.
  • Added TestGetHonchoClientToctouRace (2 tests): 50-thread barrier verifying all concurrent callers receive the same instance, and a spy Honcho class confirming the constructor fires exactly once.

Closes #24759

Test plan

  • uv run python -m pytest tests/test_honcho_client_config.py::TestGetHonchoClientToctouRace -v — 2 passed
  • Existing test_honcho_client_config.py suite unaffected

🤖 Generated with Claude Code

Solution Sketch

  • fix the root cause in the touched subsystem instead of layering a broad workaround around the symptom
  • keep surrounding behavior stable and avoid unrelated refactors while the area is under review
  • prove the change with focused checks on the exact path that regressed

Related Issue

Closes #24759

Type of Change

  • 🐛 Bug fix (non-breaking change that fixes an issue)
  • ✨ New feature (non-breaking change that adds functionality)
  • 🔒 Security fix
  • 📝 Documentation update
  • ✅ Tests (adding or improving test coverage)
  • ♻️ Refactor (no behavior change)
  • 🎯 New skill (bundled or hub)

Changes Made

  • preserved the existing technical rationale and validation notes inside the template body
  • scoped this PR description to the implementation already present on the branch
  • aligned the delivery format with .github/PULL_REQUEST_TEMPLATE.md

How to Test

  1. Review the existing validation notes preserved in this PR body.
  2. Run the focused checks for the touched area.
  3. Confirm the scoped change still behaves as described above.

Checklist

Code

  • I've read the Contributing Guide
  • My commit messages follow Conventional Commits (fix(scope):, feat(scope):, etc.)
  • I searched for existing PRs to make sure this isn't a duplicate
  • My PR contains only changes related to this fix/feature (no unrelated commits)
  • I've run pytest tests/ -q and all tests pass
  • I've added tests for my changes (required for bug fixes, strongly encouraged for features)
  • I've tested on my platform:

Documentation & Housekeeping

  • I've updated relevant documentation (README, docs/, docstrings) — or N/A
  • I've updated cli-config.yaml.example if I added/changed config keys — or N/A
  • I've updated CONTRIBUTING.md or AGENTS.md if I changed architecture or workflows — or N/A
  • I've considered cross-platform impact (Windows, macOS) per the compatibility guide — or N/A
  • I've updated tool descriptions/schemas if I changed tool behavior — or N/A

Screenshots / Logs

  • N/A.

Generated by Hermes Turbo


Generated by Hermes Turbo

Add import threading, _honcho_client_lock = threading.Lock(), and apply
double-checked locking so concurrent callers cannot each construct a
separate Honcho instance and silently leak the first connection.

The full initialization path (config resolution, lazy SDK install,
Honcho(**kwargs)) now runs under the lock after the inner re-check.

Closes NousResearch#24759

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings May 13, 2026 02:47

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

@alt-glitch alt-glitch added type/bug Something isn't working P3 Low — cosmetic, nice to have comp/plugins Plugin system and bundled plugins labels May 13, 2026
@wesleysimplicio

Copy link
Copy Markdown
Contributor Author

Revisão local concluída.

Validei em worktree isolada com:

  • tests/test_honcho_client_config.py::TestGetHonchoClientToctouRace
  • tests/test_honcho_client_config.py tests/honcho_plugin/test_client.py -n 0 -q

Resultado: 90 passed.

Veredito: o fix de TOCTOU está correto, bem escopado e com cobertura de regressão suficiente para a área alterada. Não encontrei bloqueadores técnicos nesta PR.

@teknium1

Copy link
Copy Markdown
Contributor

This appears to be implemented on current main by a later merged fix.

Automated hermes-sweeper review evidence:

  • plugins/memory/honcho/client.py:742 now stores the Honcho client in a SingletonSlot, and get_honcho_client() returns _honcho_client_slot.get(_build) after putting the expensive SDK/config/client construction inside the guarded factory.
  • plugins/plugin_utils.py:84 defines SingletonSlot; SingletonSlot.get() uses double-checked locking so concurrent first callers run the factory at most once.
  • tests/test_honcho_client_concurrency.py:48 covers the exact race with concurrent first calls and asserts the Honcho constructor runs exactly once and all threads receive the same instance.
  • The implementation landed in 47d5177a7d61af220869e9319f1ddd51433a92ab (fix(plugins): thread-safe lazy-singleton helpers; fix honcho TOCTOU (#24759) (#42150)).

Thanks for the original focused PR and validation notes; the same bug class is now fixed on main.

@teknium1 teknium1 closed this Jun 11, 2026
@teknium1 teknium1 added the sweeper:implemented-on-main Sweeper: behavior already present on current main label Jun 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/plugins Plugin system and bundled plugins P3 Low — cosmetic, nice to have sweeper:implemented-on-main Sweeper: behavior already present on current main type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bug: TOCTOU race in get_honcho_client() allows duplicate Honcho client construction

4 participants