mirror of
https://git.citron-emu.org/citron/emulator
synced 2026-01-25 12:13:27 +00:00
fix(nvdec): add memory validation for H264 decoder and VIC
- Add IsFullyMappedRange checks before GPU memory reads in H264 - Add IsFullyMappedRange checks for VIC config and output surfaces - Zero buffers and skip operations for unmapped memory regions - Prevents crashes from unmapped GPU memory access during video decode
This commit is contained in:
@@ -4,6 +4,7 @@
|
||||
#include <array>
|
||||
#include <bit>
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "common/scratch_buffer.h"
|
||||
#include "common/settings.h"
|
||||
#include "video_core/host1x/codecs/h264.h"
|
||||
@@ -32,12 +33,27 @@ H264::~H264() = default;
|
||||
std::span<const u8> H264::ComposeFrame(const Host1x::NvdecCommon::NvdecRegisters& state,
|
||||
size_t* out_configuration_size, bool is_first_frame) {
|
||||
H264DecoderContext context;
|
||||
|
||||
// Validate picture info memory is mapped before reading
|
||||
if (!host1x.GMMU().IsFullyMappedRange(state.picture_info_offset, sizeof(H264DecoderContext))) {
|
||||
LOG_WARNING(HW_GPU, "H264 picture info at unmapped GPU memory 0x{:016X}",
|
||||
state.picture_info_offset);
|
||||
*out_configuration_size = 0;
|
||||
return {};
|
||||
}
|
||||
host1x.GMMU().ReadBlock(state.picture_info_offset, &context, sizeof(H264DecoderContext));
|
||||
|
||||
const s64 frame_number = context.h264_parameter_set.frame_number.Value();
|
||||
if (!is_first_frame && frame_number != 0) {
|
||||
frame.resize_destructive(context.stream_len);
|
||||
host1x.GMMU().ReadBlock(state.frame_bitstream_offset, frame.data(), frame.size());
|
||||
// Validate bitstream memory is mapped before reading
|
||||
if (!host1x.GMMU().IsFullyMappedRange(state.frame_bitstream_offset, frame.size())) {
|
||||
LOG_WARNING(HW_GPU, "H264 bitstream at unmapped GPU memory 0x{:016X} (size: {} KB)",
|
||||
state.frame_bitstream_offset, frame.size() / 1024);
|
||||
std::memset(frame.data(), 0, frame.size());
|
||||
} else {
|
||||
host1x.GMMU().ReadBlock(state.frame_bitstream_offset, frame.data(), frame.size());
|
||||
}
|
||||
*out_configuration_size = 0;
|
||||
return frame;
|
||||
}
|
||||
@@ -158,8 +174,16 @@ std::span<const u8> H264::ComposeFrame(const Host1x::NvdecCommon::NvdecRegisters
|
||||
std::memcpy(frame.data(), encoded_header.data(), encoded_header.size());
|
||||
|
||||
*out_configuration_size = encoded_header.size();
|
||||
host1x.GMMU().ReadBlock(state.frame_bitstream_offset, frame.data() + encoded_header.size(),
|
||||
context.stream_len);
|
||||
|
||||
// Validate bitstream memory is mapped before reading
|
||||
if (!host1x.GMMU().IsFullyMappedRange(state.frame_bitstream_offset, context.stream_len)) {
|
||||
LOG_WARNING(HW_GPU, "H264 bitstream at unmapped GPU memory 0x{:016X} (size: {} KB)",
|
||||
state.frame_bitstream_offset, context.stream_len / 1024);
|
||||
std::memset(frame.data() + encoded_header.size(), 0, context.stream_len);
|
||||
} else {
|
||||
host1x.GMMU().ReadBlock(state.frame_bitstream_offset, frame.data() + encoded_header.size(),
|
||||
context.stream_len);
|
||||
}
|
||||
|
||||
return frame;
|
||||
}
|
||||
|
||||
@@ -82,6 +82,12 @@ void Vic::Execute() {
|
||||
LOG_ERROR(Service_NVDRV, "VIC Luma address not set.");
|
||||
return;
|
||||
}
|
||||
// Validate config struct memory is mapped before reading
|
||||
if (!host1x.GMMU().IsFullyMappedRange(config_struct_address + 0x20, sizeof(u64))) {
|
||||
LOG_WARNING(HW_GPU, "VIC config at unmapped GPU memory 0x{:016X}",
|
||||
config_struct_address + 0x20);
|
||||
return;
|
||||
}
|
||||
const VicConfig config{host1x.GMMU().Read<u64>(config_struct_address + 0x20)};
|
||||
auto frame = nvdec_processor->GetFrame();
|
||||
if (!frame) {
|
||||
@@ -168,12 +174,24 @@ void Vic::WriteRGBFrame(std::unique_ptr<FFmpeg::Frame> frame, const VicConfig& c
|
||||
Texture::SwizzleSubrect(luma_buffer, frame_buff, 4, width, height, 1, 0, 0, width, height,
|
||||
block_height, 0, width * 4);
|
||||
|
||||
host1x.GMMU().WriteBlock(output_surface_luma_address, luma_buffer.data(), size);
|
||||
// Validate output memory is mapped before writing
|
||||
if (host1x.GMMU().IsFullyMappedRange(output_surface_luma_address, size)) {
|
||||
host1x.GMMU().WriteBlock(output_surface_luma_address, luma_buffer.data(), size);
|
||||
} else {
|
||||
LOG_WARNING(HW_GPU, "VIC RGB output at unmapped GPU memory 0x{:016X} (size: {} KB)",
|
||||
output_surface_luma_address, size / 1024);
|
||||
}
|
||||
} else {
|
||||
// send pitch linear frame
|
||||
const size_t linear_size = width * height * 4;
|
||||
host1x.GMMU().WriteBlock(output_surface_luma_address, converted_frame_buf_addr,
|
||||
linear_size);
|
||||
// Validate output memory is mapped before writing
|
||||
if (host1x.GMMU().IsFullyMappedRange(output_surface_luma_address, linear_size)) {
|
||||
host1x.GMMU().WriteBlock(output_surface_luma_address, converted_frame_buf_addr,
|
||||
linear_size);
|
||||
} else {
|
||||
LOG_WARNING(HW_GPU, "VIC RGB output at unmapped GPU memory 0x{:016X} (size: {} KB)",
|
||||
output_surface_luma_address, linear_size / 1024);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -199,7 +217,13 @@ void Vic::WriteYUVFrame(std::unique_ptr<FFmpeg::Frame> frame, const VicConfig& c
|
||||
const std::size_t dst = y * aligned_width;
|
||||
std::memcpy(luma_buffer.data() + dst, luma_src + src, frame_width);
|
||||
}
|
||||
host1x.GMMU().WriteBlock(output_surface_luma_address, luma_buffer.data(), luma_buffer.size());
|
||||
// Validate luma output memory is mapped before writing
|
||||
if (host1x.GMMU().IsFullyMappedRange(output_surface_luma_address, luma_buffer.size())) {
|
||||
host1x.GMMU().WriteBlock(output_surface_luma_address, luma_buffer.data(), luma_buffer.size());
|
||||
} else {
|
||||
LOG_WARNING(HW_GPU, "VIC YUV luma output at unmapped GPU memory 0x{:016X} (size: {} KB)",
|
||||
output_surface_luma_address, luma_buffer.size() / 1024);
|
||||
}
|
||||
|
||||
// Chroma
|
||||
const std::size_t half_height = frame_height / 2;
|
||||
@@ -238,8 +262,14 @@ void Vic::WriteYUVFrame(std::unique_ptr<FFmpeg::Frame> frame, const VicConfig& c
|
||||
ASSERT(false);
|
||||
break;
|
||||
}
|
||||
host1x.GMMU().WriteBlock(output_surface_chroma_address, chroma_buffer.data(),
|
||||
chroma_buffer.size());
|
||||
// Validate chroma output memory is mapped before writing
|
||||
if (host1x.GMMU().IsFullyMappedRange(output_surface_chroma_address, chroma_buffer.size())) {
|
||||
host1x.GMMU().WriteBlock(output_surface_chroma_address, chroma_buffer.data(),
|
||||
chroma_buffer.size());
|
||||
} else {
|
||||
LOG_WARNING(HW_GPU, "VIC YUV chroma output at unmapped GPU memory 0x{:016X} (size: {} KB)",
|
||||
output_surface_chroma_address, chroma_buffer.size() / 1024);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Host1x
|
||||
|
||||
Reference in New Issue
Block a user