diff --git a/include/json/assertions.h b/include/json/assertions.h index d5fd661c4..fbec7ae00 100644 --- a/include/json/assertions.h +++ b/include/json/assertions.h @@ -13,22 +13,30 @@ #include "config.h" #endif // if !defined(JSON_IS_AMALGAMATION) +/** It should not be possible for a maliciously designed file to + * cause an abort() or seg-fault, so these macros are used only + * for pre-condition violations and internal logic errors. + */ #if JSON_USE_EXCEPTION -#include -#define JSON_ASSERT(condition) \ - {if (!(condition)) {throw std::logic_error( "assert json failed" );}} // @todo <= add detail about condition in exception -#define JSON_FAIL_MESSAGE(message) \ + +// @todo <= add detail about condition in exception +# define JSON_ASSERT(condition) \ + {if (!(condition)) {Json::throwLogicError( "assert json failed" );}} + +# define JSON_FAIL_MESSAGE(message) \ { \ std::ostringstream oss; oss << message; \ - throw std::logic_error(oss.str()); \ + Json::throwLogicError(oss.str()); \ + abort(); \ } -//#define JSON_FAIL_MESSAGE(message) throw std::logic_error(message) + #else // JSON_USE_EXCEPTION -#define JSON_ASSERT(condition) assert(condition) + +# define JSON_ASSERT(condition) assert(condition) // The call to assert() will show the failure message in debug builds. In -// release bugs we abort, for a core-dump or debugger. -#define JSON_FAIL_MESSAGE(message) \ +// release builds we abort, for a core-dump or debugger. +# define JSON_FAIL_MESSAGE(message) \ { \ std::ostringstream oss; oss << message; \ assert(false && oss.str().c_str()); \ diff --git a/include/json/value.h b/include/json/value.h index b752fb85b..b590825cb 100644 --- a/include/json/value.h +++ b/include/json/value.h @@ -11,6 +11,7 @@ #endif // if !defined(JSON_IS_AMALGAMATION) #include #include +#include #ifndef JSON_USE_CPPTL_SMALLMAP #include @@ -32,6 +33,23 @@ */ namespace Json { +/** Base class for all exceptions we throw. + */ +class JSON_API Exception; +/** Exceptions which the user cannot easily avoid. + * + * E.g. out-of-memory, stack-overflow, malicious input + */ +class JSON_API RuntimeError; +/** Exceptions throw by JSON_ASSERT/JSON_FAIL macros. + * + * These are precondition-violations (user bugs) and internal errors (our bugs). + */ +class JSON_API LogicError; + +JSON_API void throwRuntimeError(std::string const& msg); +JSON_API void throwLogicError(std::string const& msg); + /** \brief Type of the value held by a Value object. */ enum ValueType { diff --git a/include/json/writer.h b/include/json/writer.h index 2d6dbce47..f6fcc9c6a 100644 --- a/include/json/writer.h +++ b/include/json/writer.h @@ -46,7 +46,7 @@ class JSON_API StreamWriter { /** Write Value into document as configured in sub-class. Do not take ownership of sout, but maintain a reference during function. \pre sout != NULL - \return zero on success + \return zero on success (For now, we always return zero, so check the stream instead.) \throw std::exception possibly, depending on configuration */ virtual int write(Value const& root, std::ostream* sout) = 0; diff --git a/src/lib_json/json_reader.cpp b/src/lib_json/json_reader.cpp index 9ceaf3838..51cd6a19e 100644 --- a/src/lib_json/json_reader.cpp +++ b/src/lib_json/json_reader.cpp @@ -17,7 +17,6 @@ #include #include #include -#include #if defined(_MSC_VER) && _MSC_VER < 1500 // VC++ 8.0 and below #define snprintf _snprintf @@ -148,7 +147,7 @@ bool Reader::readValue() { // But this deprecated class has a security problem: Bad input can // cause a seg-fault. This seems like a fair, binary-compatible way // to prevent the problem. - if (stackDepth_g >= stackLimit_g) throw std::runtime_error("Exceeded stackLimit in readValue()."); + if (stackDepth_g >= stackLimit_g) throwRuntimeError("Exceeded stackLimit in readValue()."); ++stackDepth_g; Token token; @@ -1107,7 +1106,7 @@ bool OurReader::parse(const char* beginDoc, } bool OurReader::readValue() { - if (stackDepth_ >= features_.stackLimit_) throw std::runtime_error("Exceeded stackLimit in readValue()."); + if (stackDepth_ >= features_.stackLimit_) throwRuntimeError("Exceeded stackLimit in readValue()."); ++stackDepth_; Token token; skipCommentTokens(token); @@ -1431,7 +1430,7 @@ bool OurReader::readObject(Token& tokenStart) { return addErrorAndRecover( "Missing ':' after object member name", colon, tokenObjectEnd); } - if (name.length() >= (1U<<30)) throw std::runtime_error("keylength >= 2^30"); + if (name.length() >= (1U<<30)) throwRuntimeError("keylength >= 2^30"); if (features_.rejectDupKeys_ && currentValue().isMember(name)) { std::string msg = "Duplicate key: '" + name + "'"; return addErrorAndRecover( @@ -1994,7 +1993,7 @@ std::istream& operator>>(std::istream& sin, Value& root) { "Error from reader: %s", errs.c_str()); - JSON_FAIL_MESSAGE("reader error"); + throwRuntimeError("reader error"); } return sin; } diff --git a/src/lib_json/json_value.cpp b/src/lib_json/json_value.cpp index 46385b9ff..bcd2f3510 100644 --- a/src/lib_json/json_value.cpp +++ b/src/lib_json/json_value.cpp @@ -87,9 +87,11 @@ static inline char* duplicateStringValue(const char* value, length = Value::maxInt - 1; char* newString = static_cast(malloc(length + 1)); - JSON_ASSERT_MESSAGE(newString != 0, - "in Json::Value::duplicateStringValue(): " - "Failed to allocate string value buffer"); + if (newString == NULL) { + throwRuntimeError( + "in Json::Value::duplicateStringValue(): " + "Failed to allocate string value buffer"); + } memcpy(newString, value, length); newString[length] = 0; return newString; @@ -108,9 +110,11 @@ static inline char* duplicateAndPrefixStringValue( "length too big for prefixing"); unsigned actualLength = length + sizeof(unsigned) + 1U; char* newString = static_cast(malloc(actualLength)); - JSON_ASSERT_MESSAGE(newString != 0, - "in Json::Value::duplicateAndPrefixStringValue(): " - "Failed to allocate string value buffer"); + if (newString == 0) { + throwRuntimeError( + "in Json::Value::duplicateAndPrefixStringValue(): " + "Failed to allocate string value buffer"); + } *reinterpret_cast(newString) = length; memcpy(newString + sizeof(unsigned), value, length); newString[actualLength - 1U] = 0; // to avoid buffer over-run accidents by users later @@ -148,6 +152,47 @@ static inline void releaseStringValue(char* value) { free(value); } namespace Json { +class JSON_API Exception : public std::exception { +public: + Exception(std::string const& msg); + virtual ~Exception() throw(); + virtual char const* what() const throw(); +protected: + std::string const& msg_; +}; +class JSON_API RuntimeError : public Exception { +public: + RuntimeError(std::string const& msg); +}; +class JSON_API LogicError : public Exception { +public: + LogicError(std::string const& msg); +}; + +Exception::Exception(std::string const& msg) + : msg_(msg) +{} +Exception::~Exception() throw() +{} +char const* Exception::what() const throw() +{ + return msg_.c_str(); +} +RuntimeError::RuntimeError(std::string const& msg) + : Exception(msg) +{} +LogicError::LogicError(std::string const& msg) + : Exception(msg) +{} +void throwRuntimeError(std::string const& msg) +{ + throw RuntimeError(msg); +} +void throwLogicError(std::string const& msg) +{ + throw LogicError(msg); +} + // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// diff --git a/src/lib_json/json_writer.cpp b/src/lib_json/json_writer.cpp index 69323df52..c7f72ee22 100644 --- a/src/lib_json/json_writer.cpp +++ b/src/lib_json/json_writer.cpp @@ -12,7 +12,6 @@ #include #include #include -#include #include #include #include @@ -1080,7 +1079,7 @@ StreamWriter* StreamWriterBuilder::newStreamWriter() const } else if (cs_str == "None") { cs = CommentStyle::None; } else { - throw std::runtime_error("commentStyle must be 'All' or 'None'"); + throwRuntimeError("commentStyle must be 'All' or 'None'"); } std::string colonSymbol = " : "; if (eyc) { diff --git a/src/test_lib_json/main.cpp b/src/test_lib_json/main.cpp index d7304444e..2b6584ebb 100644 --- a/src/test_lib_json/main.cpp +++ b/src/test_lib_json/main.cpp @@ -6,7 +6,6 @@ #include "jsontest.h" #include #include -#include #include // Make numeric limits more convenient to talk about.