]> git.ipfire.org Git - thirdparty/openssl.git/commitdiff
Add OSSL_ECHSTORE_read_echconfiglist fuzzer
authorGreensi7 <adam.tabak04@gmail.com>
Mon, 20 Jul 2026 21:34:08 +0000 (23:34 +0200)
committerNorbert Pocs <norbertp@openssl.org>
Thu, 23 Jul 2026 13:57:53 +0000 (15:57 +0200)
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)

fuzz/build.info
fuzz/echconfiglist_parser.c [new file with mode: 0644]
test/recipes/99-test_fuzz_echconfiglist_parser.t [new file with mode: 0644]

index 5c410260e4c4dea4f9d3968b5cbba2e5e6137f1a..fcbb76234cccad42dec60cce5a8061b3cb84022a 100644 (file)
@@ -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 (file)
index 0000000..fce416e
--- /dev/null
@@ -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 (file)
index 0000000..09e115c
--- /dev/null
@@ -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);