New libFuzzer/AFL++ harnesses, each trapping on an oracle violation: p2p_json (nesting bound, round trip), p2p_connstr (endpoint round trip, pin idempotence, no silent re-pin), p2p_handshake (validators and the full acceptor bind the TLS peer), p2p_delta_stream (batch, session and bootstrap receive paths stay inside count and byte budgets), p2p_inbound (the manager inbound session through registry update) and topology_codec (decoded snapshots re-encode losslessly). p2p_delta now draws its exchange limits from the input. p2p_seeds.py writes framed-session seeds for FuzzedDataProvider layouts, dicts/p2p.dict carries the frame vocabulary, generate_corpus.sh accepts a local seedgen, and fuzz.sh no longer fails on macOS bash 3.2 when a target has no dictionary. The generated corpus stays untracked.
49 lines
1.7 KiB
C++
49 lines
1.7 KiB
C++
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
// Copyright 2026 YAMS Contributors
|
|
|
|
// Topology snapshot decoding from untrusted stored payloads (binary codec and the JSON/zstd
|
|
// envelope). Oracle: a binary batch that decodes re-encodes and decodes to the same shape.
|
|
|
|
#include <yams/topology/topology_codec.h>
|
|
|
|
#include <cstddef>
|
|
#include <cstdint>
|
|
#include <span>
|
|
#include <string_view>
|
|
|
|
namespace {
|
|
|
|
void fuzzRequire(bool condition) {
|
|
if (!condition) {
|
|
__builtin_trap();
|
|
}
|
|
}
|
|
|
|
} // namespace
|
|
|
|
extern "C" int LLVMFuzzerTestOneInput(const std::uint8_t* data, std::size_t size) {
|
|
if (data == nullptr || size < 1 || size > 1024 * 1024) {
|
|
return 0;
|
|
}
|
|
const bool binary = (data[0] & 1U) != 0;
|
|
const auto payload =
|
|
std::span<const std::byte>(reinterpret_cast<const std::byte*>(data + 1), size - 1);
|
|
if (!binary) {
|
|
const std::string_view text(reinterpret_cast<const char*>(payload.data()), payload.size());
|
|
(void)yams::topology::deserializeTopologyBatchCompressed(text);
|
|
return 0;
|
|
}
|
|
auto decoded = yams::topology::deserializeTopologyBatchBinary(payload);
|
|
if (!decoded) {
|
|
return 0;
|
|
}
|
|
auto encoded = yams::topology::serializeTopologyBatchBinary(decoded.value());
|
|
fuzzRequire(encoded.has_value());
|
|
auto again = yams::topology::deserializeTopologyBatchBinary(encoded.value());
|
|
fuzzRequire(again.has_value());
|
|
fuzzRequire(again.value().snapshotId == decoded.value().snapshotId);
|
|
fuzzRequire(again.value().clusters.size() == decoded.value().clusters.size());
|
|
fuzzRequire(again.value().memberships.size() == decoded.value().memberships.size());
|
|
fuzzRequire(again.value().topologyEpoch == decoded.value().topologyEpoch);
|
|
return 0;
|
|
}
|