Skip to content

Commit

Permalink
Add sequence which supports fuzzing (#2)
Browse files Browse the repository at this point in the history
  • Loading branch information
TinyTinni authored Mar 19, 2024
1 parent 561ef85 commit 4707c22
Show file tree
Hide file tree
Showing 11 changed files with 298 additions and 7 deletions.
5 changes: 4 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -61,5 +61,8 @@ add_subdirectory(src/)
include(CTest)
enable_testing()
if(FAIRYCAM_ENABLE_TESTING)
add_subdirectory(tests/)
add_subdirectory(tests)
if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang" OR "${CMAKE_CXX_COMPILER_ID}" MATCHES "MSVC" AND NOT APPLE)
add_subdirectory(fuzzer_example)
endif()
endif()
27 changes: 27 additions & 0 deletions fuzzer_example/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@

find_package(OpenCV REQUIRED)

set(FUZZ_RUNTIME
1
CACHE STRING "Number of seconds to run fuzz tests during ctest run")


add_executable(Example_Fuzzer
main.cpp
)

target_include_directories(Example_Fuzzer PUBLIC ${OpenCV_INCLUDE_DIRS} )
target_link_libraries(Example_Fuzzer PRIVATE FairyCam)

target_compile_features(Example_Fuzzer PUBLIC cxx_std_20)

target_link_libraries(Example_Fuzzer PRIVATE -coverage -fsanitize=fuzzer)
if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")
target_compile_options(Example_Fuzzer PRIVATE -fsanitize=fuzzer)
target_link_libraries(Example_Fuzzer PUBLIC -fsanitize=address,undefined)
elseif("${CMAKE_CXX_COMPILER_ID}" MATCHES "MSVC")
target_compile_options(Example_Fuzzer PRIVATE /fsanitize=fuzzer)
target_compile_definitions(Example_Fuzzer PRIVATE "-D_SILENCE_ALL_CXX17_DEPRECATION_WARNINGS")
endif()

add_test(NAME Example_Fuzzer COMMAND Example_Fuzzer -max_total_time=${FUZZ_RUNTIME} -timeout=${FUZZ_RUNTIME})
45 changes: 45 additions & 0 deletions fuzzer_example/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#include <cstdint>
#include <span>

#include <AnyCamera.hpp>
#include <MemoryCamera.hpp>
#include <chaos/BinaryBlobSequence.hpp>
#include <chaos/ChaosCamera.hpp>

void my_system(FairyCam::IsAnyCamera auto cam)
{
while (true)
{
try
{
if (!cam.isOpened())
cam.open(0, 0, {});
cv::Mat m;
cam.read(m);
}
catch (const std::exception &)
{
// custom error handling
}
}
}

extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
{
FairyCam::MemoryCamera cam(
{.images = {cv::Mat(1, 1, CV_8UC1)}, .circular = true});

auto span = std::span(data, data + size);
auto chaosCam = FairyCam::ChaosCamera(std::move(cam),
FairyCam::BinaryBlobSequence(span));
try
{
my_system(std::move(chaosCam));
}
catch (FairyCam::SequenceEndException)
{
// this exception indicated, that the program should stop
}

return 0;
}
3 changes: 2 additions & 1 deletion src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,15 @@ set (HEADERS
"chaos/Exceptions.hpp"
"chaos/Sequence.hpp"
"chaos/BernoulliSequence.hpp"
"chaos/BinaryBlobSequence.hpp"
"chaos/NoErrorSequence.hpp"
)

add_library(FairyCam STATIC
HttpCamera.cpp
DirectoryTriggerCamera.cpp
FileCamera.cpp
MemoryCamera.cpp

${HEADERS}
)

Expand Down
3 changes: 1 addition & 2 deletions src/chaos/BernoulliSequence.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,7 @@ class BernoulliSequence : public Sequence
};

BernoulliSequence(Options opts)
: m_gen{std::random_device{}()}, m_is_open{std::move(opts.isOpen)},
m_retrieve{std::move(opts.retrieve)}, m_grab{std::move(opts.grab)}
: BernoulliSequence(std::random_device{}(), std::move(opts))
{
}

Expand Down
80 changes: 80 additions & 0 deletions src/chaos/BinaryBlobSequence.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
#pragma once

