Skip to content

Commit 209c1bf

Browse files
authored
IFF output safety (#3676)
* Maya IFF documentation says IFF file has max resolution of 8192, and our implementation only allows writing RGB and RGBA, so check these limits when an IFF file is opened for output. * Make sure scratch space allocates enough for tile padding. * Check for non-zero image origin coordinates (which are not allowed in IFF files). * Change some 16-bit loop variables to uint32_t to avoid possible overflow. Fixes TALOS-2022-1654, TALOS-2022-1655, TALOS-2022-1656
1 parent 9ccbc43 commit 209c1bf

File tree

2 files changed

+60
-43
lines changed

2 files changed

+60
-43
lines changed

src/iff.imageio/iff_pvt.h

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -77,19 +77,17 @@ align_size(uint32_t size, uint32_t alignment)
7777
}
7878

7979
// tile width
80-
inline const int&
80+
constexpr uint32_t
8181
tile_width()
8282
{
83-
static int tile_w = 64;
84-
return tile_w;
83+
return 64;
8584
}
8685

8786
// tile height
88-
inline const int&
87+
constexpr uint32_t
8988
tile_height()
9089
{
91-
static int tile_h = 64;
92-
return tile_h;
90+
return 64;
9391
}
9492

9593
// tile width size

src/iff.imageio/iffoutput.cpp

Lines changed: 56 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ int
107107
IffOutput::supports(string_view feature) const
108108
{
109109
return (feature == "tiles" || feature == "alpha" || feature == "nchannels"
110-
|| feature == "ioproxy");
110+
|| feature == "ioproxy" || feature == "origin");
111111
}
112112

113113

@@ -142,16 +142,37 @@ IffOutput::open(const std::string& name, const ImageSpec& spec, OpenMode mode)
142142
return false;
143143
}
144144

145-
// close(); // Close any already-opened file
146145
// saving 'name' and 'spec' for later use
147146
m_filename = name;
148147
m_spec = spec;
149148

149+
// Maya docs say 8k is the limit
150+
if (m_spec.width < 1 || m_spec.width > 8192 || m_spec.height < 1
151+
|| m_spec.height > 8192) {
152+
errorfmt("Image resolution {} x {} is not valid for an IFF file",
153+
m_spec.width, m_spec.height);
154+
return false;
155+
}
156+
// This implementation only supports writing RGB and RGBA images as IFF
157+
if (m_spec.nchannels < 3 || m_spec.nchannels > 4) {
158+
errorfmt("Cannot write IFF file with {} channels", m_spec.nchannels);
159+
return false;
160+
}
161+
150162
// tiles
151163
m_spec.tile_width = tile_width();
152164
m_spec.tile_height = tile_height();
153165
m_spec.tile_depth = 1;
154166

167+
uint64_t xtiles = tile_width_size(m_spec.width);
168+
uint64_t ytiles = tile_height_size(m_spec.height);
169+
if (xtiles * ytiles >= (1 << 16)) { // The format can't store it!
170+
errorfmt(
171+
"Too high a resolution ({}x{}), exceeds maximum of 64k tiles in the image\n",
172+
m_spec.width, m_spec.height);
173+
return false;
174+
}
175+
155176
ioproxy_retrieve_from_config(m_spec);
156177
if (!ioproxy_use_or_open(name))
157178
return false;
@@ -172,16 +193,6 @@ IffOutput::open(const std::string& name, const ImageSpec& spec, OpenMode mode)
172193
m_iff_header.compression
173194
= (m_spec.get_string_attribute("compression") == "none") ? NONE : RLE;
174195

