From bef2412ca253a9d3d63fc713e03997104d350f50 Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Thu, 6 Jan 2022 16:08:40 +0100 Subject: Fix T86952: Buffer overflow reading specific DDS images Add a data boundary check in the flipping code. This code now also communicates the number of mipmap levels it processed with an intent to avoid GPU texture from using more levels than there are in the DDS data. Differential Revision: https://developer.blender.org/D13755 --- source/blender/imbuf/intern/dds/FlipDXT.cpp | 22 ++++++++++++++++++++-- source/blender/imbuf/intern/dds/FlipDXT.h | 9 +++++++-- source/blender/imbuf/intern/dds/dds_api.cpp | 9 +++++++-- 3 files changed, 34 insertions(+), 6 deletions(-) (limited to 'source') diff --git a/source/blender/imbuf/intern/dds/FlipDXT.cpp b/source/blender/imbuf/intern/dds/FlipDXT.cpp index 359d6f30cdc..6686d56e9d1 100644 --- a/source/blender/imbuf/intern/dds/FlipDXT.cpp +++ b/source/blender/imbuf/intern/dds/FlipDXT.cpp @@ -168,9 +168,16 @@ static void FlipDXT5BlockHalf(uint8_t *block) FlipDXT1BlockHalf(block + 8); } -int FlipDXTCImage( - unsigned int width, unsigned int height, unsigned int levels, int fourcc, uint8_t *data) +int FlipDXTCImage(unsigned int width, + unsigned int height, + unsigned int levels, + int fourcc, + uint8_t *data, + int data_size, + unsigned int *r_num_valid_levels) { + *r_num_valid_levels = 0; + /* Must have valid dimensions. */ if (width == 0 || height == 0) { return 0; @@ -204,14 +211,25 @@ int FlipDXTCImage( return 0; } + *r_num_valid_levels = levels; + unsigned int mip_width = width; unsigned int mip_height = height; + const uint8_t *data_end = data + data_size; + for (unsigned int i = 0; i < levels; i++) { unsigned int blocks_per_row = (mip_width + 3) / 4; unsigned int blocks_per_col = (mip_height + 3) / 4; unsigned int blocks = blocks_per_row * blocks_per_col; + if (data + block_bytes * blocks > data_end) { + /* Stop flipping when running out of data to be modified, avoiding possible buffer overrun + * on a malformed files. */ + *r_num_valid_levels = i; + break; + } + if (mip_height == 1) { /* no flip to do, and we're done. */ break; diff --git a/source/blender/imbuf/intern/dds/FlipDXT.h b/source/blender/imbuf/intern/dds/FlipDXT.h index b4f71e4eca7..f9d3ce6112e 100644 --- a/source/blender/imbuf/intern/dds/FlipDXT.h +++ b/source/blender/imbuf/intern/dds/FlipDXT.h @@ -23,5 +23,10 @@ * * Use to flip vertically to fit OpenGL convention. */ -int FlipDXTCImage( - unsigned int width, unsigned int height, unsigned int levels, int fourcc, uint8_t *data); +int FlipDXTCImage(unsigned int width, + unsigned int height, + unsigned int levels, + int fourcc, + uint8_t *data, + int data_size, + unsigned int *r_num_valid_levels); diff --git a/source/blender/imbuf/intern/dds/dds_api.cpp b/source/blender/imbuf/intern/dds/dds_api.cpp index 1729a9a64f8..f576209ff62 100644 --- a/source/blender/imbuf/intern/dds/dds_api.cpp +++ b/source/blender/imbuf/intern/dds/dds_api.cpp @@ -186,8 +186,13 @@ struct ImBuf *imb_load_dds(const unsigned char *mem, /* flip compressed texture */ if (ibuf->dds_data.data) { - FlipDXTCImage( - dds.width(), dds.height(), dds.mipmapCount(), dds.fourCC(), ibuf->dds_data.data); + FlipDXTCImage(dds.width(), + dds.height(), + ibuf->dds_data.nummipmaps, + dds.fourCC(), + ibuf->dds_data.data, + ibuf->dds_data.size, + &ibuf->dds_data.nummipmaps); } } else { -- cgit v1.2.3