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;
 }