From f346b04d12efc2fb60c26ce38327bba8a172575e Mon Sep 17 00:00:00 2001 From: ameerj <52414509+ameerj@users.noreply.github.com> Date: Sun, 10 Oct 2021 18:44:16 -0400 Subject: [PATCH] vic: Use the minimum of surface/frame dimensions when writing the final frame to the GPU Addresses possible buffer overflow behavior. --- src/video_core/command_classes/vic.cpp | 31 +++++++++++++------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/src/video_core/command_classes/vic.cpp b/src/video_core/command_classes/vic.cpp index dc768b952..051616124 100644 --- a/src/video_core/command_classes/vic.cpp +++ b/src/video_core/command_classes/vic.cpp @@ -32,7 +32,7 @@ enum class VideoPixelFormat : u64_le { RGBA8 = 0x1f, BGRA8 = 0x20, RGBX8 = 0x23, - Yuv420 = 0x44, + YUV420 = 0x44, }; } // Anonymous namespace @@ -88,12 +88,10 @@ void Vic::Execute() { const u64 surface_width = config.surface_width_minus1 + 1; const u64 surface_height = config.surface_height_minus1 + 1; if (static_cast(frame->width) != surface_width || - static_cast(frame->height) > surface_height) { + static_cast(frame->height) != surface_height) { // TODO: Properly support multiple video streams with differing frame dimensions - LOG_WARNING(Debug, - "Frame dimensions {}x{} can't be safely decoded into surface dimensions {}x{}", + LOG_WARNING(Service_NVDRV, "Frame dimensions {}x{} don't match surface dimensions {}x{}", frame->width, frame->height, surface_width, surface_height); - return; } switch (config.pixel_format) { case VideoPixelFormat::RGBA8: @@ -101,7 +99,7 @@ void Vic::Execute() { case VideoPixelFormat::RGBX8: WriteRGBFrame(frame, config); break; - case VideoPixelFormat::Yuv420: + case VideoPixelFormat::YUV420: WriteYUVFrame(frame, config); break; default: @@ -136,21 +134,20 @@ void Vic::WriteRGBFrame(const AVFrame* frame, const VicConfig& config) { scaler_height = frame->height; converted_frame_buffer.reset(); } - // Get Converted frame - const u32 width = static_cast(frame->width); - const u32 height = static_cast(frame->height); - const std::size_t linear_size = width * height * 4; - - // Only allocate frame_buffer once per stream, as the size is not expected to change if (!converted_frame_buffer) { - converted_frame_buffer = AVMallocPtr{static_cast(av_malloc(linear_size)), av_free}; + const size_t frame_size = frame->width * frame->height * 4; + converted_frame_buffer = AVMallocPtr{static_cast(av_malloc(frame_size)), av_free}; } const std::array converted_stride{frame->width * 4, frame->height * 4, 0, 0}; u8* const converted_frame_buf_addr{converted_frame_buffer.get()}; - sws_scale(scaler_ctx, frame->data, frame->linesize, 0, frame->height, &converted_frame_buf_addr, converted_stride.data()); + // Use the minimum of surface/frame dimensions to avoid buffer overflow. + const u32 surface_width = static_cast(config.surface_width_minus1) + 1; + const u32 surface_height = static_cast(config.surface_height_minus1) + 1; + const u32 width = std::min(surface_width, static_cast(frame->width)); + const u32 height = std::min(surface_height, static_cast(frame->height)); const u32 blk_kind = static_cast(config.block_linear_kind); if (blk_kind != 0) { // swizzle pitch linear to block linear @@ -158,11 +155,12 @@ void Vic::WriteRGBFrame(const AVFrame* frame, const VicConfig& config) { const auto size = Texture::CalculateSize(true, 4, width, height, 1, block_height, 0); luma_buffer.resize(size); Texture::SwizzleSubrect(width, height, width * 4, width, 4, luma_buffer.data(), - converted_frame_buffer.get(), block_height, 0, 0); + converted_frame_buf_addr, block_height, 0, 0); gpu.MemoryManager().WriteBlock(output_surface_luma_address, luma_buffer.data(), size); } else { // send pitch linear frame + const size_t linear_size = width * height * 4; gpu.MemoryManager().WriteBlock(output_surface_luma_address, converted_frame_buf_addr, linear_size); } @@ -173,9 +171,10 @@ void Vic::WriteYUVFrame(const AVFrame* frame, const VicConfig& config) { const std::size_t surface_width = config.surface_width_minus1 + 1; const std::size_t surface_height = config.surface_height_minus1 + 1; + const std::size_t aligned_width = (surface_width + 0xff) & ~0xffUL; + // Use the minimum of surface/frame dimensions to avoid buffer overflow. const auto frame_width = std::min(surface_width, static_cast(frame->width)); const auto frame_height = std::min(surface_height, static_cast(frame->height)); - const std::size_t aligned_width = (surface_width + 0xff) & ~0xffUL; const auto stride = static_cast(frame->linesize[0]);