-
Notifications
You must be signed in to change notification settings - Fork 1k
Use refcount on DER buffer to prevent UAF #10860
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -24693,13 +24693,39 @@ int AllocDer(DerBuffer** pDer, word32 length, int type, void* heap) | |
| der->heap = heap; | ||
| der->buffer = (byte*)der + sizeof(DerBuffer); | ||
| der->length = length; | ||
| wolfSSL_RefInit(&der->ref, &ret); | ||
| #ifdef WOLFSSL_REFCNT_ERROR_RETURN | ||
| if (ret != 0) { | ||
| XFREE(der, heap, dynType); | ||
| *pDer = NULL; | ||
| return ret; | ||
| } | ||
| #endif | ||
| ret = 0; /* Success */ | ||
| } else { | ||
| ret = BAD_FUNC_ARG; | ||
| } | ||
| return ret; | ||
| } | ||
|
|
||
| /* Increments the reference count on a shared DER buffer. | ||
| * | ||
| * A mutex-backed refcount can fail to lock, leaving the count un-incremented; | ||
| * return failure so callers don't proceed with an un-referenced alias. | ||
| * | ||
| * @param [in, out] der DER buffer. May be NULL. | ||
| * @return 1 on success | ||
| * @return 0 on error | ||
| */ | ||
| int RefDer(DerBuffer* der) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔵 [Low] RefDer treats NULL as success; FreeDer discards mutex-lock failure (leak-on-lock-failure) · Style RefDer(NULL) returns 1 (success), which is intentional so aliasing a NULL ctx->certificate is a no-op; callers rely on this. Worth a brief note only: in the mutex build, a lock failure inside FreeDer's wolfSSL_RefDec sets isZero=0 and the buffer is intentionally not freed (leak-on-lock-failure), and ref_err is discarded via Fix: No functional change needed; consider a one-line comment at the FreeDer |
||
| { | ||
| int err = 0; | ||
| if (der != NULL) { | ||
| wolfSSL_RefInc(&der->ref, &err); | ||
| } | ||
| return !err; | ||
| } | ||
|
|
||
| int AllocCopyDer(DerBuffer** pDer, const unsigned char* buff, word32 length, | ||
| int type, void* heap) | ||
| { | ||
|
|
@@ -24715,15 +24741,21 @@ void FreeDer(DerBuffer** pDer) | |
| { | ||
| if (pDer && *pDer) { | ||
| DerBuffer* der = (DerBuffer*)*pDer; | ||
| int isZero = 1; | ||
| int ref_err = 0; | ||
|
|
||
| /* ForceZero private keys */ | ||
| if (((der->type == PRIVATEKEY_TYPE) || | ||
| (der->type == ALT_PRIVATEKEY_TYPE)) && der->buffer != NULL) { | ||
| ForceZero(der->buffer, der->length); | ||
| wolfSSL_RefDec(&der->ref, &isZero, &ref_err); | ||
| (void)ref_err; | ||
| if (isZero) { | ||
| if (((der->type == PRIVATEKEY_TYPE) || | ||
| (der->type == ALT_PRIVATEKEY_TYPE)) && der->buffer != NULL) { | ||
| ForceZero(der->buffer, der->length); | ||
| } | ||
| der->buffer = NULL; | ||
| der->length = 0; | ||
| wolfSSL_RefFree(&der->ref); | ||
| XFREE(der, der->heap, der->dynType); | ||
| } | ||
| der->buffer = NULL; | ||
| der->length = 0; | ||
| XFREE(der, der->heap, der->dynType); | ||
|
|
||
| *pDer = NULL; | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -241,8 +241,9 @@ typedef struct DerBuffer { | |
| byte* buffer; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🟠 [Medium] Public DerBuffer struct grows a new member (ABI/size change; FreeDer now requires AllocDer-initialized ref) · API / ABI The public DerBuffer struct gains a Fix: Document in the ChangeLog/release notes the sizeof(DerBuffer) ABI change so downstream users rebuild rather than mix object files across the boundary, and add a contract note in the DerBuffer/FreeDer doxygen that a DerBuffer must be obtained from AllocDer/wc_AllocDer before being freed with FreeDer/wc_FreeDer. No functional code change required. |
||
| void* heap; | ||
| word32 length; | ||
| int type; /* enum CertType */ | ||
| int dynType; /* DYNAMIC_TYPE_* */ | ||
| int type; /* enum CertType */ | ||
| int dynType; /* DYNAMIC_TYPE_* */ | ||
| wolfSSL_Ref ref; /* refcount for aliased pointers to the der */ | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we really need to always add "wolfSSL_Ref ref;" on all builds? I am concerned about portability and code size growth. |
||
| } DerBuffer; | ||
|
|
||
| typedef struct WOLFSSL_ASN1_TIME { | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🟠 [Medium] No test coverage for add0/add1_chain_cert on an SSL that aliases the CTX chain · Missing Tests
The added test exercises only the leaf-certificate reload path (wolfSSL_CTX_use_certificate_file in the SNI callback), which correctly decrements the aliased reference via ProcessBufferCertHandleDer's now-unconditional FreeDer. The refcount change also affects the cert-CHAIN aliasing path, but there is no test that appends to an SSL's aliased chain via wolfSSL_add1_chain_cert / wolfSSL_add0_chain_cert: create an SSL from a CTX that loaded an intermediate chain (so ssl->buffers.certChain aliases ctx->certChain with refcount 2), then call wolfSSL_add1_chain_cert(ssl, x509) and free both under leak detection. That is exactly the path where the wolfssl_add_to_chain leak (see High finding) survives, which is why the leak was not caught. Severity: review Medium/SUGGEST, review-security Low; stricter view kept.
Fix: Add a test under OPENSSL_EXTRA + KEEP_OUR_CERT that (1) loads a certificate chain on the CTX, (2) creates a WOLFSSL so ssl->buffers.certChain aliases ctx->certChain, (3) calls wolfSSL_add1_chain_cert(ssl, x509) (or wolfSSL_add0_chain_cert), and (4) frees ssl then ctx, verifying no leak under ASAN/valgrind. This validates the chain-alias refcount balance and guards the fix for the High finding.