Skip to content

UDP control sender can deadlock on exception while holding sendLocker

Summary

CClientUDPSocket::SendControlData manually locks sendLocker, removes packet ownership from the queue, allocates a raw send buffer, copies packet data, and performs socket sends before manually unlocking. An allocation failure, MFC exception, or unexpected exception in that region can leave sendLocker held and leak the removed packet.

This blocks Beta 0.7.3 because the function is explicitly called from another thread and can silently freeze UDP control traffic.

Evidence

  • srchybrid/ClientUDPSocket.cpp:442 notes the function runs from a different thread.
  • srchybrid/ClientUDPSocket.cpp:446 manually locks sendLocker.
  • srchybrid/ClientUDPSocket.cpp:450 removes UDPPack ownership from the queue.
  • srchybrid/ClientUDPSocket.cpp:459 allocates a raw uchar buffer while the lock is held.
  • srchybrid/ClientUDPSocket.cpp:487 manually unlocks after the send loop.

Execution Plan

  1. Revalidate the queue ownership contract and which thread(s) can call SendControlData.
  2. Replace manual lock/unlock with an exception-safe scoped lock compatible with the existing MFC synchronization style.
  3. Make removed UDPPack and temporary send buffer ownership exception-safe without changing packet ordering or resend semantics.
  4. Preserve existing UDP throttler behavior, including requeue-on-send-failure.
  5. Add targeted coverage for allocation failure or an injected send failure while ensuring the lock is released and the queue remains usable.
  6. Run Kad/client UDP regression coverage, including the existing crypt-gating seam.

Acceptance Criteria

  • No exception path can leave sendLocker locked.
  • Removed packets are either sent, requeued, or destroyed exactly once.
  • Valid ED2K and Kad UDP send behavior is unchanged.
  • The upload bandwidth throttler can continue after injected failure.

Validation

  • 2026-05-08: Done in app commit 4796d2f.
  • python -m emule_workspace build app --workspace-root . --config Release --platform x64 --variant main passed; log root workspaces\v0.72a\state\build-logs\20260508-094757.
  • python -m emule_workspace validate --workspace-root . passed.
  • python -m emule_workspace build tests --workspace-root . --config Release --platform x64 --test-run-variant main passed; log root workspaces\v0.72a\state\build-logs\20260508-094821.
  • Focused doctest passed: repos\emulebb-build-tests\build\eMulebb-workspace-v0.72a-eMule-main\x64\Release\emule-tests.exe --test-suite=parity --test-case="Client UDP seam gates outgoing encryption on the global crypt preference".
  • Code validation: SendControlData now uses scoped locking and scoped packet and send-buffer ownership, so exceptions release sendLocker and do not leak removed packets.