]> git.kaiwu.me - haproxy.git/commitdiff
BUG/MINOR: ssl: fix a double-free of the .sctl buffer with several contexts
authorWilliam Lallemand <wlallemand@haproxy.com>
Tue, 27 Oct 2026 14:32:47 +0000 (14:32 +0000)
committerWilliam Lallemand <wlallemand@haproxy.com>
Wed, 28 Oct 2026 13:55:51 +0000 (14:55 +0100)
ssl_sock_load_sctl() stores the ckch's .sctl buffer in SSL_CTX ex_data,
whose free callback releases it when the context is destroyed. The ckch
also frees this buffer, causing a double-free during deinit or "haproxy -c",
even with a single bind and certificate. Sharing the certificate across
contexts adds further frees of the same buffer.

Give each context its own copy, leaving the original owned by the ckch.
AWS-LC already copies the list through its native API.

This bug was introduced in 2.1 by commit 0dfae6c315 ("MINOR: ssl: load sctl
from buf OR from a file") and must be backported to all versions since then.

src/ssl_sock.c

index f3bd0f5fd1d2a474e5ea2649806bf61cb94e42c4..3bcb70e791d574f8af6b6bc5b04cd599858d71eb 100644 (file)
@@ -1707,7 +1707,9 @@ static __maybe_unused int ssl_sock_sctl_parse_cbk(SSL *s, unsigned int ext_type,
 
 #endif /* HAVE_SSL_CTX_ADD_SERVER_CUSTOM_EXT */
 
-/* Attaches <sctl> to <ctx>. Returns 0 on success or -1 on failure. */
+/* Copies <sctl> into <ctx>, leaving ownership with the caller. Returns 0
+ * on success or -1 on allocation, storage or library validation failure.
+ */
 static int ssl_sock_load_sctl(SSL_CTX *ctx, struct buffer *sctl)
 {
 #ifdef HAVE_SSL_CTX_SET_SIGNED_CERT_TIMESTAMP_LIST
@@ -1716,8 +1718,23 @@ static int ssl_sock_load_sctl(SSL_CTX *ctx, struct buffer *sctl)
                return -1;
        return 0;
 #else
-       if (!SSL_CTX_set_ex_data(ctx, sctl_ex_index, sctl))
+       struct buffer *sctl_ctx;
+
+       /* The ex_data free callback owns this copy; the ckch keeps the original. */
+       sctl_ctx = calloc(1, sizeof(*sctl_ctx));
+       if (!sctl_ctx)
+               return -1;
+
+       if (!chunk_dup(sctl_ctx, sctl)) {
+               free(sctl_ctx);
                return -1;
+       }
+
+       if (!SSL_CTX_set_ex_data(ctx, sctl_ex_index, sctl_ctx)) {
+               chunk_destroy(sctl_ctx);
+               free(sctl_ctx);
+               return -1;
+       }
        return 0;
 #endif
 }