]> git.kaiwu.me - haproxy.git/commitdiff
BUG/MINOR: ssl: don't pass the address of a size_t as an int* to OpenSSL
authorRemi Tricot-Le Breton <rlebreton@haproxy.com>
Thu, 6 Aug 2026 07:35:00 +0000 (09:35 +0200)
committerWilly Tarreau <w@1wt.eu>
Thu, 10 Sep 2026 13:13:26 +0000 (15:13 +0200)
aes_process() passes &out->data, which is a size_t, to the EVP update and
final routines through an (int*) cast. OpenSSL writes an int there, so on
a 64-bit big-endian platform (s390x, ppc64) the resulting length lands in
the high half of the size_t, and the buffer's data count becomes the real
length shifted left by 32 bits. The subsequent write at out->area +
out->data then targets a wild address.

Concretely, on such builds the "aes_gcm_dec" / "aes_cbc_dec" converters
and the JWE decryption paths corrupt memory as soon as they process any
input, so any client able to submit a token to them can crash the worker.
Little-endian builds, which are the vast majority, happen to work by
chance since the count starts at zero and only the low half is written.

Let's use a proper local int for the OpenSSL output length and assign it
back to the buffer's data count.

The shared helper comes from commit f0e64de75 ("MINOR: ssl: Factorize AES
GCM data processing") in 3.4 but the same cast existed in the AES
converters before that. It must be backported to all stable versions.

Reported-by: Claude (ANT-2026-74N8W6SJ)
src/ssl_sample.c

index 4f4070b508a7354f743321e7b4897ce4f36119ae..2725b67ebb762969ee39867862663128461f071a 100644 (file)
@@ -328,6 +328,7 @@ int aes_process(struct buffer *data, struct buffer *nonce, struct buffer *key, i
        EVP_CIPHER_CTX *ctx = NULL;
        int size;
        int ret;
+       int outlen = 0;
        size_t blksize;
 
        ctx = EVP_CIPHER_CTX_new();
@@ -395,15 +396,16 @@ int aes_process(struct buffer *data, struct buffer *nonce, struct buffer *key, i
                goto err;
 
        if (aad && b_data(aad)) {
-               if (!sample_conv_aes_update(decrypt, ctx, NULL, (int*)&out->data,
+               if (!sample_conv_aes_update(decrypt, ctx, NULL, &outlen,
                                            (unsigned char*)b_orig(aad), (int)b_data(aad)))
                        goto err;
        }
 
        if (!sample_conv_aes_update(decrypt, ctx, (unsigned char*)b_orig(out),
-                                   (int*)&out->data, (unsigned char*)b_orig(data), (int)b_data(data)))
+                                   &outlen, (unsigned char*)b_orig(data), (int)b_data(data)))
                goto err;
 
+       out->data = outlen;
        size = out->data;
 
        if (decrypt && gcm) {
@@ -418,11 +420,11 @@ int aes_process(struct buffer *data, struct buffer *nonce, struct buffer *key, i
        }
 
        ret = sample_conv_aes_final(decrypt, ctx, (unsigned char*)out->area + out->data,
-                                   (int *)&out->data);
+                                   &outlen);
        if (ret <= 0)
                goto err;
 
-       out->data += size;
+       out->data = (size_t)outlen + size;
 
        if (!decrypt && gcm) {
                if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_GET_TAG, 16, b_orig(aead_tag)))