fix(q-w0): six audit fix-nows — linked-lag stereo SOLA, playable-span prime bound, declick dead-state, provenance cursor hardening, rate-derived gain ramp + fade ceiling
This commit is contained in:
@@ -7,6 +7,8 @@
|
||||
// track GUIDs, FX-chain identity) -> a MISMATCH (different string / recipe).
|
||||
// * fxChainIdentity fold: order-sensitive, field-injection-proof, empty-stable.
|
||||
// * parse of malformed / wrong-version / truncated input -> nullopt (graceful).
|
||||
// * hardened wire cursor (Q-W0 T2-01a): hostile digit-run lengths, wrap-magnitude
|
||||
// lengths, and huge GUID counts -> nullopt with no overflow and no over-allocation.
|
||||
// * parent-detection decision: positive, negative, ambiguous, empty, and the
|
||||
// edge where a source file is not in the bank (missing-from-bank).
|
||||
//
|
||||
@@ -190,6 +192,58 @@ static void testMalformedFingerprint() {
|
||||
"1:0" "0:").has_value());
|
||||
}
|
||||
|
||||
// --- Q-W0 T2-01a: hardened wire cursor (backported from assignment_request /
|
||||
// sample_usage) — corrupt or crafted persisted fingerprints must fail the parse
|
||||
// cleanly (nullopt), never wrap an integer, never throw, never over-allocate. ----
|
||||
|
||||
// Mirrors buildFingerprint's field order with benign values, except the GUID-count
|
||||
// field carries caller-supplied raw text — the attack surface under test.
|
||||
static void putF(std::string& out, const std::string& f) {
|
||||
out += std::to_string(f.size());
|
||||
out += ':';
|
||||
out += f;
|
||||
}
|
||||
static std::string forgedFingerprint(const std::string& guidCountText) {
|
||||
std::string out = "rsprov1";
|
||||
putF(out, "0"); // scope = Item
|
||||
putF(out, "0"); // sourceMode
|
||||
putF(out, "0"); // startSeconds
|
||||
putF(out, "1"); // endSeconds
|
||||
putF(out, "0"); // tailMode
|
||||
putF(out, "0"); // tailMs
|
||||
putF(out, "48000"); // sampleRate
|
||||
putF(out, "2"); // channelCount
|
||||
putF(out, guidCountText); // GUID count (no GUID fields follow)
|
||||
putF(out, ""); // fxChainIdentity (empty)
|
||||
return out;
|
||||
}
|
||||
|
||||
static void testHardenedCursorRejectsHostileLengths() {
|
||||
// A 200-digit length run: pre-hardening the accumulate wrapped std::size_t silently
|
||||
// (the digit cap + overflow guard now reject it outright).
|
||||
CHECK(!parseFingerprint("rsprov1" + std::string(200, '9') + ":x").has_value());
|
||||
// A SIZE_MAX-magnitude length: the additive bounds check `start + len > size` could
|
||||
// itself wrap and pass; the subtraction-first form rejects.
|
||||
CHECK(!parseFingerprint("rsprov118446744073709551615:x").has_value());
|
||||
// One past SIZE_MAX: the per-digit overflow guard fires during the accumulate.
|
||||
CHECK(!parseFingerprint("rsprov118446744073709551616:x").has_value());
|
||||
}
|
||||
|
||||
static void testHugeGuidCountRejectedBeforeReserve() {
|
||||
// A GUID count astronomically larger than the wire could hold must return nullopt
|
||||
// WITHOUT reaching trackGuids.reserve(count) — pre-fix this drove reserve(10^16)
|
||||
// into std::length_error / bad_alloc thrown through the shell.
|
||||
CHECK(!parseFingerprint(forgedFingerprint("9999999999999999")).has_value());
|
||||
// A count merely past the wire-size sanity bound (each GUID field needs >= 2 wire
|
||||
// bytes) is provably bogus and rejected before the field loop.
|
||||
CHECK(!parseFingerprint(forgedFingerprint("1000")).has_value());
|
||||
// A digit run past 20 fails the count parser's cap.
|
||||
CHECK(!parseFingerprint(forgedFingerprint(std::string(25, '9'))).has_value());
|
||||
// Sanity (non-vacuous forgery): the honest zero-count version of the same forged
|
||||
// shape parses fine — the rejections above are the count's doing, not the shape's.
|
||||
CHECK(parseFingerprint(forgedFingerprint("0")).has_value());
|
||||
}
|
||||
|
||||
// --- recorded-recipe model round-trips through the Sample JSON ----------------
|
||||
// The fingerprint rides in Provenance.fxChainSnapshot (one string), which M1's
|
||||
// BankIndex JSON already round-trips. Prove a real recipe survives that path intact.
|
||||
@@ -296,6 +350,8 @@ int main() {
|
||||
testFxChainIdentityInjectionProof();
|
||||
testCombineChainIdentities();
|
||||
testMalformedFingerprint();
|
||||
testHardenedCursorRejectsHostileLengths();
|
||||
testHugeGuidCountRejectedBeforeReserve();
|
||||
testRecipeThroughSampleJson();
|
||||
testDetectParentPositive();
|
||||
testDetectParentMultipleSameParent();
|
||||
|
||||
Reference in New Issue
Block a user