Skip to content

Commit 3f7294a

Browse files
committed
fix(softimage): Various fixes for malformed channel packets (#5156)
* Fix bugs when channel order isn't strictly R,G,B,A by using a `m_channel_map`. * Detect malformed file with too many channel packets. * Gracefully handle duplicate channel bits across packets by not thinking that there are duplicate channels. * Guard against channel packets that contain no channels. Assisted-by: Claude Code / Opus 4.6 (mostly for analysis/identification of the potential problems; touching the code only for the out-of-order channels fix) Signed-off-by: Larry Gritz <lg@larrygritz.com>
1 parent 6ec094c commit 3f7294a

1 file changed

Lines changed: 36 additions & 12 deletions

File tree

src/softimage.imageio/softimageinput.cpp

Lines changed: 36 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,9 @@ class SoftimageInput final : public ImageInput {
5050
std::vector<softimage_pvt::ChannelPacket> m_channel_packets;
5151
std::string m_filename;
5252
std::vector<fpos_t> m_scanline_markers;
53+
// Maps absolute channel index (0=R,1=G,2=B,3=A) to sequential output
54+
// offset within the scanline buffer. Initialized to -1 (unused).
55+
int m_channel_map[4];
5356
};
5457

5558

@@ -76,6 +79,7 @@ SoftimageInput::init()
7679
m_filename.clear();
7780
m_channel_packets.clear();
7881
m_scanline_markers.clear();
82+
std::fill(m_channel_map, m_channel_map + 4, -1);
7983
}
8084

8185

@@ -110,7 +114,6 @@ SoftimageInput::open(const std::string& name, ImageSpec& spec)
110114

111115
// Get the ChannelPackets
112116
ChannelPacket curPacket;
113-
int nchannels = 0;
114117
std::vector<std::string> encodings;
115118
do {
116119
// Read the next packet into curPacket and store it off
@@ -126,13 +129,31 @@ SoftimageInput::open(const std::string& name, ImageSpec& spec)
126129
close();
127130
return false;
128131
}
132+
if (curPacket.channelCode == 0) {
133+
errorfmt("Channel packet with no channels");
134+
close();
135+
return false;
136+
}
129137
m_channel_packets.push_back(curPacket);
130138

131-
// Add the number of channels in this packet to nchannels
132-
nchannels += curPacket.channels().size();
133139
encodings.push_back(encoding_name(m_channel_packets.back().type));
140+
141+
if (m_channel_packets.size() > 4) {
142+
errorfmt("Too many channel packets");
143+
close();
144+
return false;
145+
}
134146
} while (curPacket.chained);
135147

148+
// Build channel map: absolute RGBA index -> sequential output offset
149+
int nchannels = 0;
150+
{
151+
for (auto& cp : m_channel_packets)
152+
for (int ch : cp.channels())
153+
if (m_channel_map[ch] == -1)
154+
m_channel_map[ch] = nchannels++;
155+
}
156+
136157
// Get the depth per pixel per channel
137158
TypeDesc chanType = TypeDesc::UINT8;
138159
if (curPacket.size == 16)
@@ -314,7 +335,8 @@ SoftimageInput::read_pixels_uncompressed(
314335
//read the data into the correct place
315336
if (fread(&scanlineData[(pixelX * pixelChannelSize
316337
* m_spec.nchannels)
317-
+ (channel * pixelChannelSize)
338+
+ (m_channel_map[channel]
339+
* pixelChannelSize)
318340
+ curByte],
319341
1, 1, m_fd)
320342
!= 1)
@@ -383,7 +405,8 @@ SoftimageInput::read_pixels_pure_run_length(
383405
//put the data into the correct place
384406
scanlineData[(pixelX * pixelChannelSize
385407
* m_spec.nchannels)
386-
+ (channels[curChan] * pixelChannelSize)
408+
+ (m_channel_map[channels[curChan]]
409+
* pixelChannelSize)
387410
+ curByte]
388411
= pixelData[(curChan * pixelChannelSize) + curByte];
389412
}
@@ -445,12 +468,12 @@ SoftimageInput::read_pixels_mixed_run_length(
445468
curByte = ((pixelChannelSize)-1) - curByte;
446469

447470
//read the data into the correct place
448-
if (fread(
449-
&scanlineData[(pixelX * pixelChannelSize
450-
* m_spec.nchannels)
451-
+ (channel * pixelChannelSize)
452-
+ curByte],
453-
1, 1, m_fd)
471+
if (fread(&scanlineData[(pixelX * pixelChannelSize
472+
* m_spec.nchannels)
473+
+ (m_channel_map[channel]
474+
* pixelChannelSize)
475+
+ curByte],
476+
1, 1, m_fd)
454477
!= 1)
455478
return false;
456479
}
@@ -519,7 +542,8 @@ SoftimageInput::read_pixels_mixed_run_length(
519542
//put the data into the correct place
520543
scanlineData[(pixelX * pixelChannelSize
521544
* m_spec.nchannels)
522-
+ (channels[curChan] * pixelChannelSize)
545+
+ (m_channel_map[channels[curChan]]
546+
* pixelChannelSize)
523547
+ curByte]
524548
= pixelData[(curChan * pixelChannelSize)
525549
+ curByte];

0 commit comments

Comments
 (0)