From 29e7073c554b9448916c1d01409db921322df118 Mon Sep 17 00:00:00 2001 From: Georges Savoundararadj Date: Wed, 15 Apr 2026 01:26:02 +0000 Subject: [PATCH 1/4] libckteec: fix NULL pointer dereferences in PKCS#11 serialization Multiple functions in serialize_ck.c dereference mechanism parameters and attribute values without NULL checks, causing crashes when processing malformed input: - serialize_mecha_aes_ctr / aes_gcm / aes_cbc_encrypt_data / aes_iv / key_deriv_str / ecdh1_derive_param / rsa_pss_param / rsa_oaep_param / rsa_aes_key_wrap / eddsa / mac_general_param: NULL-check pParameter (and, in the OAEP/ECDH1 cases, defer the size computation until after the check). - serialize_mecha_rsa_aes_key_wrap also NULL-checks params->pOAEPParams, which was previously dereferenced unconditionally on the variable initialiser line. All handlers return CKR_MECHANISM_PARAM_INVALID for NULL pParameter, matching the existing return code used elsewhere in the file. CWE-476: NULL Pointer Dereference Found by fuzzing with AFL++/AddressSanitizer. Fixes: e7a4706e08c8 ("libckteec: add support for symmetric ciphers") Fixes: 0698b4264ec2 ("libckteec: serialize_ck: Add helper functions for mechanisms") Fixes: 98dc4f488c2f ("libckteec: serialize_ck.c: serialize AES-GCM mechanism parameters") Fixes: 8b3f7fe3401f ("libckteec: add support for ECDH derive") Fixes: 6e94ff57e0f7 ("libckteec: Add PKCS #1 RSA PSS signing support") Fixes: cd66d4071ca8 ("libckteec: Add PKCS #1 RSA OAEP encryption support") Fixes: d26d885c82d2 ("libckteec: Add RSA AES key wrap serialization") Fixes: 719780674ee9 ("libckteec: Add support for more HMAC mechanisms") Fixes: 140bf4630460 ("libckteec: Add EDDSA attribute serialization") Reported-by: Tobi Gaertner Signed-off-by: Georges Savoundararadj Reviewed-by: Jens Wiklander --- libckteec/src/serialize_ck.c | 50 ++++++++++++++++++++++++++++++++---- 1 file changed, 45 insertions(+), 5 deletions(-) diff --git a/libckteec/src/serialize_ck.c b/libckteec/src/serialize_ck.c index f8b5bf91..19464964 100644 --- a/libckteec/src/serialize_ck.c +++ b/libckteec/src/serialize_ck.c @@ -393,6 +393,9 @@ static CK_RV serialize_mecha_aes_ctr(struct serializer *obj, CK_RV rv = CKR_GENERAL_ERROR; uint32_t size = 0; + if (!param) + return CKR_MECHANISM_PARAM_INVALID; + rv = serialize_32b(obj, obj->type); if (rv) return rv; @@ -420,6 +423,9 @@ static CK_RV serialize_mecha_aes_gcm(struct serializer *obj, CK_RV rv = CKR_GENERAL_ERROR; CK_ULONG aad_len = 0; + if (!param) + return CKR_MECHANISM_PARAM_INVALID; + /* AAD is not manadatory */ if (param->pAAD) aad_len = param->ulAADLen; @@ -461,6 +467,9 @@ static CK_RV serialize_mecha_aes_iv(struct serializer *obj, uint32_t iv_size = mecha->ulParameterLen; CK_RV rv = CKR_GENERAL_ERROR; + if (iv_size && !mecha->pParameter) + return CKR_MECHANISM_PARAM_INVALID; + rv = serialize_32b(obj, obj->type); if (rv) return rv; @@ -479,6 +488,9 @@ static CK_RV serialize_mecha_key_deriv_str(struct serializer *obj, CK_RV rv = CKR_GENERAL_ERROR; uint32_t size = 0; + if (!param) + return CKR_MECHANISM_PARAM_INVALID; + rv = serialize_32b(obj, obj->type); if (rv) return rv; @@ -500,8 +512,13 @@ static CK_RV serialize_mecha_ecdh1_derive_param(struct serializer *obj, { CK_ECDH1_DERIVE_PARAMS *params = mecha->pParameter; CK_RV rv = CKR_GENERAL_ERROR; - size_t params_size = 3 * sizeof(uint32_t) + params->ulSharedDataLen + - params->ulPublicDataLen; + size_t params_size = 0; + + if (!params) + return CKR_MECHANISM_PARAM_INVALID; + + params_size = 3 * sizeof(uint32_t) + params->ulSharedDataLen + + params->ulPublicDataLen; rv = serialize_32b(obj, obj->type); if (rv) @@ -539,6 +556,9 @@ static CK_RV serialize_mecha_aes_cbc_encrypt_data(struct serializer *obj, CK_RV rv = CKR_GENERAL_ERROR; uint32_t size = 0; + if (!param) + return CKR_MECHANISM_PARAM_INVALID; + rv = serialize_32b(obj, obj->type); if (rv) return rv; @@ -566,6 +586,9 @@ static CK_RV serialize_mecha_rsa_pss_param(struct serializer *obj, CK_RV rv = CKR_GENERAL_ERROR; uint32_t params_size = 3 * sizeof(uint32_t); + if (!params) + return CKR_MECHANISM_PARAM_INVALID; + if (mecha->ulParameterLen != sizeof(*params)) return CKR_ARGUMENTS_BAD; @@ -593,11 +616,16 @@ static CK_RV serialize_mecha_rsa_oaep_param(struct serializer *obj, { CK_RSA_PKCS_OAEP_PARAMS *params = mecha->pParameter; CK_RV rv = CKR_GENERAL_ERROR; - size_t params_size = 4 * sizeof(uint32_t) + params->ulSourceDataLen; + size_t params_size = 0; + + if (!params) + return CKR_MECHANISM_PARAM_INVALID; if (mecha->ulParameterLen != sizeof(*params)) return CKR_ARGUMENTS_BAD; + params_size = 4 * sizeof(uint32_t) + params->ulSourceDataLen; + rv = serialize_32b(obj, obj->type); if (rv) return rv; @@ -630,13 +658,19 @@ static CK_RV serialize_mecha_rsa_aes_key_wrap(struct serializer *obj, CK_MECHANISM_PTR mecha) { CK_RSA_AES_KEY_WRAP_PARAMS *params = mecha->pParameter; - CK_RSA_PKCS_OAEP_PARAMS *aes_params = params->pOAEPParams; + CK_RSA_PKCS_OAEP_PARAMS *aes_params = NULL; CK_RV rv = CKR_GENERAL_ERROR; - size_t params_size = 5 * sizeof(uint32_t) + aes_params->ulSourceDataLen; + size_t params_size = 0; + + if (!params || !params->pOAEPParams) + return CKR_MECHANISM_PARAM_INVALID; if (mecha->ulParameterLen != sizeof(*params)) return CKR_ARGUMENTS_BAD; + aes_params = params->pOAEPParams; + params_size = 5 * sizeof(uint32_t) + aes_params->ulSourceDataLen; + rv = serialize_32b(obj, obj->type); if (rv) return rv; @@ -689,6 +723,9 @@ static CK_RV serialize_mecha_eddsa(struct serializer *obj, params_len = sizeof(*params); } + if (!params) + return CKR_MECHANISM_PARAM_INVALID; + if (params_len != sizeof(*params)) return CKR_ARGUMENTS_BAD; @@ -717,6 +754,9 @@ static CK_RV serialize_mecha_mac_general_param(struct serializer *obj, CK_RV rv = CKR_GENERAL_ERROR; CK_ULONG ck_data = 0; + if (!mecha->pParameter) + return CKR_MECHANISM_PARAM_INVALID; + if (mecha->ulParameterLen != sizeof(ck_data)) return CKR_ARGUMENTS_BAD; From 0ceb997e29f596af25e8aa049c3fa5365a521b15 Mon Sep 17 00:00:00 2001 From: Georges Savoundararadj Date: Wed, 15 Apr 2026 01:21:34 +0000 Subject: [PATCH 2/4] libckteec: add data pointer validation in serialize() serialize() calls memcpy(buf + *blen, data, len) without validating the data pointer. When a caller passes a wild or NULL pointer with a non-zero len, this causes an invalid memory access (CWE-476). Add input validation: - Reject len > 0 with NULL data, returning CKR_ARGUMENTS_BAD - Short-circuit len == 0 as a no-op, returning CKR_OK - Move variable declarations to top for C89 compliance Found by fuzzing with AFL++/AddressSanitizer. Fixes: 85a7ea7 ("libckteec: introduce helpers for serializing data") Reported-by: Tobi Gaertner Signed-off-by: Georges Savoundararadj Reviewed-by: Jens Wiklander --- libckteec/src/serializer.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/libckteec/src/serializer.c b/libckteec/src/serializer.c index d94932ba..cfccba67 100644 --- a/libckteec/src/serializer.c +++ b/libckteec/src/serializer.c @@ -5,6 +5,7 @@ #include #include +#include #include #include @@ -44,8 +45,20 @@ void release_serial_object(struct serializer *obj) */ static CK_RV serialize(char **bstart, size_t *blen, void *data, size_t len) { - size_t nlen = *blen + len; - char *buf = realloc(*bstart, nlen); + size_t nlen = 0; + char *buf = NULL; + + if (len && !data) + return CKR_ARGUMENTS_BAD; + + if (!len) + return CKR_OK; + + if (len > SIZE_MAX - *blen) + return CKR_DEVICE_MEMORY; + + nlen = *blen + len; + buf = realloc(*bstart, nlen); if (!buf) return CKR_HOST_MEMORY; From 0cd8b36407fe8d2ebe73b92240f5902688cc1959 Mon Sep 17 00:00:00 2001 From: Georges Savoundararadj Date: Mon, 18 May 2026 14:53:47 +0000 Subject: [PATCH 3/4] libckteec: add integer overflow guard in serialize_mecha_aes_cbc_encrypt_data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit param->length is CK_ULONG (64-bit on LP64) but is added into a uint32_t size variable without bounds checking. Values above UINT32_MAX silently truncate, producing a small size value. The subsequent serialize_buffer() then writes param->length bytes into a buffer sized by the truncated value — heap buffer overflow. Add an overflow check before the size calculation to reject param->length values that would exceed the uint32_t budget. CWE-190 (Integer Overflow) leading to CWE-122 (Heap Buffer Overflow). Found by code review during fuzzing triage. Fixes: 0698b42 ("libckteec: serialize_ck: Add helper functions for mechanisms") Reported-by: Tobi Gaertner Signed-off-by: Georges Savoundararadj Reviewed-by: Jens Wiklander --- libckteec/src/serialize_ck.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libckteec/src/serialize_ck.c b/libckteec/src/serialize_ck.c index 19464964..adb2b84d 100644 --- a/libckteec/src/serialize_ck.c +++ b/libckteec/src/serialize_ck.c @@ -559,6 +559,9 @@ static CK_RV serialize_mecha_aes_cbc_encrypt_data(struct serializer *obj, if (!param) return CKR_MECHANISM_PARAM_INVALID; + if (param->length > UINT32_MAX - sizeof(param->iv) - sizeof(uint32_t)) + return CKR_MECHANISM_PARAM_INVALID; + rv = serialize_32b(obj, obj->type); if (rv) return rv; From 877d082672970cb542576fb3a9c44abb87e403af Mon Sep 17 00:00:00 2001 From: Georges Savoundararadj Date: Mon, 18 May 2026 14:54:50 +0000 Subject: [PATCH 4/4] libckteec: replace strerror with strerror_r in invoke_ta.c strerror() returns a pointer to a shared static buffer that is not thread-safe. Since libckteec uses pthread_mutex for session management, concurrent threads hitting mutex errors could observe corrupted error strings. Replace with strerror_r() which writes to a caller-provided buffer. Check the return value and null-terminate on failure. Fixes: 2c812c5 ("libckteec: return CKR_CANT_LOCK if mutex fails at lib initialization") Reported-by: Tobi Gaertner Signed-off-by: Georges Savoundararadj Reviewed-by: Jens Wiklander --- libckteec/src/invoke_ta.c | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/libckteec/src/invoke_ta.c b/libckteec/src/invoke_ta.c index 07b110ea..6d9742da 100644 --- a/libckteec/src/invoke_ta.c +++ b/libckteec/src/invoke_ta.c @@ -15,6 +15,8 @@ #include #include #include + +#define STRERROR_BUF_SIZE 128 #include #include #include @@ -319,7 +321,11 @@ CK_RV ckteec_invoke_init(void) out: e = pthread_mutex_unlock(&ta_ctx.init_mutex); if (e) { - EMSG("pthread_mutex_unlock: %s", strerror(e)); + char errbuf[STRERROR_BUF_SIZE]; + + if (strerror_r(e, errbuf, sizeof(errbuf))) + errbuf[0] = '\0'; + EMSG("pthread_mutex_unlock: %s", errbuf); EMSG("terminating..."); exit(EXIT_FAILURE); } @@ -334,7 +340,11 @@ CK_RV ckteec_invoke_terminate(void) e = pthread_mutex_lock(&ta_ctx.init_mutex); if (e) { - EMSG("pthread_mutex_lock: %s", strerror(e)); + char errbuf[STRERROR_BUF_SIZE]; + + if (strerror_r(e, errbuf, sizeof(errbuf))) + errbuf[0] = '\0'; + EMSG("pthread_mutex_lock: %s", errbuf); EMSG("terminating..."); exit(EXIT_FAILURE); } @@ -351,7 +361,11 @@ CK_RV ckteec_invoke_terminate(void) out: e = pthread_mutex_unlock(&ta_ctx.init_mutex); if (e) { - EMSG("pthread_mutex_unlock: %s", strerror(e)); + char errbuf[STRERROR_BUF_SIZE]; + + if (strerror_r(e, errbuf, sizeof(errbuf))) + errbuf[0] = '\0'; + EMSG("pthread_mutex_unlock: %s", errbuf); EMSG("terminating..."); exit(EXIT_FAILURE); }