Support for initial fuzzing through oss-fuzz

pull/2756/head
Roger A. Light 3 years ago
parent 7c8af215ad
commit 5fb4b05d8f

6
.gitignore vendored

@ -40,6 +40,11 @@ examples/temperature_conversion/mqtt_temperature_conversion
examples/publish/basic-1
examples/publish/basic-websockets-1
fuzzing/broker/broker_fuzz_initial_packet
fuzzing/broker/broker_fuzz_second_packet
fuzzing/corpora/broker/*
fuzzing/corpora/client/*
lib/cpp/libmosquittopp.so*
lib/cpp/libmosquittopp.a
lib/libmosquitto.so*
@ -60,6 +65,7 @@ man/mqtt.7
out/
src/mosquitto
src/mosquitto_broker.a
test/broker/broker.pid
test/test_client

@ -65,13 +65,16 @@ mosquitto :
ifeq ($(UNAME),Darwin)
$(error Please compile using CMake on Mac OS X)
endif
set -e; for d in ${DIRS}; do $(MAKE) -C $${d}; done
fuzzing : mosquitto
$(MAKE) -C fuzzing
clean :
set -e; for d in ${DIRS}; do $(MAKE) -C $${d} clean; done
set -e; for d in ${DOCDIRS}; do $(MAKE) -C $${d} clean; done
$(MAKE) -C test clean
$(MAKE) -C fuzzing clean
reallyclean :
set -e; for d in ${DIRS}; do $(MAKE) -C $${d} reallyclean; done

@ -13,6 +13,7 @@ LIBMOSQ:=${R}/lib/libmosquitto.a
endif
endif
LOCAL_LDFLAGS:=${LDFLAGS}
LOCAL_CPPFLAGS:=-I${R}/apps/mosquitto_passwd -I${R}/plugins/dynamic-security -DWITH_CJSON -I${R}/plugins/common -I${R}/common
ifeq ($(WITH_BUNDLED_DEPS),yes)
LOCAL_CPPFLAGS+=-I${R}/deps
@ -53,7 +54,7 @@ mosquitto_ctrl : ${OBJS} ${LIBMOSQ}
${CROSS_COMPILE}${CC} ${APP_LDFLAGS} $^ -o $@ $(PASSWD_LDADD) $(LOCAL_LDFLAGS) $(LIBMOSQ) -lcjson -ldl
mosquitto_ctrl_example.so : ${EXAMPLE_OBJS}
$(CROSS_COMPILE)$(CC) $(PLUGIN_CPPFLAGS) $(PLUGIN_CFLAGS) $(PLUGIN_LDFLAGS) -shared $< -o $@
$(CROSS_COMPILE)$(CC) $(PLUGIN_CPPFLAGS) $(PLUGIN_CFLAGS) $(PLUGIN_LDFLAGS) ${LOCAL_LDFLAGS} -shared $< -o $@
mosquitto_ctrl.o : mosquitto_ctrl.c mosquitto_ctrl.h
${CROSS_COMPILE}${CC} $(LOCAL_CPPFLAGS) $(APP_CPPFLAGS) $(APP_CFLAGS) -c $< -o $@

@ -18,7 +18,7 @@ all:
endif
mosquitto_passwd : ${OBJS}
${CROSS_COMPILE}${CC} ${APP_LDFLAGS} $^ -o $@ $(PASSWD_LDADD)
${CROSS_COMPILE}${CC} ${LDFLAGS} ${APP_LDFLAGS} $^ -o $@ $(PASSWD_LDADD)
mosquitto_passwd.o : mosquitto_passwd.c
${CROSS_COMPILE}${CC} $(APP_CPPFLAGS) $(APP_CFLAGS) -c $< -o $@

@ -22,22 +22,22 @@ static : static_pub static_sub static_rr
# libmosquitto only.
static_pub : pub_client.o pub_shared.o client_props.o client_shared.o ${R}/lib/libmosquitto.a
${CROSS_COMPILE}${CC} $^ -o mosquitto_pub ${CLIENT_LDFLAGS} ${STATIC_LIB_DEPS} ${CLIENT_STATIC_LDADD}
${CROSS_COMPILE}${CC} $^ -o mosquitto_pub ${LDFLAGS} ${CLIENT_LDFLAGS} ${STATIC_LIB_DEPS} ${CLIENT_STATIC_LDADD}
static_sub : sub_client.o sub_client_output.o client_props.o client_shared.o ${R}/lib/libmosquitto.a
${CROSS_COMPILE}${CC} $^ -o mosquitto_sub ${CLIENT_LDFLAGS} ${STATIC_LIB_DEPS} ${CLIENT_STATIC_LDADD}
${CROSS_COMPILE}${CC} $^ -o mosquitto_sub ${LDFLAGS} ${CLIENT_LDFLAGS} ${STATIC_LIB_DEPS} ${CLIENT_STATIC_LDADD}
static_rr : rr_client.o client_props.o client_shared.o pub_shared.o sub_client_output.o ${R}/lib/libmosquitto.a
${CROSS_COMPILE}${CC} $^ -o mosquitto_rr ${CLIENT_LDFLAGS} ${STATIC_LIB_DEPS} ${CLIENT_STATIC_LDADD}
${CROSS_COMPILE}${CC} $^ -o mosquitto_rr ${LDFLAGS} ${CLIENT_LDFLAGS} ${STATIC_LIB_DEPS} ${CLIENT_STATIC_LDADD}
mosquitto_pub : pub_client.o pub_shared.o client_shared.o client_props.o
${CROSS_COMPILE}${CC} $(CLIENT_LDFLAGS) $^ -o $@ $(CLIENT_LDADD)
${CROSS_COMPILE}${CC} ${LDFLAGS} $(CLIENT_LDFLAGS) $^ -o $@ $(CLIENT_LDADD)
mosquitto_sub : sub_client.o sub_client_output.o client_shared.o client_props.o
${CROSS_COMPILE}${CC} $(CLIENT_LDFLAGS) $^ -o $@ $(CLIENT_LDADD)
${CROSS_COMPILE}${CC} ${LDFLAGS} $(CLIENT_LDFLAGS) $^ -o $@ $(CLIENT_LDADD)
mosquitto_rr : rr_client.o client_shared.o client_props.o pub_shared.o sub_client_output.o
${CROSS_COMPILE}${CC} $(CLIENT_LDFLAGS) $^ -o $@ $(CLIENT_LDADD)
${CROSS_COMPILE}${CC} ${LDFLAGS} $(CLIENT_LDFLAGS) $^ -o $@ $(CLIENT_LDADD)
pub_client.o : pub_client.c ${SHARED_DEP}
${CROSS_COMPILE}${CC} $(CLIENT_CPPFLAGS) $(CLIENT_CFLAGS) -c $< -o $@

@ -135,6 +135,10 @@ WITH_OLD_KEEPALIVE=no
# Build with sqlite3 support - this enables the sqlite persistence plugin.
WITH_SQLITE=yes
# Build broker for fuzzing only - does not work as a normal broker. This is
# currently only suitable for use with oss-fuzz.
WITH_FUZZING=no
# =============================================================================
# End of user configuration
# =============================================================================
@ -424,6 +428,14 @@ ifeq ($(WITH_XTREPORT),yes)
BROKER_CFLAGS:=$(BROKER_CFLAGS) -DWITH_XTREPORT
endif
ifeq ($(WITH_FUZZING),yes)
MAKE_ALL:=$(MAKE_ALL) fuzzing
BROKER_CPPFLAGS:=$(BROKER_CPPFLAGS) -DWITH_FUZZING
BROKER_CFLAGS:=$(BROKER_CFLAGS) -fPIC
BROKER_LDFLAGS:=$(BROKER_LDFLAGS) -shared
LDFLAGS:=$(LDFLAGS) $(CFLAGS)
endif
BROKER_LDADD:=${BROKER_LDADD} ${LDADD}
CLIENT_LDADD:=${CLIENT_LDADD} ${LDADD}
PASSWD_LDADD:=${PASSWD_LDADD} ${LDADD}

@ -0,0 +1,9 @@
.PHONY: all clean
all:
./generate_packet_corpora.py
$(MAKE) -C broker $@
clean:
-rm -rf corpora/broker corpora/client corpora/broker_packet_seed_corpus.zip corpora/client_packet_seed_corpus.zip
$(MAKE) -C broker $@

@ -0,0 +1,26 @@
R=../..
.PHONY: all clean
FUZZERS:= \
broker_fuzz_initial_packet \
broker_fuzz_second_packet
LOCAL_CPPFLAGS:=$(CPPFLAGS) -I${R}/include/
LOCAL_CXXFLAGS:=$(CXXFLAGS) -g -Wall -Werror -pthread
LOCAL_LDFLAGS:=$(LDFLAGS)
LOCAL_LIBADD:=$(LIBADD) $(LIB_FUZZING_ENGINE) ${R}/src/mosquitto_broker.a -lssl -lcrypto -lcjson
all: $(FUZZERS)
broker_fuzz_initial_packet : broker_fuzz_initial_packet.cpp broker_fuzz.cpp
$(CXX) $(LOCAL_CXXFLAGS) $(LOCAL_CPPFLAGS) $(LOCAL_LDFLAGS) -o $@ $^ $(LOCAL_LIBADD)
install $@ ${OUT}/$@
cp ${R}/fuzzing/corpora/broker_packet_seed_corpus.zip $@_seed_corpus.zip
broker_fuzz_second_packet : broker_fuzz_second_packet.cpp broker_fuzz.cpp
$(CXX) $(LOCAL_CXXFLAGS) $(LOCAL_CPPFLAGS) $(LOCAL_LDFLAGS) -o $@ $^ $(LOCAL_LIBADD)
install $@ ${OUT}/$@
cp ${R}/fuzzing/corpora/broker_packet_seed_corpus.zip $@_seed_corpus.zip
clean:
rm -f *.o $(FUZZERS)

@ -0,0 +1,116 @@
/*
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 <arpa/inet.h>
#include <errno.h>
#include <netinet/in.h>
#include <pthread.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include "broker_fuzz.h"
/* The broker fuzz-only main function. */
extern "C" int mosquitto_fuzz_main(int argc, char *argv[]);
void *run_broker(void *args)
{
struct fuzz_data *fuzz = (struct fuzz_data *)args;
char *argv[4];
int argc = 4;
char buf[20];
argv[0] = strdup("mosquitto");
argv[1] = strdup("-q");
argv[2] = strdup("-p");
snprintf(buf, sizeof(buf), "%d", fuzz->port);
argv[3] = buf;
mosquitto_fuzz_main(argc, argv);
for(int i=0; i<3; i++){
free(argv[i]);
}
pthread_exit(NULL);
return NULL;
}
void recv_timeout(int sock, void *buf, size_t len, int timeout_us)
{
struct timeval tv = {0, timeout_us};
setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (const char *)&tv, sizeof(tv));
(void)recv(sock, buf, len, 0);
}
int connect_retrying(int port)
{
struct sockaddr_in addr;
int sock;
int rc;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = inet_addr("127.0.0.1");
sock = socket(AF_INET, SOCK_STREAM, 0);
for(int i=0; i<500; i++){ /* 500x10ms = 5 seconds max wait */
errno = 0;
rc = connect(sock, (struct sockaddr *)&addr, sizeof(addr));
if(rc < 0 && errno == ECONNREFUSED){
struct timespec ts;
ts.tv_sec = 0;
ts.tv_nsec = 10000000; /* 10ms */
nanosleep(&ts, NULL);
}else if(rc < 0){
return -1;
}else{
break;
}
}
return sock;
}
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
{
struct fuzz_data fuzz;
pthread_t thread;
if(size < kMinInputLength || size > kMaxInputLength){
return 0;
}
signal(SIGPIPE, SIG_IGN);
memset(&fuzz, 0, sizeof(fuzz));
fuzz.port = 1883;
fuzz.size = size;
fuzz.data = (uint8_t *)data;
pthread_create(&thread, NULL, run_broker, &fuzz);
run_client(&fuzz);
pthread_join(thread, NULL);
return 0;
}

