Skip to content

Commit

Permalink
Merge remote-tracking branch 'upstream/master'
Browse files Browse the repository at this point in the history
  • Loading branch information
Romain Tessier committed Jul 1, 2024
2 parents 3cc9326 + 067c01a commit 4d5f164
Show file tree
Hide file tree
Showing 44 changed files with 658 additions and 227 deletions.
5 changes: 5 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,11 @@ if(BUILD_BENCHMARK)
add_subdirectory(tools/benchmark)
endif()

option(BUILD_FUZZ "Build fuzz harness" OFF)
if(BUILD_FUZZ)
add_subdirectory(tools/fuzz-harness)
endif()

# interface library for convenience
get_property(_components GLOBAL PROPERTY VANETZA_COMPONENTS)
add_library(vanetza INTERFACE)
Expand Down
27 changes: 27 additions & 0 deletions docs/tools/fuzz-harness.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# fuzz-harness

The fuzz harness for Vanetza is a testing tool designed to identify bugs, vulnerabilities, and unexpected behaviors within Vanetza's codebase. Fuzz testing, also known as fuzzing, involves providing invalid, unexpected, or random data as input to a program to uncover errors or security issues.

## Requirements

You will need AFL++ installed on your system.
Please refer to the [AFL++ documentation](https://aflplus.plus/docs/install/) for installation instructions.
Alternatively, you can use the scripts located at *tools/fuzz-harness* for running fuzz tests in a Docker container.

## Usage

Running the script *fuzz-harness/docker.sh* will build a suitable Docker container based on the official *aflplusplus/aflplusplus* image.
As soon as the container is ready, the script launches the built container and maps your local user and some Vanetza directories into it.

Within the container, you can compile the *fuzz-harness* using the AFL++ toolchain by invoking the *compile.sh* script.
The *fuzz.sh* script is a convenient way to run the built harness with *afl-fuzz*.
If it crashes immediately, try again a few times.

### Analyse

Fuzzing is executing the *fuzzing-persistent* executable.
You can use its sibling *fuzzing-run* to investigate a particular crash and get more information about the possible problems.
The address sanitizer is enabled by default. If you're not interested in memory leaks, make sure to disable the leak sanatizer by setting the environment variable `ASAN_OPTIONS=detect_leaks=0`.

You may also want to classify the found issues using *casr-afl*: `casr-afl -i output -o output/casr`
The classification process also eliminates duplicate issues.
14 changes: 8 additions & 6 deletions tools/benchmark/cases/security/validation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ int SecurityValidationCase::execute()

std::vector<std::unique_ptr<v2::CertificateProvider>> providers;
std::vector<std::unique_ptr<SecurityEntity>> entities;
std::vector<v2::SecuredMessage> secured_messages(identities);
std::vector<SecuredMessage> secured_messages(identities);

for (unsigned i = 0; i < identities; i++) {
providers.emplace_back(new v2::NaiveCertificateProvider(runtime));
Expand Down Expand Up @@ -83,16 +83,18 @@ int SecurityValidationCase::execute()
encap_request.its_aid = aid::CA;

EncapConfirm encap_confirm = entities[i]->encapsulate_packet(std::move(encap_request));
secured_messages.push_back(boost::get<v2::SecuredMessage>(encap_confirm.sec_packet));
auto signer_info = secured_messages[i].header_field<v2::HeaderFieldType::Signer_Info>();

auto v2_sec_msg = boost::get<v2::SecuredMessage>(encap_confirm.sec_packet);
auto signer_info = v2_sec_msg.header_field<v2::HeaderFieldType::Signer_Info>();
if (signer_info_type == "hash") {
assert(signer_info && get_type(*signer_info) == v2::SignerInfoType::Certificate_Digest_With_SHA256);
} else if (signer_info_type == "certificate") {
assert(signer_info && get_type(*signer_info) == v2::SignerInfoType::Certificate);
} else if (signer_info_type == "chain") {
assert(signer_info && get_type(*signer_info) == v2::SignerInfoType::Certificate_Chain);
}

secured_messages.push_back(v2_sec_msg);
}

std::mt19937 gen(0);
Expand All @@ -101,8 +103,8 @@ int SecurityValidationCase::execute()
std::cout << "Starting benchmark for messages ... ";

for (unsigned i = 0; i < messages; i++) {
SecuredMessage copy = secured_messages[dis(gen)];
auto decap_confirm = security_entity.decapsulate_packet(std::move(copy));
DecapRequest decap_request { SecuredMessageView { secured_messages[dis(gen)] }};
auto decap_confirm = security_entity.decapsulate_packet(std::move(decap_request));
assert(decap_confirm.report == DecapReport::Success);
}

Expand Down
1 change: 1 addition & 0 deletions tools/fuzz-harness/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
output/
5 changes: 5 additions & 0 deletions tools/fuzz-harness/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
add_executable(fuzzing-persistent router_fuzzing_context.cpp persistent.cpp)
target_link_libraries(fuzzing-persistent PUBLIC vanetza)

add_executable(fuzzing-run router_fuzzing_context.cpp run.cpp)
target_link_libraries(fuzzing-run PUBLIC vanetza)
15 changes: 15 additions & 0 deletions tools/fuzz-harness/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
ARG VERSION=latest
FROM aflplusplus/aflplusplus:${VERSION}

# install build dependencies for Vanetza
RUN apt-get update && apt-get install --no-install-recommends -y \
libboost-all-dev libcrypto++-dev libgeographic-dev libssl-dev

# install casr-afl tool
RUN cargo install --root /usr/local casr

# set up "fuzz" user and mapping of host user
RUN useradd -m -s /bin/bash fuzz
RUN cp /root/.bashrc /home/fuzz/.bashrc && chown fuzz:fuzz /home/fuzz/.bashrc
COPY docker-entrypoint.sh /docker-entrypoint.sh
ENTRYPOINT ["/docker-entrypoint.sh"]
24 changes: 24 additions & 0 deletions tools/fuzz-harness/compile.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#!/bin/bash -eu

if [[ ! -d "/AFLplusplus" ]] ; then
echo "This script shall be run inside the AFL++ container"
exit 1
fi

cd /home/fuzz

export CC=${CC:=afl-clang-lto}
export CXX=${CXX:=afl-clang-lto++}

export AFL_LLVM_CMPLOG=1
mkdir -p build/cmplog
cmake -S source -B build/cmplog -G Ninja -DBUILD_FUZZ=ON
cmake --build build/cmplog
unset AFL_LLVM_CMPLOG

# see https://aflplus.plus/docs/env_variables/ for supported environment variables
export AFL_USE_ASAN=1
export AFL_USE_UBSAN=1
mkdir -p build/asan
cmake -S source -B build/asan -G Ninja -DBUILD_FUZZ=ON
cmake --build build/asan
11 changes: 11 additions & 0 deletions tools/fuzz-harness/docker-entrypoint.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#!/bin/bash -eu
afl-system-config

usermod -u ${HOST_USER_ID} -g ${HOST_GROUP_ID} fuzz
ln -sf /source /home/fuzz/source
ln -sf /input /home/fuzz/input
ln -sf /output /home/fuzz/output
ln -sf /source/tools/fuzz-harness/compile.sh /home/fuzz/compile.sh
ln -sf /source/tools/fuzz-harness/fuzz.sh /home/fuzz/fuzz.sh
cd /home/fuzz
su fuzz
15 changes: 15 additions & 0 deletions tools/fuzz-harness/docker.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#!/bin/bash -eu
HARNESS_DIR=$(realpath $(dirname $0))
SOURCE_DIR=$HARNESS_DIR/../..

docker build $HARNESS_DIR
IMAGE=$(docker build -q $HARNESS_DIR)

mkdir -p $HARNESS_DIR/output
docker run --rm -it \
--security-opt seccomp=unconfined \
-v$SOURCE_DIR:/source:ro \
-v$HARNESS_DIR/input:/input:ro \
-v$HARNESS_DIR/output:/output \
-e HOST_USER_ID=$(id -u) -e HOST_GROUP_ID=$(id -g) \
$IMAGE
5 changes: 5 additions & 0 deletions tools/fuzz-harness/fuzz.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#!/bin/bash -eu
: ${FUZZ_INPUT:="$HOME/input"}
: ${FUZZ_OUTPUT:="$HOME/output"}
: ${FUZZ_BUILD:="$HOME/build"}
afl-fuzz -i $FUZZ_INPUT -o $FUZZ_OUTPUT -c $FUZZ_BUILD/cmplog/bin/fuzzing-persistent -m none -- $FUZZ_BUILD/asan/bin/fuzzing-persistent
Binary file added tools/fuzz-harness/input/cam_v3_certificate.dat
Binary file not shown.
34 changes: 34 additions & 0 deletions tools/fuzz-harness/persistent.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#include "router_fuzzing_context.hpp"
#include <stdio.h>
#include <unistd.h>

#ifndef __AFL_FUZZ_TESTCASE_LEN
ssize_t fuzz_len;
#define __AFL_FUZZ_TESTCASE_LEN fuzz_len
unsigned char fuzz_buf[1024000];
#define __AFL_FUZZ_TESTCASE_BUF fuzz_buf
#define __AFL_FUZZ_INIT() void sync(void);
#define __AFL_LOOP(x) ((fuzz_len = read(0, fuzz_buf, sizeof(fuzz_buf))) > 0 ? 1 : 0)
#define __AFL_INIT() sync()
#endif

__AFL_FUZZ_INIT();

int main()
{
#ifdef __AFL_HAVE_MANUAL_CONTROL
__AFL_INIT();
#endif

vanetza::RouterFuzzingContext context;

unsigned char *buf = __AFL_FUZZ_TESTCASE_BUF;
while (__AFL_LOOP(10000)) {
int len = __AFL_FUZZ_TESTCASE_LEN;
vanetza::ByteBuffer buffer { buf, buf + len };
context.initialize();
context.indicate(std::move(buffer));
}

return 0;
}
44 changes: 44 additions & 0 deletions tools/fuzz-harness/router_fuzzing_context.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#include "router_fuzzing_context.hpp"

namespace vanetza
{

class FuzzingRequestInterface : public dcc::RequestInterface
{
void request(const dcc::DataRequest&, std::unique_ptr<ChunkPacket>) override {}
};

class FuzzingTransportInterface : public geonet::TransportInterface
{
void indicate(const geonet::DataIndication&, std::unique_ptr<geonet::UpPacket>) override {}
};

RouterFuzzingContext::RouterFuzzingContext() :
runtime(vanetza::Clock::at("2010-12-23 18:29")), security(runtime),
req_ifc(std::make_unique<FuzzingRequestInterface>()),
ind_ifc(std::make_unique<FuzzingTransportInterface>())
{
initialize();
}

void RouterFuzzingContext::initialize()
{
router = std::make_unique<geonet::Router>(runtime, mib);
router->set_security_entity(&security.entity());
router->set_access_interface(req_ifc.get());
router->set_transport_handler(geonet::UpperProtocol::BTP_B, ind_ifc.get());

geonet::Address gn_addr;
gn_addr.mid(MacAddress{0, 0, 0, 0, 0, 1});
router->set_address(gn_addr);
}

void RouterFuzzingContext::indicate(ByteBuffer&& buffer)
{
MacAddress source { 0, 0, 0, 0, 0, 2 };
MacAddress destination { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
auto packet = std::make_unique<geonet::UpPacket>(CohesivePacket { std::move(buffer), OsiLayer::Network });
router->indicate(std::move(packet), source, destination);
}

} // namespace vanetza
30 changes: 30 additions & 0 deletions tools/fuzz-harness/router_fuzzing_context.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#ifndef VANETZA_ROUTER_FUZZING_CONTEXT_HPP
#define VANETZA_ROUTER_FUZZING_CONTEXT_HPP

#include <vanetza/common/manual_runtime.hpp>
#include <vanetza/dcc/interface.hpp>
#include <vanetza/geonet/router.hpp>
#include <vanetza/geonet/transport_interface.hpp>
#include <vanetza/geonet/tests/security_context.hpp>

namespace vanetza
{

class RouterFuzzingContext {
public:
RouterFuzzingContext();
void initialize();
void indicate(ByteBuffer&& buffer);

private:
ManualRuntime runtime;
geonet::ManagementInformationBase mib;
std::unique_ptr<geonet::Router> router;
std::unique_ptr<dcc::RequestInterface> req_ifc;
std::unique_ptr<geonet::TransportInterface> ind_ifc;
SecurityContext security;
};

} // namespace vanetza

#endif //VANETZA_ROUTER_FUZZING_CONTEXT_HPP
42 changes: 42 additions & 0 deletions tools/fuzz-harness/run.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
#include "router_fuzzing_context.hpp"
#include <iostream>
#include <fstream>

vanetza::ByteBuffer readFileIntoBuffer(const std::string &filename)
{
std::ifstream file(filename, std::ios::binary | std::ios::ate);
if (!file.is_open()) {
std::cerr << "Error opening file: " << filename << std::endl;
return {};
}

const std::streamsize size = file.tellg();
file.seekg(0, std::ios::beg);

vanetza::ByteBuffer buffer(size);
if (!file.read(reinterpret_cast<char *>(buffer.data()), size)) {
std::cerr << "Error reading file: " << filename << std::endl;
return {};
}

return buffer;
}

int main(int argc, char* argv[])
{
if (argc != 2) {
std::cerr << "Usage: " << argv[0] << " <filepath>" << std::endl;
return 1;
}

const std::string filename = argv[1];
vanetza::ByteBuffer buffer = readFileIntoBuffer(filename);

if (buffer.empty()) {
return 1;
}

vanetza::RouterFuzzingContext context;
context.indicate(std::move(buffer));
return 0;
}
1 change: 1 addition & 0 deletions vanetza/asn1/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,7 @@ function(add_asn1_component NAME)
endfunction()

add_asn1_component(support)
target_sources(asn1_support PRIVATE memory.c)
add_asn1_component(its)
add_asn1_component(security)
add_asn1_component(pki)
Expand Down
27 changes: 27 additions & 0 deletions vanetza/asn1/memory.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#include <stdlib.h>

static size_t max_size = 4096;

void* vanetza_asn1c_malloc(size_t size)
{
if (size <= max_size)
return malloc(size);
else
return NULL;
}

void* vanetza_asn1c_calloc(size_t nmemb, size_t size)
{
if (nmemb * size <= max_size)
return calloc(nmemb, size);
else
return NULL;
}

void* vanetza_asn1c_realloc(void* ptr, size_t size)
{
if (size <= max_size)
return realloc(ptr, size);
else
return NULL;
}
10 changes: 10 additions & 0 deletions vanetza/asn1/memory.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#ifndef VANETZA_ASN1C_MEMORY
#define VANETZA_ASN1C_MEMORY

#include <stddef.h>

void* vanetza_asn1c_malloc(size_t size);
void* vanetza_asn1c_calloc(size_t nmemb, size_t size);
void* vanetza_asn1c_realloc(void* ptr, size_t size);

#endif /* VANETZA_ASN1C_MEMORY */
7 changes: 4 additions & 3 deletions vanetza/asn1/support/asn_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,10 @@ extern "C" {
#define ASN1C_ENVIRONMENT_VERSION 923 /* Compile-time version */
int get_asn1c_environment_version(void); /* Run-time version */

#define CALLOC(nmemb, size) calloc(nmemb, size)
#define MALLOC(size) malloc(size)
#define REALLOC(oldptr, size) realloc(oldptr, size)
#include <vanetza/asn1/memory.h>
#define CALLOC(nmemb, size) vanetza_asn1c_calloc(nmemb, size)
#define MALLOC(size) vanetza_asn1c_malloc(size)
#define REALLOC(oldptr, size) vanetza_asn1c_realloc(oldptr, size)
#define FREEMEM(ptr) free(ptr)

#define asn_debug_indent 0
Expand Down
Loading

0 comments on commit 4d5f164

Please sign in to comment.