MAINT-5011: Catch kdu_exception (aka int) in case it leaks out.

KDU internally throws kdu_exception, which is a typedef for int. It's possible
that such an exception might leak out.

Our usual strategy for unknown exceptions is to catch (...) and let
boost::current_exception_diagnostic_information() handle them. However, for
int (or a class not derived from std::exception), that function will only
shrug and report no information available.

Besides, we want to format kdu_exception specially anyway. First, the KDU
#defines are in hex, so we should report the value in hex. But on inspection,
certain of those hex values are actually multibyte ASCII literals in disguise
-- so also report the byte string value.
master
Nat Goodspeed 2016-08-17 16:42:10 -04:00
parent 83eb960063
commit 0eac1f41f6
1 changed files with 75 additions and 1 deletions

View File

@ -1,4 +1,4 @@
/**
/**
* @file llimagej2ckdu.cpp
* @brief This is an implementation of JPEG2000 encode/decode using Kakadu
*
@ -36,6 +36,8 @@
#include "llexception.h"
#include <boost/exception/diagnostic_information.hpp>
#include <sstream>
#include <iomanip>
namespace {
// Failure to load an image shouldn't crash the whole viewer.
@ -43,6 +45,40 @@ struct KDUError: public LLContinueError
{
KDUError(const std::string& msg): LLContinueError(msg) {}
};
// KDU defines int error codes as hex values, so we should log them in hex
// so we can grep KDU headers for the hex. However those hex values
// generally "happen" to encode big-endian multibyte character sequences,
// e.g. KDU_ERROR_EXCEPTION is 0x6b647545: 'kduE'
// But beware because KDU_NULL_EXCEPTION is simply 0 -- which doesn't
// preclude somebody from throwing it.
std::string report_kdu_exception(kdu_exception mb)
{
std::ostringstream out;
// always report mb in hex
out << "kdu_exception " << std::hex << mb;
// Also display as many chars as are encoded in the kdu_exception
// value. Make a char array; reserve 1 extra byte for nul terminator.
char bytes[sizeof(kdu_exception) + 1];
// Back up through 'bytes'
char *bptr = bytes + sizeof(bytes);
*(--bptr) = '\0';
while (mb)
{
// store low-order byte of mb in next-left char
*(--bptr) = char(mb & 0xFF);
// then shift mb right by one byte
mb >>= 8;
}
// did that produce any characters?
if (*bptr)
{
out << " (" << bptr << ')';
}
return out.str();
}
} // anonymous namespace
class kdc_flow_control {
@ -400,6 +436,15 @@ bool LLImageJ2CKDU::initDecode(LLImageJ2C &base, LLImageRaw &raw_image, F32 deco
base.setLastError(msg.what());
return false;
}
catch (kdu_exception kdu_value)
{
// KDU internally throws kdu_exception. It's possible that such an
// exception might leak out into our code. Catch kdu_exception
// specially because boost::current_exception_diagnostic_information()
// could do nothing with it.
base.setLastError(report_kdu_exception(kdu_value));
return false;
}
catch (...)
{
base.setLastError("Unknown J2C error: " +
@ -499,6 +544,17 @@ bool LLImageJ2CKDU::decodeImpl(LLImageJ2C &base, LLImageRaw &raw_image, F32 deco
cleanupCodeStream();
return true; // done
}
catch (kdu_exception kdu_value)
{
// KDU internally throws kdu_exception. It's possible that such an
// exception might leak out into our code. Catch kdu_exception
// specially because boost::current_exception_diagnostic_information()
// could do nothing with it.
base.setLastError(report_kdu_exception(kdu_value));
base.decodeFailed();
cleanupCodeStream();
return true; // done
}
catch (...)
{
base.setLastError("Unknown J2C error: " +
@ -691,6 +747,15 @@ bool LLImageJ2CKDU::encodeImpl(LLImageJ2C &base, const LLImageRaw &raw_image, co
base.setLastError(msg.what());
return false;
}
catch (kdu_exception kdu_value)
{
// KDU internally throws kdu_exception. It's possible that such an
// exception might leak out into our code. Catch kdu_exception
// specially because boost::current_exception_diagnostic_information()
// could do nothing with it.
base.setLastError(report_kdu_exception(kdu_value));
return false;
}
catch( ... )
{
base.setLastError("Unknown J2C error: " +
@ -716,6 +781,15 @@ bool LLImageJ2CKDU::getMetadata(LLImageJ2C &base)
base.setLastError(msg.what());
return false;
}
catch (kdu_exception kdu_value)
{
// KDU internally throws kdu_exception. It's possible that such an
// exception might leak out into our code. Catch kdu_exception
// specially because boost::current_exception_diagnostic_information()
// could do nothing with it.
base.setLastError(report_kdu_exception(kdu_value));
return false;
}
catch (...)
{
base.setLastError("Unknown J2C error: " +