@ -0,0 +1,35 @@
/*
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.
*/
#ifndef BROKER_FUZZ_H
#define BROKER_FUZZ_H
#define kMinInputLength 5
#define kMaxInputLength 10000
struct fuzz_data{
uint8_t *data;
size_t size;
uint16_t port;
};
void *run_broker(void *args);
void recv_timeout(int sock, void *buf, size_t len, int timeout_us);
int connect_retrying(int port);
void run_client(struct fuzz_data *fuzz);
#endif

@ -0,0 +1,56 @@
/*
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 <cerrno>
#include <cstdint>
#include <cstdlib>
#include <cstring>
#include <sys/socket.h>
#include <unistd.h>
#include "broker_fuzz.h"
/* Set to 0 to cause the broker to exit */
extern int g_run;
/*
* This tests the first packet being sent to the broker only, with no authentication.
*/
void run_client(struct fuzz_data *fuzz)
{
int sock;
uint8_t data[20];
size_t len;
sock = connect_retrying(fuzz->port);
if(sock < 0){
abort();
}
errno = 0;
len = send(sock, fuzz->data, fuzz->size, 0);
if(len < fuzz->size){
abort();
}
errno = 0;
recv_timeout(sock, data, sizeof(data), 100000);
close(sock);
g_run = 0;
}

