Skip to content

Commit

Permalink
fuzzing test and detect the memmove error (#688)
Browse files Browse the repository at this point in the history
* fuzzing

* add an additional random walk test
  • Loading branch information
SchrodingerZhu authored Nov 19, 2024
1 parent 01885f5 commit 0b53b93
Show file tree
Hide file tree
Showing 3 changed files with 225 additions and 0 deletions.
4 changes: 4 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ option(SNMALLOC_IPO "Link with IPO/LTO support" OFF)
option(SNMALLOC_BENCHMARK_INDIVIDUAL_MITIGATIONS "Build tests and ld_preload for individual mitigations" OFF)
option(SNMALLOC_ENABLE_DYNAMIC_LOADING "Build such that snmalloc can be dynamically loaded. This is not required for LD_PRELOAD, and will harm performance if enabled." OFF)
option(SNMALLOC_ENABLE_WAIT_ON_ADDRESS "Use wait on address backoff strategy if it is available" ON)
option(SNMALLOC_ENABLE_FUZZING "Enable fuzzing instrumentation tests" OFF)
# Options that apply only if we're not building the header-only library
cmake_dependent_option(SNMALLOC_RUST_SUPPORT "Build static library for rust" OFF "NOT SNMALLOC_HEADER_ONLY_LIBRARY" OFF)
cmake_dependent_option(SNMALLOC_STATIC_LIBRARY "Build static libraries" ON "NOT SNMALLOC_HEADER_ONLY_LIBRARY" OFF)
Expand Down Expand Up @@ -599,3 +600,6 @@ install(EXPORT snmallocConfig
DESTINATION "share/snmalloc"
)

if (SNMALLOC_ENABLE_FUZZING)
add_subdirectory(fuzzing)
endif()
22 changes: 22 additions & 0 deletions fuzzing/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
include(FetchContent)

FetchContent_Declare(
fuzztest
GIT_REPOSITORY https://github.com/google/fuzztest.git
GIT_TAG 2024-10-28
)

FetchContent_MakeAvailable(fuzztest)

enable_testing()
fuzztest_setup_fuzzing_flags()

add_executable(
snmalloc-fuzzer
snmalloc-fuzzer.cpp
)

target_link_libraries(snmalloc-fuzzer PRIVATE snmalloc)
target_compile_options(snmalloc-fuzzer PRIVATE -fsanitize=address -DADDRESS_SANITIZER)

link_fuzztest(snmalloc-fuzzer)
199 changes: 199 additions & 0 deletions fuzzing/snmalloc-fuzzer.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
#include "fuzztest/fuzztest.h"
#include "snmalloc/snmalloc.h"

#include <cstddef>
#include <cstdlib>
#include <execution>
#include <new>
#include <string_view>
#include <vector>

void simple_memcpy(std::vector<char> data)
{
std::vector<char> dest(data.size());
snmalloc::memcpy<true>(dest.data(), data.data(), data.size());
if (data != dest)
abort();
}

void memcpy_with_align_offset(
size_t source_alignment,
size_t source_offset,
size_t dest_alignment,
size_t dest_offset,
std::string data)
{
source_alignment = 1 << source_alignment;
dest_alignment = 1 << dest_alignment;
source_offset = source_offset % source_alignment;
dest_offset = dest_offset % dest_alignment;
auto src_ = ::operator new(
data.size() + source_offset, std::align_val_t{source_alignment});
auto dst_ =
::operator new(data.size() + dest_offset, std::align_val_t{dest_alignment});
auto src = static_cast<char*>(src_) + source_offset;
auto dst = static_cast<char*>(dst_) + dest_offset;
snmalloc::memcpy<true>(src, data.data(), data.size());
snmalloc::memcpy<true>(dst, src, data.size());
if (std::string_view(dst, data.size()) != data)
abort();
::operator delete(src_, std::align_val_t{source_alignment});
::operator delete(dst_, std::align_val_t{dest_alignment});
}

void simple_memmove(std::vector<char> data)
{
std::vector<char> dest(data.size());
snmalloc::memmove<true>(dest.data(), data.data(), data.size());
if (data != dest)
abort();
}

void forward_memmove(std::string data, size_t offset)
{
std::string to_move = data;
offset = std::min(offset, data.size());
snmalloc::memmove<true>(
to_move.data() + offset, to_move.data(), to_move.size() - offset);
size_t after_move = to_move.size() - offset;
if (
std::string_view(data.data(), after_move) !=
std::string_view(to_move.data() + offset, after_move))
abort();
}

void backward_memmove(std::string data, size_t offset)
{
std::string to_move = data;
offset = std::min(offset, data.size());
snmalloc::memmove<true>(
to_move.data(), to_move.data() + offset, to_move.size() - offset);
size_t after_move = to_move.size() - offset;
if (
std::string_view(data.data() + offset, after_move) !=
std::string_view(to_move.data(), after_move))
abort();
}

constexpr static size_t size_limit = 16384;

enum class EventKind : unsigned
{
AllocZero = 0,
AllocNoZero = 1,
Free = 2,
Check = 3,
ReFill = 4,
};

struct Event
{
EventKind kind;
size_t size_or_index;
char filler;

Event(std::tuple<unsigned, size_t, char> payload)
: kind(static_cast<EventKind>(std::get<0>(payload) % 5)),
size_or_index(std::get<1>(payload)),
filler(std::get<2>(payload))
{
if (kind == EventKind::AllocZero || kind == EventKind::AllocNoZero)
{
size_or_index %= size_limit;
}
}
};

struct Result
{
char filler;
char* ptr;
size_t size;

void check()
{
auto res = std::reduce(
std::execution::unseq,
ptr,
ptr + size,
static_cast<unsigned char>(0),
[&](unsigned char acc, char c) -> unsigned char {
return acc | static_cast<unsigned char>(c != filler);
});
if (res)
abort();
}
};

void snmalloc_random_walk(
std::vector<std::tuple<unsigned, size_t, char>> payload)
{
std::vector<Result> results;
for (auto& p : payload)
{
Event e(p);
auto scoped = snmalloc::get_scoped_allocator();
switch (e.kind)
{
case EventKind::AllocZero:
{
auto ptr =
static_cast<char*>(scoped->alloc<snmalloc::YesZero>(e.size_or_index));
results.push_back({0, ptr, e.size_or_index});
break;
}

case EventKind::AllocNoZero:
{
auto ptr =
static_cast<char*>(scoped->alloc<snmalloc::NoZero>(e.size_or_index));
std::fill(ptr, ptr + e.size_or_index, e.filler);
results.push_back({e.filler, ptr, e.size_or_index});
break;
}

case EventKind::Free:
{
if (results.empty())
break;
auto index = e.size_or_index % results.size();
scoped->dealloc(results[index].ptr);
results.erase(results.begin() + static_cast<ptrdiff_t>(index));
break;
}

case EventKind::Check:
{
for (auto& r : results)
r.check();
break;
}

case EventKind::ReFill:
{
if (results.empty())
break;
auto index = e.size_or_index % results.size();
std::fill(
results[index].ptr,
results[index].ptr + results[index].size,
e.filler);
results[index].filler = e.filler;
break;
}
}
}
}

FUZZ_TEST(snmalloc_fuzzing, simple_memcpy);
FUZZ_TEST(snmalloc_fuzzing, simple_memmove);
FUZZ_TEST(snmalloc_fuzzing, forward_memmove);
FUZZ_TEST(snmalloc_fuzzing, backward_memmove);
FUZZ_TEST(snmalloc_fuzzing, memcpy_with_align_offset)
.WithDomains(
fuzztest::InRange(0, 6),
fuzztest::Positive<size_t>(),
fuzztest::InRange(0, 6),
fuzztest::Positive<size_t>(),
fuzztest::String());
FUZZ_TEST(snmalloc_fuzzing, snmalloc_random_walk);

0 comments on commit 0b53b93

Please sign in to comment.