Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fuzz: add fuzzer targeting sample deserialization #1940

Merged
merged 1 commit into from Mar 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions fuzz/CMakeLists.txt
Expand Up @@ -17,4 +17,5 @@ include("${CMAKE_SOURCE_DIR}/cmake/Modules/Generate.cmake")
add_subdirectory(fuzz_config_init)
add_subdirectory(fuzz_handle_rtps_message)
add_subdirectory(fuzz_type_object)
add_subdirectory(fuzz_sample_deser)
# add_subdirectory(fuzz_idlc)
20 changes: 20 additions & 0 deletions fuzz/fuzz_sample_deser/CMakeLists.txt
@@ -0,0 +1,20 @@
cmake_minimum_required(VERSION 3.5)
project(fuzz_sample_deser LANGUAGES C)

if(NOT TARGET CycloneDDS::ddsc)
# Find the CycloneDDS package.
find_package(CycloneDDS REQUIRED)
endif()

idlc_generate(TARGET fuzz_sample FILES fuzz_sample.idl WARNINGS no-implicit-extensibility)
add_executable(fuzz_sample_deser fuzz_sample_deser.c fuzz_sample.c)
target_include_directories(
fuzz_sample_deser PRIVATE
"$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}>/"
"$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/../../src/core/ddsc/src>"
"$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/../../src/core/cdr/include>"
"$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/../../src/core/ddsi/include>"
"$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/../../src/core/ddsi/src>")

target_compile_options(fuzz_sample_deser PRIVATE -fsanitize=fuzzer,address -g)
target_link_libraries(fuzz_sample_deser fuzz_sample CycloneDDS::ddsc -fsanitize=fuzzer,address)
35 changes: 35 additions & 0 deletions fuzz/fuzz_sample_deser/fuzz_sample_deser.c
@@ -0,0 +1,35 @@
#include <ddsi__serdata_cdr.h>
#include <dds/ddsrt/heap.h>
#include <string.h>

#include "fuzz_samples.h"

static void __attribute__((constructor)) print_idl_types_seed() {
printf("IDL types seed: %s\n", idl_types_seed);
}

static void topic_to_descriptor(struct dds_cdrstream_desc *desc, const dds_topic_descriptor_t *t) {
memset(desc, 0, sizeof(struct dds_cdrstream_desc));
dds_cdrstream_desc_init(desc, &dds_cdrstream_default_allocator, t->m_size, t->m_align, t->m_flagset, t->m_ops, NULL, 0);
}

int LLVMFuzzerTestOneInput(void *data, size_t size)
{
uint32_t actual_size;

for(size_t i = 0; i < sizeof(fixed_types)/sizeof(fixed_types[0]); i++) {
const struct dds_topic_descriptor *topic = fixed_types[i];
struct dds_cdrstream_desc desc;
topic_to_descriptor(&desc, topic);
if (dds_stream_normalize(data, (uint32_t) size, false, DDSI_RTPS_CDR_ENC_VERSION_2, &desc, false, &actual_size)) {
void *sample = ddsrt_calloc(1, desc.size);
dds_istream_t is;
dds_istream_init(&is, (uint32_t) size, data, DDSI_RTPS_CDR_ENC_VERSION_2);
dds_stream_read_sample(&is, sample, &dds_cdrstream_default_allocator, &desc);
dds_stream_free_sample(sample, &dds_cdrstream_default_allocator, desc.ops.ops);
ddsrt_free(sample);
}
dds_cdrstream_desc_fini(&desc, &dds_cdrstream_default_allocator);
}
return 0;
}
54 changes: 54 additions & 0 deletions fuzz/fuzz_sample_deser/generate_idl.py
@@ -0,0 +1,54 @@
#!/usr/bin/env python

import sys
import os
from fuzz_tools.rand_idl.creator import generate_random_types, generate_random_idl
from fuzz_tools.rand_idl.value import generate_random_instance
from fuzz_tools.rand_idl.compile import compile_idl

