Commit f2924392d3 for openssl.org
commit f2924392d32968f550afc0051c1108b1d553d6e1
Author: Mounir IDRASSI <mounir.idrassi@idrix.fr>
Date: Fri Jul 3 18:05:14 2026 +0900
BIO_vprintf: fix off-by-one at 512-byte buffer boundary
BIO_vprintf() first formats into a 512-byte stack buffer. Since
vsnprintf() returns the required length excluding the NUL, a return
value of 512 means truncation. The old strict greater-than check
therefore wrote the truncated buffer for exactly 512-byte output.
Use >= for the realloc path and add boundary coverage for 511-, 512-
and 513-byte outputs.
Fixes: a29d157fdb6d "Replace homebrewed implementation of *printf*() functions with libc"
Reviewed-by: Paul Dale <paul.dale@oracle.com>
Reviewed-by: Tomas Mraz <tomas@openssl.foundation>
Reviewed-by: Eugene Syromiatnikov <esyr@openssl.org>
MergeDate: Wed Jul 8 11:24:31 2026
(Merged from https://github.com/openssl/openssl/pull/31842)
diff --git a/crypto/bio/bio_print.c b/crypto/bio/bio_print.c
index 8b0c3481f0..5366587a2a 100644
--- a/crypto/bio/bio_print.c
+++ b/crypto/bio/bio_print.c
@@ -111,7 +111,7 @@ int BIO_vprintf(BIO *bio, const char *format, va_list args)
*/
sz = vsnprintf(buf, sizeof(buf), format, args);
if (sz >= 0) {
- if ((size_t)sz > sizeof(buf)) {
+ if ((size_t)sz >= sizeof(buf)) {
sz += 1;
abuf = (char *)OPENSSL_malloc(sz);
if (abuf == NULL) {
diff --git a/test/bio_core_test.c b/test/bio_core_test.c
index 677bf922e5..26ff787eec 100644
--- a/test/bio_core_test.c
+++ b/test/bio_core_test.c
@@ -108,6 +108,38 @@ err:
return testresult;
}
+static int test_bio_vprintf_boundary(void)
+{
+ BIO *bio = NULL;
+ char *data;
+ long len;
+ int w;
+ int testresult = 0;
+
+ /*
+ * At width 512, vsnprintf() reports 512 bytes excluding the NUL,
+ * so BIO_vprintf() must use its realloc path.
+ */
+ for (w = 511; w <= 513; w++) {
+ bio = BIO_new(BIO_s_mem());
+ if (!TEST_ptr(bio))
+ goto err;
+ if (!TEST_int_eq(BIO_printf(bio, "%*d", w, 0), w))
+ goto err;
+ len = BIO_get_mem_data(bio, &data);
+ if (!TEST_long_eq(len, w)
+ || !TEST_char_eq(data[w - 1], '0')
+ || !TEST_char_eq(data[0], ' '))
+ goto err;
+ BIO_free(bio);
+ bio = NULL;
+ }
+ testresult = 1;
+err:
+ BIO_free(bio);
+ return testresult;
+}
+
int setup_tests(void)
{
if (!test_skip_common_options()) {
@@ -116,5 +148,6 @@ int setup_tests(void)
}
ADD_TEST(test_bio_core);
+ ADD_TEST(test_bio_vprintf_boundary);
return 1;
}