@ -0,0 +1,70 @@
/*
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 <cstdlib>
#include <cstring>
#include <cstdint>
#include <cerrno>
#include <sys/socket.h>
#include <unistd.h>
#include "broker_fuzz.h"
extern int g_run;
/*
* This tests the second packet sent to the broker after the client has already
* connected, with no authentication.
*/
void run_client(struct fuzz_data *fuzz)
{
int sock;
const uint8_t connect_packet[] = {0x10, 0x0D, 0x00, 0x04, 0x4D, 0x51, 0x54, 0x54, 0x04, 0x02, 0x00, 0x0A, 0x00, 0x01, 0x70};
const uint8_t connack_packet[] = {0x20, 0x02, 0x00, 0x00};
uint8_t data[20];
size_t len;
sock = connect_retrying(fuzz->port);
if(sock < 0){
abort();
}
/* Do initial connect */
errno = 0;
len = send(sock, connect_packet, sizeof(connect_packet), 0);
if(len < 0){
abort();
}
/* And receive the CONNACK */
recv_timeout(sock, data, sizeof(connack_packet), 100000);
if(memcmp(data, connack_packet, sizeof(connack_packet))){
abort();
}
errno = 0;
len = send(sock, fuzz->data, fuzz->size, 0);
if(len < fuzz->size){
abort();
}
errno = 0;
recv_timeout(sock, data, sizeof(data), 100000);
close(sock);
g_run = 0;
}

