@@ -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);
925+ WP11_Session_RemoveObject(session, obj);
926926 WP11_Object_Free(obj);
927927 }
928928 session->inUse = 0;
@@ -8815,51 +8815,122 @@ 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)
8846+ int WP11_Session_RemoveObjectByHandle(WP11_Session* session,
8847+ WP11_Object* object, int onToken,
8848+ int checkDestroyable)
88268849{
88278850 int ret = 0;
8851+ int found = 0;
88288852 WP11_Object** curr;
8829- WP11_Token* token;
8830- int id;
8853+ WP11_Token* token = &session->slot->token;
8854+ WP11_Session* owner = session;
8855+ int id = 0;
8856+
8857+ /* The token lock guards every object list on the slot (session lists
8858+ * included). Take it before touching any list or the object. Token objects
8859+ * are shared across sessions, so two C_DestroyObject calls on the same
8860+ * handle can both pass the lock-free WP11_Object_Find and reach here with
8861+ * the same pointer; the loser must detect that the winner already unlinked
8862+ * (and is about to free) the object and bail out without a second free.
8863+ *
8864+ * Membership is established by pointer identity alone so that *object is
8865+ * never dereferenced until it is confirmed still linked (and therefore
8866+ * still alive). onToken comes from the handle, not from *object, so it is
8867+ * safe to trust even after the object has been freed. */
8868+ WP11_Lock_LockRW(&token->lock);
88318869
8870+ if (onToken) {
8871+ /* Token objects live on the shared token list. */
8872+ for (curr = &token->object; *curr != NULL; curr = &(*curr)->next) {
8873+ if (*curr == object) {
8874+ found = 1;
8875+ break;
8876+ }
8877+ }
8878+ }
8879+ else {
8880+ /* Session objects live on their owning session's list. Check the
8881+ * caller's own list first: it is the only possibility in the standard
8882+ * build and the common case under NSS, and needs no dereference of
8883+ * *object. */
8884+ for (curr = &owner->object; *curr != NULL; curr = &(*curr)->next) {
8885+ if (*curr == object) {
8886+ found = 1;
8887+ break;
8888+ }
8889+ }
88328890#ifdef WOLFPKCS11_NSS
8833- if (object->session && (session != object->session))
8834- session = object->session;
8891+ /* NSS makes objects visible across sessions, so the object may belong
8892+ * to a different session. Fall back to its recorded owner. This is
8893+ * reached only for a genuinely cross-session object - a freed object is
8894+ * normally caught as not-found above - and carries the same
8895+ * out-of-contract residual as a single-session concurrent misuse. */
8896+ if (!found && object->session != NULL && object->session != session) {
8897+ owner = object->session;
8898+ for (curr = &owner->object; *curr != NULL; curr = &(*curr)->next) {
8899+ if (*curr == object) {
8900+ found = 1;
8901+ break;
8902+ }
8903+ }
8904+ }
88358905#endif
8906+ }
88368907
8837- /* Find the object in list and relink. */
8838- if (object->onToken) {
8839- WP11_Lock_LockRW(object->lock);
8840- token = &session->slot->token;
8908+ if (!found) {
8909+ WP11_Lock_UnlockRW(&token->lock);
8910+ return WP11_OBJECT_ALREADY_REMOVED;
8911+ }
8912+
8913+ /* The object is confirmed linked, and therefore alive, so its fields may
8914+ * now be read safely under the lock. Enforce CKA_DESTROYABLE here rather
8915+ * than in the caller so the check cannot race a concurrent free. */
8916+ if (checkDestroyable && (object->opFlag & WP11_FLAG_NOT_DESTROYABLE)) {
8917+ WP11_Lock_UnlockRW(&token->lock);
8918+ return WP11_OBJECT_NOT_DESTROYABLE;
8919+ }
8920+
8921+ if (onToken) {
88418922 token->objCnt--;
88428923 /* Id of first object on token. */
88438924 id = token->objCnt;
88448925 curr = &token->object;
8845- }
8846- else {
8847- session->objCnt--;
8848- /* Id of first object in session. */
8849- id = session->objCnt;
8850- curr = &session->object;
8851- WP11_Lock_LockRW(&session->slot->token.lock);
8852- }
8853-
8854- /* walk list to get id for object to remove */
8855- while (*curr != NULL) {
8856- if (*curr == object) {
8857- *curr = object->next;
8858- break;
8859- }
8926+ /* walk list to get id for object to remove */
8927+ while (*curr != NULL) {
8928+ if (*curr == object) {
8929+ *curr = object->next;
8930+ break;
8931+ }
88608932
8861- #ifndef WOLFPKCS11_NO_STORE
8862- if (object->onToken) {
8933+ #ifndef WOLFPKCS11_NO_STORE
88638934 /* remove any id's with higher value */
88648935 ret = wp11_Object_Unstore(*curr, (int)session->slotId, id);
88658936 #ifdef DEBUG_WOLFPKCS11
@@ -8868,16 +8939,14 @@ int WP11_Session_RemoveObject(WP11_Session* session, WP11_Object* object)
88688939 (int)session->slotId, id, ret);
88698940 }
88708941 #endif
8871- }
8872- #endif
8942+ #endif
88738943
8874- curr = &(*curr)->next;
8875- /* Id of next object as it isn't the one being removed. */
8876- id--;
8877- }
8944+ curr = &(*curr)->next;
8945+ /* Id of next object as it isn't the one being removed. */
8946+ id--;
8947+ }
88788948
8879- if (object->onToken) {
8880- #ifndef WOLFPKCS11_NO_STORE
8949+ #ifndef WOLFPKCS11_NO_STORE
88818950 ret = wp11_Object_Unstore(object, (int)session->slotId, id);
88828951 #ifdef DEBUG_WOLFPKCS11
88838952 if (ret != 0) {
@@ -8893,16 +8962,42 @@ int WP11_Session_RemoveObject(WP11_Session* session, WP11_Object* object)
88938962 (int)session->slotId, ret);
88948963 }
88958964 #endif
8896- #endif
8897- WP11_Lock_UnlockRW(object->lock);
8965+ #endif
88988966 }
88998967 else {
8900- WP11_Lock_UnlockRW(&session->slot->token.lock);
8968+ owner->objCnt--;
8969+ for (curr = &owner->object; *curr != NULL; curr = &(*curr)->next) {
8970+ if (*curr == object) {
8971+ *curr = object->next;
8972+ break;
8973+ }
8974+ }
89018975 }
8976+
8977+ WP11_Lock_UnlockRW(&token->lock);
89028978 (void)id; /* set but not used with WOLFPKCS11_NO_STORE */
89038979 return ret;
89048980}
89058981
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+
89069001/**
89079002 * Drop any session reference to an object that is about to be freed.
89089003 *
@@ -10197,6 +10292,22 @@ int WP11_Object_SetClass(WP11_Object* object, CK_OBJECT_CLASS objClass)
1019710292 return 0;
1019810293}
1019910294
10295+ /**
10296+ * Determine whether an object handle refers to a token object.
10297+ *
10298+ * The onToken bit is encoded in the handle value itself, so this is safe to
10299+ * call even when the WP11_Object has already been freed by another thread -
10300+ * useful when deciding which list a racing C_DestroyObject must consult.
10301+ *
10302+ * @param handle [in] Object handle.
10303+ * @return 1 when the handle refers to a token object.
10304+ * 0 when the handle refers to a session object.
10305+ */
10306+ int WP11_Object_HandleOnToken(CK_OBJECT_HANDLE handle)
10307+ {
10308+ return OBJ_HANDLE_ON_TOKEN(handle);
10309+ }
10310+
1020010311/**
1020110312 * Find an object based on the handle.
1020210313 *
@@ -10221,13 +10332,13 @@ int WP11_Object_Find(WP11_Session* session, CK_OBJECT_HANDLE objHandle,
1022110332 /* The NSS cross-session walk below needs the slot lock so a concurrent
1022210333 * remove-session can't reclaim a session node. Acquire it before the
1022310334 * token lock to match the global lock order (slot then token) used by
10224- * WP11_Session_RemoveObject / WP11_Slot_CloseSessions; acquiring them
10225- * 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. */
1022610337 WP11_Lock_LockRO(&session->slot->lock);
1022710338#endif
1022810339 /* Hold the token lock across the session->object walk so a concurrent
10229- * WP11_Session_RemoveObject (which holds the same lock around the
10230- * 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. */
1023110342 WP11_Lock_LockRO(&session->slot->token.lock);
1023210343 obj = session->object;
1023310344 while (obj != NULL) {
0 commit comments