#include <cassert>
#include <concepts>
#include <cstdint>
#include <functional>
#include <span>
#include <vector>

#include "Exceptions.hpp"
#include "Sequence.hpp"

namespace FairyCam
{

class BinaryBlobSequence : public Sequence
{
std::span<const uint8_t> m_data;
std::span<const uint8_t>::iterator m_iter;

struct FailException
{
std::vector<std::function<void()>> exceptions;
};

FailException m_open;
FailException m_grab;
FailException m_retrieve;

void check(const FailException &exp)
{
if (m_iter == m_data.end())
throw SequenceEndException{};
uint8_t val = *m_iter;
++m_iter;
if (val == 0)
return;
std::invoke(exp.exceptions[val % exp.exceptions.size()]);
}

public:
class Fail
{
std::vector<std::function<void()>> exceptions;
friend BinaryBlobSequence;

public:
template <std::default_initializable ExceptionT> Fail &with()
{
exceptions.push_back([]() { throw ExceptionT{}; });
return *this;
}
};

struct Options
{
Fail isOpen = Fail().with<NotOpenException>();
Fail grab = Fail().with<GrabException>();
Fail retrieve = Fail().with<RetrieveException>();
};

BinaryBlobSequence(std::span<const uint8_t> data, Options opts)
: m_data(data), m_iter(m_data.begin()),
m_open{std::move(opts.isOpen.exceptions)},
m_grab{std::move(opts.grab.exceptions)},
m_retrieve{std::move(opts.retrieve.exceptions)}
{
}

BinaryBlobSequence(std::span<const uint8_t> data)
: BinaryBlobSequence(data, Options())
{
}

void checkIsOpen() override { check(m_open); }
void checkRetrieve() override { check(m_retrieve); }
void checkGrab() override { check(m_grab); }
};

} // namespace FairyCam
4 changes: 4 additions & 0 deletions src/chaos/Exceptions.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@
namespace FairyCam
{

class SequenceEndException
{
};

class NotOpenException : public std::runtime_error
{
public:
Expand Down
14 changes: 14 additions & 0 deletions src/chaos/NoErrorSequence.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#pragma once

#include "Sequence.hpp"

namespace FairyCam
{

class NoErrorSequence : public Sequence
{
virtual void checkIsOpen(){};
virtual void checkRetrieve(){};
virtual void checkGrab(){};
};
} // namespace FairyCam
3 changes: 2 additions & 1 deletion tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
find_package(OpenCV REQUIRED)
find_package(Poco REQUIRED Foundation Net)

add_executable(Tests main.cpp
add_executable(Tests
main.cpp
FileCamera.cpp
DirectoryCamera.cpp
Expand All @@ -12,6 +12,7 @@ add_executable(Tests main.cpp
chaos/BernoulliSequence.cpp
chaos/ChaosCamera.cpp
MemoryCamera.cpp
chaos/BinaryBlobSequence.cpp
)

target_include_directories(Tests PUBLIC ${OpenCV_INCLUDE_DIRS} )
Expand Down
5 changes: 3 additions & 2 deletions tests/HttpCamera.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -107,12 +107,13 @@ TEST_SUITE("HttpCamera")
REQUIRE(sendFileImage(client, TEST_DATA "test.png"));
}

cv::Mat m;
for (int i = 0; i < n_images; ++i)
{
cv::Mat m;
REQUIRE(cam.read(m));
CHECK(!m.empty());
}
cv::Mat m;
REQUIRE(!cam.read(m));
CHECK(m.empty());
}
Expand Down Expand Up @@ -155,7 +156,6 @@ TEST_SUITE("HttpCamera")
REQUIRE(cam.open(0, 0, {}));
REQUIRE(cam.isOpened());

cv::Mat m;
for (auto i = 0; i < n_images; ++i)
{
REQUIRE(sendFileImage(client, TEST_DATA "test.png"));
Expand All @@ -170,6 +170,7 @@ TEST_SUITE("HttpCamera")
CHECK(response.getStatus() == Poco::Net::HTTPResponse::HTTP_OK);

// no images are left in the queue
cv::Mat m;
REQUIRE(!cam.read(m));
CHECK(m.empty());
}
Expand Down
116 changes: 116 additions & 0 deletions tests/chaos/BinaryBlobSequence.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
#include <array>
#include <concepts>
#include <doctest.h>
#include <span>
#include <vector>

#include <chaos/BinaryBlobSequence.hpp>

using namespace FairyCam;

TEST_SUITE("BinaryBlobSequence")
{
TEST_CASE("always or never")
{
// for (auto [failIsOpen, failGrab, failRetrieve] :
// std::views::cartesian_product(
// std::array{0,1}, std::array{0,1}, std::array{0,1}))
for (auto [failIsOpen, failGrab, failRetrieve] : std::vector{
std::array{0, 0, 0}, std::array{0, 0, 1}, std::array{0, 1, 0},
std::array{0, 1, 1}, std::array{1, 0, 0}, std::array{1, 0, 1},
std::array{1, 1, 0}, std::array{1, 1, 1}})
{
CAPTURE(failIsOpen);
CAPTURE(failGrab);
CAPTURE(failRetrieve);

std::array blob = {static_cast<uint8_t>(failIsOpen * 255),
static_cast<uint8_t>(failGrab * 255),
static_cast<uint8_t>(failRetrieve * 255)};

BinaryBlobSequence s(std::span(blob.data(), blob.size()));

if (failIsOpen)
REQUIRE_THROWS_AS(s.checkIsOpen(), NotOpenException);
else
s.checkIsOpen();

if (failGrab)
REQUIRE_THROWS_AS(s.checkGrab(), GrabException);
else
s.checkGrab();

if (failRetrieve)
REQUIRE_THROWS_AS(s.checkRetrieve(), RetrieveException);
else
s.checkRetrieve();

REQUIRE_THROWS_AS(s.checkRetrieve(), SequenceEndException);
REQUIRE_THROWS_AS(s.checkGrab(), SequenceEndException);
REQUIRE_THROWS_AS(s.checkIsOpen(), SequenceEndException);
}
}

TEST_CASE("custom exceptions")
{
struct CustomException1
{
};

struct CustomException2
{
};

// for (auto [failIsOpen, failGrab, failRetrieve] :
// std::views::cartesian_product(
// std::array{0,1}, std::array{0,1}, std::array{0,1}))

for (auto [failIsOpen, failGrab, failRetrieve] : std::vector{
std::array{0, 0, 0}, std::array{0, 0, 1}, std::array{0, 1, 0},
std::array{0, 1, 1}, std::array{1, 0, 0}, std::array{1, 0, 1},
std::array{1, 1, 0}, std::array{1, 1, 1}})
{
CAPTURE(failIsOpen);
CAPTURE(failGrab);
CAPTURE(failRetrieve);

std::array blob = {static_cast<uint8_t>(failIsOpen * 255),
static_cast<uint8_t>(failGrab * 255),
static_cast<uint8_t>(failRetrieve * 254),
static_cast<uint8_t>(failRetrieve * 255)};

BinaryBlobSequence s(
std::span(blob.data(), blob.size()),
{.isOpen = BinaryBlobSequence::Fail().with<CustomException1>(),
.grab = BinaryBlobSequence::Fail().with<CustomException2>(),
.retrieve = BinaryBlobSequence::Fail()
.with<CustomException1>()
.with<CustomException2>()});

if (failIsOpen)
REQUIRE_THROWS_AS(s.checkIsOpen(), CustomException1);
else
s.checkIsOpen();

if (failGrab)
REQUIRE_THROWS_AS(s.checkGrab(), CustomException2);
else
s.checkGrab();

if (failRetrieve)
{
REQUIRE_THROWS_AS(s.checkRetrieve(), CustomException1);
REQUIRE_THROWS_AS(s.checkRetrieve(), CustomException2);
}
else
{
s.checkRetrieve();
s.checkRetrieve();
}

REQUIRE_THROWS_AS(s.checkRetrieve(), SequenceEndException);
REQUIRE_THROWS_AS(s.checkGrab(), SequenceEndException);
REQUIRE_THROWS_AS(s.checkIsOpen(), SequenceEndException);
}
}
}

0 comments on commit 4707c22

Please sign in to comment.