Skip to content

Commit d5c00fa

Browse files
committed
Split WP11_Session_RemoveObject into by-handle and owned-object entries
Address review feedback on the concurrent C_DestroyObject fix. Rename the full removal routine to WP11_Session_RemoveObjectByHandle, which takes a handle-derived onToken and a checkDestroyable flag. This is the entry point for C_DestroyObject, where a concurrent destroy of the same shared token-object handle may free the object, so onToken must come from the handle rather than from a dereference of the object. Add a WP11_Session_RemoveObject(session, object) wrapper for cleanup paths that hold the only reference to a live object. It reads onToken from the object itself, which removes the risk of passing the wrong object's onToken, and drops the repeated (void)..., 0 boilerplate at the six cleanup call sites. Document both params and every return code (WP11_OBJECT_ALREADY_REMOVED, WP11_OBJECT_NOT_DESTROYABLE, negative store status, 0) in the function comment and header so callers know what to do in each case.
1 parent 6d3d241 commit d5c00fa

4 files changed

Lines changed: 77 additions & 42 deletions

File tree

src/crypto.c

Lines changed: 12 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1278,9 +1278,7 @@ static CK_RV AddRSAPrivateKeyObject(WP11_Session* session,
12781278
err_out:
12791279
if (rv != CKR_OK) {
12801280
if (*phKey != CK_INVALID_HANDLE) {
1281-
/* ignore return value, logged in function */
1282-
(void)WP11_Session_RemoveObject(session, privKeyObject,
1283-
WP11_Object_OnToken(privKeyObject), 0);
1281+
WP11_Session_RemoveObject(session, privKeyObject);
12841282
*phKey = CK_INVALID_HANDLE;
12851283
}
12861284
if (privKeyObject != NULL) {
@@ -1476,9 +1474,7 @@ CK_RV C_CreateObject(CK_SESSION_HANDLE hSession, CK_ATTRIBUTE_PTR pTemplate,
14761474
}
14771475
rv = AddObject(session, object, pTemplate, ulCount, phObject);
14781476
if (rv != CKR_OK) {
1479-
/* ignore return value, logged in function */
1480-
(void)WP11_Session_RemoveObject(session, object,
1481-
WP11_Object_OnToken(object), 0);
1477+
WP11_Session_RemoveObject(session, object);
14821478
WP11_Object_Free(object);
14831479
}
14841480

@@ -1697,8 +1693,8 @@ CK_RV C_DestroyObject(CK_SESSION_HANDLE hSession,
16971693
}
16981694
/* Derive onToken from the handle, not the object: it stays valid even if a
16991695
* concurrent destroy of the same handle frees the object out from under
1700-
* us, and it lets WP11_Session_RemoveObject unlink token objects using
1701-
* pointer identity alone. */
1696+
* us, and it lets WP11_Session_RemoveObjectByHandle unlink token objects
1697+
* using pointer identity alone. */
17021698
onToken = WP11_Object_HandleOnToken(hObject);
17031699

17041700
/* Only require R/W session for token objects */
@@ -1709,11 +1705,11 @@ CK_RV C_DestroyObject(CK_SESSION_HANDLE hSession,
17091705
}
17101706

17111707
/* Remove and reject-if-not-destroyable in one locked step. The
1712-
* CKA_DESTROYABLE check is performed inside WP11_Session_RemoveObject, once
1713-
* the object is confirmed still linked (and therefore alive), so it cannot
1714-
* race a concurrent free of the same handle. */
1715-
ret = WP11_Session_RemoveObject(session, obj, onToken,
1716-
1 /* checkDestroyable */);
1708+
* CKA_DESTROYABLE check is performed inside WP11_Session_RemoveObjectByHandle,
1709+
* once the object is confirmed still linked (and therefore alive), so it
1710+
* cannot race a concurrent free of the same handle. */
1711+
ret = WP11_Session_RemoveObjectByHandle(session, obj, onToken,
1712+
1 /* checkDestroyable */);
17171713
if (ret == WP11_OBJECT_ALREADY_REMOVED) {
17181714
/* Another thread destroyed this object first (a concurrent
17191715
* C_DestroyObject on the same shared token-object handle). That thread
@@ -7936,14 +7932,12 @@ CK_RV C_GenerateKeyPair(CK_SESSION_HANDLE hSession,
79367932

79377933
if (rv != CKR_OK && pub != NULL) {
79387934
if (*phPublicKey != CK_INVALID_HANDLE)
7939-
(void)WP11_Session_RemoveObject(session, pub,
7940-
WP11_Object_OnToken(pub), 0);
7935+
WP11_Session_RemoveObject(session, pub);
79417936
WP11_Object_Free(pub);
79427937
}
79437938
if (rv != CKR_OK && priv != NULL) {
79447939
if (*phPrivateKey != CK_INVALID_HANDLE)
7945-
(void)WP11_Session_RemoveObject(session, priv,
7946-
WP11_Object_OnToken(priv), 0);
7940+
WP11_Session_RemoveObject(session, priv);
79477941
WP11_Object_Free(priv);
79487942
}
79497943

@@ -8440,9 +8434,7 @@ CK_RV C_UnwrapKey(CK_SESSION_HANDLE hSession,
84408434
}
84418435
if (rv != CKR_OK) {
84428436
if (*phKey != CK_INVALID_HANDLE) {
8443-
/* ignore return value, logged in function */
8444-
(void)WP11_Session_RemoveObject(session, keyObj,
8445-
WP11_Object_OnToken(keyObj), 0);
8437+
WP11_Session_RemoveObject(session, keyObj);
84468438
*phKey = CK_INVALID_HANDLE;
84478439
}
84488440
if (keyObj != NULL) {

src/internal.c

Lines changed: 53 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -922,7 +922,7 @@ static void wp11_Session_Final(WP11_Session* session)
922922
/* Free objects in session. */
923923
while ((obj = session->object) != NULL) {
924924
/* ignore return value, logged in function */
925-
(void)WP11_Session_RemoveObject(session, obj, obj->onToken, 0);
925+
WP11_Session_RemoveObject(session, obj);
926926
WP11_Object_Free(obj);
927927
}
928928
session->inUse = 0;
@@ -8815,15 +8815,37 @@ int WP11_Session_AddObject(WP11_Session* session, int onToken,
88158815
}
88168816

88178817
/**
8818-
* Remove object to the session or token.
8818+
* Remove an object from its session or token list, addressing it by a
8819+
* caller-supplied onToken flag rather than by dereferencing the object.
88198820
*
8820-
* @param session [in] Session object.
8821-
* @param object [in] Key Object object.
8822-
* @return -ve on failure.
8823-
* 0 on success.
8821+
* Membership is established by pointer identity alone, so *object is never
8822+
* dereferenced until it is confirmed still linked (and therefore still alive).
8823+
* onToken must be derived from the handle (see WP11_Object_HandleOnToken), not
8824+
* from *object, so it stays valid even after a concurrent caller frees the
8825+
* object. This is the entry point for C_DestroyObject, where a second thread may
8826+
* be destroying the same shared token-object handle at the same time.
8827+
*
8828+
* Callers that hold the only reference to a live object should use the simpler
8829+
* WP11_Session_RemoveObject() wrapper instead: it derives onToken from the
8830+
* object itself and so cannot mismatch onToken against the wrong object.
8831+
*
8832+
* @param session [in] Session performing the removal.
8833+
* @param object [in] Object to remove, matched by pointer identity.
8834+
* @param onToken [in] Non-zero if the handle names a token object.
8835+
* Derive from the handle, never from *object.
8836+
* @param checkDestroyable [in] Non-zero to honor CKA_DESTROYABLE and refuse to
8837+
* remove an object marked not destroyable.
8838+
* @return WP11_OBJECT_ALREADY_REMOVED if the object was no longer linked, i.e.
8839+
* a concurrent caller already removed it; the caller must not free it.
8840+
* WP11_OBJECT_NOT_DESTROYABLE if checkDestroyable is set and the object
8841+
* has CKA_DESTROYABLE = CK_FALSE; the object is left in place and must
8842+
* not be freed.
8843+
* -ve on a store failure after the object was unlinked.
8844+
* 0 on success; the caller now owns the object and must free it.
88248845
*/
8825-
int WP11_Session_RemoveObject(WP11_Session* session, WP11_Object* object,
8826-
int onToken, int checkDestroyable)
8846+
int WP11_Session_RemoveObjectByHandle(WP11_Session* session,
8847+
WP11_Object* object, int onToken,
8848+
int checkDestroyable)
88278849
{
88288850
int ret = 0;
88298851
int found = 0;
@@ -8957,6 +8979,25 @@ int WP11_Session_RemoveObject(WP11_Session* session, WP11_Object* object,
89578979
return ret;
89588980
}
89598981

8982+
/**
8983+
* Remove a live, exclusively-owned object from its session or token list.
8984+
*
8985+
* Thin wrapper over WP11_Session_RemoveObjectByHandle() for cleanup paths that
8986+
* hold the only reference to the object (object-creation rollback, keygen
8987+
* failure). onToken is read from the object itself, which is safe because no
8988+
* other thread can be removing it, and supplying onToken this way removes any
8989+
* risk of passing the wrong object's onToken. CKA_DESTROYABLE is not enforced
8990+
* (internal cleanup is not a user destroy) and any store error is logged in the
8991+
* callee, so the return value is intentionally discarded.
8992+
*
8993+
* @param session [in] Session performing the removal.
8994+
* @param object [in] Object to remove; must be live and owned by the caller.
8995+
*/
8996+
void WP11_Session_RemoveObject(WP11_Session* session, WP11_Object* object)
8997+
{
8998+
(void)WP11_Session_RemoveObjectByHandle(session, object, object->onToken, 0);
8999+
}
9000+
89609001
/**
89619002
* Drop any session reference to an object that is about to be freed.
89629003
*
@@ -10291,13 +10332,13 @@ int WP11_Object_Find(WP11_Session* session, CK_OBJECT_HANDLE objHandle,
1029110332
/* The NSS cross-session walk below needs the slot lock so a concurrent
1029210333
* remove-session can't reclaim a session node. Acquire it before the
1029310334
* token lock to match the global lock order (slot then token) used by
10294-
* WP11_Session_RemoveObject / WP11_Slot_CloseSessions; acquiring them
10295-
* in the opposite order would risk an AB-BA deadlock. */
10335+
* WP11_Session_RemoveObjectByHandle / WP11_Slot_CloseSessions; acquiring
10336+
* them in the opposite order would risk an AB-BA deadlock. */
1029610337
WP11_Lock_LockRO(&session->slot->lock);
1029710338
#endif
1029810339
/* Hold the token lock across the session->object walk so a concurrent
10299-
* WP11_Session_RemoveObject (which holds the same lock around the
10300-
* unlink) can't free a node we're stepping through. */
10340+
* WP11_Session_RemoveObjectByHandle (which holds the same lock around
10341+
* the unlink) can't free a node we're stepping through. */
1030110342
WP11_Lock_LockRO(&session->slot->token.lock);
1030210343
obj = session->object;
1030310344
while (obj != NULL) {

tests/concurrent_destroy_object_test.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
* CKF_OS_LOCKING_OK). The buggy code resolved the handle to a raw WP11_Object*
2828
* under a released read lock, so both callers reached WP11_Object_Free on the
2929
* same pointer - a double free - and the loser also dereferenced the freed
30-
* object inside WP11_Session_RemoveObject.
30+
* object inside WP11_Session_RemoveObjectByHandle.
3131
*
3232
* Each round below creates one token object and has two threads (each on its
3333
* own session) destroy it simultaneously. The fix must guarantee that exactly

wolfpkcs11/internal.h

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -441,19 +441,21 @@ WP11_LOCAL int WP11_Session_SetMldsaParams(WP11_Session* session, CK_VOID_PTR pa
441441
CK_ULONG paramsLen);
442442
WP11_LOCAL int WP11_Session_AddObject(WP11_Session* session, int onToken,
443443
WP11_Object* object);
444-
/* Returned by WP11_Session_RemoveObject when the object was no longer linked,
445-
* i.e. a concurrent caller (e.g. a second C_DestroyObject on the same shared
446-
* token-object handle) already removed it. A positive value, distinct from the
447-
* 0 / negative store-status codes returned on a successful removal. The caller
448-
* must not free the object a second time. */
444+
/* Returned by WP11_Session_RemoveObjectByHandle when the object was no longer
445+
* linked, i.e. a concurrent caller (e.g. a second C_DestroyObject on the same
446+
* shared token-object handle) already removed it. A positive value, distinct
447+
* from the 0 / negative store-status codes returned on a successful removal. The
448+
* caller must not free the object a second time. */
449449
#define WP11_OBJECT_ALREADY_REMOVED 1
450-
/* Returned by WP11_Session_RemoveObject (only when checkDestroyable is set) when
451-
* the object is still linked but has CKA_DESTROYABLE = CK_FALSE. The object is
452-
* left in place and must not be freed. */
450+
/* Returned by WP11_Session_RemoveObjectByHandle (only when checkDestroyable is
451+
* set) when the object is still linked but has CKA_DESTROYABLE = CK_FALSE. The
452+
* object is left in place and must not be freed. */
453453
#define WP11_OBJECT_NOT_DESTROYABLE 2
454-
WP11_LOCAL int WP11_Session_RemoveObject(WP11_Session* session,
454+
WP11_LOCAL int WP11_Session_RemoveObjectByHandle(WP11_Session* session,
455455
WP11_Object* object, int onToken,
456456
int checkDestroyable);
457+
WP11_LOCAL void WP11_Session_RemoveObject(WP11_Session* session,
458+
WP11_Object* object);
457459
WP11_LOCAL int WP11_Object_HandleOnToken(CK_OBJECT_HANDLE handle);
458460
WP11_LOCAL void WP11_Slot_ClearActiveObject(WP11_Slot* slot,
459461
WP11_Object* object);

0 commit comments

Comments
 (0)