Skip to content

Commit 0d33cb3

Browse files
committed
Merge pull request open-source-parsers#211 from cdunn2001/except
* Add Json::Exception and derivatives. * Clarify when exceptions are thrown, to avoid crashes caused by malicious input. * Use our own type (derived fro std::exception) so they are trappable.
2 parents ee4ea0e + 2250b3c commit 0d33cb3

File tree

7 files changed

+92
-24
lines changed

7 files changed

+92
-24
lines changed

include/json/assertions.h

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,22 +13,30 @@
1313
#include "config.h"
1414
#endif // if !defined(JSON_IS_AMALGAMATION)
1515

16+
/** It should not be possible for a maliciously designed file to
17+
* cause an abort() or seg-fault, so these macros are used only
18+
* for pre-condition violations and internal logic errors.
19+
*/
1620
#if JSON_USE_EXCEPTION
17-
#include <stdexcept>
18-
#define JSON_ASSERT(condition) \
19-
{if (!(condition)) {throw std::logic_error( "assert json failed" );}} // @todo <= add detail about condition in exception
20-
#define JSON_FAIL_MESSAGE(message) \
21+
22+
// @todo <= add detail about condition in exception
23+
# define JSON_ASSERT(condition) \
24+
{if (!(condition)) {Json::throwLogicError( "assert json failed" );}}
25+
26+
# define JSON_FAIL_MESSAGE(message) \
2127
{ \
2228
std::ostringstream oss; oss << message; \
23-
throw std::logic_error(oss.str()); \
29+
Json::throwLogicError(oss.str()); \
30+
abort(); \
2431
}
25-
//#define JSON_FAIL_MESSAGE(message) throw std::logic_error(message)
32+
2633
#else // JSON_USE_EXCEPTION
27-
#define JSON_ASSERT(condition) assert(condition)
34+
35+
# define JSON_ASSERT(condition) assert(condition)
2836

