diff --git a/src/crypto.c b/src/crypto.c index 32ad7e0f..c9729988 100644 --- a/src/crypto.c +++ b/src/crypto.c @@ -1877,8 +1877,7 @@ CK_RV C_DestroyObject(CK_SESSION_HANDLE hSession, } /* Derive onToken from the handle, not the object: it stays valid even if a * concurrent destroy of the same handle frees the object out from under - * us, and it lets WP11_Session_RemoveObjectByHandle unlink token objects - * using pointer identity alone. */ + * us. */ onToken = WP11_Object_HandleOnToken(hObject); /* Only require R/W session for token objects */ @@ -1891,8 +1890,9 @@ CK_RV C_DestroyObject(CK_SESSION_HANDLE hSession, /* Remove and reject-if-not-destroyable in one locked step. The * CKA_DESTROYABLE check is performed inside WP11_Session_RemoveObjectByHandle, * once the object is confirmed still linked (and therefore alive), so it - * cannot race a concurrent free of the same handle. */ - ret = WP11_Session_RemoveObjectByHandle(session, obj, onToken, + * cannot race a concurrent free of the same handle. Passing hObject lets it + * refuse a different object that reused obj's address after such a free. */ + ret = WP11_Session_RemoveObjectByHandle(session, obj, hObject, 1 /* checkDestroyable */); if (ret == WP11_OBJECT_ALREADY_REMOVED) { /* Another thread destroyed this object first (a concurrent diff --git a/src/internal.c b/src/internal.c index 6fa7d2a4..53992cd6 100644 --- a/src/internal.c +++ b/src/internal.c @@ -10490,24 +10490,28 @@ int WP11_Session_AddObject(WP11_Session* session, int onToken, } /** - * Remove an object from its session or token list, addressing it by a - * caller-supplied onToken flag rather than by dereferencing the object. - * - * Membership is established by pointer identity alone, so *object is never - * dereferenced until it is confirmed still linked (and therefore still alive). - * onToken must be derived from the handle (see WP11_Object_HandleOnToken), not - * from *object, so it stays valid even after a concurrent caller frees the - * object. This is the entry point for C_DestroyObject, where a second thread may - * be destroying the same shared token-object handle at the same time. + * Remove an object from its session or token list, addressing it by the handle + * the caller resolved it from rather than by dereferencing the object. + * + * A list node matches only when it is the same pointer AND carries the same + * handle. The handle field of a linked node is safe to read under the lock, so + * *object itself is never dereferenced until it is confirmed still linked (and + * therefore still alive). Matching the handle as well as the pointer guards + * against address reuse: if a concurrent caller freed the object and a new + * object was then allocated at the same address, the new object has a + * different handle (handles are never reused) and is left alone. Whether the + * object is on the token is derived from the handle too, so it stays valid even + * after a concurrent caller frees the object. This is the entry point for + * C_DestroyObject, where a second thread may be destroying the same shared + * token-object handle at the same time. * * Callers that hold the only reference to a live object should use the simpler - * WP11_Session_RemoveObject() wrapper instead: it derives onToken from the - * object itself and so cannot mismatch onToken against the wrong object. + * WP11_Session_RemoveObject() wrapper instead: it takes the handle from the + * object itself. * * @param session [in] Session performing the removal. - * @param object [in] Object to remove, matched by pointer identity. - * @param onToken [in] Non-zero if the handle names a token object. - * Derive from the handle, never from *object. + * @param object [in] Object to remove, as resolved from handle. + * @param handle [in] Handle the object was resolved from. * @param checkDestroyable [in] Non-zero to honor CKA_DESTROYABLE and refuse to * remove an object marked not destroyable. * @return WP11_OBJECT_ALREADY_REMOVED if the object was no longer linked, i.e. @@ -10519,11 +10523,13 @@ int WP11_Session_AddObject(WP11_Session* session, int onToken, * 0 on success; the caller now owns the object and must free it. */ int WP11_Session_RemoveObjectByHandle(WP11_Session* session, - WP11_Object* object, int onToken, + WP11_Object* object, + CK_OBJECT_HANDLE handle, int checkDestroyable) { int ret = 0; int found = 0; + int onToken = OBJ_HANDLE_ON_TOKEN(handle); WP11_Object** curr; WP11_Token* token = &session->slot->token; WP11_Session* owner = session; @@ -10535,17 +10541,14 @@ int WP11_Session_RemoveObjectByHandle(WP11_Session* session, * handle can both pass the lock-free WP11_Object_Find and reach here with * the same pointer; the loser must detect that the winner already unlinked * (and is about to free) the object and bail out without a second free. - * - * Membership is established by pointer identity alone so that *object is - * never dereferenced until it is confirmed still linked (and therefore - * still alive). onToken comes from the handle, not from *object, so it is - * safe to trust even after the object has been freed. */ + * By then the address may even belong to a newly created object, which + * the handle comparison rejects. */ WP11_Lock_LockRW(&token->lock); if (onToken) { /* Token objects live on the shared token list. */ for (curr = &token->object; *curr != NULL; curr = &(*curr)->next) { - if (*curr == object) { + if (*curr == object && (*curr)->handle == handle) { found = 1; break; } @@ -10557,21 +10560,24 @@ int WP11_Session_RemoveObjectByHandle(WP11_Session* session, * build and the common case under NSS, and needs no dereference of * *object. */ for (curr = &owner->object; *curr != NULL; curr = &(*curr)->next) { - if (*curr == object) { + if (*curr == object && (*curr)->handle == handle) { found = 1; break; } } #ifdef WOLFPKCS11_NSS /* NSS makes objects visible across sessions, so the object may belong - * to a different session. Fall back to its recorded owner. This is - * reached only for a genuinely cross-session object - a freed object is - * normally caught as not-found above - and carries the same - * out-of-contract residual as a single-session concurrent misuse. */ + * to a different session. Fall back to its recorded owner. This reads + * object->session before linkage is confirmed, so it can touch freed + * memory if a concurrent destroy has already freed the object; the + * pointer and handle match below still rejects a stale or reused + * object. Walking every session instead would need the slot lock, + * which cannot be taken here: wp11_Session_Final calls this with the + * slot lock already held. */ if (!found && object->session != NULL && object->session != session) { owner = object->session; for (curr = &owner->object; *curr != NULL; curr = &(*curr)->next) { - if (*curr == object) { + if (*curr == object && (*curr)->handle == handle) { found = 1; break; } @@ -10659,19 +10665,59 @@ int WP11_Session_RemoveObjectByHandle(WP11_Session* session, * * Thin wrapper over WP11_Session_RemoveObjectByHandle() for cleanup paths that * hold the only reference to the object (object-creation rollback, keygen - * failure). onToken is read from the object itself, which is safe because no - * other thread can be removing it, and supplying onToken this way removes any - * risk of passing the wrong object's onToken. CKA_DESTROYABLE is not enforced - * (internal cleanup is not a user destroy) and any store error is logged in the - * callee, so the return value is intentionally discarded. + * failure). The handle is read from the object itself, which is safe because no + * other thread can be removing it. CKA_DESTROYABLE is not enforced (internal + * cleanup is not a user destroy) and any store error is logged in the callee, + * so the return value is intentionally discarded. * * @param session [in] Session performing the removal. * @param object [in] Object to remove; must be live and owned by the caller. */ void WP11_Session_RemoveObject(WP11_Session* session, WP11_Object* object) { - (void)WP11_Session_RemoveObjectByHandle(session, object, object->onToken, 0); + (void)WP11_Session_RemoveObjectByHandle(session, object, object->handle, 0); +} + +#ifdef DEBUG_WOLFPKCS11 +/** + * Test hook: call WP11_Session_RemoveObjectByHandle() with a stale handle. + * + * Models the C_DestroyObject race where the object a caller resolved from + * staleHandle has been freed by another thread and its memory reused for the + * live object liveHandle: the caller then holds liveHandle's pointer but + * staleHandle. The removal must be refused. Exposed only in debug builds so a + * white-box test can drive that state deterministically. + * + * @param hSession [in] Session handle. + * @param liveHandle [in] Handle of a live object; supplies the pointer. + * @param staleHandle [in] Handle of an object that was already destroyed. + * @return BAD_FUNC_ARG when the session or live object is not found. + * Otherwise the WP11_Session_RemoveObjectByHandle() result. If that + * removed the live object, the object has also been freed. + */ +WP11_API int WP11_Test_RemoveObjectStale(CK_SESSION_HANDLE hSession, + CK_OBJECT_HANDLE liveHandle, CK_OBJECT_HANDLE staleHandle) +{ + WP11_Session* session; + WP11_Object* object; + int ret; + + if (WP11_Session_Get(hSession, &session) != 0) + return BAD_FUNC_ARG; + if (WP11_Object_Find(session, liveHandle, &object) != 0) + return BAD_FUNC_ARG; + + ret = WP11_Session_RemoveObjectByHandle(session, object, staleHandle, 1); + if (ret != WP11_OBJECT_ALREADY_REMOVED && + ret != WP11_OBJECT_NOT_DESTROYABLE) { + /* Removed: this caller now owns the object, as C_DestroyObject would. */ + WP11_Slot_ClearActiveObject(session->slot, object); + WP11_Object_Free(object); + } + + return ret; } +#endif /* DEBUG_WOLFPKCS11 */ /** * Drop any session reference to an object that is about to be freed. diff --git a/tests/destroy_stale_handle_test.c b/tests/destroy_stale_handle_test.c new file mode 100644 index 00000000..61839923 --- /dev/null +++ b/tests/destroy_stale_handle_test.c @@ -0,0 +1,399 @@ +/* destroy_stale_handle_test.c + * + * Copyright (C) 2026 wolfSSL Inc. + * + * This file is part of wolfPKCS11. + * + * wolfPKCS11 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfPKCS11 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + * + * White-box test for address reuse (ABA) in C_DestroyObject. + * + * C_DestroyObject resolves the handle to an object pointer, releases the + * lock, then removes the object by that pointer. If another thread destroys + * the same handle in between and a third thread creates a new object that is + * allocated at the freed address, the first caller holds a pointer that now + * names the new object. Matching on the pointer alone would unlink and free + * the new object and report success for the old handle. + * + * Address reuse cannot be forced from the public API, so this test uses the + * DEBUG_WOLFPKCS11-only hook WP11_Test_RemoveObjectStale() to put the removal + * directly into that state: the pointer of a live object paired with the + * handle of an object that was already destroyed. The removal must be refused + * and the live object must survive, for both token and session objects. A + * control call with matching handles confirms the hook does remove otherwise. + */ + +#ifdef HAVE_CONFIG_H + #include +#endif + +#include + +#ifndef WOLFSSL_USER_SETTINGS + #include +#endif +#include +#include + +#ifndef WOLFPKCS11_USER_SETTINGS + #include +#endif +#include + +#ifndef HAVE_PKCS11_STATIC +#include +#endif + +#include "testdata.h" + +#if defined(DEBUG_WOLFPKCS11) && !defined(NO_AES) + +/* DEBUG_WOLFPKCS11-only test hook exported by libwolfpkcs11. */ +extern int WP11_Test_RemoveObjectStale(CK_SESSION_HANDLE hSession, + CK_OBJECT_HANDLE liveHandle, CK_OBJECT_HANDLE staleHandle); + +/* Mirrors WP11_OBJECT_ALREADY_REMOVED in wolfpkcs11/internal.h. */ +#define OBJECT_ALREADY_REMOVED 1 + +#define STALE_HANDLE_TEST_DIR "./store/destroy_stale_handle_test" +#define WOLFPKCS11_TOKEN_FILENAME "wp11_token_0000000000000001" + +static int test_passed = 0; +static int test_failed = 0; + +#define CHECK_CKR(rv, op, expected) do { \ + if (rv != expected) { \ + fprintf(stderr, "FAIL: %s: expected %ld, got %ld\n", op, (long)expected, (long)rv); \ + test_failed++; \ + result = -1; \ + goto cleanup; \ + } else { \ + printf("PASS: %s\n", op); \ + test_passed++; \ + } \ +} while(0) + +#define CHECK_COND(cond, op) do { \ + if (!(cond)) { \ + fprintf(stderr, "FAIL: %s\n", op); \ + test_failed++; \ + result = -1; \ + goto cleanup; \ + } else { \ + printf("PASS: %s\n", op); \ + test_passed++; \ + } \ +} while(0) + +#ifndef HAVE_PKCS11_STATIC +static void* dlib; +#endif +static CK_FUNCTION_LIST* funcList; +static CK_SLOT_ID slot = 0; +static const char* tokenName = "wolfpkcs11"; +static byte* soPin = (byte*)"password123456"; +static int soPinLen = 14; +static byte* userPin = (byte*)"wolfpkcs11-test"; +static int userPinLen = 15; + +static CK_OBJECT_CLASS secretKeyClass = CKO_SECRET_KEY; +static CK_KEY_TYPE aesKeyType = CKK_AES; + +static CK_RV pkcs11_init(void) +{ + CK_RV ret; + CK_C_INITIALIZE_ARGS args; + CK_SLOT_ID slotList[16]; + CK_ULONG slotCount = sizeof(slotList) / sizeof(slotList[0]); + +#ifndef HAVE_PKCS11_STATIC + CK_C_GetFunctionList func; + + dlib = dlopen(WOLFPKCS11_DLL_FILENAME, RTLD_NOW | RTLD_LOCAL); + if (dlib == NULL) { + fprintf(stderr, "dlopen error: %s\n", dlerror()); + return -1; + } + func = (CK_C_GetFunctionList)dlsym(dlib, "C_GetFunctionList"); + if (func == NULL) { + fprintf(stderr, "Failed to get function list function\n"); + dlclose(dlib); + return -1; + } + ret = func(&funcList); + if (ret != CKR_OK) { + fprintf(stderr, "Failed to get function list: 0x%lx\n", + (unsigned long)ret); + dlclose(dlib); + return ret; + } +#else + ret = C_GetFunctionList(&funcList); + if (ret != CKR_OK) { + fprintf(stderr, "Failed to get function list: 0x%lx\n", + (unsigned long)ret); + return ret; + } +#endif + + XMEMSET(&args, 0, sizeof(args)); + args.flags = CKF_OS_LOCKING_OK; + ret = funcList->C_Initialize(&args); + if (ret != CKR_OK) + return ret; + + ret = funcList->C_GetSlotList(CK_TRUE, slotList, &slotCount); + if (ret != CKR_OK) + return ret; + + if (slotCount > 0) + slot = slotList[0]; + else { + fprintf(stderr, "No slots available\n"); + return CKR_GENERAL_ERROR; + } + + return ret; +} + +static CK_RV pkcs11_final(void) +{ + if (funcList != NULL) { + funcList->C_Finalize(NULL); + funcList = NULL; + } +#ifndef HAVE_PKCS11_STATIC + if (dlib) { + dlclose(dlib); + dlib = NULL; + } +#endif + return CKR_OK; +} + +static CK_RV pkcs11_init_token(void) +{ + unsigned char label[32]; + + XMEMSET(label, ' ', sizeof(label)); + XMEMCPY(label, tokenName, XSTRLEN(tokenName)); + return funcList->C_InitToken(slot, soPin, soPinLen, label); +} + +static CK_RV pkcs11_set_user_pin(void) +{ + CK_SESSION_HANDLE soSession; + int sessFlags = CKF_SERIAL_SESSION | CKF_RW_SESSION; + CK_RV ret; + + ret = funcList->C_OpenSession(slot, sessFlags, NULL, NULL, &soSession); + if (ret != CKR_OK) + return ret; + ret = funcList->C_Login(soSession, CKU_SO, soPin, soPinLen); + if (ret != CKR_OK) { + funcList->C_CloseSession(soSession); + return ret; + } + ret = funcList->C_InitPIN(soSession, userPin, userPinLen); + funcList->C_Logout(soSession); + funcList->C_CloseSession(soSession); + return ret; +} + +static void cleanup_test_files(const char* dir) +{ + char filepath[512]; + + snprintf(filepath, sizeof(filepath), "%s" PATH_SEP "%s", dir, + WOLFPKCS11_TOKEN_FILENAME); + (void)remove(filepath); +} + +/* Create an AES secret key, on the token or in the session. */ +static CK_RV create_key(CK_SESSION_HANDLE session, CK_BBOOL onToken, + CK_OBJECT_HANDLE* key) +{ + CK_ATTRIBUTE tmpl[] = { + { CKA_CLASS, &secretKeyClass, sizeof(secretKeyClass) }, + { CKA_KEY_TYPE, &aesKeyType, sizeof(aesKeyType) }, + { CKA_VALUE, aes_128_key, sizeof(aes_128_key) }, + { CKA_TOKEN, &onToken, sizeof(onToken) }, + }; + CK_ULONG tmplCnt = sizeof(tmpl) / sizeof(*tmpl); + + return funcList->C_CreateObject(session, tmpl, tmplCnt, key); +} + +/* Check that the object behind a handle can still be read. */ +static CK_RV read_class(CK_SESSION_HANDLE session, CK_OBJECT_HANDLE key) +{ + CK_OBJECT_CLASS cls = 0; + CK_ATTRIBUTE tmpl[] = { + { CKA_CLASS, &cls, sizeof(cls) }, + }; + + return funcList->C_GetAttributeValue(session, key, tmpl, 1); +} + +/* + * Destroy one object, create another, then ask for the removal of the new + * object's pointer under the destroyed object's handle. It must be refused and + * leave the new object in place. + */ +static int stale_handle_case(CK_SESSION_HANDLE session, CK_BBOOL onToken) +{ + CK_RV ret; + CK_OBJECT_HANDLE stale = CK_INVALID_HANDLE; + CK_OBJECT_HANDLE live = CK_INVALID_HANDLE; + const char* kind = onToken ? "token" : "session"; + char msg[96]; + int rc; + int result = 0; + + ret = create_key(session, onToken, &stale); + snprintf(msg, sizeof(msg), "%s: create first key", kind); + CHECK_CKR(ret, msg, CKR_OK); + + ret = funcList->C_DestroyObject(session, stale); + snprintf(msg, sizeof(msg), "%s: destroy first key", kind); + CHECK_CKR(ret, msg, CKR_OK); + + ret = create_key(session, onToken, &live); + snprintf(msg, sizeof(msg), "%s: create second key", kind); + CHECK_CKR(ret, msg, CKR_OK); + + snprintf(msg, sizeof(msg), "%s: handles differ", kind); + CHECK_COND(live != stale, msg); + + rc = WP11_Test_RemoveObjectStale(session, live, stale); + snprintf(msg, sizeof(msg), + "%s: removal by live pointer and stale handle refused", kind); + CHECK_CKR((CK_RV)rc, msg, (CK_RV)OBJECT_ALREADY_REMOVED); + + ret = read_class(session, live); + snprintf(msg, sizeof(msg), "%s: second key still present", kind); + CHECK_CKR(ret, msg, CKR_OK); + + /* Control: with the matching handle the hook does remove (and free). */ + rc = WP11_Test_RemoveObjectStale(session, live, live); + snprintf(msg, sizeof(msg), "%s: removal with matching handle succeeds", + kind); + CHECK_CKR((CK_RV)rc, msg, (CK_RV)0); + + ret = read_class(session, live); + snprintf(msg, sizeof(msg), "%s: second key gone after removal", kind); + CHECK_CKR(ret, msg, CKR_OBJECT_HANDLE_INVALID); + +cleanup: + return result; +} + +static int destroy_stale_handle_test(void) +{ + CK_RV ret; + CK_SESSION_HANDLE session = 0; + int sessFlags = CKF_SERIAL_SESSION | CKF_RW_SESSION; + int result = 0; + + printf("\n=== Testing object removal with a stale handle ===\n"); + + cleanup_test_files(STALE_HANDLE_TEST_DIR); + + ret = pkcs11_init(); + if (ret != CKR_OK) { + fprintf(stderr, "FAIL: pkcs11_init: 0x%lx\n", (unsigned long)ret); + test_failed++; + return -1; + } + ret = pkcs11_init_token(); + if (ret != CKR_OK) { + fprintf(stderr, "FAIL: C_InitToken: 0x%lx\n", (unsigned long)ret); + test_failed++; + pkcs11_final(); + return -1; + } + ret = pkcs11_set_user_pin(); + if (ret != CKR_OK) { + fprintf(stderr, "FAIL: set user PIN: 0x%lx\n", (unsigned long)ret); + test_failed++; + pkcs11_final(); + return -1; + } + + ret = funcList->C_OpenSession(slot, sessFlags, NULL, NULL, &session); + CHECK_CKR(ret, "C_OpenSession", CKR_OK); + + /* Secret keys default to CKA_PRIVATE, so resolving them needs a user + * login. */ + ret = funcList->C_Login(session, CKU_USER, userPin, userPinLen); + CHECK_CKR(ret, "C_Login user", CKR_OK); + + if (stale_handle_case(session, CK_TRUE) != 0) + result = -1; + if (stale_handle_case(session, CK_FALSE) != 0) + result = -1; + +cleanup: + if (session != 0) { + funcList->C_Logout(session); + funcList->C_CloseSession(session); + } + pkcs11_final(); + return result; +} + +static void print_results(void) +{ + printf("\n=== Test Results ===\n"); + printf("Tests passed: %d\n", test_passed); + printf("Tests failed: %d\n", test_failed); + + if (test_failed == 0) + printf("ALL TESTS PASSED!\n"); + else + printf("SOME TESTS FAILED!\n"); +} + +int main(int argc, char* argv[]) +{ +#ifndef WOLFPKCS11_NO_ENV + XSETENV("WOLFPKCS11_TOKEN_PATH", STALE_HANDLE_TEST_DIR, 1); +#endif + + (void)argc; + (void)argv; + + printf("=== wolfPKCS11 Destroy Stale Handle Test ===\n"); + + (void)destroy_stale_handle_test(); + + print_results(); + + return (test_failed == 0) ? 0 : 1; +} + +#else /* !DEBUG_WOLFPKCS11 || NO_AES */ + +int main(int argc, char* argv[]) +{ + (void)argc; + (void)argv; + + printf("Debug hook or AES not available, skipping destroy stale handle " + "test\n"); + return 77; /* automake: test skipped */ +} + +#endif /* DEBUG_WOLFPKCS11 && !NO_AES */ diff --git a/tests/include.am b/tests/include.am index 89040ccb..eb59bc50 100644 --- a/tests/include.am +++ b/tests/include.am @@ -245,6 +245,11 @@ noinst_PROGRAMS += tests/concurrent_destroy_object_test tests_concurrent_destroy_object_test_SOURCES = tests/concurrent_destroy_object_test.c tests_concurrent_destroy_object_test_LDADD = +check_PROGRAMS += tests/destroy_stale_handle_test +noinst_PROGRAMS += tests/destroy_stale_handle_test +tests_destroy_stale_handle_test_SOURCES = tests/destroy_stale_handle_test.c +tests_destroy_stale_handle_test_LDADD = + check_PROGRAMS += tests/hbs_persistence_test noinst_PROGRAMS += tests/hbs_persistence_test tests_hbs_persistence_test_SOURCES = tests/hbs_persistence_test.c @@ -355,6 +360,7 @@ tests_mlkem_secret_scrub_test_LDADD += src/libwolfpkcs11.la tests_logout_token_key_zero_test_LDADD += src/libwolfpkcs11.la tests_tpm_decode_bounds_test_LDADD += src/libwolfpkcs11.la tests_concurrent_destroy_object_test_LDADD += src/libwolfpkcs11.la +tests_destroy_stale_handle_test_LDADD += src/libwolfpkcs11.la tests_hbs_persistence_test_LDADD += src/libwolfpkcs11.la tests_hkdf_persistence_test_LDADD += src/libwolfpkcs11.la tests_unique_id_test_LDADD += src/libwolfpkcs11.la @@ -409,6 +415,7 @@ tests_mlkem_secret_scrub_test_LDADD += src/libwolfpkcs11.la tests_logout_token_key_zero_test_LDADD += src/libwolfpkcs11.la tests_tpm_decode_bounds_test_LDADD += src/libwolfpkcs11.la tests_concurrent_destroy_object_test_LDADD += src/libwolfpkcs11.la +tests_destroy_stale_handle_test_LDADD += src/libwolfpkcs11.la tests_hbs_persistence_test_LDADD += src/libwolfpkcs11.la tests_hkdf_persistence_test_LDADD += src/libwolfpkcs11.la tests_unique_id_test_LDADD += src/libwolfpkcs11.la diff --git a/wolfpkcs11/internal.h b/wolfpkcs11/internal.h index a77ad662..b14cd3d9 100644 --- a/wolfpkcs11/internal.h +++ b/wolfpkcs11/internal.h @@ -481,10 +481,14 @@ WP11_LOCAL int WP11_Session_AddObject(WP11_Session* session, int onToken, * object is left in place and must not be freed. */ #define WP11_OBJECT_NOT_DESTROYABLE 2 WP11_LOCAL int WP11_Session_RemoveObjectByHandle(WP11_Session* session, - WP11_Object* object, int onToken, + WP11_Object* object, CK_OBJECT_HANDLE handle, int checkDestroyable); WP11_LOCAL void WP11_Session_RemoveObject(WP11_Session* session, WP11_Object* object); +#ifdef DEBUG_WOLFPKCS11 +WP11_API int WP11_Test_RemoveObjectStale(CK_SESSION_HANDLE hSession, + CK_OBJECT_HANDLE liveHandle, CK_OBJECT_HANDLE staleHandle); +#endif WP11_LOCAL int WP11_Object_HandleOnToken(CK_OBJECT_HANDLE handle); WP11_LOCAL void WP11_Slot_ClearActiveObject(WP11_Slot* slot, WP11_Object* object);