fix(ingest): convert-on-import to 32f WAV, undo-group bank+assign, overflow guards
Non-WAV sources decode via PCM_source::GetSamples and land as canonical 32f RIFF/WAVE; hash taken post-conversion so re-imports dedup. Undo block covers bank mutation + assign_request atomically. Overflow guards + adversarial tests.
This commit is contained in:
@@ -131,6 +131,43 @@ static void testTrailingGarbageRejected() {
|
||||
CHECK(!decodeAssignmentRequest(wire + "0:").has_value());
|
||||
}
|
||||
|
||||
// --- overflow / adversarial integer inputs ------------------------------------
|
||||
|
||||
// A 21-digit length field overflows SIZE_MAX and must be rejected safely (no UB,
|
||||
// no wrap-around that could make a huge length appear small and pass the bounds check).
|
||||
static void testOverflowFieldLength() {
|
||||
// Craft a wire where the bankId length token is 21 digits that exceed SIZE_MAX.
|
||||
// The decoder must fail cleanly, not access memory out of bounds.
|
||||
// "rsassign1" + "999999999999999999999:" (21 nines) + junk: rejects before OOB.
|
||||
const std::string wire = std::string("rsassign1") + "999999999999999999999:junk";
|
||||
CHECK(!decodeAssignmentRequest(wire).has_value());
|
||||
}
|
||||
|
||||
// A 20-digit generation (exceeds the 19-digit cap) must be rejected safely.
|
||||
static void testOverflowFieldInt64() {
|
||||
// Encode a valid record then manually substitute the generation with a 20-digit value.
|
||||
// We cannot use encode (it would produce a correct 19-digit generation), so we
|
||||
// build the wire manually. Generation "99999999999999999999" (20 nines) exceeds cap.
|
||||
// bankId = "pool" (4 bytes), sampleId = "s1" (2 bytes).
|
||||
const std::string wire = std::string("rsassign1")
|
||||
+ "4:pool"
|
||||
+ "2:s1"
|
||||
+ "20:99999999999999999999";
|
||||
CHECK(!decodeAssignmentRequest(wire).has_value());
|
||||
}
|
||||
|
||||
// SIZE_MAX as a length field (20 digits, within the digit-count cap) must not UB or
|
||||
// wrap. The overflow-guard in field() caps the multiplication; even if the value itself
|
||||
// does not trigger the multiply guard (SIZE_MAX accumulates cleanly digit by digit),
|
||||
// the subsequent "len > s_.size() - start" bounds check catches it because the actual
|
||||
// string is tiny — no OOB access, no wraparound, clean rejection.
|
||||
static void testOverflowExactSizeMax() {
|
||||
// 18446744073709551615 = SIZE_MAX on 64-bit. 20 digits: within the digit cap, but the
|
||||
// trailing bounds check rejects it because the wire string is far smaller than SIZE_MAX.
|
||||
const std::string wire = std::string("rsassign1") + "18446744073709551615:X";
|
||||
CHECK(!decodeAssignmentRequest(wire).has_value());
|
||||
}
|
||||
|
||||
int main() {
|
||||
testRoundTrip();
|
||||
testRoundTripPoolAndZeroGeneration();
|
||||
@@ -139,6 +176,9 @@ int main() {
|
||||
testLargeGeneration();
|
||||
testMalformedParse();
|
||||
testTrailingGarbageRejected();
|
||||
testOverflowFieldLength();
|
||||
testOverflowFieldInt64();
|
||||
testOverflowExactSizeMax();
|
||||
|
||||
if (g_fail == 0) std::printf("All tests passed.\n");
|
||||
return g_fail ? 1 : 0;
|
||||
|
||||
Reference in New Issue
Block a user