def die(code, s):
print(s, file=sys.stderr)
sys.exit(code)

MODULE_NAME = "Fuzz"
CORPUS="fuzz_sample_deser_seed_corpus"
if __name__ == '__main__':
if len(sys.argv) != 3:
die(0, "Usage: {} <seed> <target_directory>")
seed = int(sys.argv[1], 16)
directory = sys.argv[2]
if not os.path.isdir(directory):
die(-1, f"{directory} is not a directory")

# Generate random idl
scope = generate_random_types(MODULE_NAME, number=25, seed=seed)
idl_text = generate_random_idl(scope)
with open(os.path.join(directory, "fuzz_sample.idl"), "w") as f:
f.write(idl_text)

# Hacky way to identify top-level types.
toplvltypes = [e for e in scope.entities if getattr(e, "extensibility", False)]
# Generate fuzz_samples.h, collecting all generated types
with open(os.path.join(directory, "fuzz_samples.h"), "w") as f:
f.write("#include \"fuzz_sample.h\"\n")
f.write("static const char *idl_types_seed = \"{}\";".format(sys.argv[1]))
f.write("static const struct dds_topic_descriptor *fixed_types[] = {\n")
for entity in toplvltypes:
entry = "&{}_{}_desc".format(MODULE_NAME, entity.name)
if entity != scope.entities[-1]:
f.write(f"\t{entry},\n")
else:
f.write(f"\t{entry}\n")
f.write("};\n")

# Generate initial corpus
imported, tdir = compile_idl(idl_text, MODULE_NAME)
corpus = os.path.join(directory, CORPUS)
if not os.path.isdir(corpus):
os.mkdir(corpus)
for entity in toplvltypes:
t = getattr(imported.__fuzzytypes, entity.name)
sample = generate_random_instance(t, seed=seed)
fname = "seed_{}".format(entity.name)
with open(os.path.join(corpus, fname), "wb") as f:
f.write(sample.serialize())
34 changes: 34 additions & 0 deletions fuzz/fuzz_sample_deser/prepare.sh
@@ -0,0 +1,34 @@
#!/bin/bash -eu

prepare_fuzz_deser() {
apt-get -y install python3 python3-pip libssl-dev
mkdir -p build_python install
export CYCLONEDDS_HOME="$(pwd)/install"
fuzzer="$(pwd)/fuzz/fuzz_sample_deser"
cd build_python

# This builds cyclone for the python tool, the fuzzer is built later
cmake \
-DBUILD_IDLC=ON \
-DBUILD_TESTING=OFF \
-DBUILD_SHARED_LIBS=ON \
-DBUILD_EXAMPLES=NO \
-DENABLE_SECURITY=NO \
-DENABLE_SSL=NO \
-DCMAKE_POSITION_INDEPENDENT_CODE=ON \
-DCMAKE_INSTALL_PREFIX=../install ..
cmake --build .
cmake --build . --target install

rm -rf cyclonedds-python
git clone --depth=1 https://github.com/eclipse-cyclonedds/cyclonedds-python.git
pip3 install ./cyclonedds-python
ln -sf "$(pwd)/cyclonedds-python/tests/support_modules/fuzz_tools" "${fuzzer}/fuzz_tools"

# Use current git HEAD hash as seed
seed=$(git ls-remote https://github.com/eclipse-cyclonedds/cyclonedds HEAD |cut -f1)
PATH=${CYCLONEDDS_HOME}/bin:$PATH python3 "${fuzzer}/generate_idl.py" $seed "${fuzzer}"
}

export -f prepare_fuzz_deser
env -u CFLAGS -u CXXFLAGS -u LIB_FUZZING_ENGINE bash -euc prepare_fuzz_deser
1 change: 1 addition & 0 deletions fuzz/oss-fuzz-build.sh
Expand Up @@ -12,6 +12,7 @@
# SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
#

source fuzz/fuzz_sample_deser/prepare.sh
(
mkdir build
cd build
Expand Down