Support for initial fuzzing through oss-fuzz
parent
7c8af215ad
commit
5fb4b05d8f
@ -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
|
Loading…
Reference in New Issue