Skip to content

Commit bd17511

Browse files
Viktor Dukhovnit8m
authored andcommitted
Reject oversized inputs in ASN1_mbstring_ncopy()
In ASN1_mbstring_ncopy() the destination size for BMPSTRING and UNIVERSALSTRING output was computed by a signed left shift on an int: outlen = nchar << 1; /* MBSTRING_BMP */ outlen = nchar << 2; /* MBSTRING_UNIV */ For nchar large enough the result is not representable in int. In the worst case (nchar == 0x40000000) nchar << 2 wraps to zero, OPENSSL_malloc(1) is called, and traverse_string() then writes 4*nchar bytes into the one-byte allocation: a heap buffer overflow. The MBSTRING_UTF8 path computes outlen by summing per-character byte counts in out_utf8(), and that sum can overflow the same int under similarly large inputs. Neither path is reachable from code that processes X.509 certificates through the DIRSTRING_TYPE mask used by ASN1_STRING_set_by_NID(): UNIVERSALSTRING is absent from that mask, and the UTF-8 sum requires inputs on the order of half a gigabyte. Reaching them needs an application that calls ASN1_mbstring_copy()/ASN1_mbstring_ncopy() directly, or registers a custom NID via ASN1_STRING_TABLE_add(), with an oversized attacker-controlled input. Add range checks before each shift and in out_utf8(), raising ASN1_R_STRING_TOO_LONG at the point of detection. Move the existing ASN1_R_INVALID_UTF8STRING raise into out_utf8() too so the two failure modes report distinct codes; the MBSTRING_UTF8 caller is left with cleanup only and now frees dest on error, matching the BMP/UNIV branches. Fixes CVE-2026-7383 Reviewed-by: Nikola Pajkovsky <[email protected]> Reviewed-by: Norbert Pocs <[email protected]> Reviewed-by: Daniel Kubec <[email protected]> MergeDate: Mon Jun 8 13:59:47 2026 (cherry picked from commit d32350a)
1 parent 51ab230 commit bd17511

1 file changed

Lines changed: 28 additions & 3 deletions

File tree

crypto/asn1/a_mbstr.c

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -174,20 +174,39 @@ int ASN1_mbstring_ncopy(ASN1_STRING **out, const unsigned char *in, int len,
174174
break;
175175

176176
case MBSTRING_BMP:
177+
if (nchar > INT_MAX / 2) {
178+
ERR_raise(ERR_LIB_ASN1, ASN1_R_STRING_TOO_LONG);
179+
if (free_out) {
180+
ASN1_STRING_free(dest);
181+
*out = NULL;
182+
}
183+
return -1;
184+
}
177185
outlen = nchar << 1;
178186
cpyfunc = cpy_bmp;
179187
break;
180188

181189
case MBSTRING_UNIV:
190+
if (nchar > INT_MAX / 4) {
191+
ERR_raise(ERR_LIB_ASN1, ASN1_R_STRING_TOO_LONG);
192+
if (free_out) {
193+
ASN1_STRING_free(dest);
194+
*out = NULL;
195+
}
196+
return -1;
197+
}
182198
outlen = nchar << 2;
183199
cpyfunc = cpy_univ;
184200
break;
185201

186202
case MBSTRING_UTF8:
187203
outlen = 0;
188204
ret = traverse_string(in, len, inform, out_utf8, &outlen);
189-
if (ret < 0) {
190-
ERR_raise(ERR_LIB_ASN1, ASN1_R_INVALID_UTF8STRING);
205+
if (ret < 0) { /* error already raised in out_utf8() */
206+
if (free_out) {
207+
ASN1_STRING_free(dest);
208+
*out = NULL;
209+
}
191210
return -1;
192211
}
193212
cpyfunc = cpy_utf8;
@@ -271,9 +290,15 @@ static int out_utf8(unsigned long value, void *arg)
271290
int *outlen, len;
272291

273292
len = UTF8_putc(NULL, -1, value);
274-
if (len <= 0)
293+
if (len <= 0) {
294+
ERR_raise(ERR_LIB_ASN1, ASN1_R_INVALID_UTF8STRING);
275295
return len;
296+
}
276297
outlen = arg;
298+
if (*outlen > INT_MAX - len) {
299+
ERR_raise(ERR_LIB_ASN1, ASN1_R_STRING_TOO_LONG);
300+
return -1;
301+
}
277302
*outlen += len;
278303
return 1;
279304
}

0 commit comments

Comments
 (0)