Prevent out of memory errors when the game passes in an improper length value
HACK In Luigi's Mansion Dark Moon in HLE audio, the game mysteriously passes in an extremely large value for length, which without any checks, causes HLE audio to allocate an extremely large buffer. This value seemingly is caused by some other HLE audio feature is missing, and Luigi's Mansion subtracts two values to get a length, without checking for overflow first. This appears to be caused by an incorrect HLE audio emulation, as its fixed entirely by only changing to LLE. As such, further investigation is required, but in the meantime, completely eating up our users RAM is unacceptable.
This commit is contained in:
parent
8b1738aeac
commit
87facaa2e2
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
Reference in New Issue