From ef48810aafdc3b8c6c4a85e52314caeec0cb596c Mon Sep 17 00:00:00 2001 From: Viktor Dukhovni Date: Wed, 7 Jan 2026 01:21:58 +1100 Subject: [PATCH] Report truncation in oneshot `openssl dgst -sign` Previously input was silently truncated at 16MB, now if the input is longer than limit, an error is reported. The bio_to_mem() apps helper function was changed to return 0 or 1, and return the size of the result via an output size_t pointer. Fixes CVE-2025-15469 --- apps/dgst.c | 7 +++--- apps/include/apps.h | 2 +- apps/lib/apps.c | 55 +++++++++++++++++++++++---------------------- apps/pkeyutl.c | 36 ++++++++++++++--------------- 4 files changed, 50 insertions(+), 50 deletions(-) Index: openssl-3.5.0/apps/dgst.c =================================================================== --- openssl-3.5.0.orig/apps/dgst.c +++ openssl-3.5.0/apps/dgst.c @@ -704,12 +704,11 @@ static int do_fp_oneshot_sign(BIO *out, { int res, ret = EXIT_FAILURE; size_t len = 0; - int buflen = 0; - int maxlen = 16 * 1024 * 1024; + size_t buflen = 0; + size_t maxlen = 16 * 1024 * 1024; uint8_t *buf = NULL, *sig = NULL; - buflen = bio_to_mem(&buf, maxlen, in); - if (buflen <= 0) { + if (!bio_to_mem(&buf, &buflen, maxlen, in)) { BIO_printf(bio_err, "Read error in %s\n", file); return ret; } Index: openssl-3.5.0/apps/include/apps.h =================================================================== --- openssl-3.5.0.orig/apps/include/apps.h +++ openssl-3.5.0/apps/include/apps.h @@ -254,7 +254,7 @@ int parse_yesno(const char *str, int def X509_NAME *parse_name(const char *str, int chtype, int multirdn, const char *desc); void policies_print(X509_STORE_CTX *ctx); -int bio_to_mem(unsigned char **out, int maxlen, BIO *in); +int bio_to_mem(unsigned char **out, size_t *outlen, size_t maxlen, BIO *in); int pkey_ctrl_string(EVP_PKEY_CTX *ctx, const char *value); int x509_ctrl_string(X509 *x, const char *value); int x509_req_ctrl_string(X509_REQ *x, const char *value); Index: openssl-3.5.0/apps/lib/apps.c =================================================================== --- openssl-3.5.0.orig/apps/lib/apps.c +++ openssl-3.5.0/apps/lib/apps.c @@ -49,6 +49,7 @@ #include "apps.h" #include "internal/sockets.h" /* for openssl_fdset() */ +#include "internal/numbers.h" /* for LONG_MAX */ #include "internal/e_os.h" #ifdef _WIN32 @@ -2059,45 +2060,45 @@ X509_NAME *parse_name(const char *cp, in } /* - * Read whole contents of a BIO into an allocated memory buffer and return - * it. + * Read whole contents of a BIO into an allocated memory buffer. + * The return value is one on success, zero on error. + * If `maxlen` is non-zero, at most `maxlen` bytes are returned, or else, if + * the input is longer than `maxlen`, an error is returned. + * If `maxlen` is zero, the limit is effectively `SIZE_MAX`. */ - -int bio_to_mem(unsigned char **out, int maxlen, BIO *in) +int bio_to_mem(unsigned char **out, size_t *outlen, size_t maxlen, BIO *in) { + unsigned char tbuf[4096]; BIO *mem; - int len, ret; - unsigned char tbuf[1024]; + BUF_MEM *bufm; + size_t sz = 0; + int len; mem = BIO_new(BIO_s_mem()); if (mem == NULL) - return -1; + return 0; for (;;) { - if ((maxlen != -1) && maxlen < 1024) - len = maxlen; - else - len = 1024; - len = BIO_read(in, tbuf, len); - if (len < 0) { - BIO_free(mem); - return -1; - } - if (len == 0) + if ((len = BIO_read(in, tbuf, 4096)) == 0) break; - if (BIO_write(mem, tbuf, len) != len) { + if (len < 0 + || BIO_write(mem, tbuf, len) != len + || sz > SIZE_MAX - len + || ((sz += len) > maxlen && maxlen != 0)) { BIO_free(mem); - return -1; + return 0; } - if (maxlen != -1) - maxlen -= len; - - if (maxlen == 0) - break; } - ret = BIO_get_mem_data(mem, (char **)out); - BIO_set_flags(mem, BIO_FLAGS_MEM_RDONLY); + + /* So BIO_free orphans BUF_MEM */ + (void)BIO_set_close(mem, BIO_NOCLOSE); + BIO_get_mem_ptr(mem, &bufm); BIO_free(mem); - return ret; + *out = (unsigned char *)bufm->data; + *outlen = bufm->length; + /* Tell BUF_MEM to orphan data */ + bufm->data = NULL; + BUF_MEM_free(bufm); + return 1; } int pkey_ctrl_string(EVP_PKEY_CTX *ctx, const char *value) Index: openssl-3.5.0/apps/pkeyutl.c =================================================================== --- openssl-3.5.0.orig/apps/pkeyutl.c +++ openssl-3.5.0/apps/pkeyutl.c @@ -40,7 +40,7 @@ static int do_keyop(EVP_PKEY_CTX *ctx, i static int do_raw_keyop(int pkey_op, EVP_MD_CTX *mctx, EVP_PKEY *pkey, BIO *in, - int filesize, unsigned char *sig, int siglen, + int filesize, unsigned char *sig, size_t siglen, unsigned char **out, size_t *poutlen); static int only_nomd(EVP_PKEY *pkey) @@ -133,7 +133,7 @@ int pkeyutl_main(int argc, char **argv) char hexdump = 0, asn1parse = 0, rev = 0, *prog; unsigned char *buf_in = NULL, *buf_out = NULL, *sig = NULL, *secret = NULL; OPTION_CHOICE o; - int buf_inlen = 0, siglen = -1; + size_t buf_inlen = 0, siglen = 0; int keyform = FORMAT_UNDEF, peerform = FORMAT_UNDEF; int keysize = -1, pkey_op = EVP_PKEY_OP_SIGN, key_type = KEY_PRIVKEY; int engine_impl = 0; @@ -485,31 +485,31 @@ int pkeyutl_main(int argc, char **argv) if (sigfile != NULL) { BIO *sigbio = BIO_new_file(sigfile, "rb"); + size_t maxsiglen = 16 * 1024 * 1024; if (sigbio == NULL) { BIO_printf(bio_err, "Can't open signature file %s\n", sigfile); goto end; } - siglen = bio_to_mem(&sig, keysize * 10, sigbio); - BIO_free(sigbio); - if (siglen < 0) { + if (!bio_to_mem(&sig, &siglen, maxsiglen, sigbio)) { + BIO_free(sigbio); BIO_printf(bio_err, "Error reading signature data\n"); goto end; } + BIO_free(sigbio); } /* Raw input data is handled elsewhere */ if (in != NULL && !rawin) { /* Read the input data */ - buf_inlen = bio_to_mem(&buf_in, -1, in); - if (buf_inlen < 0) { + if (!bio_to_mem(&buf_in, &buf_inlen, 0, in)) { BIO_printf(bio_err, "Error reading input Data\n"); goto end; } if (rev) { size_t i; unsigned char ctmp; - size_t l = (size_t)buf_inlen; + size_t l = buf_inlen; for (i = 0; i < l / 2; i++) { ctmp = buf_in[i]; @@ -524,7 +524,8 @@ int pkeyutl_main(int argc, char **argv) && (pkey_op == EVP_PKEY_OP_SIGN || pkey_op == EVP_PKEY_OP_VERIFY)) { if (buf_inlen > EVP_MAX_MD_SIZE) { BIO_printf(bio_err, - "Error: The non-raw input data length %d is too long - max supported hashed size is %d\n", + "Error: The non-raw input data length %zd is too long - " + "max supported hashed size is %d\n", buf_inlen, EVP_MAX_MD_SIZE); goto end; } @@ -535,8 +536,8 @@ int pkeyutl_main(int argc, char **argv) rv = do_raw_keyop(pkey_op, mctx, pkey, in, filesize, sig, siglen, NULL, 0); } else { - rv = EVP_PKEY_verify(ctx, sig, (size_t)siglen, - buf_in, (size_t)buf_inlen); + rv = EVP_PKEY_verify(ctx, sig, siglen, + buf_in, buf_inlen); } if (rv == 1) { BIO_puts(out, "Signature Verified Successfully\n"); @@ -555,8 +556,8 @@ int pkeyutl_main(int argc, char **argv) buf_outlen = kdflen; rv = 1; } else { - rv = do_keyop(ctx, pkey_op, NULL, (size_t *)&buf_outlen, - buf_in, (size_t)buf_inlen, NULL, (size_t *)&secretlen); + rv = do_keyop(ctx, pkey_op, NULL, &buf_outlen, + buf_in, buf_inlen, NULL, &secretlen); } if (rv > 0 && (secretlen > 0 || (pkey_op != EVP_PKEY_OP_ENCAPSULATE @@ -567,8 +568,8 @@ int pkeyutl_main(int argc, char **argv) if (secretlen > 0) secret = app_malloc(secretlen, "secret output"); rv = do_keyop(ctx, pkey_op, - buf_out, (size_t *)&buf_outlen, - buf_in, (size_t)buf_inlen, secret, (size_t *)&secretlen); + buf_out, &buf_outlen, + buf_in, buf_inlen, secret, &secretlen); } } if (rv <= 0) { @@ -837,7 +838,7 @@ static int do_keyop(EVP_PKEY_CTX *ctx, i static int do_raw_keyop(int pkey_op, EVP_MD_CTX *mctx, EVP_PKEY *pkey, BIO *in, - int filesize, unsigned char *sig, int siglen, + int filesize, unsigned char *sig, size_t siglen, unsigned char **out, size_t *poutlen) { int rv = 0; @@ -860,7 +861,7 @@ static int do_raw_keyop(int pkey_op, EVP BIO_printf(bio_err, "Error reading raw input data\n"); goto end; } - rv = EVP_DigestVerify(mctx, sig, (size_t)siglen, mbuf, buf_len); + rv = EVP_DigestVerify(mctx, sig, siglen, mbuf, buf_len); break; case EVP_PKEY_OP_SIGN: buf_len = BIO_read(in, mbuf, filesize); @@ -894,7 +895,7 @@ static int do_raw_keyop(int pkey_op, EVP goto end; } } - rv = EVP_DigestVerifyFinal(mctx, sig, (size_t)siglen); + rv = EVP_DigestVerifyFinal(mctx, sig, siglen); break; case EVP_PKEY_OP_SIGN: for (;;) {