Repository navigation
fix(ffi): dispose CredSSP generator before reuse - #2160
Conversation
There was a problem hiding this comment.
🟢 Approval recommended
The scoped disposal resolves the overlapping borrow while preserving the independently owned client state.
No open issues 🎉
What changed in this PR
Fixes the .NET CredSSP loop by disposing its generator before reusing the sequence.
Changes:
- Scopes generator disposal around client-state resolution.
- Releases the exclusive borrow before calling
HandleProcessResult.
| File | Description |
|---|---|
| ffi/dotnet/Devolutions.IronRdp/src/ConnectionHelpers.cs | Adds deterministic generator disposal before sequence reuse. |
Review effort: Balanced
There was a problem hiding this comment.
The PR wraps the CredSSP generator in a block-scoped using in PerformCredsspSteps so the generator box (which holds an exclusive &mut CredsspSequence across the FFI boundary) is destroyed before credsspSequence.HandleProcessResult takes a second mutable borrow. Independently verified: get_client_state_if_completed(&self) clones the ClientState out (ffi/src/credssp/network.rs:62), so disposing before HandleProcessResult is safe and covers ResolveGenerator exception paths. The fix is correct and load-bearing (a using-var form would keep the generator alive across HandleProcessResult). The single valid skeptical candidate is a low-severity, pre-existing hygiene note that the same loop still abandons sibling FFI objects (TsRequest replacement at line 91, GeneratorState boxes in Connection.ResolveGenerator) to nondeterministic finalizers; it holds no borrow of the sequence, so no aliasing recurs, and it does not block the PR.
- [skeptical] Generator disposal fixes the aliasing, but the same loop still abandons other Rust-owned FFI objects to finalizers — low 🟡 — ffi/dotnet/Devolutions.IronRdp/src/ConnectionHelpers.cs
The fix deterministically releases the CredsspProcessGenerator box, which uniquely borrows &mut CredsspSequence, so the stated aliasing UB is resolved (get_client_state_if_completed(&self) clones the ClientState out, so disposal cannot invalidate it, and using also covers ResolveGenerator exception paths). However, in the same loop each iteration replaces tsRequest (tsRequest = decoded, line 91) without disposing the previous TsRequest box, and Connection.ResolveGenerator (Connection.cs:61,88) allocates a new GeneratorState box per Start/Resume without disposing any; all rely on nondeterministic diplomat finalizers. These hold no borrow of the CredsspSequence, so no aliasing recurs, but ownership handling in this method becomes inconsistent: one FFI object gets explicit using while siblings get finalization. Pre-existing and non-regressing; it does not block this PR and should be addressed in a follow-up or the PR should record that only the generator uniquely borrows the sequence and thus warrants explicit disposal.
Push a commit after addressing these findings. If no code change is needed, you may resolve inline threads and comment @github-actions review-ready to request human review.
cfe38f7 to
b2f19ee
Compare
`ProcessTsRequest` returns a generator that holds an exclusive borrow of the CredSSP sequence. The CredSSP loop left it alive and then called `HandleProcessResult` on the same sequence, so two `&mut` borrows of it were live at once. Dispose the generator as soon as it has produced the client state. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
b2f19ee to
869e3d0
Compare
There was a problem hiding this comment.
🟢 PR 2160 wraps the CredsspProcessGenerator returned by ProcessTsRequest in a scoped using block in ConnectionHelpers.PerformCredsspSteps so it is disposed before credsspSequence.HandleProcessResult is invoked on the same sequence. Independently verified: CredsspProcessGenerator is a Diplomat IDisposable freeing a raw Rust allocation; ProcessTsRequest returns it holding an exclusive borrow of the sequence; Connection.ResolveGenerator consumes the generator to completion and returns an independently owned ClientState, so disposal after resolution (including on exception paths via the using block) is safe. The scoped form is the minimal correct construct - a using-declaration would dispose only at end of loop iteration, after HandleProcessResult, reintroducing the aliasing. Single call site, no scope creep. No valid specialist candidates and no independently discovered issues; findings and candidate_dispositions are empty.
3a3c27d
into
master
ProcessTsRequestreturns a generator that holds an exclusive borrow of the CredSSP sequence. The CredSSP loop left it alive and then calledHandleProcessResulton the same sequence, so two&mutborrows of it were live at once.Dispose the generator as soon as it has produced the client state.
Co-Authored-By: Claude Opus 5.5 (1M context) noreply@anthropic.com