Merge pull request #5024 from jroweboy/temp-hle-audio-fix

Prevent out of memory errors when the game passes in an improper length value
This commit is contained in:
James Rowe 2020-01-21 15:30:20 -07:00 committed by GitHub
commit 5fd1ff08d7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -171,20 +171,35 @@ void Source::ParseConfig(SourceConfiguration::Configuration& config,
if (config.embedded_buffer_dirty) { if (config.embedded_buffer_dirty) {
config.embedded_buffer_dirty.Assign(0); config.embedded_buffer_dirty.Assign(0);
state.input_queue.emplace(Buffer{ // HACK
config.physical_address, // Luigi's Mansion Dark Moon configures the embedded buffer with an extremely large value
config.length, // for length, causing the Dequeue method to allocate a buffer of that size, eating up all
static_cast<u8>(config.adpcm_ps), // of the users RAM. It appears that the game is calculating the length of the sample by
{config.adpcm_yn[0], config.adpcm_yn[1]}, // using some value from the DSP and subtracting another value, which causes it to
static_cast<bool>(config.adpcm_dirty), // underflow. We need to investigate further into what value the game is reading from and
static_cast<bool>(config.is_looping), // fix that, but as a stop gap, we can just prevent these underflowed values from playing in
config.buffer_id, // the mean time
state.mono_or_stereo, if (static_cast<s32>(config.length) < 0) {
state.format, LOG_ERROR(Audio_DSP,
false, "Skipping embedded buffer sample! Game passed in improper value for length. "
play_position, "addr {:X} length {:X}",
false, config.physical_address, config.length);
}); } else {
state.input_queue.emplace(Buffer{
config.physical_address,
config.length,
static_cast<u8>(config.adpcm_ps),
{config.adpcm_yn[0], config.adpcm_yn[1]},
static_cast<bool>(config.adpcm_dirty),
static_cast<bool>(config.is_looping),
config.buffer_id,
state.mono_or_stereo,
state.format,
false,
play_position,
false,
});
}
LOG_TRACE(Audio_DSP, "enqueuing embedded addr={:#010x} len={} id={} start={}", LOG_TRACE(Audio_DSP, "enqueuing embedded addr={:#010x} len={} id={} start={}",
config.physical_address, config.length, config.buffer_id, config.physical_address, config.length, config.buffer_id,
static_cast<u32>(config.play_position)); static_cast<u32>(config.play_position));
@ -201,20 +216,27 @@ void Source::ParseConfig(SourceConfiguration::Configuration& config,
for (std::size_t i = 0; i < 4; i++) { for (std::size_t i = 0; i < 4; i++) {
if (config.buffers_dirty & (1 << i)) { if (config.buffers_dirty & (1 << i)) {
const auto& b = config.buffers[i]; const auto& b = config.buffers[i];
state.input_queue.emplace(Buffer{ if (static_cast<s32>(b.length) < 0) {
b.physical_address, LOG_ERROR(Audio_DSP,
b.length, "Skipping buffer queue sample! Game passed in improper value for "
static_cast<u8>(b.adpcm_ps), "length. addr {:X} length {:X}",
{b.adpcm_yn[0], b.adpcm_yn[1]}, b.physical_address, b.length);
b.adpcm_dirty != 0, } else {
b.is_looping != 0, state.input_queue.emplace(Buffer{
b.buffer_id, b.physical_address,
state.mono_or_stereo, b.length,
state.format, static_cast<u8>(b.adpcm_ps),
true, {b.adpcm_yn[0], b.adpcm_yn[1]},
{}, // 0 in u32_dsp b.adpcm_dirty != 0,
false, b.is_looping != 0,
}); b.buffer_id,
state.mono_or_stereo,
state.format,
true,
{}, // 0 in u32_dsp
false,
});
}
LOG_TRACE(Audio_DSP, "enqueuing queued {} addr={:#010x} len={} id={}", i, LOG_TRACE(Audio_DSP, "enqueuing queued {} addr={:#010x} len={} id={}", i,
b.physical_address, b.length, b.buffer_id); b.physical_address, b.length, b.buffer_id);
} }