From 7de624ff087964abe014f2feb2dba56ea7a40a99 Mon Sep 17 00:00:00 2001 From: Colton Willey Date: Mon, 13 Apr 2026 11:42:17 -0700 Subject: [PATCH] Add negative length validation to d2i wrappers, PEM helpers, and buffer loaders Reject negative signed lengths before they are cast to unsigned (word32/size_t), preventing heap buffer over-reads and oversized allocations. Covers d2i_* OpenSSL compat wrappers, ProcessBuffer, PemToDer, certgen helpers, and CRL buffer paths. --- src/crl.c | 4 ++-- src/ocsp.c | 2 ++ src/pk_ec.c | 5 +++++ src/pk_rsa.c | 8 ++++++++ src/ssl_asn1.c | 13 +++++++++---- src/ssl_load.c | 4 ++++ src/x509.c | 4 ++-- wolfcrypt/src/asn.c | 33 +++++++++++++++++++++++++++++++-- wolfcrypt/src/evp_pk.c | 10 ++++++++-- 9 files changed, 71 insertions(+), 12 deletions(-) diff --git a/src/crl.c b/src/crl.c index 3adc15889f..ed32de3f36 100644 --- a/src/crl.c +++ b/src/crl.c @@ -849,7 +849,7 @@ int BufferLoadCRL(WOLFSSL_CRL* crl, const byte* buff, long sz, int type, WOLFSSL_ENTER("BufferLoadCRL"); - if (crl == NULL || buff == NULL || sz == 0) + if (crl == NULL || buff == NULL || sz <= 0) return BAD_FUNC_ARG; if (type == WOLFSSL_FILETYPE_PEM) { @@ -1175,7 +1175,7 @@ int GetCRLInfo(WOLFSSL_CRL* crl, CrlInfo* info, const byte* buff, WOLFSSL_ENTER("GetCRLInfo"); - if (crl == NULL || info == NULL || buff == NULL || sz == 0) + if (crl == NULL || info == NULL || buff == NULL || sz <= 0) return BAD_FUNC_ARG; if (type == WOLFSSL_FILETYPE_PEM) { diff --git a/src/ocsp.c b/src/ocsp.c index b90fcc8af9..bae2a499ce 100644 --- a/src/ocsp.c +++ b/src/ocsp.c @@ -1272,6 +1272,8 @@ OcspResponse* wolfSSL_d2i_OCSP_RESPONSE(OcspResponse** response, if (data == NULL) return NULL; + if (len <= 0) + return NULL; if (response != NULL) resp = *response; diff --git a/src/pk_ec.c b/src/pk_ec.c index 883f758150..02af9bacc5 100644 --- a/src/pk_ec.c +++ b/src/pk_ec.c @@ -449,6 +449,8 @@ static WOLFSSL_EC_GROUP* wolfssl_ec_group_d2i(WOLFSSL_EC_GROUP** group, if (in_pp == NULL || *in_pp == NULL) return NULL; + if (inSz <= 0) + return NULL; in = *in_pp; @@ -5001,6 +5003,9 @@ WOLFSSL_ECDSA_SIG* wolfSSL_d2i_ECDSA_SIG(WOLFSSL_ECDSA_SIG** sig, if (pp == NULL) { err = 1; } + if ((!err) && (len <= 0)) { + err = 1; + } if (!err) { if (sig != NULL) { /* Use the ECDSA signature object passed in. */ diff --git a/src/pk_rsa.c b/src/pk_rsa.c index ba68929bfa..685fdb9fd1 100644 --- a/src/pk_rsa.c +++ b/src/pk_rsa.c @@ -454,6 +454,10 @@ WOLFSSL_RSA *wolfSSL_d2i_RSAPublicKey(WOLFSSL_RSA **out, WOLFSSL_ERROR_MSG("Bad argument"); err = 1; } + if ((!err) && (derSz <= 0)) { + WOLFSSL_ERROR_MSG("Bad argument"); + err = 1; + } /* Create a new RSA key to return. */ if ((!err) && ((rsa = wolfSSL_RSA_new()) == NULL)) { WOLFSSL_ERROR_MSG("RSA_new failed"); @@ -503,6 +507,10 @@ WOLFSSL_RSA *wolfSSL_d2i_RSAPrivateKey(WOLFSSL_RSA **out, WOLFSSL_ERROR_MSG("Bad argument"); err = 1; } + if ((!err) && (derSz <= 0)) { + WOLFSSL_ERROR_MSG("Bad argument"); + err = 1; + } /* Create a new RSA key to return. */ if ((!err) && ((rsa = wolfSSL_RSA_new()) == NULL)) { WOLFSSL_ERROR_MSG("RSA_new failed"); diff --git a/src/ssl_asn1.c b/src/ssl_asn1.c index 8dd158ef19..f7734eddba 100644 --- a/src/ssl_asn1.c +++ b/src/ssl_asn1.c @@ -913,7 +913,7 @@ WOLFSSL_ASN1_BIT_STRING* wolfSSL_d2i_ASN1_BIT_STRING( WOLFSSL_ENTER("wolfSSL_d2i_ASN1_BIT_STRING"); - if (src == NULL || *src == NULL || len == 0) + if (src == NULL || *src == NULL || len <= 0) return NULL; if (GetASNTag(*src, &idx, &tag, (word32)len) < 0) @@ -2984,7 +2984,7 @@ static WOLFSSL_ASN1_STRING* d2i_ASN1_STRING(WOLFSSL_ASN1_STRING** out, WOLFSSL_ENTER("d2i_ASN1_STRING"); - if (src == NULL || *src == NULL || len == 0) + if (src == NULL || *src == NULL || len <= 0) return NULL; if (GetASNTag(*src, &idx, &tag, (word32)len) < 0) @@ -3159,6 +3159,11 @@ int wolfSSL_ASN1_STRING_set(WOLFSSL_ASN1_STRING* asn1, const void* data, int sz) } if (ret == 1) { + /* Cast to size_t BEFORE adding 1 to prevent signed overflow + * when sz == INT_MAX. By this point sz >= 0 (negative sz is + * handled above as OpenSSL -1/strlen compat). */ + size_t allocSz = (size_t)sz + 1; + /* Dispose of any existing dynamic data. */ if (asn1->isDynamic) { XFREE(asn1->data, NULL, DYNAMIC_TYPE_OPENSSL); @@ -3166,9 +3171,9 @@ int wolfSSL_ASN1_STRING_set(WOLFSSL_ASN1_STRING* asn1, const void* data, int sz) } /* Check string will fit - including NUL. */ - if (sz + 1 > CTC_NAME_SIZE) { + if (allocSz > CTC_NAME_SIZE) { /* Allocate new buffer. */ - asn1->data = (char*)XMALLOC((size_t)(sz + 1), NULL, + asn1->data = (char*)XMALLOC(allocSz, NULL, DYNAMIC_TYPE_OPENSSL); if (asn1->data == NULL) { ret = 0; diff --git a/src/ssl_load.c b/src/ssl_load.c index 888c578334..9831518005 100644 --- a/src/ssl_load.c +++ b/src/ssl_load.c @@ -2423,6 +2423,10 @@ int ProcessBuffer(WOLFSSL_CTX* ctx, const unsigned char* buff, long sz, if ((ret == 0) && (type == CHAIN_CERT_TYPE)) { ret = BAD_FUNC_ARG; } + /* Reject negative size - would wrap to huge word32. */ + if ((ret == 0) && (sz < 0)) { + ret = BAD_FUNC_ARG; + } #ifdef WOLFSSL_SMALL_STACK if (ret == 0) { diff --git a/src/x509.c b/src/x509.c index 73f4c3c92e..a3e412f1ed 100644 --- a/src/x509.c +++ b/src/x509.c @@ -4147,7 +4147,7 @@ static WOLFSSL_X509* d2i_X509orX509REQ(WOLFSSL_X509** x509, WOLFSSL_ENTER("wolfSSL_X509_d2i"); - if (in != NULL && len != 0 + if (in != NULL && len > 0 #ifndef WOLFSSL_CERT_REQ && req == 0 #else @@ -11291,7 +11291,7 @@ WOLFSSL_X509_ALGOR* wolfSSL_d2i_X509_ALGOR(WOLFSSL_X509_ALGOR** out, WOLFSSL_ENTER("wolfSSL_d2i_X509_ALGOR"); - if (src == NULL || *src == NULL || len == 0) + if (src == NULL || *src == NULL || len <= 0) return NULL; if (GetAlgoId(*src, &idx, &oid, oidIgnoreType, (word32)len) != 0) diff --git a/wolfcrypt/src/asn.c b/wolfcrypt/src/asn.c index a4bd0d7ae2..433d6caac3 100644 --- a/wolfcrypt/src/asn.c +++ b/wolfcrypt/src/asn.c @@ -24671,10 +24671,10 @@ int PemToDer(const unsigned char* buff, long longSz, int type, const char* headerEnd = NULL; const char* footerEnd = NULL; const char* consumedEnd = NULL; - const char* bufferEnd = (const char*)(buff + longSz); + const char* bufferEnd = NULL; long neededSz; int ret = 0; - word32 sz = (word32)longSz; + word32 sz = 0; int encrypted_key = 0; DerBuffer* der; word32 algId = 0; @@ -24695,6 +24695,14 @@ int PemToDer(const unsigned char* buff, long longSz, int type, WOLFSSL_ENTER("PemToDer"); + /* Reject negative size - would wrap word32 and corrupt pointer arithmetic. */ + if (longSz < 0) { + return BAD_FUNC_ARG; + } + + bufferEnd = (const char*)(buff + longSz); + sz = (word32)longSz; + /* get PEM header and footer based on type */ ret = wc_PemGetHeaderFooter(type, &header, &footer); if (ret != 0) @@ -29958,6 +29966,9 @@ int wc_SetAuthKeyIdFromCert(Cert *cert, const byte *der, int derSz) if (cert == NULL) { ret = BAD_FUNC_ARG; } + else if (derSz < 0) { + ret = BAD_FUNC_ARG; + } else { /* Check if decodedCert is cached */ if (cert->der != der) { @@ -30462,6 +30473,9 @@ int wc_SetIssuerBuffer(Cert* cert, const byte* der, int derSz) if (cert == NULL) { ret = BAD_FUNC_ARG; } + else if (derSz < 0) { + ret = BAD_FUNC_ARG; + } else { cert->selfSigned = 0; @@ -30491,6 +30505,9 @@ int wc_SetSubjectBuffer(Cert* cert, const byte* der, int derSz) if (cert == NULL) { ret = BAD_FUNC_ARG; } + else if (derSz < 0) { + ret = BAD_FUNC_ARG; + } else { /* Check if decodedCert is cached */ if (cert->der != der) { @@ -30518,6 +30535,9 @@ int wc_SetSubjectRaw(Cert* cert, const byte* der, int derSz) if (cert == NULL) { ret = BAD_FUNC_ARG; } + else if (derSz < 0) { + ret = BAD_FUNC_ARG; + } else { /* Check if decodedCert is cached */ if (cert->der != der) { @@ -30552,6 +30572,9 @@ int wc_SetIssuerRaw(Cert* cert, const byte* der, int derSz) if (cert == NULL) { ret = BAD_FUNC_ARG; } + else if (derSz < 0) { + ret = BAD_FUNC_ARG; + } else { /* Check if decodedCert is cached */ if (cert->der != der) { @@ -30589,6 +30612,9 @@ int wc_SetAltNamesBuffer(Cert* cert, const byte* der, int derSz) if (cert == NULL) { ret = BAD_FUNC_ARG; } + else if (derSz < 0) { + ret = BAD_FUNC_ARG; + } else { /* Check if decodedCert is cached */ if (cert->der != der) { @@ -30616,6 +30642,9 @@ int wc_SetDatesBuffer(Cert* cert, const byte* der, int derSz) if (cert == NULL) { ret = BAD_FUNC_ARG; } + else if (derSz < 0) { + ret = BAD_FUNC_ARG; + } else { /* Check if decodedCert is cached */ if (cert->der != der) { diff --git a/wolfcrypt/src/evp_pk.c b/wolfcrypt/src/evp_pk.c index 03eb059b8c..f601b4a994 100644 --- a/wolfcrypt/src/evp_pk.c +++ b/wolfcrypt/src/evp_pk.c @@ -1464,12 +1464,18 @@ WOLFSSL_EVP_PKEY* wolfSSL_d2i_AutoPrivateKey(WOLFSSL_EVP_PKEY** pkey, { int ret; WOLFSSL_EVP_PKEY* key = NULL; - const byte* der = *pp; + const byte* der; word32 idx = 0; int len = 0; int cnt = 0; word32 algId; - word32 keyLen = (word32)length; + word32 keyLen; + + if (pp == NULL || *pp == NULL || length <= 0) + return NULL; + + der = *pp; + keyLen = (word32)length; /* Take off PKCS#8 wrapper if found. */ if ((len = ToTraditionalInline_ex(der, &idx, keyLen, &algId)) >= 0) {