diff --git a/.gitignore b/.gitignore index 39d90c2..7dacb0b 100644 --- a/.gitignore +++ b/.gitignore @@ -45,3 +45,8 @@ jsoncpp_lib_static.dir/ /src/lib_json/Makefile /src/test_lib_json/Makefile /src/test_lib_json/jsoncpp_test + +# eclipse project files +.project +.cproject +/.settings/ diff --git a/CMakeLists.txt b/CMakeLists.txt index 22567b9..9a89f6e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -69,6 +69,7 @@ jsoncpp_parse_version( ${JSONCPP_VERSION} JSONCPP_VERSION ) #IF(NOT JSONCPP_VERSION_FOUND) # MESSAGE(FATAL_ERROR "Failed to parse version string properly. Expect X.Y.Z") #ENDIF(NOT JSONCPP_VERSION_FOUND) +SET( JSONCPP_USE_SECURE_MEMORY "0" CACHE STRING "-D...=1 to use memory-wiping allocator for STL" ) MESSAGE(STATUS "JsonCpp Version: ${JSONCPP_VERSION_MAJOR}.${JSONCPP_VERSION_MINOR}.${JSONCPP_VERSION_PATCH}") # File version.h is only regenerated on CMake configure step diff --git a/amalgamate.py b/amalgamate.py index 1916bb0..2102f30 100644 --- a/amalgamate.py +++ b/amalgamate.py @@ -67,6 +67,7 @@ def amalgamate_source(source_top_dir=None, header.add_text("/// to prevent private header inclusion.") header.add_text("#define JSON_IS_AMALGAMATION") header.add_file("include/json/version.h") + header.add_file("include/json/allocator.h") header.add_file("include/json/config.h") header.add_file("include/json/forwards.h") header.add_file("include/json/features.h") diff --git a/include/json/allocator.h b/include/json/allocator.h new file mode 100644 index 0000000..9d8b9fc --- /dev/null +++ b/include/json/allocator.h @@ -0,0 +1,94 @@ +// Copyright 2007-2010 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef CPPTL_JSON_ALLOCATOR_H_INCLUDED +#define CPPTL_JSON_ALLOCATOR_H_INCLUDED + +#include +#include + +namespace Json { +template +class SecureAllocator { + public: + // Type definitions + using value_type = T; + using pointer = T*; + using const_pointer = const T*; + using reference = T&; + using const_reference = const T&; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + + /** + * Allocate memory for N items using the standard allocator. + */ + pointer allocate(size_type n) { + // allocate using "global operator new" + return static_cast(::operator new(n * sizeof(T))); + } + + /** + * Release memory which was allocated for N items at pointer P. + * + * The memory block is filled with zeroes before being released. + * The pointer argument is tagged as "volatile" to prevent the + * compiler optimizing out this critical step. + */ + void deallocate(volatile pointer p, size_type n) { + std::memset(p, 0, n * sizeof(T)); + // free using "global operator delete" + ::operator delete(p); + } + + /** + * Construct an item in-place at pointer P. + */ + template + void construct(pointer p, Args&&... args) { + // construct using "placement new" and "perfect forwarding" + ::new (static_cast(p)) T(std::forward(args)...); + } + + size_type max_size() const { + return size_t(-1) / sizeof(T); + } + + pointer address( reference x ) const { + return std::addressof(x); + } + + const_pointer address( const_reference x ) const { + return std::addressof(x); + } + + /** + * Destroy an item in-place at pointer P. + */ + void destroy(pointer p) { + // destroy using "explicit destructor" + p->~T(); + } + + // Boilerplate + SecureAllocator() {} + template SecureAllocator(const SecureAllocator&) {} + template struct rebind { using other = SecureAllocator; }; +}; + + +template +bool operator==(const SecureAllocator&, const SecureAllocator&) { + return true; +} + +template +bool operator!=(const SecureAllocator&, const SecureAllocator&) { + return false; +} + +} //namespace Json + +#endif // CPPTL_JSON_ALLOCATOR_H_INCLUDED diff --git a/include/json/config.h b/include/json/config.h index 8254727..1e96210 100644 --- a/include/json/config.h +++ b/include/json/config.h @@ -119,6 +119,12 @@ # define JSON_USE_INT64_DOUBLE_CONVERSION 1 #endif +#include "version.h" + +#if JSONCPP_USING_SECURE_MEMORY +#include "allocator.h" //typedef Allocator +#endif + namespace Json { typedef int Int; typedef unsigned int UInt; @@ -139,11 +145,19 @@ typedef Int64 LargestInt; typedef UInt64 LargestUInt; #define JSON_HAS_INT64 #endif // if defined(JSON_NO_INT64) +#if JSONCPP_USING_SECURE_MEMORY +#define JSONCPP_STRING std::basic_string, Json::SecureAllocator > +#define JSONCPP_OSTRINGSTREAM std::basic_ostringstream, Json::SecureAllocator > +#define JSONCPP_OSTREAM std::basic_ostream> +#define JSONCPP_ISTRINGSTREAM std::basic_istringstream, Json::SecureAllocator > +#define JSONCPP_ISTREAM std::istream +#else #define JSONCPP_STRING std::string #define JSONCPP_OSTRINGSTREAM std::ostringstream #define JSONCPP_OSTREAM std::ostream #define JSONCPP_ISTRINGSTREAM std::istringstream #define JSONCPP_ISTREAM std::istream +#endif // if JSONCPP_USING_SECURE_MEMORY } // end namespace Json #endif // JSON_CONFIG_H_INCLUDED diff --git a/include/json/value.h b/include/json/value.h index 5948935..20747fe 100644 --- a/include/json/value.h +++ b/include/json/value.h @@ -336,6 +336,9 @@ Json::Value obj_value(Json::objectValue); // {} int compare(const Value& other) const; const char* asCString() const; ///< Embedded zeroes could cause you trouble! +#if JSONCPP_USING_SECURE_MEMORY + unsigned getCStringLength() const; //Allows you to understand the length of the CString +#endif JSONCPP_STRING asString() const; ///< Embedded zeroes are possible. /** Get raw char* of string-value. * \return false if !string. (Seg-fault if str or end are NULL.) diff --git a/include/json/version.h b/include/json/version.h index 6c570a1..dc975ff 100644 --- a/include/json/version.h +++ b/include/json/version.h @@ -10,4 +10,11 @@ # define JSONCPP_VERSION_QUALIFIER # define JSONCPP_VERSION_HEXA ((JSONCPP_VERSION_MAJOR << 24) | (JSONCPP_VERSION_MINOR << 16) | (JSONCPP_VERSION_PATCH << 8)) +#ifdef JSONCPP_USING_SECURE_MEMORY +#undef JSONCPP_USING_SECURE_MEMORY +#endif +#define JSONCPP_USING_SECURE_MEMORY 1 +// If non-zero, the library zeroes any memory that it has allocated before +// it frees its memory. + #endif // JSON_VERSION_H_INCLUDED diff --git a/src/jsontestrunner/main.cpp b/src/jsontestrunner/main.cpp index 60f7e32..a1d911d 100644 --- a/src/jsontestrunner/main.cpp +++ b/src/jsontestrunner/main.cpp @@ -33,9 +33,6 @@ static JSONCPP_STRING normalizeFloatingPointStr(double value) { #endif buffer[sizeof(buffer) - 1] = 0; JSONCPP_STRING s(buffer); -#if JSON_USE_SECURE_MEMORY - memset(&buffer, 0, sizeof(buffer)); -#endif JSONCPP_STRING::size_type index = s.find_last_of("eE"); if (index != JSONCPP_STRING::npos) { JSONCPP_STRING::size_type hasSign = @@ -69,9 +66,6 @@ static JSONCPP_STRING readInputTestFile(const char* path) { if (fread(buffer, 1, usize, file) == usize) text = buffer; fclose(file); -#if JSON_USE_SECURE_MEMORY - memset(buffer, 0, static_cast(size + 1)); -#endif delete[] buffer; return text; } @@ -151,7 +145,7 @@ static int parseAndSaveValueTree(const JSONCPP_STRING& input, Json::Value* root) { Json::Reader reader(features); - bool parsingSuccessful = reader.parse(input, *root); + bool parsingSuccessful = reader.parse(input.data(), input.data() + input.size(), *root); if (!parsingSuccessful) { printf("Failed to parse %s file: \n%s\n", kind.c_str(), diff --git a/src/lib_json/json_reader.cpp b/src/lib_json/json_reader.cpp index f7e7bb1..ef71e19 100644 --- a/src/lib_json/json_reader.cpp +++ b/src/lib_json/json_reader.cpp @@ -98,7 +98,8 @@ Reader::Reader(const Features& features) bool Reader::parse(const std::string& document, Value& root, bool collectComments) { - document_ = document; + JSONCPP_STRING documentCopy(document.data(), document.data() + document.capacity()); + std::swap(documentCopy, document_); const char* begin = document_.c_str(); const char* end = begin + document_.length(); return parse(begin, end, root, collectComments); @@ -114,7 +115,7 @@ bool Reader::parse(std::istream& sin, Value& root, bool collectComments) { // create an extra copy. JSONCPP_STRING doc; std::getline(sin, doc, (char)EOF); - return parse(doc, root, collectComments); + return parse(doc.data(), doc.data() + doc.size(), root, collectComments); } bool Reader::parse(const char* beginDoc, diff --git a/src/lib_json/json_value.cpp b/src/lib_json/json_value.cpp index 112d86d..bbfa12c 100644 --- a/src/lib_json/json_value.cpp +++ b/src/lib_json/json_value.cpp @@ -138,7 +138,29 @@ inline static void decodePrefixedString( } /** Free the string duplicated by duplicateStringValue()/duplicateAndPrefixStringValue(). */ -static inline void releaseStringValue(char* value) { free(value); } +#if JSONCPP_USING_SECURE_MEMORY +static inline void releasePrefixedStringValue(char* value) { + unsigned length = 0; + char const* valueDecoded; + decodePrefixedString(true, value, &length, &valueDecoded); + size_t const size = sizeof(unsigned) + length + 1U; + memset(value, 0, size); + free(value); +} +static inline void releaseStringValue(char* value, unsigned length) { + // length==0 => we allocated the strings memory + size_t size = (length==0) ? strlen(value) : length; + memset(value, 0, size); + free(value); +} +#else // !JSONCPP_USING_SECURE_MEMORY +static inline void releasePrefixedStringValue(char* value) { + free(value); +} +static inline void releaseStringValue(char* value, unsigned length) { + free(value); +} +#endif // JSONCPP_USING_SECURE_MEMORY } // namespace Json @@ -193,12 +215,12 @@ Value::CommentInfo::CommentInfo() : comment_(0) Value::CommentInfo::~CommentInfo() { if (comment_) - releaseStringValue(comment_); + releaseStringValue(comment_, 0u); } void Value::CommentInfo::setComment(const char* text, size_t len) { if (comment_) { - releaseStringValue(comment_); + releaseStringValue(comment_, 0u); comment_ = 0; } JSON_ASSERT(text != 0); @@ -229,10 +251,10 @@ Value::CZString::CZString(char const* str, unsigned ulength, DuplicationPolicy a storage_.length_ = ulength & 0x3FFFFFFF; } -Value::CZString::CZString(const CZString& other) - : cstr_(other.storage_.policy_ != noDuplication && other.cstr_ != 0 - ? duplicateStringValue(other.cstr_, other.storage_.length_) - : other.cstr_) { +Value::CZString::CZString(const CZString& other) { + cstr_ = (other.storage_.policy_ != noDuplication && other.cstr_ != 0 + ? duplicateStringValue(other.cstr_, other.storage_.length_) + : other.cstr_); storage_.policy_ = static_cast(other.cstr_ ? (static_cast(other.storage_.policy_) == noDuplication ? noDuplication : duplicate) @@ -248,8 +270,9 @@ Value::CZString::CZString(CZString&& other) #endif Value::CZString::~CZString() { - if (cstr_ && storage_.policy_ == duplicate) - releaseStringValue(const_cast(cstr_)); + if (cstr_ && storage_.policy_ == duplicate) { + releaseStringValue(const_cast(cstr_), storage_.length_ + 1u); //+1 for null terminating character for sake of completeness but not actually necessary + } } void Value::CZString::swap(CZString& other) { @@ -455,7 +478,7 @@ Value::~Value() { break; case stringValue: if (allocated_) - releaseStringValue(value_.string_); + releasePrefixedStringValue(value_.string_); break; case arrayValue: case objectValue: @@ -467,6 +490,8 @@ Value::~Value() { if (comments_) delete[] comments_; + + value_.uint_ = 0; } Value& Value::operator=(Value other) { @@ -611,6 +636,18 @@ const char* Value::asCString() const { return this_str; } +#if JSONCPP_USING_SECURE_MEMORY +unsigned Value::getCStringLength() const { + JSON_ASSERT_MESSAGE(type_ == stringValue, + "in Json::Value::asCString(): requires stringValue"); + if (value_.string_ == 0) return 0; + unsigned this_len; + char const* this_str; + decodePrefixedString(this->allocated_, this->value_.string_, &this_len, &this_str); + return this_len; +} +#endif + bool Value::getString(char const** str, char const** cend) const { if (type_ != stringValue) return false; if (value_.string_ == 0) return false; diff --git a/src/lib_json/version.h.in b/src/lib_json/version.h.in index 692ab5e..47aac69 100644 --- a/src/lib_json/version.h.in +++ b/src/lib_json/version.h.in @@ -10,4 +10,11 @@ # define JSONCPP_VERSION_QUALIFIER # define JSONCPP_VERSION_HEXA ((JSONCPP_VERSION_MAJOR << 24) | (JSONCPP_VERSION_MINOR << 16) | (JSONCPP_VERSION_PATCH << 8)) +#ifdef JSONCPP_USING_SECURE_MEMORY +#undef JSONCPP_USING_SECURE_MEMORY +#endif +#define JSONCPP_USING_SECURE_MEMORY @JSONCPP_USE_SECURE_MEMORY@ +// If non-zero, the library zeroes any memory that it has allocated before +// it frees its memory. + #endif // JSON_VERSION_H_INCLUDED diff --git a/src/test_lib_json/jsontest.cpp b/src/test_lib_json/jsontest.cpp index 6ac5ceb..4c10a37 100644 --- a/src/test_lib_json/jsontest.cpp +++ b/src/test_lib_json/jsontest.cpp @@ -434,7 +434,7 @@ JSONCPP_STRING ToJsonString(JSONCPP_STRING in) { return in; } -#if JSON_USE_SECURE_MEMORY +#if JSONCPP_USING_SECURE_MEMORY JSONCPP_STRING ToJsonString(std::string in) { return JSONCPP_STRING(in.data(), in.data() + in.length()); } diff --git a/src/test_lib_json/jsontest.h b/src/test_lib_json/jsontest.h index ecd9079..69ecab7 100644 --- a/src/test_lib_json/jsontest.h +++ b/src/test_lib_json/jsontest.h @@ -193,7 +193,7 @@ TestResult& checkEqual(TestResult& result, JSONCPP_STRING ToJsonString(const char* toConvert); JSONCPP_STRING ToJsonString(JSONCPP_STRING in); -#if JSON_USE_SECURE_MEMORY +#if JSONCPP_USING_SECURE_MEMORY JSONCPP_STRING ToJsonString(std::string in); #endif diff --git a/travis.sh b/travis.sh index 2b25f47..a9811ec 100755 --- a/travis.sh +++ b/travis.sh @@ -19,6 +19,8 @@ env | sort cmake -DJSONCPP_WITH_CMAKE_PACKAGE=$CMAKE_PKG -DBUILD_SHARED_LIBS=$SHARED_LIB -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_VERBOSE_MAKEFILE=$VERBOSE_MAKE . make +cmake -DJSONCPP_WITH_CMAKE_PACKAGE=$CMAKE_PKG -DBUILD_SHARED_LIBS=$SHARED_LIB -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_VERBOSE_MAKEFILE=$VERBOSE_MAKE -DJSONCPP_USE_SECURE_MEMORY=1 . +make # Python is not available in Travis for osx. # https://github.com/travis-ci/travis-ci/issues/2320