Repository navigation
Fix Duplicate ownership state machine under concurrency for NCrypt handles - #130804
Conversation
|
Azure Pipelines: Successfully started running 3 pipeline(s). 12 pipeline(s) were filtered out due to trigger conditions. There may be pipelines that require an authorized user to comment /azp run to run. |
|
Tagging subscribers to this area: @bartonjs, @vcsjones, @dotnet/area-system-security |
There was a problem hiding this comment.
Pull request overview
This PR addresses a concurrency bug in the NCrypt safe-handle duplication logic by synchronizing the first “owner → duplicate” transition so concurrent CngKey.Handle callers can’t create competing holders for the same native handle.
Changes:
- Make
SafeNCryptHandle.Duplicate<T>()thread-safe for the initial duplication by guarding the owner-state transition with a lazily-initialized lock and making_ownershipStatevolatile. - Add a Windows-only regression test that races concurrent
CngKey.Handlefirst access and validates that duplicated handles remain usable after disposing the original key and most duplicates.
Show a summary per file
| File | Description |
|---|---|
| src/libraries/System.Security.Cryptography/src/Microsoft/Win32/SafeHandles/NCryptSafeHandles.cs | Adds synchronization + volatile state publication around the first duplication transition. |
| src/libraries/System.Security.Cryptography/tests/CngKeyTests.cs | Adds a concurrency regression test for CngKey.Handle duplication. |
Copilot's findings
- Files reviewed: 2/2 changed files
- Comments generated: 2
|
/backport to release/8.0-staging |
|
Started backporting to |
|
/backport to release/9.0-staging |
|
Started backporting to |
|
/backport to release/10.0 |
|
Started backporting to |
|
@bartonjs backporting to git am output$ git cherry-pick 2e77bf12d7af1f8b639b20bf8307655b13e56ab8
CONFLICT (modify/delete): src/libraries/System.Security.Cryptography/tests/CngKeyTests.cs deleted in HEAD and modified in 2e77bf12d7a (Fix Duplicate ownership state machine under concurrency for NCrypt handles). Version 2e77bf12d7a (Fix Duplicate ownership state machine under concurrency for NCrypt handles) of src/libraries/System.Security.Cryptography/tests/CngKeyTests.cs left in tree.
error: could not apply 2e77bf12d7a... Fix Duplicate ownership state machine under concurrency for NCrypt handles
hint: After resolving the conflicts, mark them with
hint: "git add/rm <pathspec>", then run
hint: "git cherry-pick --continue".
hint: You can instead skip this commit with "git cherry-pick --skip".
hint: To abort and get back to the state before "git cherry-pick",
hint: run "git cherry-pick --abort".
hint: Disable this message with "git config set advice.mergeConflict false"
$ git am --3way --empty=keep --ignore-whitespace --keep-non-patch changes.patch
Applying: Fix Duplicate ownership state machine under concurrency for NCrypt handles
Using index info to reconstruct a base tree...
A src/libraries/System.Security.Cryptography/tests/CngKeyTests.cs
Falling back to patching base and 3-way merge...
CONFLICT (modify/delete): src/libraries/System.Security.Cryptography/tests/CngKeyTests.cs deleted in HEAD and modified in Fix Duplicate ownership state machine under concurrency for NCrypt handles. Version Fix Duplicate ownership state machine under concurrency for NCrypt handles of src/libraries/System.Security.Cryptography/tests/CngKeyTests.cs left in tree.
error: Failed to merge in the changes.
hint: Use 'git am --show-current-patch=diff' to see the failed patch
hint: When you have resolved this problem, run "git am --continue".
hint: If you prefer to skip this patch, run "git am --skip" instead.
hint: To restore the original branch and stop patching, run "git am --abort".
hint: Disable this message with "git config set advice.mergeConflict false"
Patch failed at 0001 Fix Duplicate ownership state machine under concurrency for NCrypt handles
Error: The process '/usr/bin/git' failed with exit code 128 |
Duplicating NCrypt safe handles is not currently thread safe. This is observable through
CngKey.Handlewhich callsDuplicateon aSafeNCryptKeyHandle. The crux of the issue is that two threads can both observe_ownershipState == Ownerand enterDuplicateOwnerHandle(). Each creates a different holder for the same native handle. The first transitions the original handle toDuplicate, the second then hits the assertion when it attempts the same transition.This assert was observed in #130760.
In a release build, this can result in an invalid
NCryptKeyHandlegetting passed to native functions.NCryptFreeObjectwhile duplicates associated with an overwritten holder remain open.This simplest solution is to take a lock, which will unfortunately ding performance a little bit when accessing
Handle.A unit test was added to attempt racing the handles. Before the fix, this test would fail quickly with
After the lock the test consistently passes.
Doing this lock-free is non-trivial. There are multiple fields we need to ensure get published atomically, namely
_ownershipState,_holder, and_parentHandle. However we avoid the lock entirely unless the current state isOwnershipState.Owner.Fixes #130760