Skip to content

Commit 8818064

Browse files
mattcaswellt8m
authored andcommitted
Fix a UAF resulting from a bug in BIO_new_NDEF
If the aux->asn1_cb() call fails in BIO_new_NDEF then the "out" BIO will be part of an invalid BIO chain. This causes a "use after free" when the BIO is eventually freed. Based on an original patch by Viktor Dukhovni and an idea from Theo Buehler. Thanks to Octavio Galland for reporting this issue. Reviewed-by: Paul Dale <pauli@openssl.org> Reviewed-by: Tomas Mraz <tomas@openssl.org>
1 parent cbafa34 commit 8818064

File tree

1 file changed

+32
-8
lines changed

1 file changed

+32
-8
lines changed

crypto/asn1/bio_ndef.c

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -49,13 +49,19 @@ static int ndef_suffix(BIO *b, unsigned char **pbuf, int *plen, void *parg);
4949
static int ndef_suffix_free(BIO *b, unsigned char **pbuf, int *plen,
5050
void *parg);
5151

52-
/* unfortunately cannot constify this due to CMS_stream() and PKCS7_stream() */
52+
/*
53+
* On success, the returned BIO owns the input BIO as part of its BIO chain.
54+
* On failure, NULL is returned and the input BIO is owned by the caller.
55+
*
56+
* Unfortunately cannot constify this due to CMS_stream() and PKCS7_stream()
57+
*/
5358
BIO *BIO_new_NDEF(BIO *out, ASN1_VALUE *val, const ASN1_ITEM *it)
5459
{
5560
NDEF_SUPPORT *ndef_aux = NULL;
5661
BIO *asn_bio = NULL;
5762
const ASN1_AUX *aux = it->funcs;
5863
ASN1_STREAM_ARG sarg;
64+
BIO *pop_bio = NULL;
5965

6066
if (!aux || !aux->asn1_cb) {
6167
ERR_raise(ERR_LIB_ASN1, ASN1_R_STREAMING_NOT_SUPPORTED);
@@ -70,33 +76,51 @@ BIO *BIO_new_NDEF(BIO *out, ASN1_VALUE *val, const ASN1_ITEM *it)
7076
out = BIO_push(asn_bio, out);
7177
if (out == NULL)
7278
goto err;
79+
pop_bio = asn_bio;
7380

74-
BIO_asn1_set_prefix(asn_bio, ndef_prefix, ndef_prefix_free);
75-
BIO_asn1_set_suffix(asn_bio, ndef_suffix, ndef_suffix_free);
81+
if (BIO_asn1_set_prefix(asn_bio, ndef_prefix, ndef_prefix_free) <= 0
82+
|| BIO_asn1_set_suffix(asn_bio, ndef_suffix, ndef_suffix_free) <= 0
83+
|| BIO_ctrl(asn_bio, BIO_C_SET_EX_ARG, 0, ndef_aux) <= 0)
84+
goto err;
7685

7786
/*
78-
* Now let callback prepends any digest, cipher etc BIOs ASN1 structure
79-
* needs.
87+
* Now let the callback prepend any digest, cipher, etc., that the BIO's
88+
* ASN1 structure needs.
8089
*/
8190

8291
sarg.out = out;
8392
sarg.ndef_bio = NULL;
8493
sarg.boundary = NULL;
8594

86-
if (aux->asn1_cb(ASN1_OP_STREAM_PRE, &val, it, &sarg) <= 0)
95+
/*
96+
* The asn1_cb(), must not have mutated asn_bio on error, leaving it in the
97+
* middle of some partially built, but not returned BIO chain.
98+
*/
99+
if (aux->asn1_cb(ASN1_OP_STREAM_PRE, &val, it, &sarg) <= 0) {
100+
/*
101+
* ndef_aux is now owned by asn_bio so we must not free it in the err
102+
* clean up block
103+
*/
104+
ndef_aux = NULL;
87105
goto err;
106+
}
107+
108+
/*
109+
* We must not fail now because the callback has prepended additional
110+
* BIOs to the chain
111+
*/
88112

89113
ndef_aux->val = val;
90114
ndef_aux->it = it;
91115
ndef_aux->ndef_bio = sarg.ndef_bio;
92116
ndef_aux->boundary = sarg.boundary;
93117
ndef_aux->out = out;
94118

95-
BIO_ctrl(asn_bio, BIO_C_SET_EX_ARG, 0, ndef_aux);
96-
97119
return sarg.ndef_bio;
98120

99121
err:
122+
/* BIO_pop() is NULL safe */
123+
(void)BIO_pop(pop_bio);
100124
BIO_free(asn_bio);
101125
OPENSSL_free(ndef_aux);
102126
return NULL;

0 commit comments

Comments
 (0)