2937
// The call to assert() will show the failure message in debug builds. In
30-
// release bugs we abort, for a core-dump or debugger.
31-
#define JSON_FAIL_MESSAGE(message) \
38+
// release builds we abort, for a core-dump or debugger.
39+
# define JSON_FAIL_MESSAGE(message) \
3240
{ \
3341
std::ostringstream oss; oss << message; \
3442
assert(false && oss.str().c_str()); \

include/json/value.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#endif // if !defined(JSON_IS_AMALGAMATION)
1212
#include <string>
1313
#include <vector>
14+
#include <exception>
1415

1516
#ifndef JSON_USE_CPPTL_SMALLMAP
1617
#include <map>
@@ -32,6 +33,23 @@
3233
*/
3334
namespace Json {
3435

36+
/** Base class for all exceptions we throw.
37+
*/
38+
class JSON_API Exception;
39+
/** Exceptions which the user cannot easily avoid.
40+
*
41+
* E.g. out-of-memory, stack-overflow, malicious input
42+
*/
43+
class JSON_API RuntimeError;
44+
/** Exceptions throw by JSON_ASSERT/JSON_FAIL macros.
45+
*
46+
* These are precondition-violations (user bugs) and internal errors (our bugs).
47+
*/
48+
class JSON_API LogicError;
49+
50+
JSON_API void throwRuntimeError(std::string const& msg);
51+
JSON_API void throwLogicError(std::string const& msg);
52+
3553
/** \brief Type of the value held by a Value object.
3654
*/
3755
enum ValueType {

include/json/writer.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ class JSON_API StreamWriter {
4646
/** Write Value into document as configured in sub-class.
4747
Do not take ownership of sout, but maintain a reference during function.
4848
\pre sout != NULL
49-
\return zero on success
49+
\return zero on success (For now, we always return zero, so check the stream instead.)
5050
\throw std::exception possibly, depending on configuration
5151
*/
5252
virtual int write(Value const& root, std::ostream* sout) = 0;

src/lib_json/json_reader.cpp

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
#include <sstream>
1818
#include <memory>
1919
#include <set>
20-
#include <stdexcept>
2120

2221
#if defined(_MSC_VER) && _MSC_VER < 1500 // VC++ 8.0 and below
2322
#define snprintf _snprintf
@@ -148,7 +147,7 @@ bool Reader::readValue() {
148147
// But this deprecated class has a security problem: Bad input can
149148
// cause a seg-fault. This seems like a fair, binary-compatible way
150149
// to prevent the problem.
151-
if (stackDepth_g >= stackLimit_g) throw std::runtime_error("Exceeded stackLimit in readValue().");
150+
if (stackDepth_g >= stackLimit_g) throwRuntimeError("Exceeded stackLimit in readValue().");
152151
++stackDepth_g;
153152

154153
Token token;
@@ -1107,7 +1106,7 @@ bool OurReader::parse(const char* beginDoc,
11071106
}
11081107

11091108
bool OurReader::readValue() {
1110-
if (stackDepth_ >= features_.stackLimit_) throw std::runtime_error("Exceeded stackLimit in readValue().");
1109+
if (stackDepth_ >= features_.stackLimit_) throwRuntimeError("Exceeded stackLimit in readValue().");
11111110
++stackDepth_;
11121111
Token token;
11131112
skipCommentTokens(token);
@@ -1431,7 +1430,7 @@ bool OurReader::readObject(Token& tokenStart) {
14311430
return addErrorAndRecover(
14321431
"Missing ':' after object member name", colon, tokenObjectEnd);
14331432
}
1434-
if (name.length() >= (1U<<30)) throw std::runtime_error("keylength >= 2^30");
1433+
if (name.length() >= (1U<<30)) throwRuntimeError("keylength >= 2^30");
14351434
if (features_.rejectDupKeys_ && currentValue().isMember(name)) {
14361435
std::string msg = "Duplicate key: '" + name + "'";
14371436
return addErrorAndRecover(
@@ -1994,7 +1993,7 @@ std::istream& operator>>(std::istream& sin, Value& root) {
19941993
"Error from reader: %s",
19951994
errs.c_str());
19961995

1997-
JSON_FAIL_MESSAGE("reader error");
1996+
throwRuntimeError("reader error");
19981997
}
19991998
return sin;
20001999
}

src/lib_json/json_value.cpp

Lines changed: 51 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -87,9 +87,11 @@ static inline char* duplicateStringValue(const char* value,
8787
length = Value::maxInt - 1;
8888

8989
char* newString = static_cast<char*>(malloc(length + 1));
90-
JSON_ASSERT_MESSAGE(newString != 0,
91-
"in Json::Value::duplicateStringValue(): "
92-
"Failed to allocate string value buffer");
90+
if (newString == NULL) {
91+
throwRuntimeError(
92+
"in Json::Value::duplicateStringValue(): "
93+
"Failed to allocate string value buffer");
94+
}
9395
memcpy(newString, value, length);
9496
newString[length] = 0;
9597
return newString;
@@ -108,9 +110,11 @@ static inline char* duplicateAndPrefixStringValue(
108110
"length too big for prefixing");
109111
unsigned actualLength = length + sizeof(unsigned) + 1U;
110112
char* newString = static_cast<char*>(malloc(actualLength));
111-
JSON_ASSERT_MESSAGE(newString != 0,
112-
"in Json::Value::duplicateAndPrefixStringValue(): "
113-
"Failed to allocate string value buffer");
113+
if (newString == 0) {
114+
throwRuntimeError(
115+
"in Json::Value::duplicateAndPrefixStringValue(): "
116+
"Failed to allocate string value buffer");
117+
}
114118
*reinterpret_cast<unsigned*>(newString) = length;
115119
memcpy(newString + sizeof(unsigned), value, length);
116120
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); }
148152

149153
namespace Json {
150154

155+
class JSON_API Exception : public std::exception {
156+
public:
157+
Exception(std::string const& msg);
158+
virtual ~Exception() throw();
159+
virtual char const* what() const throw();
160+
protected:
161+
std::string const& msg_;
162+
};
163+
class JSON_API RuntimeError : public Exception {
164+
public:
165+
RuntimeError(std::string const& msg);
166+
};
167+
class JSON_API LogicError : public Exception {
168+
public:
169+
LogicError(std::string const& msg);
170+
};
171+
172+
Exception::Exception(std::string const& msg)
173+
: msg_(msg)
174+
{}
175+
Exception::~Exception() throw()
176+
{}
177+
char const* Exception::what() const throw()
178+
{
179+
return msg_.c_str();
180+
}
181+
RuntimeError::RuntimeError(std::string const& msg)
182+
: Exception(msg)
183+
{}
184+
LogicError::LogicError(std::string const& msg)
185+
: Exception(msg)
186+
{}
187+
void throwRuntimeError(std::string const& msg)
188+
{
189+
throw RuntimeError(msg);
190+
}
191+
void throwLogicError(std::string const& msg)
192+
{
193+
throw LogicError(msg);
194+
}
195+
151196
// //////////////////////////////////////////////////////////////////
152197
// //////////////////////////////////////////////////////////////////
153198
// //////////////////////////////////////////////////////////////////

src/lib_json/json_writer.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
#include <sstream>
1313
#include <utility>
1414
#include <set>
15-
#include <stdexcept>
1615
#include <assert.h>
1716
#include <math.h>
1817
#include <stdio.h>
@@ -1080,7 +1079,7 @@ StreamWriter* StreamWriterBuilder::newStreamWriter() const
10801079
} else if (cs_str == "None") {
10811080
cs = CommentStyle::None;
10821081
} else {
1083-
throw std::runtime_error("commentStyle must be 'All' or 'None'");
1082+
throwRuntimeError("commentStyle must be 'All' or 'None'");
10841083
}
10851084
std::string colonSymbol = " : ";
10861085
if (eyc) {

src/test_lib_json/main.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
#include "jsontest.h"
77
#include <json/config.h>
88
#include <json/json.h>
9-
#include <stdexcept>
109
#include <cstring>
1110

1211
// Make numeric limits more convenient to talk about.

0 commit comments

Comments
 (0)