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 <nikolap@openssl.org>
Reviewed-by: Norbert Pocs <norbertp@openssl.org>
Reviewed-by: Eugene Syromiatnikov <esyr@openssl.org>
MergeDate: Mon Jun 8 14:02:18 2026
This commit is contained in:
parent
cb005ceaaf
commit
97f6b621f7
1 changed files with 28 additions and 3 deletions
|
|
@ -187,11 +187,27 @@ int ASN1_mbstring_ncopy(ASN1_STRING **out, const unsigned char *in, int len,
|
|||
break;
|
||||
|
||||
case MBSTRING_BMP:
|
||||
if (nchar > INT_MAX / 2) {
|
||||
ERR_raise(ERR_LIB_ASN1, ASN1_R_STRING_TOO_LONG);
|
||||
if (free_out) {
|
||||
ASN1_STRING_free(dest);
|
||||
*out = NULL;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
outlen = nchar << 1;
|
||||
cpyfunc = cpy_bmp;
|
||||
break;
|
||||
|
||||
case MBSTRING_UNIV:
|
||||
if (nchar > INT_MAX / 4) {
|
||||
ERR_raise(ERR_LIB_ASN1, ASN1_R_STRING_TOO_LONG);
|
||||
if (free_out) {
|
||||
ASN1_STRING_free(dest);
|
||||
*out = NULL;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
outlen = nchar << 2;
|
||||
cpyfunc = cpy_univ;
|
||||
break;
|
||||
|
|
@ -199,8 +215,11 @@ int ASN1_mbstring_ncopy(ASN1_STRING **out, const unsigned char *in, int len,
|
|||
case MBSTRING_UTF8:
|
||||
outlen = 0;
|
||||
ret = traverse_string(in, len, inform, out_utf8, &outlen);
|
||||
if (ret < 0) {
|
||||
ERR_raise(ERR_LIB_ASN1, ASN1_R_INVALID_UTF8STRING);
|
||||
if (ret < 0) { /* error already raised in out_utf8() */
|
||||
if (free_out) {
|
||||
ASN1_STRING_free(dest);
|
||||
*out = NULL;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
cpyfunc = cpy_utf8;
|
||||
|
|
@ -281,9 +300,15 @@ static int out_utf8(uint32_t value, void *arg)
|
|||
int *outlen, len;
|
||||
|
||||
len = ossl_utf8_putc_internal(NULL, -1, value);
|
||||
if (len <= 0)
|
||||
if (len <= 0) {
|
||||
ERR_raise(ERR_LIB_ASN1, ASN1_R_INVALID_UTF8STRING);
|
||||
return len;
|
||||
}
|
||||
outlen = arg;
|
||||
if (*outlen > INT_MAX - len) {
|
||||
ERR_raise(ERR_LIB_ASN1, ASN1_R_STRING_TOO_LONG);
|
||||
return -1;
|
||||
}
|
||||
*outlen += len;
|
||||
return 1;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue