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,6 +171,20 @@ 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);
// HACK
// Luigi's Mansion Dark Moon configures the embedded buffer with an extremely large value
// for length, causing the Dequeue method to allocate a buffer of that size, eating up all
// of the users RAM. It appears that the game is calculating the length of the sample by
// using some value from the DSP and subtracting another value, which causes it to
// underflow. We need to investigate further into what value the game is reading from and
// fix that, but as a stop gap, we can just prevent these underflowed values from playing in
// the mean time
if (static_cast<s32>(config.length) < 0) {
LOG_ERROR(Audio_DSP,
"Skipping embedded buffer sample! Game passed in improper value for length. "
"addr {:X} length {:X}",
config.physical_address, config.length);
} else {
state.input_queue.emplace(Buffer{ state.input_queue.emplace(Buffer{
config.physical_address, config.physical_address,
config.length, config.length,
@ -185,6 +199,7 @@ void Source::ParseConfig(SourceConfiguration::Configuration& config,
play_position, play_position,
false, 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,6 +216,12 @@ 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];
if (static_cast<s32>(b.length) < 0) {
LOG_ERROR(Audio_DSP,
"Skipping buffer queue sample! Game passed in improper value for "
"length. addr {:X} length {:X}",
b.physical_address, b.length);
} else {
state.input_queue.emplace(Buffer{ state.input_queue.emplace(Buffer{
b.physical_address, b.physical_address,
b.length, b.length,
@ -215,6 +236,7 @@ void Source::ParseConfig(SourceConfiguration::Configuration& config,
{}, // 0 in u32_dsp {}, // 0 in u32_dsp
false, 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);
} }