diff --git a/rpcs3/Emu/Cell/Modules/cellDmuxPamf.cpp b/rpcs3/Emu/Cell/Modules/cellDmuxPamf.cpp index ba621ac3bd..99234de01e 100644 --- a/rpcs3/Emu/Cell/Modules/cellDmuxPamf.cpp +++ b/rpcs3/Emu/Cell/Modules/cellDmuxPamf.cpp @@ -671,19 +671,19 @@ bool dmux_pamf_base::process_next_pack() case state::initial: { // Search for the next pack start code or prog end code - std::span pack{ static_cast(nullptr), PACK_SIZE }; // This initial value is not used, can't be default constructed + std::span pack; for (;;) { - if (stream->empty()) + if (stream->size() < PACK_STUFFING_LENGTH_OFFSET + sizeof(u8)) { stream.reset(); demux_done_notified = on_demux_done(); return true; } - pack = stream->subspan<0, PACK_SIZE>(); - stream = stream->subspan(); + pack = stream->first(std::min(stream->size(), PACK_SIZE)); + stream = stream->subspan(std::min(stream->size(), PACK_SIZE)); // If the input stream is a raw elementary stream, skip everything MPEG-PS related and go straight to elementary stream parsing if (raw_es) @@ -719,6 +719,14 @@ bool dmux_pamf_base::process_next_pack() // Skip over pack header const u8 pack_stuffing_length = read_from_ptr(pack.subspan()) & 0x7; + + // Not checked on LLE, the SPU task would just increment the reading position and read random data in the SPU local store + if (PACK_STUFFING_LENGTH_OFFSET + sizeof(u8) + pack_stuffing_length + PES_HEADER_DATA_LENGTH_OFFSET + sizeof(u8) > pack.size()) + { + cellDmuxPamf.error("Invalid pack stuffing length"); + return false; + } + std::span current_pes_packet = pack.subspan(PACK_STUFFING_LENGTH_OFFSET + sizeof(u8) + pack_stuffing_length); if (read_from_ptr>(current_pes_packet) >> 8 != PACKET_START_CODE_PREFIX) @@ -785,10 +793,8 @@ bool dmux_pamf_base::process_next_pack() return false; } - // The size of the stream is not checked here because if coming from a pack header, it is guaranteed that there is enough space, - // and if coming from a system header or private stream 2, it was already checked above - const u16 pes_packet_length = read_from_ptr>(current_pes_packet.begin() + PES_PACKET_LENGTH_OFFSET) + PES_PACKET_LENGTH_OFFSET + sizeof(u16); - const u8 pes_header_data_length = read_from_ptr(current_pes_packet.begin() + PES_HEADER_DATA_LENGTH_OFFSET) + PES_HEADER_DATA_LENGTH_OFFSET + sizeof(u8); + const u16 pes_packet_length = read_from_ptr>(current_pes_packet.begin(), PES_PACKET_LENGTH_OFFSET) + PES_PACKET_LENGTH_OFFSET + sizeof(u16); + const u8 pes_header_data_length = read_from_ptr(current_pes_packet.begin(), PES_HEADER_DATA_LENGTH_OFFSET) + PES_HEADER_DATA_LENGTH_OFFSET + sizeof(u8); // Not checked on LLE, the SPU task would just increment the reading position and read random data in the SPU local store if (pes_packet_length > current_pes_packet.size() || pes_packet_length <= pes_header_data_length) diff --git a/rpcs3/tests/test_dmux_pamf.cpp b/rpcs3/tests/test_dmux_pamf.cpp index 40a8a02b32..b6fdbf73b4 100644 --- a/rpcs3/tests/test_dmux_pamf.cpp +++ b/rpcs3/tests/test_dmux_pamf.cpp @@ -228,6 +228,14 @@ INSTANTIATE_TEST_SUITE_P(Instance, DmuxPamfInvalidStream, Values []() consteval { auto pack = AVC_SINGLE_PACK_STREAM; pack[0x118] = 0x00; pack[0x119] = 0x03; pack[0x11c] = 0x00; return pack; }() // PES packet header size too large )); +// Since the "pack stuffing length" field only has a size of three bits, this can only occur if the stream is not a multiple of 0x800 bytes large. +// Like the other invalid streams above, LLE does not check for this +TEST_F(DmuxPamfTest, InvalidPackStuffingLength) +{ + demuxer.set_stream({ AVC_SINGLE_PACK_STREAM.cbegin(), AVC_SINGLE_PACK_STREAM.cbegin() + 0x16 }, false); + EXPECT_FALSE(demuxer.process_next_pack()); +} + // Tests if the program end code is properly detected and the corresponding event is fired TEST_F(DmuxPamfTest, ProgEnd) {