From 02442238cb96095d0e80561ad7958d49b4c70cc2 Mon Sep 17 00:00:00 2001 From: "Roger A. Light" Date: Wed, 20 Nov 2024 11:02:39 +0000 Subject: [PATCH] Fuzz: Add fuzzers to allow packet handling to get greater coverage --- .gitignore | 6 ++ fuzzing/broker/Makefile | 22 ++++- fuzzing/broker/broker_fuzz_handle_auth.cpp | 38 +++++++++ fuzzing/broker/broker_fuzz_handle_connect.cpp | 59 +++++++++++++ fuzzing/broker/broker_fuzz_handle_publish.cpp | 59 +++++++++++++ .../broker/broker_fuzz_handle_subscribe.cpp | 59 +++++++++++++ .../broker/broker_fuzz_handle_unsubscribe.cpp | 59 +++++++++++++ fuzzing/broker/broker_fuzz_read_handle.cpp | 70 ++------------- fuzzing/broker/fuzz_packet_read_base.c | 85 +++++++++++++++++++ fuzzing/broker/fuzz_packet_read_base.h | 41 +++++++++ 10 files changed, 432 insertions(+), 66 deletions(-) create mode 100644 fuzzing/broker/broker_fuzz_handle_auth.cpp create mode 100644 fuzzing/broker/broker_fuzz_handle_connect.cpp create mode 100644 fuzzing/broker/broker_fuzz_handle_publish.cpp create mode 100644 fuzzing/broker/broker_fuzz_handle_subscribe.cpp create mode 100644 fuzzing/broker/broker_fuzz_handle_unsubscribe.cpp create mode 100644 fuzzing/broker/fuzz_packet_read_base.c create mode 100644 fuzzing/broker/fuzz_packet_read_base.h diff --git a/.gitignore b/.gitignore index 76eff21f..de1bce23 100644 --- a/.gitignore +++ b/.gitignore @@ -53,6 +53,11 @@ fuzzing/apps/db_dump/db_dump_fuzz_load_client_stats fuzzing/apps/db_dump/db_dump_fuzz_load_stats fuzzing/apps/mosquitto_passwd/mosquitto_passwd_fuzz_load fuzzing/broker/broker_fuzz_acl_file +fuzzing/broker/broker_fuzz_handle_auth +fuzzing/broker/broker_fuzz_handle_connect +fuzzing/broker/broker_fuzz_handle_publish +fuzzing/broker/broker_fuzz_handle_subscribe +fuzzing/broker/broker_fuzz_handle_unsubscribe fuzzing/broker/broker_fuzz_initial_packet fuzzing/broker/broker_fuzz_initial_packet_with_init fuzzing/broker/broker_fuzz_password_file @@ -62,6 +67,7 @@ fuzzing/broker/broker_fuzz_read_handle fuzzing/broker/broker_fuzz_second_packet fuzzing/broker/broker_fuzz_second_packet_with_init fuzzing/broker/broker_fuzz_test_config + fuzzing/corpora/broker/* fuzzing/corpora/broker_packet_seed_corpus.zip fuzzing/corpora/client/* diff --git a/fuzzing/broker/Makefile b/fuzzing/broker/Makefile index 51ac7d53..867faa72 100644 --- a/fuzzing/broker/Makefile +++ b/fuzzing/broker/Makefile @@ -19,7 +19,21 @@ LOCAL_CXXFLAGS+=-g -Wall -Werror -pthread LOCAL_LDFLAGS+= LOCAL_LIBADD+=$(LIB_FUZZING_ENGINE) ${R}/src/mosquitto_broker.a -lssl -lcrypto -lcjson -lm ${R}/libcommon/libmosquitto_common.a -Wl,-Bdynamic -Wl,-Bstatic -largon2 -Wl,-Bdynamic -all: $(FUZZERS) +PACKET_FUZZERS:= \ + broker_fuzz_handle_auth \ + broker_fuzz_handle_connect \ + broker_fuzz_handle_publish \ + broker_fuzz_handle_subscribe \ + broker_fuzz_handle_unsubscribe + +all: $(FUZZERS) $(PACKET_FUZZERS) + +${PACKET_FUZZERS} : %: %.cpp fuzz_packet_read_base.o ${R}/src/mosquitto_broker.a + $(CXX) $(LOCAL_CXXFLAGS) $(LOCAL_CPPFLAGS) $(LOCAL_LDFLAGS) -o $@ $< fuzz_packet_read_base.o $(LOCAL_LIBADD) + install $@ ${OUT}/$@ + +fuzz_packet_read_base.o : fuzz_packet_read_base.c + $(CC) $(LOCAL_CXXFLAGS) $(LOCAL_CPPFLAGS) -c -o $@ $< broker_fuzz_acl_file : broker_fuzz_acl_file.cpp ${R}/src/mosquitto_broker.a $(CXX) $(LOCAL_CXXFLAGS) $(LOCAL_CPPFLAGS) $(LOCAL_LDFLAGS) -o $@ $< $(LOCAL_LIBADD) @@ -42,8 +56,8 @@ broker_fuzz_queue_msg : broker_fuzz_queue_msg.cpp ${R}/src/mosquitto_broker.a install $@ ${OUT}/$@ cp ${R}/fuzzing/corpora/broker_queue_msg_seed_corpus.zip ${OUT}/$@_seed_corpus.zip -broker_fuzz_read_handle : broker_fuzz_read_handle.cpp ${R}/src/mosquitto_broker.a - $(CXX) $(LOCAL_CXXFLAGS) $(LOCAL_CPPFLAGS) $(LOCAL_LDFLAGS) -o $@ $< $(LOCAL_LIBADD) +broker_fuzz_read_handle : broker_fuzz_read_handle.cpp fuzz_packet_read_base.o ${R}/src/mosquitto_broker.a + $(CXX) $(LOCAL_CXXFLAGS) $(LOCAL_CPPFLAGS) $(LOCAL_LDFLAGS) -o $@ $^ $(LOCAL_LIBADD) install $@ ${OUT}/$@ cp ${R}/fuzzing/corpora/broker_packet_seed_corpus.zip ${OUT}/$@_seed_corpus.zip @@ -54,4 +68,4 @@ broker_fuzz_test_config : broker_fuzz_test_config.cpp ${R}/src/mosquitto_broker. cp ${R}/fuzzing/corpora/broker_conf.dict ${OUT}/$@.dict clean: - rm -f *.o $(FUZZERS) + rm -f *.o $(FUZZERS) $(PACKET_FUZZERS) diff --git a/fuzzing/broker/broker_fuzz_handle_auth.cpp b/fuzzing/broker/broker_fuzz_handle_auth.cpp new file mode 100644 index 00000000..732ec204 --- /dev/null +++ b/fuzzing/broker/broker_fuzz_handle_auth.cpp @@ -0,0 +1,38 @@ +/* +Copyright (c) 2023 Cedalo GmbH + +All rights reserved. This program and the accompanying materials +are made available under the terms of the Eclipse Public License 2.0 +and Eclipse Distribution License v1.0 which accompany this distribution. + +The Eclipse Public License is available at + https://www.eclipse.org/legal/epl-2.0/ +and the Eclipse Distribution License is available at + http://www.eclipse.org/org/documents/edl-v10.php. + +SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause + +Contributors: + Roger Light - initial implementation and documentation. +*/ + +#include "fuzz_packet_read_base.h" + +extern "C" int fuzz_packet_read_init(struct mosquitto *context) +{ + context->protocol = mosq_p_mqtt5; + context->auth_method = strdup("FUZZ"); + return !context->auth_method; +} + +extern "C" void fuzz_packet_read_cleanup(struct mosquitto *context) +{ + free(context->auth_method); + context->auth_method = NULL; +} + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) +{ + int rc = fuzz_packet_read_base(data, size, handle__auth); + return rc; +} diff --git a/fuzzing/broker/broker_fuzz_handle_connect.cpp b/fuzzing/broker/broker_fuzz_handle_connect.cpp new file mode 100644 index 00000000..38f408e7 --- /dev/null +++ b/fuzzing/broker/broker_fuzz_handle_connect.cpp @@ -0,0 +1,59 @@ +/* +Copyright (c) 2023 Cedalo GmbH + +All rights reserved. This program and the accompanying materials +are made available under the terms of the Eclipse Public License 2.0 +and Eclipse Distribution License v1.0 which accompany this distribution. + +The Eclipse Public License is available at + https://www.eclipse.org/legal/epl-2.0/ +and the Eclipse Distribution License is available at + http://www.eclipse.org/org/documents/edl-v10.php. + +SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause + +Contributors: + Roger Light - initial implementation and documentation. +*/ + +#include "fuzz_packet_read_base.h" + +extern "C" int fuzz_basic_auth(int event, void *event_data, void *userdata) +{ + struct mosquitto_evt_basic_auth *ed = (struct mosquitto_evt_basic_auth *)event_data; + + /* This is a check that is ultimately determined by the fuzz input data, so + * the fuzzer can discover how to access both the fail/success cases. + */ + if(ed->client->id && (ed->client->id[0]%2 == 0)){ + return MOSQ_ERR_SUCCESS; + }else{ + return MOSQ_ERR_AUTH; + } +} + +extern "C" int fuzz_packet_read_init(struct mosquitto *context) +{ + context->listener->security_options->pid = (mosquitto_plugin_id_t *)calloc(1, sizeof(mosquitto_plugin_id_t)); + if(!context->listener->security_options->pid){ + return 1; + } + mosquitto_callback_register(context->listener->security_options->pid, + MOSQ_EVT_BASIC_AUTH, fuzz_basic_auth, NULL, NULL); + + return 0; +} + +extern "C" void fuzz_packet_read_cleanup(struct mosquitto *context) +{ + mosquitto_callback_unregister(context->listener->security_options->pid, + MOSQ_EVT_BASIC_AUTH, fuzz_basic_auth, NULL); + + free(context->listener->security_options->pid); + context->listener->security_options->pid = NULL; +} + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) +{ + return fuzz_packet_read_base(data, size, handle__connect); +} diff --git a/fuzzing/broker/broker_fuzz_handle_publish.cpp b/fuzzing/broker/broker_fuzz_handle_publish.cpp new file mode 100644 index 00000000..41a92c1a --- /dev/null +++ b/fuzzing/broker/broker_fuzz_handle_publish.cpp @@ -0,0 +1,59 @@ +/* +Copyright (c) 2023 Cedalo GmbH + +All rights reserved. This program and the accompanying materials +are made available under the terms of the Eclipse Public License 2.0 +and Eclipse Distribution License v1.0 which accompany this distribution. + +The Eclipse Public License is available at + https://www.eclipse.org/legal/epl-2.0/ +and the Eclipse Distribution License is available at + http://www.eclipse.org/org/documents/edl-v10.php. + +SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause + +Contributors: + Roger Light - initial implementation and documentation. +*/ + +#include "fuzz_packet_read_base.h" + +extern "C" int fuzz_acl_check(int event, void *event_data, void *userdata) +{ + struct mosquitto_evt_acl_check *ed = (struct mosquitto_evt_acl_check *)event_data; + + /* This is a check that is ultimately determined by the fuzz input data, so + * the fuzzer can discover how to access both the fail/success cases. + */ + if(ed->topic && (ed->topic[0]%2 == 0)){ + return MOSQ_ERR_SUCCESS; + }else{ + return MOSQ_ERR_AUTH; + } +} + +extern "C" int fuzz_packet_read_init(struct mosquitto *context) +{ + context->listener->security_options->pid = (mosquitto_plugin_id_t *)calloc(1, sizeof(mosquitto_plugin_id_t)); + if(!context->listener->security_options->pid){ + return 1; + } + mosquitto_callback_register(context->listener->security_options->pid, + MOSQ_EVT_ACL_CHECK, fuzz_acl_check, NULL, NULL); + + return 0; +} + +extern "C" void fuzz_packet_read_cleanup(struct mosquitto *context) +{ + mosquitto_callback_unregister(context->listener->security_options->pid, + MOSQ_EVT_ACL_CHECK, fuzz_acl_check, NULL); + + free(context->listener->security_options->pid); + context->listener->security_options->pid = NULL; +} + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) +{ + return fuzz_packet_read_base(data, size, handle__publish); +} diff --git a/fuzzing/broker/broker_fuzz_handle_subscribe.cpp b/fuzzing/broker/broker_fuzz_handle_subscribe.cpp new file mode 100644 index 00000000..5fd20a75 --- /dev/null +++ b/fuzzing/broker/broker_fuzz_handle_subscribe.cpp @@ -0,0 +1,59 @@ +/* +Copyright (c) 2023 Cedalo GmbH + +All rights reserved. This program and the accompanying materials +are made available under the terms of the Eclipse Public License 2.0 +and Eclipse Distribution License v1.0 which accompany this distribution. + +The Eclipse Public License is available at + https://www.eclipse.org/legal/epl-2.0/ +and the Eclipse Distribution License is available at + http://www.eclipse.org/org/documents/edl-v10.php. + +SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause + +Contributors: + Roger Light - initial implementation and documentation. +*/ + +#include "fuzz_packet_read_base.h" + +extern "C" int fuzz_acl_check(int event, void *event_data, void *userdata) +{ + struct mosquitto_evt_acl_check *ed = (struct mosquitto_evt_acl_check *)event_data; + + /* This is a check that is ultimately determined by the fuzz input data, so + * the fuzzer can discover how to access both the fail/success cases. + */ + if(ed->topic && (ed->topic[0]%2 == 0)){ + return MOSQ_ERR_SUCCESS; + }else{ + return MOSQ_ERR_AUTH; + } +} + +extern "C" int fuzz_packet_read_init(struct mosquitto *context) +{ + context->listener->security_options->pid = (mosquitto_plugin_id_t *)calloc(1, sizeof(mosquitto_plugin_id_t)); + if(!context->listener->security_options->pid){ + return 1; + } + mosquitto_callback_register(context->listener->security_options->pid, + MOSQ_EVT_ACL_CHECK, fuzz_acl_check, NULL, NULL); + + return 0; +} + +extern "C" void fuzz_packet_read_cleanup(struct mosquitto *context) +{ + mosquitto_callback_unregister(context->listener->security_options->pid, + MOSQ_EVT_ACL_CHECK, fuzz_acl_check, NULL); + + free(context->listener->security_options->pid); + context->listener->security_options->pid = NULL; +} + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) +{ + return fuzz_packet_read_base(data, size, handle__subscribe); +} diff --git a/fuzzing/broker/broker_fuzz_handle_unsubscribe.cpp b/fuzzing/broker/broker_fuzz_handle_unsubscribe.cpp new file mode 100644 index 00000000..60480574 --- /dev/null +++ b/fuzzing/broker/broker_fuzz_handle_unsubscribe.cpp @@ -0,0 +1,59 @@ +/* +Copyright (c) 2023 Cedalo GmbH + +All rights reserved. This program and the accompanying materials +are made available under the terms of the Eclipse Public License 2.0 +and Eclipse Distribution License v1.0 which accompany this distribution. + +The Eclipse Public License is available at + https://www.eclipse.org/legal/epl-2.0/ +and the Eclipse Distribution License is available at + http://www.eclipse.org/org/documents/edl-v10.php. + +SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause + +Contributors: + Roger Light - initial implementation and documentation. +*/ + +#include "fuzz_packet_read_base.h" + +extern "C" int fuzz_acl_check(int event, void *event_data, void *userdata) +{ + struct mosquitto_evt_acl_check *ed = (struct mosquitto_evt_acl_check *)event_data; + + /* This is a check that is ultimately determined by the fuzz input data, so + * the fuzzer can discover how to access both the fail/success cases. + */ + if(ed->topic && (ed->topic[0]%2 == 0)){ + return MOSQ_ERR_SUCCESS; + }else{ + return MOSQ_ERR_AUTH; + } +} + +extern "C" int fuzz_packet_read_init(struct mosquitto *context) +{ + context->listener->security_options->pid = (mosquitto_plugin_id_t *)calloc(1, sizeof(mosquitto_plugin_id_t)); + if(!context->listener->security_options->pid){ + return 1; + } + mosquitto_callback_register(context->listener->security_options->pid, + MOSQ_EVT_ACL_CHECK, fuzz_acl_check, NULL, NULL); + + return 0; +} + +extern "C" void fuzz_packet_read_cleanup(struct mosquitto *context) +{ + mosquitto_callback_unregister(context->listener->security_options->pid, + MOSQ_EVT_ACL_CHECK, fuzz_acl_check, NULL); + + free(context->listener->security_options->pid); + context->listener->security_options->pid = NULL; +} + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) +{ + return fuzz_packet_read_base(data, size, handle__unsubscribe); +} diff --git a/fuzzing/broker/broker_fuzz_read_handle.cpp b/fuzzing/broker/broker_fuzz_read_handle.cpp index ffa85521..ffe3803c 100644 --- a/fuzzing/broker/broker_fuzz_read_handle.cpp +++ b/fuzzing/broker/broker_fuzz_read_handle.cpp @@ -16,72 +16,18 @@ Contributors: Roger Light - initial implementation and documentation. */ -#include -#include -#include -#include -#include +#include "fuzz_packet_read_base.h" -#ifdef __cplusplus -extern "C" { -#endif - -#include "mosquitto_broker_internal.h" -#include "mosquitto_internal.h" - -#ifdef __cplusplus +extern "C" int fuzz_packet_read_init(struct mosquitto *context) +{ + return 0; } -#endif -#define kMinInputLength 3 -#define kMaxInputLength 268435455U +extern "C" void fuzz_packet_read_cleanup(struct mosquitto *context) +{ +} extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { - struct mosquitto *context = NULL; - uint8_t *data_heap; - struct mosquitto__listener listener; - struct mosquitto__security_options secopts; - struct mosquitto__bridge bridge; - - if(size < kMinInputLength || size > kMaxInputLength){ - return 0; - } - - db.config = (struct mosquitto__config *)calloc(1, sizeof(struct mosquitto__config)); - log__init(db.config); - - memset(&listener, 0, sizeof(listener)); - memset(&bridge, 0, sizeof(bridge)); - memset(&secopts, 0, sizeof(secopts)); - - context = context__init(); - if(!context) return 1; - listener.security_options = &secopts; - context->listener = &listener; - context->bridge = &bridge; - - context->state = (enum mosquitto_client_state )data[0]; - context->protocol = (enum mosquitto__protocol )data[1]; - size -= 2; - - data_heap = (uint8_t *)malloc(size); - if(!data_heap) return 1; - - memcpy(data_heap, &data[2], size); - - context->in_packet.command = data_heap[0]; - context->in_packet.payload = (uint8_t *)data_heap; - context->in_packet.packet_length = (uint32_t )size; /* Safe cast, because we've already limited the size */ - context->in_packet.remaining_length = (uint32_t )(size-1); - context->in_packet.pos = 1; - - handle__packet(context); - - context->bridge = NULL; - context__cleanup(context, true); - - free(db.config); - - return 0; + return fuzz_packet_read_base(data, size, handle__packet); } diff --git a/fuzzing/broker/fuzz_packet_read_base.c b/fuzzing/broker/fuzz_packet_read_base.c new file mode 100644 index 00000000..02b8958e --- /dev/null +++ b/fuzzing/broker/fuzz_packet_read_base.c @@ -0,0 +1,85 @@ +/* +Copyright (c) 2023 Cedalo GmbH + +All rights reserved. This program and the accompanying materials +are made available under the terms of the Eclipse Public License 2.0 +and Eclipse Distribution License v1.0 which accompany this distribution. + +The Eclipse Public License is available at + https://www.eclipse.org/legal/epl-2.0/ +and the Eclipse Distribution License is available at + http://www.eclipse.org/org/documents/edl-v10.php. + +SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause + +Contributors: + Roger Light - initial implementation and documentation. +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "fuzz_packet_read_base.h" +#include "mosquitto_broker_internal.h" +#include "mosquitto_internal.h" + +#define kMinInputLength 3 +#define kMaxInputLength 268435455U + +int fuzz_packet_read_base(const uint8_t *data, size_t size, int (*packet_func)(struct mosquitto *)) +{ + struct mosquitto *context = NULL; + uint8_t *data_heap; + struct mosquitto__listener listener; + struct mosquitto__security_options secopts; + struct mosquitto__bridge bridge; + + if(size < kMinInputLength || size > kMaxInputLength){ + return 0; + } + + db.config = (struct mosquitto__config *)calloc(1, sizeof(struct mosquitto__config)); + log__init(db.config); + + memset(&listener, 0, sizeof(listener)); + memset(&bridge, 0, sizeof(bridge)); + memset(&secopts, 0, sizeof(secopts)); + + context = context__init(); + if(!context) return 1; + listener.security_options = &secopts; + context->listener = &listener; + context->bridge = &bridge; + + context->state = (enum mosquitto_client_state )data[0]; + context->protocol = (enum mosquitto__protocol )data[1]; + size -= 2; + + data_heap = (uint8_t *)malloc(size); + if(!data_heap) return 1; + + memcpy(data_heap, &data[2], size); + + context->in_packet.command = data_heap[0]; + context->in_packet.payload = (uint8_t *)data_heap; + context->in_packet.packet_length = (uint32_t )size; /* Safe cast, because we've already limited the size */ + context->in_packet.remaining_length = (uint32_t )(size-1); + context->in_packet.pos = 1; + + if(fuzz_packet_read_init(context)){ + return 1; + } + packet_func(context); + fuzz_packet_read_cleanup(context); + + context->bridge = NULL; + context__cleanup(context, true); + + free(db.config); + + return 0; +} +#ifdef __cplusplus +} +#endif diff --git a/fuzzing/broker/fuzz_packet_read_base.h b/fuzzing/broker/fuzz_packet_read_base.h new file mode 100644 index 00000000..6ebe396b --- /dev/null +++ b/fuzzing/broker/fuzz_packet_read_base.h @@ -0,0 +1,41 @@ +#ifndef FUZZ_PACKET_READ_BASE_H +/* +Copyright (c) 2023 Cedalo GmbH + +All rights reserved. This program and the accompanying materials +are made available under the terms of the Eclipse Public License 2.0 +and Eclipse Distribution License v1.0 which accompany this distribution. + +The Eclipse Public License is available at + https://www.eclipse.org/legal/epl-2.0/ +and the Eclipse Distribution License is available at + http://www.eclipse.org/org/documents/edl-v10.php. + +SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause + +Contributors: + Roger Light - initial implementation and documentation. +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#include "mosquitto_broker_internal.h" +#include "mosquitto_internal.h" +#include "read_handle.h" + +#define kMinInputLength 3 +#define kMaxInputLength 268435455U + +int fuzz_packet_read_base(const uint8_t *data, size_t size, int (*packet_func)(struct mosquitto *)); +int fuzz_packet_read_init(struct mosquitto *context); +void fuzz_packet_read_cleanup(struct mosquitto *context); + +#ifdef __cplusplus +} +#endif + +#endif