Commit 10413cceb6 for openssl.org

commit 10413cceb66411389bbd65a9e4698dc6a172113c
Author: Greensi7 <adam.tabak04@gmail.com>
Date:   Mon Jul 20 23:34:08 2026 +0200

    Add OSSL_ECHSTORE_read_echconfiglist fuzzer

    Fuzz OSSL_ECHSTORE_read_echconfiglist with raw
    fuzzer input in stage 1. Then fixup the starting
    bytes to reach deeper code withotu relying only on
    corpus. Fixup is done for binary encoding only (not base64).

    Assisted-by: ChatGPT:gpt-5.5

    Reviewed-by: Milan Broz <mbroz@openssl.org>
    Reviewed-by: Nikola Pajkovsky <nikolap@openssl.org>
    Reviewed-by: Norbert Pocs <norbertp@openssl.org>
    MergeDate: Thu Jul 23 13:57:54 2026
    (Merged from https://github.com/openssl/openssl/pull/32021)

diff --git a/fuzz/build.info b/fuzz/build.info
index 5c410260e4..fcbb76234c 100644
--- a/fuzz/build.info
+++ b/fuzz/build.info
@@ -50,6 +50,10 @@ IF[{- !$disabled{"fuzz-afl"} || !$disabled{"fuzz-libfuzzer"} -}]
     PROGRAMS{noinst}=dtlsclient dtlsserver
   ENDIF

+  IF[{- !$disabled{"ech"} -}]
+    PROGRAMS{noinst}=echconfiglist_parser
+  ENDIF
+
   SOURCE[asn1]=asn1.c driver.c fuzz_rand.c
   INCLUDE[asn1]=../include {- $ex_inc -}
   DEPEND[asn1]=../libcrypto ../libssl {- $ex_lib -}
@@ -106,6 +110,10 @@ IF[{- !$disabled{"fuzz-afl"} || !$disabled{"fuzz-libfuzzer"} -}]
   INCLUDE[dtlsserver]=../include {- $ex_inc -}
   DEPEND[dtlsserver]=../libcrypto ../libssl {- $ex_lib -}

+  SOURCE[echconfiglist_parser]=echconfiglist_parser.c driver.c
+  INCLUDE[echconfiglist_parser]=../include {- $ex_inc -}
+  DEPEND[echconfiglist_parser]=../libcrypto ../libssl {- $ex_lib -}
+
   SOURCE[pem]=pem.c driver.c
   INCLUDE[pem]=../include {- $ex_inc -}
   DEPEND[pem]=../libcrypto.a {- $ex_lib -}
@@ -220,6 +228,10 @@ IF[{- !$disabled{tests} -}]
     PROGRAMS{noinst}=dtlsclient-test dtlsserver-test
   ENDIF

+  IF[{- !$disabled{"ech"} -}]
+    PROGRAMS{noinst}=echconfiglist_parser-test
+  ENDIF
+
   SOURCE[asn1-test]=asn1.c $FUZZTESTSRC fuzz_rand.c
   INCLUDE[asn1-test]=../include ../test/mfail
   DEPEND[asn1-test]=../libcrypto.a ../libssl.a
@@ -288,6 +300,10 @@ IF[{- !$disabled{tests} -}]
   INCLUDE[dtlsserver-test]=../include ../test/mfail
   DEPEND[dtlsserver-test]=../libcrypto.a ../libssl.a

+  SOURCE[echconfiglist_parser-test]=echconfiglist_parser.c $FUZZTESTSRC
+  INCLUDE[echconfiglist_parser-test]=../include ../test/mfail
+  DEPEND[echconfiglist_parser-test]=../libcrypto.a ../libssl.a
+
   SOURCE[pem-test]=pem.c $FUZZTESTSRC
   INCLUDE[pem-test]=../include ../test/mfail
   DEPEND[pem-test]=../libcrypto.a
diff --git a/fuzz/echconfiglist_parser.c b/fuzz/echconfiglist_parser.c
new file mode 100644
index 0000000000..fce416ec4d
--- /dev/null
+++ b/fuzz/echconfiglist_parser.c
@@ -0,0 +1,88 @@
+/*
+ * 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 may obtain a copy of the License at
+ * https://www.openssl.org/source/license.html
+ * or in the file LICENSE in the source distribution.
+ */
+#include <limits.h>
+#include <openssl/ech.h>
+#include <openssl/err.h>
+#include <openssl/bio.h>
+#include <openssl/crypto.h>
+#include <openssl/e_os2.h>
+#include <openssl/byteorder.h>
+#include "fuzzer.h"
+
+static void parse_one(const uint8_t *buf, int len)
+{
+    OSSL_ECHSTORE *es;
+    BIO *in;
+
+    es = OSSL_ECHSTORE_new(NULL, NULL);
+    if (es == NULL)
+        return;
+
+    in = BIO_new_mem_buf(buf, len);
+    if (in == NULL) {
+        OSSL_ECHSTORE_free(es);
+        return;
+    }
+
+    OSSL_ECHSTORE_read_echconfiglist(es, in);
+
+    OSSL_ECHSTORE_free(es);
+    BIO_free(in);
+}
+
+int FuzzerInitialize(int *argc, char ***argv)
+{
+    return 1;
+}
+
+int FuzzerTestOneInput(const uint8_t *buf, size_t len)
+{
+    uint8_t *fixed_buf = NULL;
+    int bio_len;
+    uint16_t outer_len, inner_len;
+
+    if (len > INT_MAX)
+        return 0;
+    bio_len = (int)len;
+
+    /* Target raw without any fixup */
+    parse_one(buf, bio_len);
+
+    /*
+     * ech_decode_and_flatten has a strict size check:
+     * OSSL_ECH_MIN_ECHCONFIG_LEN = 32
+     * OSSL_ECH_MAX_ECHCONFIG_LEN = 1500
+     */
+    if (len < OSSL_ECH_MIN_ECHCONFIG_LEN || len >= OSSL_ECH_MAX_ECHCONFIG_LEN)
+        goto end;
+    outer_len = (uint16_t)(len - 2);
+    inner_len = (uint16_t)(len - 6);
+
+    fixed_buf = OPENSSL_memdup(buf, len);
+    if (fixed_buf == NULL)
+        goto end;
+
+    /* Fix up to pass initial checks*/
+    OPENSSL_store_u16_be(fixed_buf, outer_len);
+    OPENSSL_store_u16_be(fixed_buf + 2, OSSL_ECH_RFC9849_VERSION);
+    OPENSSL_store_u16_be(fixed_buf + 4, inner_len);
+
+    parse_one(fixed_buf, bio_len);
+
+end:
+    OPENSSL_free(fixed_buf);
+    ERR_clear_error();
+
+    return 0;
+}
+
+void FuzzerCleanup(void)
+{
+}
diff --git a/test/recipes/99-test_fuzz_echconfiglist_parser.t b/test/recipes/99-test_fuzz_echconfiglist_parser.t
new file mode 100644
index 0000000000..09e115caa6
--- /dev/null
+++ b/test/recipes/99-test_fuzz_echconfiglist_parser.t
@@ -0,0 +1,25 @@
+#!/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 strict;
+use warnings;
+
+use OpenSSL::Test qw/:DEFAULT srctop_file/;
+use OpenSSL::Test::Utils;
+
+my $fuzzer = "echconfiglist_parser";
+setup("test_fuzz_${fuzzer}");
+
+plan skip_all => "This test requires ech support"
+    if disabled("ech");
+
+plan tests => 2; # one more due to below require_ok(...)
+
+require_ok(srctop_file('test','recipes','fuzz.pl'));
+
+fuzz_ok($fuzzer);