Repository navigation
Match the handle when removing an object by handle - #252
Merged
Merged
Conversation
WP11_Session_RemoveObjectByHandle found the object to unlink by pointer identity only. C_DestroyObject resolves the handle to a pointer, drops the lock and then removes by that pointer, so if another thread destroyed the object and a new object was allocated at the same address, the new object would be unlinked and freed instead, and C_DestroyObject would report success for the old handle. Pass the handle instead of onToken and require both the pointer and the handle to match. Handles are never reused, so a recycled address no longer matches. onToken is now derived from the handle inside the function. Address reuse cannot be forced through the PKCS#11 API, so add a DEBUG_WOLFPKCS11-only hook, WP11_Test_RemoveObjectStale, and destroy_stale_handle_test, which drives the removal with a live object's pointer and a destroyed object's handle, for token and session objects.
Contributor
There was a problem hiding this comment.
🔵 Needs a closer look
The concurrency-sensitive removal path retains an acknowledged NSS lifetime hazard that warrants human review.
1 open finding
What changed in this PR
Protects object destruction from address reuse by requiring both pointer and handle identity before unlinking an object.
Changes:
- Passes the original handle through removal paths.
- Adds a debug-only hook and regression tests for stale token and session handles.
- Documents the existing NSS lifetime limitation.
| File | Description |
|---|---|
| wolfpkcs11/internal.h | Updates removal signature and declares the debug hook. |
| tests/include.am | Registers and links the regression test. |
| tests/destroy_stale_handle_test.c | Tests stale-handle rejection and matching-handle removal. |
| src/internal.c | Checks pointer and handle identity; implements the hook. |
| src/crypto.c | Passes the original handle from C_DestroyObject. |
🧠 Review effort: Balanced
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Without DEBUG_WOLFPKCS11 or AES the test returned 0, so automake counted it as a pass although no check ran. Return 77, as debug_test and empty_pin_store_test do, so it is reported as skipped.
Frauschi
approved these changes
Oct 16, 2026
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.

WP11_Session_RemoveObjectByHandlefound the object to unlink by pointer identity only, despite its name.C_DestroyObjectresolves the handle withWP11_Object_Find, drops the lock, then removes by that pointer. If another thread destroyed the object in between and a third thread created an object that landed at the same address, the first caller would unlink and free the new object and returnCKR_OKfor the old handle.WP11_Session_RemoveObjectByHandlenow takes the handle instead ofonToken, derivesonTokenfrom it, and only matches a list node when both the pointer and the handle are equal. The handle of a linked node is safe to read under the token lock, and published handles come from a counter that is never reused, so a recycled address no longer matches.C_DestroyObjectpasseshObject; the cleanup wrapperWP11_Session_RemoveObjectpasses the object's own handle.object->sessionbefore it knows the object is linked. The pointer-and-handle match stops a stale or reused object being removed, but the read itself can touch freed memory. Avoiding it needs the slot lock, which this function cannot take becausewp11_Session_Finalcalls it with the slot lock already held. This is noted in the code.Address reuse cannot be forced through the PKCS#11 API (and ASAN quarantines freed memory), so this adds a
DEBUG_WOLFPKCS11-only hook,WP11_Test_RemoveObjectStale, following the existingWP11_Slot_TokenKeyIsZeropattern, andtests/destroy_stale_handle_test.c. The test pairs a live object's pointer with the handle of an already destroyed object, for both token and session objects, and checks that the removal is refused and the object survives. A control call with matching handles checks the hook does remove. It runs in--enable-debugbuilds (theunit-test.ymldebug job) and skips otherwise.Testing:
destroy_stale_handle_testpasses all 18 checks. With the handle comparison removed (pointer-only, as on master) 2 checks fail and the live object is freed.make testpasses (debug and default configs); alsomake checkwith--enable-nss --enable-debugunder ASAN.