From 5708e1e562840896d368bd8540799a80013e4977 Mon Sep 17 00:00:00 2001 From: MarcZX10 <67475268+MarcZX10@users.noreply.github.com> Date: Mon, 2 Nov 2026 10:09:54 +0000 Subject: [PATCH 1/2] fix(libSceNgs2): reject ATRAC9 data chunks larger than the buffer ParseAtrac9 took the data chunk size from ReadChunks without checking it against the bytes supplied to sceNgs2ParseWaveformData. ReadChunks stops at the end of the buffer and keeps a data chunk that runs past it, so a truncated ATRAC9 file reported a data size larger than its own buffer. ParsePcm already rejects this case with SCE_NGS2_ERROR_INVALID_WAVEFORM_DATA; ParseAtrac9 now does the same and receives the buffer size for it. --- core/libs/prx/libSceNgs2.native/src/Atrac9.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/core/libs/prx/libSceNgs2.native/src/Atrac9.cpp b/core/libs/prx/libSceNgs2.native/src/Atrac9.cpp index 71c988fb9b..f0a03034f8 100644 --- a/core/libs/prx/libSceNgs2.native/src/Atrac9.cpp +++ b/core/libs/prx/libSceNgs2.native/src/Atrac9.cpp @@ -348,9 +348,10 @@ static int ParseAtrac9Loop(const RiffChunks& chunks, Ngs2WaveformInfo& info) { return SCE_NGS2_OK; } -static int ParseAtrac9(const RiffChunks& chunks, Ngs2WaveformInfo& info) { +static int ParseAtrac9(const RiffChunks& chunks, std::size_t size, Ngs2WaveformInfo& info) { const auto* format = chunks.format; if (chunks.formatSize < FMT_ATRAC9_SIZE || chunks.fact == nullptr || chunks.factSize < FACT_ATRAC9_SIZE) return SCE_NGS2_ERROR_INVALID_WAVEFORM_FORMAT; + if (chunks.dataSize > size - chunks.dataOffset) return SCE_NGS2_ERROR_INVALID_WAVEFORM_DATA; std::uint8_t config[4]; std::memcpy(config, format + FMT_CONFIG_OFFSET, sizeof(config)); Atrac9CodecInfo codec{}; @@ -420,7 +421,7 @@ static int ParseWaveform(const RiffChunks& chunks, std::size_t size, Ngs2Wavefor if (chunks.formatSize < FMT_EXTENSIBLE_SIZE) return SCE_NGS2_ERROR_UNKNOWN_WAVEFORM_FORMAT; const auto* guid = chunks.format + FMT_GUID_OFFSET; if (std::memcmp(guid, PCM_GUID, sizeof(PCM_GUID)) == 0) return ParsePcm(chunks, size, info, false); - if (std::memcmp(guid, ATRAC9_GUID, sizeof(ATRAC9_GUID)) == 0) return ParseAtrac9(chunks, info); + if (std::memcmp(guid, ATRAC9_GUID, sizeof(ATRAC9_GUID)) == 0) return ParseAtrac9(chunks, size, info); if (chunks.formatSize == FMT_ATRAC9_SIZE) throw std::runtime_error("NGS2: parsing an extensible waveform of format size 0x34 without the ATRAC9 GUID is not implemented"); if (std::memcmp(guid, TYPE_43_GUID, sizeof(TYPE_43_GUID)) == 0) throw std::runtime_error("NGS2: parsing waveform type 0x43 is not implemented"); return SCE_NGS2_ERROR_UNKNOWN_WAVEFORM_FORMAT; From 090116357302157a73b8aae6bc3f81ff4372793d Mon Sep 17 00:00:00 2001 From: MarcZX10 <67475268+MarcZX10@users.noreply.github.com> Date: Mon, 2 Nov 2026 10:09:55 +0000 Subject: [PATCH 2/2] test(libSceNgs2): cover an ATRAC9 data chunk running past the buffer A RIFF size that covers the declared data chunk while the buffer ends inside it must fail with SCE_NGS2_ERROR_INVALID_WAVEFORM_DATA. The exact file keeps parsing. The looped-file check now passes the whole file instead of a 168-byte prefix whose data chunk runs past the buffer. --- .../libs/prx/libSceNgs2.native/tests/Ngs2Atrac9.cpp | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/core/libs/prx/libSceNgs2.native/tests/Ngs2Atrac9.cpp b/core/libs/prx/libSceNgs2.native/tests/Ngs2Atrac9.cpp index 85e367f247..dd4201e64d 100644 --- a/core/libs/prx/libSceNgs2.native/tests/Ngs2Atrac9.cpp +++ b/core/libs/prx/libSceNgs2.native/tests/Ngs2Atrac9.cpp @@ -376,7 +376,7 @@ static void TestParseLoops() { } const auto file = LoopedFile(100, 700, 0); Ngs2WaveformInfo info{}; - Require(sceNgs2ParseWaveformData(file.data(), 168, &info) == SCE_NGS2_OK); + Require(sceNgs2ParseWaveformData(file.data(), file.size(), &info) == SCE_NGS2_OK); Require(info.block[1].num_repeats == UINT32_MAX); const auto system = CreateSystem(); const auto voice = Voice(CreateRack(system, SCE_NGS2_RACK_ID_SAMPLER)); @@ -743,6 +743,16 @@ static void TestStreamingReplay() { } } +static void TestDataChunkBeyondBuffer() { + const auto file = At9File(48000); + Ngs2WaveformInfo info{}; + Require(sceNgs2ParseWaveformData(file.data(), file.size(), &info) == SCE_NGS2_OK && info.data_size == SuperframeBytes); + auto truncated = file; + Set32(truncated, 4, static_cast(truncated.size() - 8 + 16)); + Set32(truncated, 96, SuperframeBytes + 16); + Require(sceNgs2ParseWaveformData(truncated.data(), truncated.size(), &info) == SCE_NGS2_ERROR_INVALID_WAVEFORM_DATA); +} + int main() { const auto reference = Reference(); TestSkipAndBlockEnd(reference); @@ -764,5 +774,6 @@ int main() { TestBlockOnEmptyAtrac9Voice(); TestEmptyAddsCloseLikePcm(); TestSilenceFlagRejected(); + TestDataChunkBeyondBuffer(); return 0; }