diff --git a/fuzzing/Makefile b/fuzzing/Makefile index c94f12d271..4857ae31df 100644 --- a/fuzzing/Makefile +++ b/fuzzing/Makefile @@ -5,6 +5,7 @@ all: zip -r corpora/db_dump_seed_corpus.zip ../test/apps/db_dump/data/ $(MAKE) -C apps $@ $(MAKE) -C broker $@ + $(MAKE) -C client $@ $(MAKE) -C libcommon $@ $(MAKE) -C plugins $@ @@ -14,5 +15,6 @@ clean: -rm -f corpora/db_dump_seed_corpus.zip $(MAKE) -C apps $@ $(MAKE) -C broker $@ + $(MAKE) -C client $@ $(MAKE) -C libcommon $@ $(MAKE) -C plugins $@ diff --git a/fuzzing/client/Makefile b/fuzzing/client/Makefile new file mode 100644 index 0000000000..7592049c7e --- /dev/null +++ b/fuzzing/client/Makefile @@ -0,0 +1,34 @@ +R=../.. +include ${R}/fuzzing/config.mk + +.PHONY: all clean + +PACKET_FUZZERS:= \ + client_fuzz_read_handle + +# IMPORTANT: these -D flags must exactly match the ones used to compile +# lib/libmosquitto.a (see config.mk with WITH_FUZZING=yes). A mismatch changes +# the layout of `struct mosquitto` between this harness and the library and +# leads to memory corruption. In particular WITH_WEBSOCKETS=WS_IS_BUILTIN adds +# fields to the struct, and WITH_BROKER must NOT be defined for the client lib. +LOCAL_CPPFLAGS+=-I${R}/include/ -I${R}/lib -I${R} -I${R}/common \ + -I${R}/deps -I${R}/deps/picohttpparser \ + -DWITH_THREADING -DWITH_TLS -DWITH_TLS_PSK -DWITH_SOCKS \ + -DWITH_UNIX_SOCKETS -DWITH_WEBSOCKETS=WS_IS_BUILTIN -DWITH_FUZZING +LOCAL_CXXFLAGS+=-g -Wall -Werror -pthread +LOCAL_CFLAGS+=-g -Wall -Werror -pthread +LOCAL_LDFLAGS+= +LOCAL_LIBADD+=$(LIB_FUZZING_ENGINE) -lssl -lcrypto -lcjson -lm \ + ${R}/libcommon/libmosquitto_common.a \ + -Wl,-Bdynamic -Wl,-Bstatic -largon2 -Wl,-Bdynamic +LIBMOSQ_A=${R}/lib/libmosquitto.a + +all: $(PACKET_FUZZERS) + +${PACKET_FUZZERS} : %: %.c ${R}/lib/libmosquitto.a + $(CC) $(LOCAL_CFLAGS) $(LOCAL_CPPFLAGS) $(LOCAL_LDFLAGS) -o $@ $< $(LIBMOSQ_A) $(LOCAL_LIBADD) + install $@ ${OUT}/$@ + cp ${R}/fuzzing/corpora/client_packet_seed_corpus.zip ${OUT}/$@_seed_corpus.zip + +clean: + rm -f *.o $(PACKET_FUZZERS) diff --git a/fuzzing/client/client_fuzz_read_handle.c b/fuzzing/client/client_fuzz_read_handle.c new file mode 100644 index 0000000000..a470af8eff --- /dev/null +++ b/fuzzing/client/client_fuzz_read_handle.c @@ -0,0 +1,80 @@ +/* +Copyright (c) 2026 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: + AdaLogics - client-side read-handle fuzzer. +*/ + +#include +#include +#include + +#include "mosquitto.h" +#include "mosquitto/libcommon_memory.h" +#include "mosquitto_internal.h" +#include "packet_mosq.h" +#include "read_handle.h" + +#define kMinInputLength 3 +#define kMaxInputLength 268435455U + +static int initialised = 0; + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) +{ + struct mosquitto *mosq; + uint8_t *payload; + size_t payload_len; + + if(size < kMinInputLength || size > kMaxInputLength){ + return 0; + } + + if(!initialised){ + mosquitto_lib_init(); + initialised = 1; + } + + mosq = mosquitto_new("fuzz-client", true, NULL); + if(!mosq){ + return 0; + } + + /* Put the client in a connected state; v5 also exercises the property parser */ + mosq->protocol = (data[0] & 0x01) ? mosq_p_mqtt311 : mosq_p_mqtt5; + mosq->state = mosq_cs_connected; + mosq->alias_max_l2r = 10; + + payload_len = size - 1; + payload = (uint8_t *)mosquitto_malloc(payload_len); + if(!payload){ + mosquitto_destroy(mosq); + return 0; + } + memcpy(payload, &data[1], payload_len); + + /* Stage the bytes as a received packet with the command byte consumed (pos=1) */ + mosq->in_packet.command = payload[0]; + mosq->in_packet.payload = payload; + mosq->in_packet.packet_length = (uint32_t)payload_len; + mosq->in_packet.remaining_length = (uint32_t)(payload_len - 1); + mosq->in_packet.pos = 1; + + handle__packet(mosq); + + packet__cleanup(&mosq->in_packet); + mosquitto_destroy(mosq); + + return 0; +}