Skip to content

Prevent double free in concurrent C_DestroyObject on shared token object#205

Open
LinuxJedi wants to merge 2 commits into
wolfSSL:masterfrom
LinuxJedi:concurrent-destroy-object-double-free
Open

Prevent double free in concurrent C_DestroyObject on shared token object#205
LinuxJedi wants to merge 2 commits into
wolfSSL:masterfrom
LinuxJedi:concurrent-destroy-object-double-free

Conversation

@LinuxJedi

Copy link
Copy Markdown
Member

C_DestroyObject resolved a handle to a raw WP11_Object* under a released read lock and then unconditionally freed it. Token objects are shared across sessions, so two sessions destroying the same handle at once (a supported concurrent use under CKF_OS_LOCKING_OK) could both reach WP11_Object_Free on the same pointer, and the losing thread also dereferenced the freed object inside WP11_Session_RemoveObject.

WP11_Session_RemoveObject now takes an onToken argument derived from the handle rather than the object, confirms the object is still linked using pointer identity under the token lock (never dereferencing a possibly freed object on the token path), and returns WP11_OBJECT_ALREADY_REMOVED when a concurrent caller already unlinked it. C_DestroyObject gates the free on that result: the loser returns CKR_OBJECT_HANDLE_INVALID without touching the object again. The successful-removal path is unchanged. Only the token lock is taken, since wp11_Session_Final already calls WP11_Session_RemoveObject while holding the slot lock.

Add WP11_Object_HandleOnToken to derive onToken from a handle, update the remaining callers to pass onToken, and add
tests/concurrent_destroy_object_test.c which races two sessions destroying one token object and checks exactly one winner per round with no double free.

F-5252

C_DestroyObject resolved a handle to a raw WP11_Object* under a released
read lock and then unconditionally freed it. Token objects are shared
across sessions, so two sessions destroying the same handle at once (a
supported concurrent use under CKF_OS_LOCKING_OK) could both reach
WP11_Object_Free on the same pointer, and the losing thread also
dereferenced the freed object inside WP11_Session_RemoveObject.

WP11_Session_RemoveObject now takes an onToken argument derived from the
handle rather than the object, confirms the object is still linked using
pointer identity under the token lock (never dereferencing a possibly
freed object on the token path), and returns WP11_OBJECT_ALREADY_REMOVED
when a concurrent caller already unlinked it. C_DestroyObject gates the
free on that result: the loser returns CKR_OBJECT_HANDLE_INVALID without
touching the object again. The successful-removal path is unchanged. Only
the token lock is taken, since wp11_Session_Final already calls
WP11_Session_RemoveObject while holding the slot lock.

Add WP11_Object_HandleOnToken to derive onToken from a handle, update the
remaining callers to pass onToken, and add
tests/concurrent_destroy_object_test.c which races two sessions destroying
one token object and checks exactly one winner per round with no double
free.

F-5252
Copilot AI review requested due to automatic review settings July 10, 2026 13:25

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.

Pull request overview

This PR hardens C_DestroyObject against concurrent destruction of shared token objects (valid under CKF_OS_LOCKING_OK) by preventing a double-free / use-after-free when two sessions destroy the same handle at the same time.

Changes:

  • Extend WP11_Session_RemoveObject() to accept an onToken argument and return WP11_OBJECT_ALREADY_REMOVED when a concurrent caller already unlinked the object.
  • Add WP11_Object_HandleOnToken() and update C_DestroyObject to derive onToken from the handle and avoid freeing on the “already removed” path.
  • Add a new multithreaded regression test and wire it into the test build.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
wolfpkcs11/internal.h Updates WP11_Session_RemoveObject API and introduces WP11_OBJECT_ALREADY_REMOVED and WP11_Object_HandleOnToken() declaration.
src/internal.c Implements the new remove semantics under the token lock and adds WP11_Object_HandleOnToken().
src/crypto.c Updates callers for new signature and adjusts C_DestroyObject to avoid double-free on concurrent destroys.
tests/include.am Adds the new concurrent destroy test target to the test build.
tests/concurrent_destroy_object_test.c Adds a race-focused regression test for concurrent C_DestroyObject on a shared token object.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/crypto.c Outdated
Comment thread tests/concurrent_destroy_object_test.c
Comment thread src/internal.c Outdated
Comment thread src/crypto.c Outdated
Address review feedback on the concurrent-destroy fix:

- Perform the CKA_DESTROYABLE check inside WP11_Session_RemoveObject, under
  the token lock and only after the object is confirmed still linked (and
  therefore alive), via a new checkDestroyable argument. Previously
  C_DestroyObject dereferenced the object for WP11_Object_IsDestroyable
  before removal could confirm liveness, which could read freed memory when
  another thread destroyed the same handle first.

- Look up session objects on the caller's own session list first, by
  pointer identity, and only fall back to object->session under NSS. This
  avoids dereferencing a possibly freed object on the common path; the
  remaining NSS cross-session fallback is the same out-of-contract case as
  a single-session concurrent misuse.

- Return CKR_FUNCTION_FAILED from C_DestroyObject when the object was
  unlinked but persisting the token afterwards failed, instead of always
  reporting CKR_OK and masking the error.

- Include <stdlib.h> in the regression test for atoi().
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants