Skip to content

Commit 94ec2de

Browse files
authored
fix(dds): corruption protection: validate resolution + overflow care (#5131)
Signed-off-by: Larry Gritz <lg@larrygritz.com>
1 parent ec92d1e commit 94ec2de

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
@@ -903,13 +903,42 @@ DDSInput::seek_subimage(int subimage, int miplevel)
903903
m_spec.attribute("textureformat", "Plain Texture");
904904
}
905905

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

911940
bool
912-
DDSInput::internal_readimg(unsigned char* dst, int w, int h, int d)
941+
DDSInput::internal_readimg(unsigned char* dst, size_t w, size_t h, size_t d)
913942
{
914943
if (m_compression != Compression::None) {
915944
// compressed image
@@ -920,15 +949,15 @@ DDSInput::internal_readimg(unsigned char* dst, int w, int h, int d)
920949
if (!ioread(tmp.get(), bufsize, 1))
921950
return false;
922951
// decompress image
923-
DecompressImage(dst, w, h, tmp.get(), m_compression, m_dds.fmt,
924-
threads());
952+
DecompressImage(dst, int(w), int(h), tmp.get(), m_compression,
953+
m_dds.fmt, threads());
925954
tmp.reset();
926955
// correct pre-multiplied alpha, if necessary
927956
if (m_compression == Compression::DXT2
928957
|| m_compression == Compression::DXT4) {
929-
int k;
930-
for (int y = 0; y < h; y++) {
931-
for (int x = 0; x < w; x++) {
958+
size_t k;
959+
for (size_t y = 0; y < h; y++) {
960+
for (size_t x = 0; x < w; x++) {
932961
k = (y * w + x) * 4;
933962
if (dst[k + 3]) {
934963
dst[k + 0] = (unsigned char)((int)dst[k + 0] * 255
@@ -961,12 +990,12 @@ DDSInput::internal_readimg(unsigned char* dst, int w, int h, int d)
961990
}
962991

963992
std::unique_ptr<uint8_t[]> tmp(new uint8_t[w * m_Bpp]);
964-
for (int z = 0; z < d; z++) {
965-
for (int y = 0; y < h; y++) {
993+
for (size_t z = 0; z < d; z++) {
994+
for (size_t y = 0; y < h; y++) {
966995
if (!ioread(tmp.get(), w, m_Bpp))
967996
return false;
968997
size_t k = (z * h * w + y * w) * m_spec.nchannels;
969-
for (int x = 0; x < w; x++, k += m_spec.nchannels) {
998+
for (size_t x = 0; x < w; x++, k += m_spec.nchannels) {
970999
uint32_t pixel = 0;
9711000
memcpy(&pixel, tmp.get() + x * m_Bpp, m_Bpp);
9721001
for (int ch = 0; ch < m_spec.nchannels; ++ch) {
@@ -992,8 +1021,8 @@ DDSInput::readimg_scanlines()
9921021
m_buf.resize(m_spec.scanline_bytes() * m_spec.height * m_spec.depth
9931022
/*/ (1 << m_miplevel)*/);
9941023

995-
return internal_readimg(&m_buf[0], m_spec.width, m_spec.height,
996-
m_spec.depth);
1024+
return internal_readimg(&m_buf[0], size_t(m_spec.width),
1025+
size_t(m_spec.height), size_t(m_spec.depth));
9971026
}
9981027

9991028

@@ -1003,8 +1032,9 @@ DDSInput::readimg_tiles()
10031032
{
10041033
// resize destination buffer
10051034
OIIO_ASSERT(m_buf.size() >= m_spec.tile_bytes());
1006-
return internal_readimg(&m_buf[0], m_spec.tile_width, m_spec.tile_height,
1007-
m_spec.tile_depth);
1035+
return internal_readimg(&m_buf[0], size_t(m_spec.tile_width),
1036+
size_t(m_spec.tile_height),
1037+
size_t(m_spec.tile_depth));
10081038
}
10091039

10101040

@@ -1032,8 +1062,9 @@ DDSInput::read_native_scanline(int subimage, int miplevel, int y, int z,
10321062
if (m_buf.empty())
10331063
readimg_scanlines();
10341064

1035-
size_t size = spec().scanline_bytes();
1036-
memcpy(data, &m_buf[0] + z * m_spec.height * size + y * size, size);
1065+
size_t size = spec().scanline_bytes();
1066+
size_t offset = size_t(z) * m_spec.height * size + size_t(y) * size;
1067+
memcpy(data, &m_buf[0] + offset, size);
10371068
return true;
10381069
}
10391070

@@ -1075,10 +1106,10 @@ DDSInput::read_native_tile(int subimage, int miplevel, int x, int y, int z,
10751106
|| z % m_spec.tile_width)
10761107
return false;
10771108
if (m_buf.empty() || x != lastx || y != lasty || z != lastz) {
1078-
lastx = x;
1079-
lasty = y;
1080-
lastz = z;
1081-
unsigned int w = 0, h = 0, d = 0;
1109+
lastx = x;
1110+
lasty = y;
1111+
lastz = z;
1112+
size_t w = 0, h = 0, d = 0;
10821113
#ifdef DDS_3X2_CUBE_MAP_LAYOUT
10831114
internal_seek_subimage(((x / m_spec.tile_width) << 1)
10841115
+ y / m_spec.tile_height,

0 commit comments

Comments
 (0)