Commit a502c66ce4 for openssl.org
commit a502c66ce4d8784c2a1f3c195fd876f19fbe8392
Author: Darren Carreras <carrerasdarren@gmail.com>
Date: Fri Sep 4 11:07:18 2026 -0400
CONF: reject a NUL list separator in CONF_parse_list()
CONF_parse_list() treated any integer separator that converts to NUL
as a match for the input string terminator. After processing the final
element, the loop advanced beyond the input and searched
from that invalid pointer on the next iteration.
Reject every NUL-equivalent separator before parsing and raise
ERR_R_PASSED_INVALID_ARGUMENT. Add a regression test that verifies
the direct NUL value and positive/negative integer aliases, confirms
that no callback is invoked, checks the error queue, and checks
that normal comma-separated parsing keeps working as expected.
Resolves: https://github.com/openssl/openssl/issues/32667
Fixes: df5eaa8a5297 "default_algorithms option in ENGINE config."
Reported-by: x-ray-z
Assisted-by: OpenAI Codex:gpt-5.6-sol
Reviewed-by: Mounir Idrassi <mounir.idrassi@idrix.fr>
Reviewed-by: Daniel Kubec <kubec@openssl.foundation>
Reviewed-by: Eugene Syromiatnikov <esyr@openssl.org>
Merge-date: Fri Oct 9 21:25:58 2026
Merged-from: https://github.com/openssl/openssl/pull/32684
diff --git a/crypto/conf/conf_mod.c b/crypto/conf/conf_mod.c
index f8dbd27dab..fb6294d8dc 100644
--- a/crypto/conf/conf_mod.c
+++ b/crypto/conf/conf_mod.c
@@ -690,6 +690,11 @@ int CONF_parse_list(const char *list_, int sep, int nospc,
ERR_raise(ERR_LIB_CONF, CONF_R_LIST_CANNOT_BE_NULL);
return 0;
}
+ /* Reject separators whose byte value is NUL. */
+ if ((unsigned char)sep == '\0') {
+ ERR_raise(ERR_LIB_CONF, ERR_R_PASSED_INVALID_ARGUMENT);
+ return 0;
+ }
lstart = list_;
for (;;) {
diff --git a/test/build.info b/test/build.info
index a6cb881d31..655f8c25de 100644
--- a/test/build.info
+++ b/test/build.info
@@ -56,7 +56,7 @@ IF[{- !$disabled{tests} -}]
evp_fetch_prov_test evp_libctx_test ossl_store_test \
v3nametest v3ext byteorder_test punycode_test evp_byname_test \
crltest danetest bad_dtls_test lhash_test sparse_array_test \
- conf_include_test params_api_test params_conversion_test \
+ conf_include_test conf_parse_list_test params_api_test params_conversion_test \
constant_time_test crypto_memcmp_test ct_validation_helpers_test \
safe_math_test verify_extra_test clienthellotest \
packettest asynctest secmemtest srptest memleaktest stack_test \
@@ -929,6 +929,10 @@ IF[{- !$disabled{tests} -}]
INCLUDE[conf_include_test]=../include ../apps/include
DEPEND[conf_include_test]=../libcrypto libtestutil.a
+ SOURCE[conf_parse_list_test]=conf_parse_list_test.c
+ INCLUDE[conf_parse_list_test]=../include ../apps/include
+ DEPEND[conf_parse_list_test]=../libcrypto libtestutil.a
+
IF[{- !$disabled{cmp} -}]
PROGRAMS{noinst}=cmp_asn_test cmp_ctx_test cmp_status_test cmp_hdr_test \
cmp_protect_test cmp_msg_test cmp_vfy_test \
diff --git a/test/conf_parse_list_test.c b/test/conf_parse_list_test.c
new file mode 100644
index 0000000000..cc3c29fb80
--- /dev/null
+++ b/test/conf_parse_list_test.c
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2026 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <limits.h>
+#include <string.h>
+
+#include <openssl/conf.h>
+#include <openssl/err.h>
+
+#include "testutil.h"
+
+static int callback_count;
+
+static int list_cb(const char *elem, int len, void *arg)
+{
+ (void)elem;
+ (void)len;
+ (void)arg;
+
+ callback_count++;
+ return 1;
+}
+
+static int count_and_stop_cb(const char *elem, int len, void *arg)
+{
+ (void)list_cb(elem, len, arg);
+ return 0;
+}
+
+static const char *expected[] = { "one", "two" };
+
+static int check_list_cb(const char *elem, int len, void *arg)
+{
+ int *idx = arg;
+ size_t expected_len;
+
+ if (!TEST_int_lt(*idx, (int)OSSL_NELEM(expected)))
+ return 0;
+
+ expected_len = strlen(expected[*idx]);
+ if (!TEST_ptr(elem)
+ || !TEST_int_eq(len, (int)expected_len)
+ || !TEST_mem_eq(elem, expected_len, expected[*idx], expected_len))
+ return 0;
+
+ (*idx)++;
+ return 1;
+}
+
+static int test_invalid_separator(int sep)
+{
+ unsigned long err;
+ int ret = 0;
+
+ ERR_clear_error();
+ callback_count = 0;
+ if (!TEST_false(CONF_parse_list("entry", sep, 0, count_and_stop_cb, NULL))
+ || !TEST_int_eq(callback_count, 0))
+ goto end;
+
+ err = ERR_get_error();
+ if (!TEST_int_eq(ERR_GET_LIB(err), ERR_LIB_CONF)
+ || !TEST_int_eq(ERR_GET_REASON(err), ERR_R_PASSED_INVALID_ARGUMENT)
+ || !TEST_ulong_eq(ERR_get_error(), 0))
+ goto end;
+
+ ret = 1;
+end:
+ ERR_clear_error();
+ return ret;
+}
+
+static int test_valid_separator(void)
+{
+ int idx = 0;
+
+ ERR_clear_error();
+
+ return TEST_true(CONF_parse_list("one,two", ',', 0, check_list_cb, &idx))
+ && TEST_int_eq(idx, (int)OSSL_NELEM(expected))
+ && TEST_ulong_eq(ERR_get_error(), 0);
+}
+
+static int test_nul_separator(void)
+{
+ int ret = test_invalid_separator('\0');
+
+#if UCHAR_MAX < INT_MAX
+ ret &= test_invalid_separator(UCHAR_MAX + 1);
+ ret &= test_invalid_separator(-(UCHAR_MAX + 1));
+#endif
+ return ret;
+}
+
+int setup_tests(void)
+{
+ ADD_TEST(test_valid_separator);
+ ADD_TEST(test_nul_separator);
+ return 1;
+}
diff --git a/test/recipes/02-test_conf_parse_list.t b/test/recipes/02-test_conf_parse_list.t
new file mode 100644
index 0000000000..54ab1739fd
--- /dev/null
+++ b/test/recipes/02-test_conf_parse_list.t
@@ -0,0 +1,11 @@
+#! /usr/bin/env perl
+# Copyright 2026 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the Apache License 2.0 (the "License"). You may not use
+# this file except in compliance with the License. You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+use OpenSSL::Test::Simple;
+
+simple_test("test_conf_parse_list", "conf_parse_list_test");