sink_stream: Resolve heap buffer corruption due to out of bounds write
Also, remove the use of ScratchBuffer when upmixing, as other channels may not be initialized with zeroed out data.
This commit is contained in:
parent
45be4c3214
commit
ea8d5ef5e8
2 changed files with 20 additions and 17 deletions
|
@ -12,6 +12,7 @@
|
||||||
#include "audio_core/sink/sink_stream.h"
|
#include "audio_core/sink/sink_stream.h"
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "common/fixed_point.h"
|
#include "common/fixed_point.h"
|
||||||
|
#include "common/scope_exit.h"
|
||||||
#include "common/settings.h"
|
#include "common/settings.h"
|
||||||
#include "core/core.h"
|
#include "core/core.h"
|
||||||
#include "core/core_timing.h"
|
#include "core/core_timing.h"
|
||||||
|
@ -19,9 +20,12 @@
|
||||||
namespace AudioCore::Sink {
|
namespace AudioCore::Sink {
|
||||||
|
|
||||||
void SinkStream::AppendBuffer(SinkBuffer& buffer, std::span<s16> samples) {
|
void SinkStream::AppendBuffer(SinkBuffer& buffer, std::span<s16> samples) {
|
||||||
if (type == StreamType::In) {
|
SCOPE_EXIT({
|
||||||
queue.enqueue(buffer);
|
queue.enqueue(buffer);
|
||||||
queued_buffers++;
|
++queued_buffers;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (type == StreamType::In) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -66,16 +70,17 @@ void SinkStream::AppendBuffer(SinkBuffer& buffer, std::span<s16> samples) {
|
||||||
static_cast<s16>(std::clamp(right_sample, min, max));
|
static_cast<s16>(std::clamp(right_sample, min, max));
|
||||||
}
|
}
|
||||||
|
|
||||||
samples = samples.subspan(0, samples.size() / system_channels * device_channels);
|
samples_buffer.Push(samples.subspan(0, samples.size() / system_channels * device_channels));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
} else if (system_channels == 2 && device_channels == 6) {
|
if (system_channels == 2 && device_channels == 6) {
|
||||||
// We need moar samples! Not all games will provide 6 channel audio.
|
// We need moar samples! Not all games will provide 6 channel audio.
|
||||||
// TODO: Implement some upmixing here. Currently just passthrough, with other
|
// TODO: Implement some upmixing here. Currently just passthrough, with other
|
||||||
// channels left as silence.
|
// channels left as silence.
|
||||||
auto new_size = samples.size() / system_channels * device_channels;
|
std::vector<s16> new_samples(samples.size() / system_channels * device_channels);
|
||||||
tmp_samples.resize_destructive(new_size);
|
|
||||||
|
|
||||||
for (u32 read_index = 0, write_index = 0; read_index < new_size;
|
for (u32 read_index = 0, write_index = 0; read_index < samples.size();
|
||||||
read_index += system_channels, write_index += device_channels) {
|
read_index += system_channels, write_index += device_channels) {
|
||||||
const auto left_sample{static_cast<s16>(std::clamp(
|
const auto left_sample{static_cast<s16>(std::clamp(
|
||||||
static_cast<s32>(
|
static_cast<s32>(
|
||||||
|
@ -83,7 +88,7 @@ void SinkStream::AppendBuffer(SinkBuffer& buffer, std::span<s16> samples) {
|
||||||
volume),
|
volume),
|
||||||
min, max))};
|
min, max))};
|
||||||
|
|
||||||
tmp_samples[write_index + static_cast<u32>(Channels::FrontLeft)] = left_sample;
|
new_samples[write_index + static_cast<u32>(Channels::FrontLeft)] = left_sample;
|
||||||
|
|
||||||
const auto right_sample{static_cast<s16>(std::clamp(
|
const auto right_sample{static_cast<s16>(std::clamp(
|
||||||
static_cast<s32>(
|
static_cast<s32>(
|
||||||
|
@ -91,20 +96,21 @@ void SinkStream::AppendBuffer(SinkBuffer& buffer, std::span<s16> samples) {
|
||||||
volume),
|
volume),
|
||||||
min, max))};
|
min, max))};
|
||||||
|
|
||||||
tmp_samples[write_index + static_cast<u32>(Channels::FrontRight)] = right_sample;
|
new_samples[write_index + static_cast<u32>(Channels::FrontRight)] = right_sample;
|
||||||
}
|
}
|
||||||
samples = std::span<s16>(tmp_samples);
|
|
||||||
|
|
||||||
} else if (volume != 1.0f) {
|
samples_buffer.Push(new_samples);
|
||||||
for (u32 i = 0; i < samples.size(); i++) {
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (volume != 1.0f) {
|
||||||
|
for (u32 i = 0; i < samples.size(); ++i) {
|
||||||
samples[i] = static_cast<s16>(
|
samples[i] = static_cast<s16>(
|
||||||
std::clamp(static_cast<s32>(static_cast<f32>(samples[i]) * volume), min, max));
|
std::clamp(static_cast<s32>(static_cast<f32>(samples[i]) * volume), min, max));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
samples_buffer.Push(samples);
|
samples_buffer.Push(samples);
|
||||||
queue.enqueue(buffer);
|
|
||||||
queued_buffers++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<s16> SinkStream::ReleaseBuffer(u64 num_samples) {
|
std::vector<s16> SinkStream::ReleaseBuffer(u64 num_samples) {
|
||||||
|
|
|
@ -16,7 +16,6 @@
|
||||||
#include "common/polyfill_thread.h"
|
#include "common/polyfill_thread.h"
|
||||||
#include "common/reader_writer_queue.h"
|
#include "common/reader_writer_queue.h"
|
||||||
#include "common/ring_buffer.h"
|
#include "common/ring_buffer.h"
|
||||||
#include "common/scratch_buffer.h"
|
|
||||||
#include "common/thread.h"
|
#include "common/thread.h"
|
||||||
|
|
||||||
namespace Core {
|
namespace Core {
|
||||||
|
@ -256,8 +255,6 @@ private:
|
||||||
/// Signalled when ring buffer entries are consumed
|
/// Signalled when ring buffer entries are consumed
|
||||||
std::condition_variable_any release_cv;
|
std::condition_variable_any release_cv;
|
||||||
std::mutex release_mutex;
|
std::mutex release_mutex;
|
||||||
/// Temporary buffer for appending samples when upmixing
|
|
||||||
Common::ScratchBuffer<s16> tmp_samples{};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
using SinkStreamPtr = std::unique_ptr<SinkStream>;
|
using SinkStreamPtr = std::unique_ptr<SinkStream>;
|
||||||
|
|
Loading…
Reference in a new issue