From 0eac1f41f68bd2cd3b7e73db9eb96c690507b448 Mon Sep 17 00:00:00 2001
From: Nat Goodspeed <nat@lindenlab.com>
Date: Wed, 17 Aug 2016 16:42:10 -0400
Subject: 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.
---
 indra/llkdu/llimagej2ckdu.cpp | 76 ++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 75 insertions(+), 1 deletion(-)

(limited to 'indra/llkdu')

diff --git a/indra/llkdu/llimagej2ckdu.cpp b/indra/llkdu/llimagej2ckdu.cpp
index 4c2eac9eea..9e1dfc7213 100644
--- a/indra/llkdu/llimagej2ckdu.cpp
+++ b/indra/llkdu/llimagej2ckdu.cpp
@@ -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: " +
-- 
cgit v1.2.3