libprotobuf-mutator
When auditing C++ servers that ingest Protocol Buffers (such as real-time simulation ticks or distributed RPC handlers), raw byte-level coverage-guided fuzzers spend most cycles mutating varint wire tags that are immediately rejected by ParseFromArray(). However, the highest-severity vulnerabilities live past wire deserialization:
Wire Packet --> [Protobuf ParseFromArray()] --> [Missing Post-Parse Invariants!] --> [Simulation / State Engine]
|- 1. Unbounded repeated_field_size() (CWE-1284 / CWE-400)
|- 2. IEEE-754 NaN / +/-Inf floats (CWE-20 / CWE-682)
|- 3. Unclamped tick / history offsets (CWE-20)
--emit-lpm-harness)
In protobuf-bounds-auditor v1.1.0, I added structure-aware C++ harness generation for Google's libprotobuf-mutator (DEFINE_PROTO_FUZZER). Running:
$ python proto_audit.py usercmd.proto --root-message CBaseUserCmdPB --emit-lpm-harness fuzz_usercmd.cc
synthesizes both the static SARIF v2.1.0 report and the defensive C++ post-parse validator:
// Auto-generated by protobuf-bounds-auditor v1.1.0
#include <cmath>
#include <cstdint>
#include "src/libfuzzer/libfuzzer_macro.h"
static constexpr int kMaxRepeatedElements = 64;
static constexpr uint64_t kMaxWindowOffset = 4096;
template <typename MsgT>
bool ValidateDeserializedInvariants(const MsgT& input) {
// PB-003 (CWE-20): Bound integer tick/offset window
if (input.prediction_offset_ticks_x256() > kMaxWindowOffset) {
return false;
}
// PB-001 (CWE-1284 / CWE-400): Guard repeated field cardinality
if (input.subtick_moves_size() > kMaxRepeatedElements) {
return false;
}
return true;
}
DEFINE_PROTO_FUZZER(const CBaseUserCmdPB& input) {
if (!ValidateDeserializedInvariants(input)) {
return;
}
// Forward validated protobuf message to target ingestion handler under ASAN/UBSAN
}
ValveSoftware/GameNetworkingSockets Schemas
Applying protobuf-bounds-auditor across ValveSoftware/GameNetworkingSockets/src/common/ (Allman-style proto2 wire definitions) automatically identifies 6 repeated wire fields requiring explicit cardinality caps at the transport ingestion boundary (examples/gns_lpm_invariant_harness.cc):
[HIGH] PB-001 (CWE-1284) CMsgSteamDatagramSessionCryptInfo.ciphers (#5)
[HIGH] PB-001 (CWE-1284) CMsgSteamDatagramLinkLifetimeStats.multipath_packets_recv_sequenced (#12)
[HIGH] PB-001 (CWE-1284) CMsgSteamDatagramLinkLifetimeStats.multipath_packets_recv_later (#13)
[HIGH] PB-001 (CWE-1284) CMsgSteamDatagramCertificate.gameserver_datacenter_ids (#5)
[HIGH] PB-001 (CWE-1284) CMsgSteamDatagramCertificate.app_ids (#10)
[HIGH] PB-001 (CWE-1284) CMsgSteamDatagramCertificate.ip_addresses (#13)
When a Protobuf message references external backing files (such as out-of-line tensor weights or replay segments), schema validation alone is insufficient. A defensive post-parse validator must enforce three structural invariants before opening or mapping external descriptors:
// 1. Canonicalize relative paths and reject parent-directory traversal (".."),
// absolute paths, or URI-like prefixes regardless of whether base_dir is empty.
// 2. Verify that the opened file descriptor refers to a regular file (S_ISREG on POSIX,
// FILE_TYPE_DISK on Windows) prior to mmap() or read().
// 3. Validate that (offset >= 0), (length >= 0), and (offset <= file_size - length)
// using overflow-safe subtraction before allocating or slicing buffers.