Skip to content

Commit 3b61628

Browse files
committed
fix(dds): corruption protection: validate resolution + overflow care (AcademySoftwareFoundation#5131)
Signed-off-by: Larry Gritz <lg@larrygritz.com>
1 parent 9ff6a7d commit 3b61628

1 file changed

Lines changed: 58 additions & 27 deletions

File tree

src/dds.imageio/ddsinput.cpp

Lines changed: 58 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -97,11 +97,11 @@ class DDSInput final : public ImageInput {
9797

9898
/// Helper function: performs the actual file seeking.
9999
///
100-
void internal_seek_subimage(int cubeface, int miplevel, unsigned int& w,
101-
unsigned int& h, unsigned int& d);
100+
void internal_seek_subimage(int cubeface, int miplevel, size_t& w,
101+
size_t& h, size_t& d);
102102

103103
/// Helper function: performs the actual pixel decoding.
104-
bool internal_readimg(unsigned char* dst, int w, int h, int d);
104+
bool internal_readimg(unsigned char* dst, size_t w, size_t h, size_t d);
105105

106106
static bool validate_signature(uint32_t signature);
107107
};
@@ -671,8 +671,8 @@ DDSInput::calc_shifts(uint32_t mask, uint32_t& count, uint32_t& right)
671671
// NOTE: This function has no sanity checks! It's a private method and relies
672672
// on the input being correct and valid!
673673
void
674-
DDSInput::internal_seek_subimage(int cubeface, int miplevel, unsigned int& w,
675-
unsigned int& h, unsigned int& d)
674+
DDSInput::internal_seek_subimage(int cubeface, int miplevel, size_t& w,
675+
size_t& h, size_t& d)
676676
{
677677
// early out for cubemaps that don't contain the requested face
678678
if (m_dds.caps.flags2 & DDS_CAPS2_CUBEMAP
@@ -683,10 +683,10 @@ DDSInput::internal_seek_subimage(int cubeface, int miplevel, unsigned int& w,
683683
// we can easily calculate the offsets because both compressed and
684684
// uncompressed images have predictable length
685685
// calculate the offset; start with after the header
686-
unsigned int ofs = sizeof(dds_header);
686+
size_t ofs = sizeof(dds_header);
687687
if (m_dds.fmt.fourCC == DDS_4CC_DX10)
688688
ofs += sizeof(dds_header_dx10);
689-
unsigned int len;
689+
size_t len;
690690
// this loop is used to iterate over cube map sides, or run once in the
691691
// case of ordinary 2D or 3D images
692692
for (int j = 0; j <= cubeface; j++) {
@@ -754,7 +754,7 @@ DDSInput::seek_subimage(int subimage, int miplevel)
754754
m_buf.clear();
755755

756756
// for cube maps, the seek will be performed when reading a tile instead
757-
unsigned int w = 0, h = 0, d = 0;
757+
size_t w = 0, h = 0, d = 0;
758758
TypeDesc::BASETYPE basetype = GetBaseType(m_compression);
759759
if (m_dds.caps.flags2 & DDS_CAPS2_CUBEMAP) {
760760
// calc sizes separately for cube maps
@@ -896,13 +896,42 @@ DDSInput::seek_subimage(int subimage, int miplevel)
896896
m_spec.attribute("textureformat", "Plain Texture");
897897
}
898898

899+
// Check validity of resolutions.
900+
if (m_dds.caps.flags2 & DDS_CAPS2_CUBEMAP) {
901+
// cube maps must be square and each face can be up to 16384x16384
902+
// (currently). But remember that they are stored in a 3x2 or 1x6
903+
// layout.
904+
#ifdef DDS_3X2_CUBE_MAP_LAYOUT
905+
if (!check_open(m_spec, { 0, 16384 * 3, 0, 16384 * 2, 0, 1, 0, 4 }))
906+
return false;
907+
#else
908+
if (!check_open(m_spec, { 0, 16384, 0, 16384 * 6, 0, 1, 0, 4 }))
909+
return false;
910+
#endif
911+
if (m_spec.full_width != m_spec.full_height) {
912+
errorfmt(
913+
"Invalid cube map layout: width {} does not match height {}",
914+
m_spec.full_width, m_spec.full_height);
915+
return false;
916+
}
917+
} else if (m_dds.caps.flags2 & DDS_CAPS2_VOLUME) {
918+
// volume textures are limited to 4096x4096x4096 (currently)
919+
if (!check_open(m_spec, { 0, 4096, 0, 4096, 0, 4096, 0, 4 })) {
920+
return false;
921+
}
922+
} else {
923+
// 2D textures can be up to 32768x32768
924+
if (!check_open(m_spec, { 0, 32768, 0, 32768, 0, 1, 0, 4 }))
925+
return false;
926+
}
927+
899928
m_subimage = subimage;
900929
m_miplevel = miplevel;
901930
return true;
902931
}
903932

904933
bool
905-
DDSInput::internal_readimg(unsigned char* dst, int w, int h, int d)
934+
DDSInput::internal_readimg(unsigned char* dst, size_t w, size_t h, size_t d)
906935
{
907936
if (m_compression != Compression::None) {
908937
// compressed image
@@ -913,15 +942,15 @@ DDSInput::internal_readimg(unsigned char* dst, int w, int h, int d)
913942
if (!ioread(tmp.get(), bufsize, 1))
914943
return false;
915944
// decompress image
916-
DecompressImage(dst, w, h, tmp.get(), m_compression, m_dds.fmt,
917-
threads());
945+
DecompressImage(dst, int(w), int(h), tmp.get(), m_compression,
946+
m_dds.fmt, threads());
918947
tmp.reset();
919948
// correct pre-multiplied alpha, if necessary
920949
if (m_compression == Compression::DXT2
921950
|| m_compression == Compression::DXT4) {
922-
int k;
923-
for (int y = 0; y < h; y++) {
924-
for (int x = 0; x < w; x++) {
951+
size_t k;
952+
for (size_t y = 0; y < h; y++) {
953+
for (size_t x = 0; x < w; x++) {
925954
k = (y * w + x) * 4;
926955
if (dst[k + 3]) {
927956
dst[k + 0] = (unsigned char)((int)dst[k + 0] * 255
@@ -954,12 +983,12 @@ DDSInput::internal_readimg(unsigned char* dst, int w, int h, int d)
954983
}
955984

956985
std::unique_ptr<uint8_t[]> tmp(new uint8_t[w * m_Bpp]);
957-
for (int z = 0; z < d; z++) {
958-
for (int y = 0; y < h; y++) {
986+
for (size_t z = 0; z < d; z++) {
987+
for (size_t y = 0; y < h; y++) {
959988
if (!ioread(tmp.get(), w, m_Bpp))
960989
return false;
961990
size_t k = (z * h * w + y * w) * m_spec.nchannels;
962-
for (int x = 0; x < w; x++, k += m_spec.nchannels) {
991+
for (size_t x = 0; x < w; x++, k += m_spec.nchannels) {
963992
uint32_t pixel = 0;
964993
memcpy(&pixel, tmp.get() + x * m_Bpp, m_Bpp);
965994
for (int ch = 0; ch < m_spec.nchannels; ++ch) {
@@ -985,8 +1014,8 @@ DDSInput::readimg_scanlines()
9851014
m_buf.resize(m_spec.scanline_bytes() * m_spec.height * m_spec.depth
9861015
/*/ (1 << m_miplevel)*/);
9871016

988-
return internal_readimg(&m_buf[0], m_spec.width, m_spec.height,
989-
m_spec.depth);
1017+
return internal_readimg(&m_buf[0], size_t(m_spec.width),
1018+
size_t(m_spec.height), size_t(m_spec.depth));
9901019
}
9911020

9921021

@@ -996,8 +1025,9 @@ DDSInput::readimg_tiles()
9961025
{
9971026
// resize destination buffer
9981027
OIIO_ASSERT(m_buf.size() >= m_spec.tile_bytes());
999-
return internal_readimg(&m_buf[0], m_spec.tile_width, m_spec.tile_height,
1000-
m_spec.tile_depth);
1028+
return internal_readimg(&m_buf[0], size_t(m_spec.tile_width),
1029+
size_t(m_spec.tile_height),
1030+
size_t(m_spec.tile_depth));
10011031
}
10021032

10031033

@@ -1025,8 +1055,9 @@ DDSInput::read_native_scanline(int subimage, int miplevel, int y, int z,
10251055
if (m_buf.empty())
10261056
readimg_scanlines();
10271057

1028-
size_t size = spec().scanline_bytes();
1029-
memcpy(data, &m_buf[0] + z * m_spec.height * size + y * size, size);
1058+
size_t size = spec().scanline_bytes();
1059+
size_t offset = size_t(z) * m_spec.height * size + size_t(y) * size;
1060+
memcpy(data, &m_buf[0] + offset, size);
10301061
return true;
10311062
}
10321063

@@ -1051,10 +1082,10 @@ DDSInput::read_native_tile(int subimage, int miplevel, int x, int y, int z,
10511082
|| z % m_spec.tile_width)
10521083
return false;
10531084
if (m_buf.empty() || x != lastx || y != lasty || z != lastz) {
1054-
lastx = x;
1055-
lasty = y;
1056-
lastz = z;
1057-
unsigned int w = 0, h = 0, d = 0;
1085+
lastx = x;
1086+
lasty = y;
1087+
lastz = z;
1088+
size_t w = 0, h = 0, d = 0;
10581089
#ifdef DDS_3X2_CUBE_MAP_LAYOUT
10591090
internal_seek_subimage(((x / m_spec.tile_width) << 1)
10601091
+ y / m_spec.tile_height,

0 commit comments

Comments
 (0)