Commit a27b2ca26a for openssl.org

commit a27b2ca26a8ceb87e6042cc9f0c32f0db8f8fb1d
Author: Tomas Mraz <tomas@openssl.org>
Date:   Mon Jan 5 18:47:23 2026 +0100

    base64 encoder: Make ctx->length a constant

    It is never changed anywhere.

    Fixes #29518

    Reviewed-by: Saša NedvÄ›dický <sashan@openssl.org>
    Reviewed-by: Paul Dale <paul.dale@oracle.com>
    Reviewed-by: Matt Caswell <matt@openssl.org>
    MergeDate: Mon Jan 19 14:20:35 2026
    (Merged from https://github.com/openssl/openssl/pull/29550)

diff --git a/crypto/evp/enc_b64_scalar.c b/crypto/evp/enc_b64_scalar.c
index 89ccb735c1..1ca46a8357 100644
--- a/crypto/evp/enc_b64_scalar.c
+++ b/crypto/evp/enc_b64_scalar.c
@@ -134,7 +134,7 @@ int evp_encodeblock_int(EVP_ENCODE_CTX *ctx, unsigned char *t,
     int srp = (ctx != NULL
         && (ctx->flags & EVP_ENCODE_CTX_USE_SRP_ALPHABET) != 0);
     int wrap_cnt_by_input = *wrap_cnt / 4 * 3;
-    const int ctx_length = (ctx != NULL) ? ctx->length : 0;
+    const int ctx_length = (ctx != NULL) ? EVP_ENCODE_B64_LENGTH : 0;

     if (srp) {
         e0 = base64_srp_bin2ascii_0;
@@ -168,11 +168,11 @@ int evp_encodeblock_int(EVP_ENCODE_CTX *ctx, unsigned char *t,
         int wrap_cnt_nm3 = 0;
         while (i + 2 < dlen) {
             if (ctx != NULL) {
-                if ((wrap_cnt_nm3 < ctx->length
-                        && (wrap_cnt_nm3 + 3 + wrap_cnt_by_input) > ctx->length)
+                if ((wrap_cnt_nm3 < EVP_ENCODE_B64_LENGTH
+                        && (wrap_cnt_nm3 + 3 + wrap_cnt_by_input) > EVP_ENCODE_B64_LENGTH)
                     && ((ctx->flags & EVP_ENCODE_CTX_NO_NEWLINES) == 0)) {

-                    switch (ctx->length % 3) {
+                    switch (EVP_ENCODE_B64_LENGTH % 3) {
                     case 0:
                         break;
                     case 1:
@@ -228,7 +228,9 @@ int evp_encodeblock_int(EVP_ENCODE_CTX *ctx, unsigned char *t,
             ret += 4;

             if (ctx != NULL) {
-                if ((i + 3 + wrap_cnt_by_input) % ctx->length == 0 && ((ctx->flags & EVP_ENCODE_CTX_NO_NEWLINES) == 0) && ctx->length % 3 == 0) {
+                if ((i + 3 + wrap_cnt_by_input) % EVP_ENCODE_B64_LENGTH == 0
+                    && ((ctx->flags & EVP_ENCODE_CTX_NO_NEWLINES) == 0)
+                    && EVP_ENCODE_B64_LENGTH % 3 == 0) {
                     *(t++) = '\n';
                     ret++;
                 }
@@ -249,7 +251,9 @@ int evp_encodeblock_int(EVP_ENCODE_CTX *ctx, unsigned char *t,
         ret += 4;

         if (ctx != NULL) {
-            if ((i + 1 + wrap_cnt_by_input) % ctx->length == 0 && ((ctx->flags & EVP_ENCODE_CTX_NO_NEWLINES) == 0) && ctx->length % 3 == 0) {
+            if ((i + 1 + wrap_cnt_by_input) % EVP_ENCODE_B64_LENGTH == 0
+                && ((ctx->flags & EVP_ENCODE_CTX_NO_NEWLINES) == 0)
+                && EVP_ENCODE_B64_LENGTH % 3 == 0) {
                 *(t++) = '\n';
                 ret++;
             }
@@ -266,7 +270,9 @@ int evp_encodeblock_int(EVP_ENCODE_CTX *ctx, unsigned char *t,
         ret += 4;

         if (ctx != NULL) {
-            if ((i + 2 + wrap_cnt_by_input) % ctx->length == 0 && ((ctx->flags & EVP_ENCODE_CTX_NO_NEWLINES) == 0) && ctx->length % 3 == 0) {
+            if ((i + 2 + wrap_cnt_by_input) % EVP_ENCODE_B64_LENGTH == 0
+                && ((ctx->flags & EVP_ENCODE_CTX_NO_NEWLINES) == 0)
+                && EVP_ENCODE_B64_LENGTH % 3 == 0) {
                 *(t++) = '\n';
                 ret++;
             }
diff --git a/crypto/evp/encode.c b/crypto/evp/encode.c
index dd5992d09e..49b02ed4dd 100644
--- a/crypto/evp/encode.c
+++ b/crypto/evp/encode.c
@@ -9,6 +9,7 @@

 #include <stdio.h>
 #include <limits.h>
+#include <assert.h>
 #include "internal/cryptlib.h"
 #include <openssl/evp.h>
 #include "crypto/evp.h"
@@ -364,7 +365,6 @@ void evp_encode_ctx_set_flags(EVP_ENCODE_CTX *ctx, unsigned int flags)

 void EVP_EncodeInit(EVP_ENCODE_CTX *ctx)
 {
-    ctx->length = 48;
     ctx->num = 0;
     ctx->line_num = 0;
     ctx->flags = 0;
@@ -379,19 +379,19 @@ int EVP_EncodeUpdate(EVP_ENCODE_CTX *ctx, unsigned char *out, int *outl,
     *outl = 0;
     if (inl <= 0)
         return 0;
-    OPENSSL_assert(ctx->length <= (int)sizeof(ctx->enc_data));
-    if (ctx->length - ctx->num > inl) {
+    assert(EVP_ENCODE_B64_LENGTH <= (int)sizeof(ctx->enc_data));
+    if (EVP_ENCODE_B64_LENGTH - ctx->num > inl) {
         memcpy(&(ctx->enc_data[ctx->num]), in, inl);
         ctx->num += inl;
         return 1;
     }
     if (ctx->num != 0) {
-        i = ctx->length - ctx->num;
+        i = EVP_ENCODE_B64_LENGTH - ctx->num;
         memcpy(&(ctx->enc_data[ctx->num]), in, i);
         in += i;
         inl -= i;
         int wrap_cnt = 0;
-        j = evp_encodeblock_int(ctx, out, ctx->enc_data, ctx->length,
+        j = evp_encodeblock_int(ctx, out, ctx->enc_data, EVP_ENCODE_B64_LENGTH,
             &wrap_cnt);
         ctx->num = 0;
         out += j;
@@ -399,39 +399,39 @@ int EVP_EncodeUpdate(EVP_ENCODE_CTX *ctx, unsigned char *out, int *outl,
         *out = '\0';
     }
     int wrap_cnt = 0;
-    if (ctx->length % 3 != 0) {
-        j = evp_encodeblock_int(ctx, out, in, inl - (inl % ctx->length),
+    if (EVP_ENCODE_B64_LENGTH % 3 != 0) {
+        j = evp_encodeblock_int(ctx, out, in, inl - (inl % EVP_ENCODE_B64_LENGTH),
             &wrap_cnt);
     } else {
 #if defined(__AVX2__)
-        const int newlines = !(ctx->flags & EVP_ENCODE_CTX_NO_NEWLINES) ? ctx->length : 0;
+        const int newlines = !(ctx->flags & EVP_ENCODE_CTX_NO_NEWLINES) ? EVP_ENCODE_B64_LENGTH : 0;

         j = encode_base64_avx2(ctx,
             (unsigned char *)out,
             (const unsigned char *)in,
-            inl - (inl % ctx->length), newlines, &wrap_cnt);
+            inl - (inl % EVP_ENCODE_B64_LENGTH), newlines, &wrap_cnt);
 #elif defined(HAS_IA32CAP_IS_64)
         if ((OPENSSL_ia32cap_P[2] & (1u << 5)) != 0) {
-            const int newlines = !(ctx->flags & EVP_ENCODE_CTX_NO_NEWLINES) ? ctx->length : 0;
+            const int newlines = !(ctx->flags & EVP_ENCODE_CTX_NO_NEWLINES) ? EVP_ENCODE_B64_LENGTH : 0;

             j = encode_base64_avx2(ctx,
                 (unsigned char *)out,
                 (const unsigned char *)in,
-                inl - (inl % ctx->length), newlines, &wrap_cnt);
+                inl - (inl % EVP_ENCODE_B64_LENGTH), newlines, &wrap_cnt);
         } else {
-            j = evp_encodeblock_int(ctx, out, in, inl - (inl % ctx->length),
+            j = evp_encodeblock_int(ctx, out, in, inl - (inl % EVP_ENCODE_B64_LENGTH),
                 &wrap_cnt);
         }
 #else
-        j = evp_encodeblock_int(ctx, out, in, inl - (inl % ctx->length),
+        j = evp_encodeblock_int(ctx, out, in, inl - (inl % EVP_ENCODE_B64_LENGTH),
             &wrap_cnt);
 #endif
     }
-    in += inl - (inl % ctx->length);
-    inl -= inl - (inl % ctx->length);
+    in += inl - (inl % EVP_ENCODE_B64_LENGTH);
+    inl -= inl - (inl % EVP_ENCODE_B64_LENGTH);
     out += j;
     total += j;
-    if ((ctx->flags & EVP_ENCODE_CTX_NO_NEWLINES) == 0 && ctx->length % 3 != 0) {
+    if ((ctx->flags & EVP_ENCODE_CTX_NO_NEWLINES) == 0 && EVP_ENCODE_B64_LENGTH % 3 != 0) {
         *(out++) = '\n';
         total++;
     }
@@ -487,7 +487,6 @@ void EVP_DecodeInit(EVP_ENCODE_CTX *ctx)
 {
     /* Only ctx->num and ctx->flags are used during decoding. */
     ctx->num = 0;
-    ctx->length = 0;
     ctx->line_num = 0;
     ctx->flags = 0;
 }
diff --git a/crypto/evp/evp_local.h b/crypto/evp/evp_local.h
index 08baae95d8..f8550bdeff 100644
--- a/crypto/evp/evp_local.h
+++ b/crypto/evp/evp_local.h
@@ -12,6 +12,12 @@

 #define EVP_CTRL_RET_UNSUPPORTED -1

+/*
+ * Length of the BASE64-encoded lines when encoding.
+ * This needs to be divisible by 3 to keep the AVX2 optimized code path.
+ */
+#define EVP_ENCODE_B64_LENGTH 48
+
 struct evp_md_ctx_st {
     const EVP_MD *reqdigest; /* The original requested digest */
     const EVP_MD *digest;
@@ -272,12 +278,6 @@ int PKCS5_v2_PBKDF2_keyivgen_ex(EVP_CIPHER_CTX *ctx, const char *pass,
 struct evp_Encode_Ctx_st {
     /* number saved in a partial encode/decode */
     int num;
-    /*
-     * The length is either the output line length (in input bytes) or the
-     * shortest input line length that is ok.  Once decoding begins, the
-     * length is adjusted up each time a longer line is decoded
-     */
-    int length;
     /* data to encode */
     unsigned char enc_data[80];
     /* number read on current line */
diff --git a/test/test_base64_simdutf.c b/test/test_base64_simdutf.c
index e6026c4f38..d2d4072789 100644
--- a/test/test_base64_simdutf.c
+++ b/test/test_base64_simdutf.c
@@ -30,8 +30,7 @@ static void fuzz_fill_encode_ctx(EVP_ENCODE_CTX *ctx, int max_fill)

     for (int i = 0; i < num; i++)
         ctx->enc_data[i] = (unsigned char)(rand() & 0xFF);
-    ctx->length = (rand() % 80) + 1;
-    ctx->line_num = rand() % (ctx->length + 1);
+    ctx->line_num = rand() % (EVP_ENCODE_B64_LENGTH + 1);
 }
 static inline uint32_t next_u32(uint32_t *state)
 {
@@ -100,18 +99,18 @@ static int evp_encodeupdate_old(EVP_ENCODE_CTX *ctx, unsigned char *out, int *ou
     *outl = 0;
     if (inl <= 0)
         return 0;
-    OPENSSL_assert(ctx->length <= (int)sizeof(ctx->enc_data));
-    if (ctx->length - ctx->num > inl) {
+    OPENSSL_assert(EVP_ENCODE_B64_LENGTH <= (int)sizeof(ctx->enc_data));
+    if (EVP_ENCODE_B64_LENGTH - ctx->num > inl) {
         memcpy(&(ctx->enc_data[ctx->num]), in, inl);
         ctx->num += inl;
         return 1;
     }
     if (ctx->num != 0) {
-        i = ctx->length - ctx->num;
+        i = EVP_ENCODE_B64_LENGTH - ctx->num;
         memcpy(&(ctx->enc_data[ctx->num]), in, i);
         in += i;
         inl -= i;
-        j = evp_encodeblock_int_old(ctx, out, ctx->enc_data, ctx->length);
+        j = evp_encodeblock_int_old(ctx, out, ctx->enc_data, EVP_ENCODE_B64_LENGTH);
         ctx->num = 0;
         out += j;
         total = j;
@@ -121,10 +120,10 @@ static int evp_encodeupdate_old(EVP_ENCODE_CTX *ctx, unsigned char *out, int *ou
         }
         *out = '\0';
     }
-    while (inl >= ctx->length) {
-        j = evp_encodeblock_int_old(ctx, out, in, ctx->length);
-        in += ctx->length;
-        inl -= ctx->length;
+    while (inl >= EVP_ENCODE_B64_LENGTH) {
+        j = evp_encodeblock_int_old(ctx, out, in, EVP_ENCODE_B64_LENGTH);
+        in += EVP_ENCODE_B64_LENGTH;
+        inl -= EVP_ENCODE_B64_LENGTH;
         out += j;
         total += j;
         if ((ctx->flags & EVP_ENCODE_CTX_NO_NEWLINES) == 0) {
@@ -154,7 +153,8 @@ static void evp_encodefinal_old(EVP_ENCODE_CTX *ctx, unsigned char *out, int *ou
     }
     *outl = ret;
 }
-static int test_encode_line_lengths_reinforced(void)
+
+static int test_encode_line_length_reinforced(void)
 {
     const int trials = 50;
     uint32_t seed = 12345;
@@ -175,65 +175,59 @@ static int test_encode_line_lengths_reinforced(void)

         for (int partial_ctx_fill = 0; partial_ctx_fill <= 80;
             partial_ctx_fill += 1) {
-            for (int ctx_len = 1; ctx_len <= 80; ctx_len += 1) {
-                ctx_simd = EVP_ENCODE_CTX_new();
-                ctx_ref = EVP_ENCODE_CTX_new();
+            ctx_simd = EVP_ENCODE_CTX_new();
+            ctx_ref = EVP_ENCODE_CTX_new();

-                if (!ctx_simd || !ctx_ref) {
-                    TEST_error("Out of memory for contexts");
-                    goto fail;
-                }
+            if (!TEST_ptr(ctx_simd) || !TEST_ptr(ctx_ref))
+                goto fail;
+
+            fuzz_fill_encode_ctx(ctx_simd, partial_ctx_fill);
+
+            memset(out_simd, 0xCC, sizeof(out_simd)); /* poison to catch short writes */
+            memset(out_ref, 0xDD, sizeof(out_ref));
+
+            int outlen_simd = 0, outlen_ref = 0; /* bytes produced by Update */
+            int finlen_simd = 0, finlen_ref = 0; /* bytes produced by Final */
+
+            EVP_EncodeInit(ctx_simd);
+            EVP_EncodeInit(ctx_ref);

-                fuzz_fill_encode_ctx(ctx_simd, partial_ctx_fill);
-
-                memset(out_simd, 0xCC, sizeof(out_simd)); /* poison to catch short writes */
-                memset(out_ref, 0xDD, sizeof(out_ref));
-
-                int outlen_simd = 0, outlen_ref = 0; /* bytes produced by Update */
-                int finlen_simd = 0, finlen_ref = 0; /* bytes produced by Final */
-
-                EVP_EncodeInit(ctx_simd);
-                EVP_EncodeInit(ctx_ref);
-                ctx_simd->length = ctx_len;
-                ctx_ref->length = ctx_len;
-
-                for (int i = 0; i < 2; i++) {
-                    if (i % 2 == 0) {
-                        /* Turn SRP alphabet OFF */
-                        ctx_simd->flags &= ~EVP_ENCODE_CTX_USE_SRP_ALPHABET;
-                        ctx_ref->flags &= ~EVP_ENCODE_CTX_USE_SRP_ALPHABET;
-                    } else {
-                        /* Turn SRP alphabet ON */
-                        ctx_simd->flags |= EVP_ENCODE_CTX_USE_SRP_ALPHABET;
-                        ctx_ref->flags |= EVP_ENCODE_CTX_USE_SRP_ALPHABET;
-                    }
-
-                    int ret_simd = EVP_EncodeUpdate(ctx_simd, out_simd, &outlen_simd,
-                        input, (int)inl);
-                    int ret_ref = evp_encodeupdate_old(ctx_ref, out_ref, &outlen_ref,
-                        input, (int)inl);
-
-                    if (!TEST_int_eq(ret_simd, ret_ref)
-                        || !TEST_mem_eq(out_ref, outlen_ref, out_simd, outlen_simd)
-                        || !TEST_int_eq(outlen_simd, outlen_ref))
-                        goto fail;
-
-                    EVP_EncodeFinal(ctx_simd, out_simd + outlen_simd,
-                        &finlen_simd);
-                    evp_encodefinal_old(ctx_ref, out_ref + outlen_ref,
-                        &finlen_ref);
-
-                    int total_ref = outlen_ref + finlen_ref;
-                    int total_simd = outlen_simd + finlen_simd;
-
-                    if (!TEST_int_eq(finlen_simd, finlen_ref)
-                        || !TEST_mem_eq(out_ref, total_ref, out_simd, total_simd))
-                        goto fail;
+            for (int i = 0; i < 2; i++) {
+                if (i % 2 == 0) {
+                    /* Turn SRP alphabet OFF */
+                    ctx_simd->flags &= ~EVP_ENCODE_CTX_USE_SRP_ALPHABET;
+                    ctx_ref->flags &= ~EVP_ENCODE_CTX_USE_SRP_ALPHABET;
+                } else {
+                    /* Turn SRP alphabet ON */
+                    ctx_simd->flags |= EVP_ENCODE_CTX_USE_SRP_ALPHABET;
+                    ctx_ref->flags |= EVP_ENCODE_CTX_USE_SRP_ALPHABET;
                 }

-                EVP_ENCODE_CTX_free(ctx_simd);
-                EVP_ENCODE_CTX_free(ctx_ref);
+                int ret_simd = EVP_EncodeUpdate(ctx_simd, out_simd, &outlen_simd,
+                    input, (int)inl);
+                int ret_ref = evp_encodeupdate_old(ctx_ref, out_ref, &outlen_ref,
+                    input, (int)inl);
+
+                if (!TEST_int_eq(ret_simd, ret_ref)
+                    || !TEST_mem_eq(out_ref, outlen_ref, out_simd, outlen_simd)
+                    || !TEST_int_eq(outlen_simd, outlen_ref))
+                    goto fail;
+
+                EVP_EncodeFinal(ctx_simd, out_simd + outlen_simd,
+                    &finlen_simd);
+                evp_encodefinal_old(ctx_ref, out_ref + outlen_ref,
+                    &finlen_ref);
+
+                int total_ref = outlen_ref + finlen_ref;
+                int total_simd = outlen_simd + finlen_simd;
+
+                if (!TEST_int_eq(finlen_simd, finlen_ref)
+                    || !TEST_mem_eq(out_ref, total_ref, out_simd, total_simd))
+                    goto fail;
             }
+
+            EVP_ENCODE_CTX_free(ctx_simd);
+            EVP_ENCODE_CTX_free(ctx_ref);
         }
     }

@@ -247,7 +241,7 @@ fail:

 int setup_tests(void)
 {
-    ADD_TEST(test_encode_line_lengths_reinforced);
+    ADD_TEST(test_encode_line_length_reinforced);

     return 1;
 }