From 3d7f83ebdca1d66f7ddb2ad1d23866d3a6c4e3cf Mon Sep 17 00:00:00 2001 From: Hugo Landau Date: Fri, 3 Nov 2023 17:16:23 +0000 Subject: [PATCH] QUIC LCIDM: Add fuzzer Reviewed-by: Neil Horman Reviewed-by: Matt Caswell (Merged from https://github.com/openssl/openssl/pull/22673) --- fuzz/build.info | 12 +- fuzz/quic-lcidm.c | 180 +++++++++++++++++++++++++ ssl/quic/quic_lcidm.c | 20 +++ test/recipes/99-test_fuzz_quic_lcidm.t | 25 ++++ 4 files changed, 235 insertions(+), 2 deletions(-) create mode 100644 fuzz/quic-lcidm.c create mode 100644 test/recipes/99-test_fuzz_quic_lcidm.t diff --git a/fuzz/build.info b/fuzz/build.info index bbbc7c9654..3fc4345a89 100644 --- a/fuzz/build.info +++ b/fuzz/build.info @@ -30,7 +30,7 @@ IF[{- !$disabled{"fuzz-afl"} || !$disabled{"fuzz-libfuzzer"} -}] ENDIF IF[{- !$disabled{"quic"} -}] - PROGRAMS{noinst}=quic-client quic-srtm + PROGRAMS{noinst}=quic-client quic-srtm quic-lcidm ENDIF SOURCE[asn1]=asn1.c driver.c fuzz_rand.c @@ -101,6 +101,10 @@ IF[{- !$disabled{"fuzz-afl"} || !$disabled{"fuzz-libfuzzer"} -}] INCLUDE[quic-srtm]=../include {- $ex_inc -} DEPEND[quic-srtm]=../libcrypto.a ../libssl.a {- $ex_lib -} + SOURCE[quic-lcidm]=quic-lcidm.c driver.c fuzz_rand.c + INCLUDE[quic-lcidm]=../include {- $ex_inc -} + DEPEND[quic-lcidm]=../libcrypto.a ../libssl.a {- $ex_lib -} + SOURCE[server]=server.c driver.c fuzz_rand.c INCLUDE[server]=../include {- $ex_inc -} DEPEND[server]=../libcrypto ../libssl {- $ex_lib -} @@ -132,7 +136,7 @@ IF[{- !$disabled{tests} -}] ENDIF IF[{- !$disabled{"quic"} -}] - PROGRAMS{noinst}=quic-client-test quic-srtm-test + PROGRAMS{noinst}=quic-client-test quic-srtm-test quic-lcidm-test ENDIF SOURCE[asn1-test]=asn1.c test-corpus.c fuzz_rand.c @@ -204,6 +208,10 @@ IF[{- !$disabled{tests} -}] INCLUDE[quic-srtm-test]=../include DEPEND[quic-srtm-test]=../libcrypto.a ../libssl.a + SOURCE[quic-lcidm-test]=quic-lcidm.c test-corpus.c fuzz_rand.c + INCLUDE[quic-lcidm-test]=../include + DEPEND[quic-lcidm-test]=../libcrypto.a ../libssl.a + SOURCE[server-test]=server.c test-corpus.c fuzz_rand.c INCLUDE[server-test]=../include DEPEND[server-test]=../libcrypto ../libssl diff --git a/fuzz/quic-lcidm.c b/fuzz/quic-lcidm.c new file mode 100644 index 0000000000..f72f091860 --- /dev/null +++ b/fuzz/quic-lcidm.c @@ -0,0 +1,180 @@ +/* + * Copyright 2016-2022 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 +#include +#include +#include "fuzzer.h" +#include "internal/quic_lcidm.h" +#include "internal/packet.h" + +int FuzzerInitialize(int *argc, char ***argv) +{ + FuzzerSetRand(); + OPENSSL_init_crypto(OPENSSL_INIT_LOAD_CRYPTO_STRINGS | OPENSSL_INIT_ASYNC, NULL); + OPENSSL_init_ssl(OPENSSL_INIT_LOAD_SSL_STRINGS, NULL); + ERR_clear_error(); + return 1; +} + +/* + * Fuzzer input "protocol": + * Big endian + * u8(LCID length) + * Zero or more of: + * ENROL_ODCID u0(0x00) u64(opaque) u8(cidl):cid + * RETIRE_ODCID u8(0x01) u64(opaque) + * GENERATE_INITIAL u8(0x02) u64(opaque) + * GENERATE u8(0x03) u64(opaque) + * RETIRE u8(0x04) u64(opaque) u64(retire_prior_to) + * CULL u8(0x05) u64(opaque) + * LOOKUP u8(0x06) u8(cidl):cid + */ + +enum { + CMD_ENROL_ODCID, + CMD_RETIRE_ODCID, + CMD_GENERATE_INITIAL, + CMD_GENERATE, + CMD_RETIRE, + CMD_CULL, + CMD_LOOKUP +}; + +static int get_cid(PACKET *pkt, QUIC_CONN_ID *cid) +{ + unsigned int cidl; + + if (!PACKET_get_1(pkt, &cidl) + || cidl > QUIC_MAX_CONN_ID_LEN + || !PACKET_copy_bytes(pkt, cid->id, cidl)) + return 0; + + cid->id_len = (unsigned char)cidl; + return 1; +} + +int FuzzerTestOneInput(const uint8_t *buf, size_t len) +{ + int rc = 0; + QUIC_LCIDM *lcidm = NULL; + PACKET pkt; + uint64_t arg_opaque, arg_retire_prior_to, seq_num_out; + unsigned int cmd, lcidl; + QUIC_CONN_ID arg_cid, cid_out; + OSSL_QUIC_FRAME_NEW_CONN_ID ncid_frame; + int did_retire; + void *opaque_out; + + if (!PACKET_buf_init(&pkt, buf, len)) + goto err; + + if (!PACKET_get_1(&pkt, &lcidl) + || lcidl > QUIC_MAX_CONN_ID_LEN) { + rc = -1; + goto err; + } + + if ((lcidm = ossl_quic_lcidm_new(NULL, lcidl)) == NULL) { + rc = -1; + goto err; + } + + while (PACKET_remaining(&pkt) > 0) { + if (!PACKET_get_1(&pkt, &cmd)) + goto err; + + switch (cmd) { + case CMD_ENROL_ODCID: + if (!PACKET_get_net_8(&pkt, &arg_opaque) + || !get_cid(&pkt, &arg_cid)) { + rc = -1; + goto err; + } + + ossl_quic_lcidm_enrol_odcid(lcidm, (void *)(uintptr_t)arg_opaque, + &arg_cid); + break; + + case CMD_RETIRE_ODCID: + if (!PACKET_get_net_8(&pkt, &arg_opaque)) { + rc = -1; + goto err; + } + + ossl_quic_lcidm_retire_odcid(lcidm, (void *)(uintptr_t)arg_opaque); + break; + + case CMD_GENERATE_INITIAL: + if (!PACKET_get_net_8(&pkt, &arg_opaque)) { + rc = -1; + goto err; + } + + ossl_quic_lcidm_generate_initial(lcidm, (void *)(uintptr_t)arg_opaque, + &cid_out); + break; + + case CMD_GENERATE: + if (!PACKET_get_net_8(&pkt, &arg_opaque)) { + rc = -1; + goto err; + } + + ossl_quic_lcidm_generate(lcidm, (void *)(uintptr_t)arg_opaque, + &ncid_frame); + break; + + case CMD_RETIRE: + if (!PACKET_get_net_8(&pkt, &arg_opaque) + || !PACKET_get_net_8(&pkt, &arg_retire_prior_to)) { + rc = -1; + goto err; + } + + ossl_quic_lcidm_retire(lcidm, (void *)(uintptr_t)arg_opaque, + arg_retire_prior_to, + NULL, &cid_out, + &seq_num_out, &did_retire); + break; + + case CMD_CULL: + if (!PACKET_get_net_8(&pkt, &arg_opaque)) { + rc = -1; + goto err; + } + + ossl_quic_lcidm_cull(lcidm, (void *)(uintptr_t)arg_opaque); + break; + + case CMD_LOOKUP: + if (!get_cid(&pkt, &arg_cid)) { + rc = -1; + goto err; + } + + ossl_quic_lcidm_lookup(lcidm, &arg_cid, &seq_num_out, &opaque_out); + break; + + default: + rc = -1; + goto err; + } + } + +err: + ossl_quic_lcidm_free(lcidm); + return rc; +} + +void FuzzerCleanup(void) +{ + FuzzerClearRand(); +} diff --git a/ssl/quic/quic_lcidm.c b/ssl/quic/quic_lcidm.c index 8427eb4d41..5c956e4edd 100644 --- a/ssl/quic/quic_lcidm.c +++ b/ssl/quic/quic_lcidm.c @@ -58,6 +58,9 @@ struct quic_lcidm_st { LHASH_OF(QUIC_LCID) *lcids; /* (QUIC_CONN_ID) -> (QUIC_LCID *) */ LHASH_OF(QUIC_LCIDM_CONN) *conns; /* (void *opaque) -> (QUIC_LCIDM_CONN *) */ size_t lcid_len; /* Length in bytes for all LCIDs */ +#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + QUIC_CONN_ID next_lcid; +#endif }; static unsigned long bin_hash(const unsigned char *buf, size_t buf_len) @@ -233,6 +236,8 @@ size_t ossl_quic_lcidm_get_num_active_lcid(const QUIC_LCIDM *lcidm, return conn->num_active_lcid; } +#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + static int gen_rand_conn_id(OSSL_LIB_CTX *libctx, size_t len, QUIC_CONN_ID *cid) { if (len > QUIC_MAX_CONN_ID_LEN) @@ -249,10 +254,25 @@ static int gen_rand_conn_id(OSSL_LIB_CTX *libctx, size_t len, QUIC_CONN_ID *cid) return 1; } +#endif + static int lcidm_generate_cid(QUIC_LCIDM *lcidm, QUIC_CONN_ID *cid) { +#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + int i; + + lcidm->next_lcid.id_len = (unsigned char)lcidm->lcid_len; + *cid = lcidm->next_lcid; + + for (i = lcidm->lcid_len - 1; i >= 0; --i) + if (++lcidm->next_lcid.id[i] != 0) + break; + + return 1; +#else return gen_rand_conn_id(lcidm->libctx, lcidm->lcid_len, cid); +#endif } static int lcidm_generate(QUIC_LCIDM *lcidm, diff --git a/test/recipes/99-test_fuzz_quic_lcidm.t b/test/recipes/99-test_fuzz_quic_lcidm.t new file mode 100644 index 0000000000..2ae61d9190 --- /dev/null +++ b/test/recipes/99-test_fuzz_quic_lcidm.t @@ -0,0 +1,25 @@ +#!/usr/bin/env perl +# Copyright 2023 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 = "quic-lcidm"; +setup("test_fuzz_${fuzzer}"); + +plan skip_all => "This test requires quic support" + if disabled("quic"); + +plan tests => 2; # one more due to below require_ok(...) + +require_ok(srctop_file('test','recipes','fuzz.pl')); + +fuzz_ok($fuzzer);