@ -0,0 +1,57 @@
#!/usr/bin/env python3
from collections import deque
from os import mkdir,walk
from pathlib import Path
import json
import re
import shutil
def gen_packet_corpus(packet_type, input_path):
try:
mkdir("corpora")
except FileExistsError:
pass
try:
mkdir(f"corpora/{packet_type}")
except FileExistsError:
pass
data_path=Path(input_path)
rc = 0
sequences = []
for (_, _, filenames) in walk(data_path):
sequences.extend(filenames)
break
written_packets = {}
for seq in sorted(sequences):
if seq[-5:] != ".json":
continue
with open(data_path/seq, "r") as f:
test_file = json.load(f)
for g in test_file:
group_name = g["group"]
tests = g["tests"]
for t in tests:
tname = group_name + " " + t["name"]
fuzzi = 1
for m in t["msgs"]:
if m["type"] == "send" or m["type"] == "recv":
fname = re.sub(r'[ \[\]\(\)]+', '-', tname) + str(fuzzi) + ".raw"
payload = m["payload"].replace(" ", "")
# No duplicates please
if payload not in written_packets:
written_packets[payload] = 1
with open(f"corpora/{packet_type}/{fname}", "wb") as f:
f.write(bytes.fromhex(payload))
fuzzi += 1
shutil.make_archive(f"corpora/{packet_type}_packet_seed_corpus", 'zip', f"corpora/{packet_type}")
gen_packet_corpus("broker", "../test/broker/data")
gen_packet_corpus("client", "../test/lib/data")

@ -0,0 +1,30 @@
#!/bin/bash -eu
#
# 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.
# Build direct broker dependency - cJSON
# Note that other dependencies, i.e. sqlite are not yet built because they are
# only used by plugins and not currently otherwise used.
cd ${SRC}/cJSON
cmake -DBUILD_SHARED_LIBS=OFF -DENABLE_CJSON_TEST=OFF -DCMAKE_C_FLAGS=-fPIC .
make
make install
# Build broker and library static libraries
cd ${SRC}/mosquitto
make WITH_STATIC_LIBRARIES=yes WITH_DOCS=no WITH_FUZZING=yes

@ -0,0 +1,24 @@
#!/bin/bash -eu
#
# 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.
# Note that sqlite3 is required as a build dep of a plugin which is not
# currently part of fuzz testing. Once it is part of fuzz testing, sqlite will
# need to be built statically.
apt-get update && apt-get install -y libtool-bin make libsqlite3-dev
git clone https://github.com/DaveGamble/cJSON ${SRC}/cJSON

@ -3,7 +3,11 @@ include ${R}/config.mk
.PHONY: all install uninstall clean reallyclean
ifeq ($(WITH_FUZZING),yes)
all : mosquitto_broker.a
else
all : mosquitto
endif
OBJS= mosquitto.o \
alias_mosq.o \
@ -113,6 +117,9 @@ endif
mosquitto : ${OBJS}
${CROSS_COMPILE}${CC} ${BROKER_LDFLAGS} $^ -o $@ $(BROKER_LDADD)
mosquitto_broker.a : ${OBJS}
${CROSS_COMPILE}$(AR) cr $@ $^
mosquitto.o : mosquitto.c mosquitto_broker_internal.h
${CROSS_COMPILE}${CC} $(BROKER_CPPFLAGS) $(BROKER_CFLAGS) -c $< -o $@
@ -429,7 +436,7 @@ uninstall :
-rm -f "${DESTDIR}${prefix}/include/mosquitto_plugin.h"
clean :
-rm -f *.o mosquitto *.gcda *.gcno
-rm -f *.o mosquitto mosquitto_broker.a *.gcda *.gcno
reallyclean : clean
-rm -rf *.orig *.db

@ -296,7 +296,11 @@ static void report_features(void)
}
#ifdef WITH_FUZZING
int mosquitto_fuzz_main(int argc, char *argv[])
#else
int main(int argc, char *argv[])
#endif
{
struct mosquitto__config config;
int rc;

Loading…
Cancel
Save