Skip to content

Commit 2c3c560

Browse files
committed
Fix decrypt-context leak on abandoned streaming EnvelopedData decode
wc_PKCS7_Free() released cachedEncryptedContent but never released pkcs7->decryptKey, the content-cipher context (Aes/Des/Des3) allocated by wc_PKCS7_DecryptContentInit() on the streaming EnvelopedData decode path. On WC_PKCS7_WANT_READ_E the decode deliberately keeps that context to resume on the next input chunk, so a decode abandoned mid-message (e.g. a truncated or malformed streaming message the caller gives up on) and then freed leaked the cipher context. wc_PKCS7_DecodeEnvelopedData()'s own error cleanup only runs for hard errors (ret < 0 && ret != WC_PKCS7_WANT_READ_E), so it does not cover the abandoned-WANT_READ case; the release has to happen in wc_PKCS7_Free(). Before tearing down the stream, and only when a decrypt context is still pending (decryptKey non-NULL), retrieve the content cipher OID from the stream's saved var and call wc_PKCS7_DecryptContentFree(). decryptKey is only ever left allocated by this flow, where that saved var holds the content cipher OID; gating on it leaves completed decodes and other streaming flows (whose saved var is unrelated to any cipher) untouched, and avoids a spurious "Unsupported content cipher type" debug message on their teardown. Exposed by the new constructed-definite [0] negative tests, which decode a malformed message (returning WC_PKCS7_WANT_READ_E) and then free the wc_PKCS7 -- flagged by LeakSanitizer in CI.
1 parent 855f70d commit 2c3c560

1 file changed

Lines changed: 29 additions & 0 deletions

File tree

wolfcrypt/src/pkcs7.c

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1494,13 +1494,42 @@ static int wc_PKCS7_SignerInfoSetSID(wc_PKCS7* pkcs7, byte* in, int inSz)
14941494
}
14951495

14961496

1497+
#if !defined(NO_PKCS7_STREAM) && (!defined(NO_DES3) || !defined(NO_AES))
1498+
/* defined later in this file; needed here to release a decrypt context left
1499+
* initialized by an abandoned streaming decode */
1500+
static void wc_PKCS7_DecryptContentFree(wc_PKCS7* pkcs7, word32 encryptOID,
1501+
void* heap);
1502+
#endif
1503+
14971504
/* releases any memory allocated by a PKCS7 initializer */
14981505
void wc_PKCS7_Free(wc_PKCS7* pkcs7)
14991506
{
15001507
if (pkcs7 == NULL)
15011508
return;
15021509

15031510
#ifndef NO_PKCS7_STREAM
1511+
/* A streaming EnvelopedData decode that was abandoned mid-message (e.g. it
1512+
* returned WC_PKCS7_WANT_READ_E and was never resumed) leaves the
1513+
* content-decryption context allocated by wc_PKCS7_DecryptContentInit().
1514+
* Release it before tearing down the stream so it is not leaked. Only the
1515+
* EnvelopedData decode path allocates decryptKey, and only there does the
1516+
* stream's first saved var hold the content cipher OID, so gate on a
1517+
* pending context: when decryptKey is NULL there is nothing to free and
1518+
* the (unrelated) saved var of another streaming decode must not be
1519+
* interpreted as a cipher OID. */
1520+
#if !defined(NO_DES3) || !defined(NO_AES)
1521+
if (pkcs7->stream != NULL &&
1522+
#ifndef NO_AES
1523+
pkcs7->decryptKey.aes != NULL
1524+
#else
1525+
pkcs7->decryptKey.des3 != NULL
1526+
#endif
1527+
) {
1528+
word32 encOID = 0;
1529+
wc_PKCS7_StreamGetVar(pkcs7, &encOID, NULL, NULL);
1530+
wc_PKCS7_DecryptContentFree(pkcs7, encOID, pkcs7->heap);
1531+
}
1532+
#endif
15041533
wc_PKCS7_FreeStream(pkcs7);
15051534
#endif
15061535

0 commit comments

Comments
 (0)