Commit 3741076a34 for openssl.org
commit 3741076a34f4953ccbb6e1e131756ac206c8f27e
Author: eclipse07077 <eclipse07077@gmail.com>
Date: Mon Mar 9 21:35:43 2026 +0900
Fix integer overflow in EVP_ENCODE_LENGTH and base64 encoding paths
The EVP_ENCODE_LENGTH macro performs all arithmetic in the type of
its argument. When the argument is int and exceeds approximately
1.6 billion, intermediate results overflow signed int, potentially
wrapping to a smaller positive value rather than a negative one.
In b64_write() (crypto/evp/bio_b64.c), this causes OPENSSL_malloc
to allocate a buffer smaller than the actual encoded output size.
EVP_EncodeUpdate then writes past the end of the undersized buffer.
Changes:
- Cast macro argument to size_t in EVP_ENCODE_LENGTH to prevent
signed integer overflow
- Change encoded_length in b64_write() from int to size_t and add
an explicit overflow sanity check before allocation
- Change return type of evp_encodeblock_int() and
encode_base64_avx2() from int to size_t so that large encoded
output lengths are not truncated
- Update EVP_EncodeUpdate() to use size_t for the encoder return
value accumulator (j), consistent with the existing size_t total
- Add explicit (int) casts in EVP_EncodeBlock() and EVP_EncodeFinal()
where the public API requires int return values
Reviewed-by: Saša NedvÄ›dický <sashan@openssl.org>
Reviewed-by: Tomas Mraz <tomas@openssl.foundation>
MergeDate: Fri Apr 3 14:55:29 2026
(Merged from https://github.com/openssl/openssl/pull/30321)
diff --git a/crypto/evp/bio_b64.c b/crypto/evp/bio_b64.c
index 33ddb6334d..7f32d7a5d0 100644
--- a/crypto/evp/bio_b64.c
+++ b/crypto/evp/bio_b64.c
@@ -334,7 +334,7 @@ static int b64_write(BIO *b, const char *in, int inl)
int i;
BIO_B64_CTX *ctx;
BIO *next;
- int encoded_length;
+ size_t encoded_length;
unsigned char *encoded;
int n_bytes_enc;
@@ -393,7 +393,12 @@ static int b64_write(BIO *b, const char *in, int inl)
encoded_length = EVP_ENCODE_LENGTH(inl);
- if (ctx->encoded_buf == NULL || (size_t)encoded_length > ctx->encoded_buf_len) {
+ if (encoded_length > SIZE_MAX / 2) {
+ ERR_raise(ERR_LIB_BIO, BIO_R_LENGTH_TOO_LONG);
+ return -1;
+ }
+
+ if (ctx->encoded_buf == NULL || encoded_length > ctx->encoded_buf_len) {
OPENSSL_free(ctx->encoded_buf);
ctx->encoded_buf = OPENSSL_malloc(encoded_length);
if (ctx->encoded_buf == NULL) {
diff --git a/crypto/evp/enc_b64_avx2.c b/crypto/evp/enc_b64_avx2.c
index e6f571dd8e..acf35739b5 100644
--- a/crypto/evp/enc_b64_avx2.c
+++ b/crypto/evp/enc_b64_avx2.c
@@ -477,7 +477,7 @@ static inline size_t insert_nl_str8(const __m256i v0, uint8_t *output)
OPENSSL_UNTARGET_AVX2
OPENSSL_TARGET_AVX2
-int encode_base64_avx2(EVP_ENCODE_CTX *ctx, unsigned char *dst,
+size_t encode_base64_avx2(EVP_ENCODE_CTX *ctx, unsigned char *dst,
const unsigned char *src, int srclen, int ctx_length,
int *final_wrap_cnt)
{
@@ -665,7 +665,7 @@ int encode_base64_avx2(EVP_ENCODE_CTX *ctx, unsigned char *dst,
*out++ = '\n';
}
- return (int)(out - (uint8_t *)dst) + +evp_encodeblock_int(ctx, out, src + i, srclen - i, final_wrap_cnt);
+ return (size_t)(out - (uint8_t *)dst) + evp_encodeblock_int(ctx, out, src + i, srclen - i, final_wrap_cnt);
}
OPENSSL_UNTARGET_AVX2
#endif /* !defined(_M_ARM64EC) */
diff --git a/crypto/evp/enc_b64_avx2.h b/crypto/evp/enc_b64_avx2.h
index db67b5cf8a..89dba6285d 100644
--- a/crypto/evp/enc_b64_avx2.h
+++ b/crypto/evp/enc_b64_avx2.h
@@ -2,10 +2,11 @@
#define OSSL_CRYPTO_EVP_B64_AVX2_H
#include <openssl/evp.h>
+#include <stddef.h>
#if defined(__x86_64) || defined(__x86_64__) || defined(_M_AMD64) || defined(_M_X64)
#if !defined(_M_ARM64EC)
-int encode_base64_avx2(EVP_ENCODE_CTX *ctx,
+size_t encode_base64_avx2(EVP_ENCODE_CTX *ctx,
unsigned char *out, const unsigned char *src, int srclen,
int newlines, int *wrap_cnt);
#endif /* !defined(_M_ARM64EC) */
diff --git a/crypto/evp/enc_b64_scalar.c b/crypto/evp/enc_b64_scalar.c
index 1ca46a8357..4bdadcea0c 100644
--- a/crypto/evp/enc_b64_scalar.c
+++ b/crypto/evp/enc_b64_scalar.c
@@ -124,11 +124,11 @@ static const unsigned char base64_std_bin2ascii_2[256] = {
'/'
};
-int evp_encodeblock_int(EVP_ENCODE_CTX *ctx, unsigned char *t,
+size_t evp_encodeblock_int(EVP_ENCODE_CTX *ctx, unsigned char *t,
const unsigned char *f, int dlen, int *wrap_cnt)
{
int i = 0;
- int ret = 0;
+ size_t ret = 0;
uint8_t t1, t2, t3;
const unsigned char *e0, *e1, *e2;
int srp = (ctx != NULL
diff --git a/crypto/evp/enc_b64_scalar.h b/crypto/evp/enc_b64_scalar.h
index 91d416f758..43059938cc 100644
--- a/crypto/evp/enc_b64_scalar.h
+++ b/crypto/evp/enc_b64_scalar.h
@@ -1,8 +1,9 @@
#ifndef OSSL_CRYPTO_EVP_B64_SCALAR_H
#define OSSL_CRYPTO_EVP_B64_SCALAR_H
#include <openssl/evp.h>
+#include <stddef.h>
-int evp_encodeblock_int(EVP_ENCODE_CTX *ctx, unsigned char *t,
+size_t evp_encodeblock_int(EVP_ENCODE_CTX *ctx, unsigned char *t,
const unsigned char *f, int dlen, int *wrap_cnt);
#endif
diff --git a/crypto/evp/encode.c b/crypto/evp/encode.c
index eacc68bb96..faf9a3887d 100644
--- a/crypto/evp/encode.c
+++ b/crypto/evp/encode.c
@@ -26,7 +26,7 @@
static unsigned char conv_ascii2bin(unsigned char a,
const unsigned char *table);
-int evp_encodeblock_int(EVP_ENCODE_CTX *ctx, unsigned char *t,
+size_t evp_encodeblock_int(EVP_ENCODE_CTX *ctx, unsigned char *t,
const unsigned char *f, int dlen, int *wrap_cnt);
static int evp_decodeblock_int(EVP_ENCODE_CTX *ctx, unsigned char *t,
const unsigned char *f, int n, int eof);
@@ -374,7 +374,8 @@ void EVP_EncodeInit(EVP_ENCODE_CTX *ctx)
int EVP_EncodeUpdate(EVP_ENCODE_CTX *ctx, unsigned char *out, int *outl,
const unsigned char *in, int inl)
{
- int i, j;
+ int i;
+ size_t j;
size_t total = 0;
*outl = 0;
@@ -452,20 +453,20 @@ int EVP_EncodeUpdate(EVP_ENCODE_CTX *ctx, unsigned char *out, int *outl,
void EVP_EncodeFinal(EVP_ENCODE_CTX *ctx, unsigned char *out, int *outl)
{
- int ret = 0;
+ size_t ret = 0;
int wrap_cnt = 0;
if (ctx->num != 0) {
ret = evp_encodeblock_int(ctx, out, ctx->enc_data, ctx->num,
&wrap_cnt);
- if (ossl_assert(ret >= 0)) {
+ if (ret > 0) {
if ((ctx->flags & EVP_ENCODE_CTX_NO_NEWLINES) == 0)
out[ret++] = '\n';
out[ret] = '\0';
ctx->num = 0;
}
}
- *outl = ret;
+ *outl = (int)ret;
}
int EVP_EncodeBlock(unsigned char *t, const unsigned char *f, int dlen)
@@ -473,14 +474,14 @@ int EVP_EncodeBlock(unsigned char *t, const unsigned char *f, int dlen)
int wrap_cnt = 0;
#if defined(__AVX2__)
- return encode_base64_avx2(NULL, t, f, dlen, 0, &wrap_cnt);
+ return (int)encode_base64_avx2(NULL, t, f, dlen, 0, &wrap_cnt);
#elif defined(HAS_IA32CAP_IS_64)
if ((OPENSSL_ia32cap_P[2] & (1u << 5)) != 0)
- return encode_base64_avx2(NULL, t, f, dlen, 0, &wrap_cnt);
+ return (int)encode_base64_avx2(NULL, t, f, dlen, 0, &wrap_cnt);
else
- return evp_encodeblock_int(NULL, t, f, dlen, &wrap_cnt);
+ return (int)evp_encodeblock_int(NULL, t, f, dlen, &wrap_cnt);
#else
- return evp_encodeblock_int(NULL, t, f, dlen, &wrap_cnt);
+ return (int)evp_encodeblock_int(NULL, t, f, dlen, &wrap_cnt);
#endif
}
diff --git a/include/openssl/evp.h b/include/openssl/evp.h
index 464a9f1efa..39a6204545 100644
--- a/include/openssl/evp.h
+++ b/include/openssl/evp.h
@@ -570,7 +570,7 @@ void *EVP_CIPHER_CTX_set_cipher_data(EVP_CIPHER_CTX *ctx, void *cipher_data);
#define EVP_CIPHER_CTX_get_mode(c) EVP_CIPHER_get_mode(EVP_CIPHER_CTX_get0_cipher(c))
#define EVP_CIPHER_CTX_mode EVP_CIPHER_CTX_get_mode
-#define EVP_ENCODE_LENGTH(l) ((((l) + 2) / 3 * 4) + ((l) / 48 + 1) * 2 + 80)
+#define EVP_ENCODE_LENGTH(l) (((((size_t)(l)) + 2) / 3 * 4) + (((size_t)(l)) / 48 + 1) * 2 + 80)
#define EVP_DECODE_LENGTH(l) (((l) + 3) / 4 * 3 + 80)
#define EVP_SignInit_ex(a, b, c) EVP_DigestInit_ex(a, b, c)