fix(cineon): more robust to invalid numbers of channels & bit depth (#5250)
Guard against corrupted images, which if they said they had more than the 8 channels allowed by Cineon format, would overrun array bounds while reading the file. And check for valid bit depths. Also opportunistically fix a bug I spotted where a red channel was incrementing the count of greyscale channels rather than the count of red channels -- which must have been a copy and paste error from literally 15 years ago. (But those counters are only used for channel naming, and only if there is more than one greyscale or red channel in the file. So I doubt anybody has ever hit this bug, or ever would have in the future.) Fix possible memory leak by having init() call `m_cin.SetInStream()` with a null handle to clear out the internals. Signed-off-by: Larry Gritz <lg@larrygritz.com>
This commit is contained in:
@@ -41,6 +41,7 @@ private:
|
||||
m_stream = nullptr;
|
||||
}
|
||||
m_userBuf.clear();
|
||||
m_cin.SetInStream(nullptr);
|
||||
}
|
||||
|
||||
/// Helper function - retrieve string for libcineon descriptor
|
||||
@@ -89,12 +90,23 @@ CineonInput::open(const std::string& name, ImageSpec& newspec)
|
||||
return false;
|
||||
}
|
||||
|
||||
int nchannels = m_cin.header.NumberOfElements();
|
||||
if (nchannels < 1 || nchannels > 8) {
|
||||
errorfmt("Invalid number of channels {} (must be 1-8)", nchannels);
|
||||
close();
|
||||
return false;
|
||||
}
|
||||
|
||||
// create imagespec
|
||||
TypeDesc typedesc;
|
||||
int maxbits = 0;
|
||||
for (int i = 0; i < m_cin.header.NumberOfElements(); i++) {
|
||||
if (maxbits < m_cin.header.BitDepth(i))
|
||||
maxbits = m_cin.header.BitDepth(i);
|
||||
for (int i = 0; i < nchannels; i++) {
|
||||
int b(m_cin.header.BitDepth(i));
|
||||
if (b < 1 || b > 32) {
|
||||
errorfmt("Invalid bitdepth in channel {}: {} bits", i, b);
|
||||
return false;
|
||||
}
|
||||
maxbits = std::max(maxbits, b);
|
||||
}
|
||||
switch ((maxbits + 7) / 8) {
|
||||
case 1: typedesc = TypeDesc::UINT8; break;
|
||||
@@ -103,8 +115,13 @@ CineonInput::open(const std::string& name, ImageSpec& newspec)
|
||||
case 4: typedesc = TypeDesc::UINT32; break;
|
||||
default: errorfmt("Unsupported bit depth {}", maxbits); return false;
|
||||
}
|
||||
m_spec = ImageSpec(m_cin.header.Width(), m_cin.header.Height(),
|
||||
m_cin.header.NumberOfElements(), typedesc);
|
||||
m_spec = ImageSpec(m_cin.header.Width(), m_cin.header.Height(), nchannels,
|
||||
typedesc);
|
||||
|
||||
if (!check_open(m_spec, { 0, 1 << 20, 0, 1 << 20, 0, 1, 0, 8 })) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// fill channel names
|
||||
m_spec.channelnames.clear();
|
||||
int gscount = 0, rcount = 0, gcount = 0, bcount = 0;
|
||||
@@ -119,7 +136,7 @@ CineonInput::open(const std::string& name, ImageSpec& newspec)
|
||||
break;
|
||||
case cineon::kPrintingDensityRed:
|
||||
case cineon::kRec709Red:
|
||||
if (++gscount > 1) {
|
||||
if (++rcount > 1) {
|
||||
std::string ch = Strutil::fmt::format("R{}", rcount);
|
||||
m_spec.channelnames.push_back(ch);
|
||||
} else
|
||||
|
||||
@@ -33,3 +33,6 @@ Reading ../oiio-images/cineon/checker.cin
|
||||
cineon:YOffset: 0
|
||||
oiio:BitsPerSample: 10
|
||||
oiio:ColorSpace: "KodakLog"
|
||||
oiiotool ERROR: read : "src/broken_bitdepth.cin": Invalid number of channels 255 (must be 1-8)
|
||||
Full command line was:
|
||||
> oiiotool --info --hash src/broken_bitdepth.cin
|
||||
|
||||
@@ -4,7 +4,15 @@
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# https://github.com/AcademySoftwareFoundation/OpenImageIO
|
||||
|
||||
# save the error output
|
||||
redirect = " >> out.txt 2>&1 "
|
||||
|
||||
files = [ "checker.cin" ]
|
||||
for f in files:
|
||||
command += info_command ("../oiio-images/cineon/" + f)
|
||||
|
||||
|
||||
# Regression tests for broken files
|
||||
command += info_command ("src/broken_bitdepth.cin", verbose=False, failureok=True)
|
||||
|
||||
outputs = [ "out.txt" ]
|
||||
|
||||
Binary file not shown.
Reference in New Issue
Block a user