Skip to content

Commit 8499b20

Browse files
committed
fix(rla): Harden against buffer overruns from malformed RLE blocks
Fixes 5152 Signed-off-by: Larry Gritz <lg@larrygritz.com>
1 parent c6c80bc commit 8499b20

4 files changed

Lines changed: 23 additions & 13 deletions

File tree

src/rla.imageio/rlainput.cpp

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -86,11 +86,11 @@ class RLAInput final : public ImageInput {
8686
bool decode_channel_group(int first_channel, short num_channels,
8787
short num_bits, int y);
8888

89-
/// Helper: decode a span of n RLE-encoded bytes from encoded[0..elen-1]
89+
/// Helper: decode a span of n RLE-encoded bytes from encoded[]
9090
/// into buf[0],buf[stride],buf[2*stride]...buf[(n-1)*stride].
9191
/// Return the number of encoded bytes we ate to fill buf.
92-
size_t decode_rle_span(unsigned char* buf, int n, int stride,
93-
const char* encoded, size_t elen);
92+
size_t decode_rle_span(span<unsigned char> buf, int n, int stride,
93+
cspan<char> encoded);
9494

9595
/// Helper: determine channel TypeDesc
9696
inline TypeDesc get_channel_typedesc(short chan_type, short chan_bits);
@@ -490,23 +490,28 @@ RLAInput::close()
490490

491491

492492
size_t
493-
RLAInput::decode_rle_span(unsigned char* buf, int n, int stride,
494-
const char* encoded, size_t elen)
493+
RLAInput::decode_rle_span(span<unsigned char> buf, int n, int stride,
494+
cspan<char> encoded)
495495
{
496-
size_t e = 0;
496+
size_t e = 0; // position we're reading in encoded
497+
size_t elen = encoded.size(); // Number of encoded bytes to decode
498+
size_t b = 0; // postition we're writing in buf
497499
while (n > 0 && e < elen) {
498500
int count = (signed char)encoded[e++];
499501
if (count >= 0) {
500502
// run count positive: value repeated count+1 times
501-
for (int i = 0; i <= count && n && e < elen;
502-
++i, buf += stride, --n)
503-
*buf = encoded[e];
503+
if (count + 1 > n)
504+
break; // asking for a count that will overrun the buffer
505+
for (int i = 0; i <= count; ++i, b += stride, --n)
506+
buf[b] = encoded[e];
504507
++e;
505508
} else {
506509
// run count negative: repeat bytes literally
510+
if (count > n)
511+
break; // asking for a count that will overrun the buffer
507512
count = -count; // make it positive
508-
for (; count && n > 0 && e < elen; --count, buf += stride, --n)
509-
*buf = encoded[e++];
513+
for (; count && n > 0 && e < elen; --count, b += stride, --n)
514+
buf[b] = encoded[e++];
510515
}
511516
}
512517
if (n != 0) {
@@ -583,9 +588,10 @@ RLAInput::decode_channel_group(int first_channel, short num_channels,
583588
// and strides to decode_rle_span.
584589
size_t eoffset = 0;
585590
for (int bytes = 0; bytes < chsize && length > 0; ++bytes) {
586-
size_t e = decode_rle_span(&m_buf[offset + c * chsize + bytes],
591+
size_t e = decode_rle_span(make_span(m_buf).subspan(
592+
offset + c * chsize + bytes),
587593
m_spec.width, pixelsize,
588-
&encoded[eoffset], length);
594+
make_span(encoded).subspan(eoffset));
589595
if (!e)
590596
return false;
591597
eoffset += e;

testsuite/rla/ref/out.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,5 +322,8 @@ Full command line was:
322322
oiiotool ERROR: read : "src/crash-1.rla": rla image full/display resolution must be at least 1x1, but the file specified -281x18999x1. Possible corrupt input?
323323
Full command line was:
324324
> oiiotool src/crash-1.rla -o crash5.exr
325+
oiiotool ERROR: read : "src/crash-5152.rla": Read error: malformed RLE record
326+
Full command line was:
327+
> oiiotool src/crash-5152.rla -o crash6.exr
325328
Comparing "rlacrop.rla" and "ref/rlacrop.rla"
326329
PASS

testsuite/rla/run.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,5 +23,6 @@
2323
command += oiiotool("src/crash-1629.rla -o crash3.exr", failureok = True)
2424
command += oiiotool("src/crash-3951.rla -o crash4.exr", failureok = True)
2525
command += oiiotool("src/crash-1.rla -o crash5.exr", failureok = True)
26+
command += oiiotool("src/crash-5152.rla -o crash6.exr", failureok = True)
2627

2728
outputs = [ "rlacrop.rla", 'out.txt' ]

testsuite/rla/src/crash-5152.rla

922 Bytes
Binary file not shown.

0 commit comments

Comments
 (0)