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:
@@ -3,6 +3,7 @@
|
||||
#include "assignment_request.h"
|
||||
|
||||
#include <cstddef>
|
||||
#include <limits>
|
||||
|
||||
namespace reasampler {
|
||||
|
||||
@@ -29,27 +30,44 @@ public:
|
||||
bool atEnd() const { return pos_ >= s_.size(); }
|
||||
|
||||
// Reads one length-prefixed field into `out`. Fails on a missing ':', an empty or
|
||||
// non-numeric length, or a length that runs past the end.
|
||||
// non-numeric length, a length that overflows SIZE_MAX, or a length that runs past
|
||||
// the end. The digit count is capped at 20 (the decimal width of SIZE_MAX on a
|
||||
// 64-bit host) so a crafted 200-digit length cannot accumulate past SIZE_MAX via
|
||||
// repeated multiply. "never UB" promise from the header is upheld here.
|
||||
bool field(std::string& out) {
|
||||
if (!ok_) return false;
|
||||
const std::size_t colon = s_.find(':', pos_);
|
||||
if (colon == std::string::npos) return fail();
|
||||
if (colon == pos_) return fail(); // empty length token
|
||||
// Cap: SIZE_MAX fits in at most 20 decimal digits; a longer run is bogus.
|
||||
if (colon - pos_ > 20u) return fail();
|
||||
std::size_t len = 0;
|
||||
for (std::size_t i = pos_; i < colon; ++i) {
|
||||
const char c = s_[i];
|
||||
if (c < '0' || c > '9') return fail();
|
||||
len = len * 10 + static_cast<std::size_t>(c - '0');
|
||||
const std::size_t digit = static_cast<std::size_t>(c - '0');
|
||||
// Overflow guard: if len would exceed SIZE_MAX after multiply+add, fail.
|
||||
if (len > (std::numeric_limits<std::size_t>::max() - digit) / 10u)
|
||||
return fail();
|
||||
len = len * 10u + digit;
|
||||
}
|
||||
const std::size_t start = colon + 1;
|
||||
if (start + len > s_.size()) return fail();
|
||||
// Guard: start may equal s_.size() (empty remainder), in which case only len==0
|
||||
// is valid; start > s_.size() cannot happen (colon < s_.size() by find()).
|
||||
// Use subtraction-first form to avoid start+len wrapping on a huge len.
|
||||
if (start > s_.size() || len > s_.size() - start) return fail();
|
||||
out.assign(s_, start, len);
|
||||
pos_ = start + len;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Reads a length-prefixed field and parses it as a signed 64-bit decimal (an
|
||||
// optional leading '-'). Fails on empty, non-digit, or trailing bytes.
|
||||
// optional leading '-'). Fails on empty, non-digit, trailing bytes, or a value
|
||||
// that would overflow INT64_MAX / underflow INT64_MIN. The digit count is capped
|
||||
// at 19 (the decimal width of INT64_MAX, plus 1 for the optional sign = 20
|
||||
// characters maximum) so a crafted 21-digit field cannot accumulate UB. "never UB"
|
||||
// promise from the header is upheld: all arithmetic is done on positive digits
|
||||
// and capped before applying the sign.
|
||||
bool fieldInt64(std::int64_t& out) {
|
||||
std::string f;
|
||||
if (!field(f)) return false;
|
||||
@@ -61,11 +79,20 @@ public:
|
||||
i = 1;
|
||||
if (f.size() == 1) return fail(); // bare "-"
|
||||
}
|
||||
// Cap at 19 digits (INT64_MAX = 9223372036854775807 — 19 digits). A 20-digit
|
||||
// positive value would overflow INT64_MAX; a 20-digit negative might be valid
|
||||
// (INT64_MIN = -9223372036854775808) but we conservatively reject it too: the
|
||||
// generation field is a unix timestamp, never near INT64 limits in practice.
|
||||
if (f.size() - i > 19u) return fail();
|
||||
std::int64_t v = 0;
|
||||
for (; i < f.size(); ++i) {
|
||||
const char c = f[i];
|
||||
if (c < '0' || c > '9') return fail();
|
||||
v = v * 10 + static_cast<std::int64_t>(c - '0');
|
||||
const std::int64_t digit = static_cast<std::int64_t>(c - '0');
|
||||
// Overflow guard: v * 10 + digit must not exceed INT64_MAX.
|
||||
if (v > (std::numeric_limits<std::int64_t>::max() - digit) / 10)
|
||||
return fail();
|
||||
v = v * 10 + digit;
|
||||
}
|
||||
out = neg ? -v : v;
|
||||
return true;
|
||||
|
||||
Reference in New Issue
Block a user