175-
uint64_t xtiles = tile_width_size(m_spec.width);
176-
uint64_t ytiles = tile_height_size(m_spec.height);
177-
if (xtiles * ytiles >= (1 << 16)) { // The format can't store it!
178-
errorfmt(
179-
"Too high a resolution ({}x{}), exceeds maximum of 64k tiles in the image\n",
180-
m_spec.width, m_spec.height);
181-
close();
182-
return false;
183-
}
184-
185196
// we write the header of the file
186197
m_iff_header.x = m_spec.x;
187198
m_iff_header.y = m_spec.y;
@@ -274,6 +285,11 @@ bool
274285
IffOutput::write_scanline(int y, int z, TypeDesc format, const void* data,
275286
stride_t xstride)
276287
{
288+
if (!ioproxy_opened()) {
289+
errorfmt("write_scanline called but file is not open.");
290+
return false;
291+
}
292+
277293
// scanline not used for Maya IFF, uses tiles instead.
278294
// Emulate by copying the scanline to the buffer we're accumulating.
279295
std::vector<unsigned char> scratch;
@@ -291,6 +307,11 @@ bool
291307
IffOutput::write_tile(int x, int y, int z, TypeDesc format, const void* data,
292308
stride_t xstride, stride_t ystride, stride_t zstride)
293309
{
310+
if (!ioproxy_opened()) {
311+
errorfmt("write_tile called but file is not open.");
312+
return false;
313+
}
314+
294315
// auto stride
295316
m_spec.auto_stride(xstride, ystride, zstride, format, spec().nchannels,
296317
spec().tile_width, spec().tile_height);
@@ -353,15 +374,13 @@ IffOutput::close(void)
353374
uint8_t channels = m_iff_header.pixel_channels;
354375

355376
// set tile coordinates
356-
uint16_t xmin, xmax, ymin, ymax;
357-
358-
// set xmin and xmax
359-
xmin = tx * tile_width();
360-
xmax = std::min(xmin + tile_width(), m_spec.width) - 1;
361-
362-
// set ymin and ymax
363-
ymin = ty * tile_height();
364-
ymax = std::min(ymin + tile_height(), m_spec.height) - 1;
377+
uint32_t xmin = tx * tile_width();
378+
uint32_t xmax
379+
= std::min(xmin + tile_width(), uint32_t(m_spec.width)) - 1;
380+
uint32_t ymin = ty * tile_height();
381+
uint32_t ymax = std::min(ymin + tile_height(),
382+
uint32_t(m_spec.height))
383+
- 1;
365384

366385
// set width and height
367386
uint32_t tw = xmax - xmin + 1;
@@ -408,13 +427,11 @@ IffOutput::close(void)
408427
uint8_t* in_p = &in[0];
409428

410429
// set tile
411-
for (uint16_t py = ymin; py <= ymax; py++) {
430+
for (uint32_t py = ymin; py <= ymax; py++) {
412431
const uint8_t* in_dy
413-
= &m_buf[0]
414-
+ (py * m_spec.width)
415-
* m_spec.pixel_bytes();
432+
= &m_buf[0] + py * m_spec.scanline_bytes();
416433

417-
for (uint16_t px = xmin; px <= xmax; px++) {
434+
for (uint32_t px = xmin; px <= xmax; px++) {
418435
// get pixel
419436
uint8_t pixel;
420437
const uint8_t* in_dx
@@ -445,6 +462,8 @@ IffOutput::close(void)
445462
// set length
446463
uint32_t align = align_size(length, 4);
447464
if (align > length) {
465+
if (scratch.size() < index + align - length)
466+
scratch.resize(index + align - length);
448467
out_p = &scratch[0] + index;
449468
// Pad.
450469
for (uint32_t i = 0; i < align - length; i++) {
@@ -457,12 +476,12 @@ IffOutput::close(void)
457476
}
458477
}
459478
if (!tile_compress) {
460-
for (uint16_t py = ymin; py <= ymax; py++) {
479+
for (uint32_t py = ymin; py <= ymax; py++) {
461480
const uint8_t* in_dy = &m_buf[0]
462481
+ (py * m_spec.width)
463482
* m_spec.pixel_bytes();
464483

465-
for (uint16_t px = xmin; px <= xmax; px++) {
484+
for (uint32_t px = xmin; px <= xmax; px++) {
466485
// Map: RGB(A)8 RGBA to BGRA
467486
for (int c = channels - 1; c >= 0; --c) {
468487
// get pixel
@@ -517,13 +536,11 @@ IffOutput::close(void)
517536
uint8_t* in_p = &in[0];
518537

519538
// set tile
520-
for (uint16_t py = ymin; py <= ymax; py++) {
539+
for (uint32_t py = ymin; py <= ymax; py++) {
521540
const uint8_t* in_dy
522-
= &m_buf[0]
523-
+ (py * m_spec.width)
524-
* m_spec.pixel_bytes();
541+
= &m_buf[0] + py * m_spec.scanline_bytes();
525542

526-
for (uint16_t px = xmin; px <= xmax; px++) {
543+
for (uint32_t px = xmin; px <= xmax; px++) {
527544
// get pixel
528545
uint8_t pixel;
529546
const uint8_t* in_dx
@@ -555,6 +572,8 @@ IffOutput::close(void)
555572
// set length
556573
uint32_t align = align_size(length, 4);
557574
if (align > length) {
575+
if (scratch.size() < index + align - length)
576+
scratch.resize(index + align - length);
558577
out_p = &scratch[0] + index;
559578
// Pad.
560579
for (uint32_t i = 0; i < align - length; i++) {
@@ -568,12 +587,12 @@ IffOutput::close(void)
568587
}
569588

570589
if (!tile_compress) {
571-
for (uint16_t py = ymin; py <= ymax; py++) {
590+
for (uint32_t py = ymin; py <= ymax; py++) {
572591
const uint8_t* in_dy = &m_buf[0]
573592
+ (py * m_spec.width)
574593
* m_spec.pixel_bytes();
575594

576-
for (uint16_t px = xmin; px <= xmax; px++) {
595+
for (uint32_t px = xmin; px <= xmax; px++) {
577596
// map: RGB(A) to BGRA
578597
for (int c = channels - 1; c >= 0; --c) {
579598
uint16_t pixel;
@@ -597,8 +616,8 @@ IffOutput::close(void)
597616
return false;
598617

599618
// write xmin, xmax, ymin and ymax
600-
if (!write(&xmin) || !write(&ymin) || !write(&xmax)
601-
|| !write(&ymax))
619+
if (!write_short(xmin) || !write_short(ymin)
620+
|| !write_short(xmax) || !write_short(ymax))
602621
return false;
603622

604623
// write tile

0 commit comments

Comments
 (0)