Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/miloyip/rapidjson.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMilo Yip <miloyip@gmail.com>2016-06-15 04:35:12 +0300
committerGitHub <noreply@github.com>2016-06-15 04:35:12 +0300
commit62ff0a9df9b9faeaf6c2ccc39422b2a810610a41 (patch)
tree88a9dbd3ec6dce1355d1405b704c1411075d63f0
parent2e6633913718b923a949f31ce76ce14ccf4bea8d (diff)
parent8c4059766e88626ad1213d40e05c308f62644d01 (diff)
Merge pull request #646 from efidler/ubsan
Fix undefined behaviour
-rw-r--r--CMakeLists.txt31
-rw-r--r--include/rapidjson/document.h12
-rw-r--r--include/rapidjson/encodings.h6
-rw-r--r--include/rapidjson/internal/dtoa.h3
-rw-r--r--include/rapidjson/internal/strtod.h2
-rw-r--r--include/rapidjson/pointer.h8
-rw-r--r--include/rapidjson/reader.h1
-rw-r--r--include/rapidjson/schema.h12
-rw-r--r--test/unittest/CMakeLists.txt15
-rw-r--r--test/unittest/dtoatest.cpp1
-rw-r--r--test/unittest/itoatest.cpp2
-rw-r--r--test/unittest/schematest.cpp2
-rw-r--r--test/unittest/valuetest.cpp30
-rw-r--r--test/unittest/writertest.cpp9
14 files changed, 106 insertions, 28 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index d315b749..96bfdc2a 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,4 +1,8 @@
CMAKE_MINIMUM_REQUIRED(VERSION 2.8)
+if(POLICY CMP0025)
+ # detect Apple's Clang
+ cmake_policy(SET CMP0025 NEW)
+endif()
if(POLICY CMP0054)
cmake_policy(SET CMP0054 NEW)
endif()
@@ -28,6 +32,9 @@ option(RAPIDJSON_BUILD_THIRDPARTY_GTEST
option(RAPIDJSON_BUILD_CXX11 "Build rapidjson with C++11 (gcc/clang)" ON)
+option(RAPIDJSON_BUILD_ASAN "Build rapidjson with address sanitizer (gcc/clang)" OFF)
+option(RAPIDJSON_BUILD_UBSAN "Build rapidjson with undefined behavior sanitizer (gcc/clang)" OFF)
+
option(RAPIDJSON_HAS_STDSTRING "" OFF)
if(RAPIDJSON_HAS_STDSTRING)
add_definitions(-DRAPIDJSON_HAS_STDSTRING)
@@ -51,11 +58,35 @@ if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
endif()
endif()
+ if (RAPIDJSON_BUILD_ASAN)
+ if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS "4.8.0")
+ message(FATAL_ERROR "GCC < 4.8 doesn't support the address sanitizer")
+ else()
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address")
+ endif()
+ endif()
+ if (RAPIDJSON_BUILD_UBSAN)
+ if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS "4.9.0")
+ message(FATAL_ERROR "GCC < 4.9 doesn't support the undefined behavior sanitizer")
+ else()
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=undefined")
+ endif()
+ endif()
elseif (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=native -Wall -Wextra -Werror -Wno-missing-field-initializers")
if (RAPIDJSON_BUILD_CXX11)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
endif()
+ if (RAPIDJSON_BUILD_ASAN)
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address")
+ endif()
+ if (RAPIDJSON_BUILD_UBSAN)
+ if (CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=undefined-trap -fsanitize-undefined-trap-on-error")
+ else()
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=undefined")
+ endif()
+ endif()
elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
add_definitions(-D_CRT_SECURE_NO_WARNINGS=1)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /EHsc")
diff --git a/include/rapidjson/document.h b/include/rapidjson/document.h
index d286eb1e..b0162f55 100644
--- a/include/rapidjson/document.h
+++ b/include/rapidjson/document.h
@@ -23,6 +23,7 @@
#include "memorystream.h"
#include "encodedstream.h"
#include <new> // placement new
+#include <limits>
#ifdef _MSC_VER
RAPIDJSON_DIAG_PUSH
@@ -952,12 +953,16 @@ public:
if (IsUint64()) {
uint64_t u = GetUint64();
volatile double d = static_cast<double>(u);
- return static_cast<uint64_t>(d) == u;
+ return (d >= 0.0)
+ && (d < static_cast<double>(std::numeric_limits<uint64_t>::max()))
+ && (u == static_cast<uint64_t>(d));
}
if (IsInt64()) {
int64_t i = GetInt64();
volatile double d = static_cast<double>(i);
- return static_cast< int64_t>(d) == i;
+ return (d >= static_cast<double>(std::numeric_limits<int64_t>::min()))
+ && (d < static_cast<double>(std::numeric_limits<int64_t>::max()))
+ && (i == static_cast<int64_t>(d));
}
return true; // double, int, uint are always lossless
}
@@ -973,6 +978,9 @@ public:
bool IsLosslessFloat() const {
if (!IsNumber()) return false;
double a = GetDouble();
+ if (a < static_cast<double>(-std::numeric_limits<float>::max())
+ || a > static_cast<double>(std::numeric_limits<float>::max()))
+ return false;
double b = static_cast<double>(static_cast<float>(a));
return a >= b && a <= b; // Prevent -Wfloat-equal
}
diff --git a/include/rapidjson/encodings.h b/include/rapidjson/encodings.h
index edfc9901..baa7c2b1 100644
--- a/include/rapidjson/encodings.h
+++ b/include/rapidjson/encodings.h
@@ -154,7 +154,11 @@ struct UTF8 {
}
unsigned char type = GetRange(static_cast<unsigned char>(c));
- *codepoint = (0xFF >> type) & static_cast<unsigned char>(c);
+ if (type >= 32) {
+ *codepoint = 0;
+ } else {
+ *codepoint = (0xFF >> type) & static_cast<unsigned char>(c);
+ }
bool result = true;
switch (type) {
case 2: TAIL(); return result;
diff --git a/include/rapidjson/internal/dtoa.h b/include/rapidjson/internal/dtoa.h
index bc454960..8d6350e6 100644
--- a/include/rapidjson/internal/dtoa.h
+++ b/include/rapidjson/internal/dtoa.h
@@ -102,7 +102,8 @@ inline void DigitGen(const DiyFp& W, const DiyFp& Mp, uint64_t delta, char* buff
kappa--;
if (p2 < delta) {
*K += kappa;
- GrisuRound(buffer, *len, delta, p2, one.f, wp_w.f * kPow10[-static_cast<int>(kappa)]);
+ int index = -static_cast<int>(kappa);
+ GrisuRound(buffer, *len, delta, p2, one.f, wp_w.f * (index < 9 ? kPow10[-static_cast<int>(kappa)] : 0));
return;
}
}
diff --git a/include/rapidjson/internal/strtod.h b/include/rapidjson/internal/strtod.h
index fd4b01e8..289c413b 100644
--- a/include/rapidjson/internal/strtod.h
+++ b/include/rapidjson/internal/strtod.h
@@ -142,7 +142,7 @@ inline bool StrtodDiyFp(const char* decimals, size_t length, size_t decimalPosit
size_t remaining = length - i;
const unsigned kUlpShift = 3;
const unsigned kUlp = 1 << kUlpShift;
- int error = (remaining == 0) ? 0 : kUlp / 2;
+ int64_t error = (remaining == 0) ? 0 : kUlp / 2;
DiyFp v(significand, 0);
v = v.Normalize();
diff --git a/include/rapidjson/pointer.h b/include/rapidjson/pointer.h
index c9852779..0206ac1c 100644
--- a/include/rapidjson/pointer.h
+++ b/include/rapidjson/pointer.h
@@ -767,8 +767,12 @@ private:
tokenCount_ = rhs.tokenCount_ + extraToken;
tokens_ = static_cast<Token *>(allocator_->Malloc(tokenCount_ * sizeof(Token) + (nameBufferSize + extraNameBufferSize) * sizeof(Ch)));
nameBuffer_ = reinterpret_cast<Ch *>(tokens_ + tokenCount_);
- std::memcpy(tokens_, rhs.tokens_, rhs.tokenCount_ * sizeof(Token));
- std::memcpy(nameBuffer_, rhs.nameBuffer_, nameBufferSize * sizeof(Ch));
+ if (rhs.tokenCount_ > 0) {
+ std::memcpy(tokens_, rhs.tokens_, rhs.tokenCount_ * sizeof(Token));
+ }
+ if (nameBufferSize > 0) {
+ std::memcpy(nameBuffer_, rhs.nameBuffer_, nameBufferSize * sizeof(Ch));
+ }
// Adjust pointers to name buffer
std::ptrdiff_t diff = nameBuffer_ - rhs.nameBuffer_;
diff --git a/include/rapidjson/reader.h b/include/rapidjson/reader.h
index 13fd1268..19f8849b 100644
--- a/include/rapidjson/reader.h
+++ b/include/rapidjson/reader.h
@@ -43,6 +43,7 @@ RAPIDJSON_DIAG_OFF(4702) // unreachable code
#ifdef __clang__
RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(old-style-cast)
RAPIDJSON_DIAG_OFF(padded)
RAPIDJSON_DIAG_OFF(switch-enum)
#endif
diff --git a/include/rapidjson/schema.h b/include/rapidjson/schema.h
index 0a8bb7c5..80812f06 100644
--- a/include/rapidjson/schema.h
+++ b/include/rapidjson/schema.h
@@ -413,9 +413,11 @@ public:
}
}
- AssignIfExist(allOf_, *schemaDocument, p, value, GetAllOfString(), document);
- AssignIfExist(anyOf_, *schemaDocument, p, value, GetAnyOfString(), document);
- AssignIfExist(oneOf_, *schemaDocument, p, value, GetOneOfString(), document);
+ if (schemaDocument) {
+ AssignIfExist(allOf_, *schemaDocument, p, value, GetAllOfString(), document);
+ AssignIfExist(anyOf_, *schemaDocument, p, value, GetAnyOfString(), document);
+ AssignIfExist(oneOf_, *schemaDocument, p, value, GetOneOfString(), document);
+ }
if (const ValueType* v = GetMember(value, GetNotString())) {
schemaDocument->CreateSchema(&not_, p.Append(GetNotString(), allocator_), *v, document);
@@ -578,7 +580,9 @@ public:
}
~Schema() {
- allocator_->Free(enum_);
+ if (allocator_) {
+ allocator_->Free(enum_);
+ }
if (properties_) {
for (SizeType i = 0; i < propertyCount_; i++)
properties_[i].~Property();
diff --git a/test/unittest/CMakeLists.txt b/test/unittest/CMakeLists.txt
index 4e3b0714..b3204d6c 100644
--- a/test/unittest/CMakeLists.txt
+++ b/test/unittest/CMakeLists.txt
@@ -1,3 +1,5 @@
+include(CheckCXXCompilerFlag)
+
set(UNITTEST_SOURCES
allocatorstest.cpp
bigintegertest.cpp
@@ -38,11 +40,14 @@ if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror -Wall -Wextra -Weffc++ -Wswitch-default -Wfloat-equal")
elseif (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror -Wall -Wextra -Weffc++ -Wswitch-default -Wfloat-equal -Wimplicit-fallthrough -Weverything")
- # If the user is running a newer version of Clang that includes the
- # -Wdouble-promotion, we will ignore that warning.
- # if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 3.7)
- # set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-double-promotion")
- # endif()
+ # If the user is running a newer version of Clang that includes the
+ # -Wdouble-promotion, we will ignore that warning.
+ if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 3.7)
+ CHECK_CXX_COMPILER_FLAG("-Wno-double-promotion" HAS_NO_DOUBLE_PROMOTION)
+ if (HAS_NO_DOUBLE_PROMOTION)
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-double-promotion")
+ endif()
+ endif()
elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
# Force to always compile with /W4
if(CMAKE_CXX_FLAGS MATCHES "/W[0-4]")
diff --git a/test/unittest/dtoatest.cpp b/test/unittest/dtoatest.cpp
index fe28271f..afd76eb0 100644
--- a/test/unittest/dtoatest.cpp
+++ b/test/unittest/dtoatest.cpp
@@ -37,6 +37,7 @@ TEST(dtoa, normal) {
TEST_DTOA(1.2345678, "1.2345678");
TEST_DTOA(0.123456789012, "0.123456789012");
TEST_DTOA(1234567.8, "1234567.8");
+ TEST_DTOA(-79.39773355813419, "-79.39773355813419");
TEST_DTOA(0.000001, "0.000001");
TEST_DTOA(0.0000001, "1e-7");
TEST_DTOA(1e30, "1e30");
diff --git a/test/unittest/itoatest.cpp b/test/unittest/itoatest.cpp
index 79db1c71..b752a6a2 100644
--- a/test/unittest/itoatest.cpp
+++ b/test/unittest/itoatest.cpp
@@ -84,6 +84,8 @@ static void Verify(void(*f)(T, char*), char* (*g)(T, char*)) {
VerifyValue<T>(Traits<T>::Negate(i + 1), f, g);
}
last = i;
+ if (i > static_cast<T>(std::numeric_limits<T>::max() / static_cast<T>(power)))
+ break;
i *= power;
} while (last < i);
}
diff --git a/test/unittest/schematest.cpp b/test/unittest/schematest.cpp
index d1027ad5..d75b1e59 100644
--- a/test/unittest/schematest.cpp
+++ b/test/unittest/schematest.cpp
@@ -111,7 +111,7 @@ TEST(SchemaValidator, Hasher) {
EXPECT_FALSE(d.HasParseError());\
EXPECT_TRUE(expected == d.Accept(validator));\
EXPECT_TRUE(expected == validator.IsValid());\
- if (expected && !validator.IsValid()) {\
+ if ((expected) && !validator.IsValid()) {\
StringBuffer sb;\
validator.GetInvalidSchemaPointer().StringifyUriFragment(sb);\
printf("Invalid schema: %s\n", sb.GetString());\
diff --git a/test/unittest/valuetest.cpp b/test/unittest/valuetest.cpp
index feec049d..fefc001d 100644
--- a/test/unittest/valuetest.cpp
+++ b/test/unittest/valuetest.cpp
@@ -545,8 +545,10 @@ TEST(Value, Int64) {
// Templated functions
EXPECT_TRUE(z.Is<int64_t>());
EXPECT_EQ(i, z.Get<int64_t>());
+#if 0 // signed integer underflow is undefined behaviour
EXPECT_EQ(i - 1, z.Set(i - 1).Get<int64_t>());
EXPECT_EQ(i - 2, z.Set<int64_t>(i - 2).Get<int64_t>());
+#endif
}
TEST(Value, Uint64) {
@@ -671,6 +673,7 @@ TEST(Value, Float) {
}
TEST(Value, IsLosslessDouble) {
+ EXPECT_TRUE(Value(0.0).IsLosslessDouble());
EXPECT_TRUE(Value(12.34).IsLosslessDouble());
EXPECT_TRUE(Value(-123).IsLosslessDouble());
EXPECT_TRUE(Value(2147483648u).IsLosslessDouble());
@@ -679,8 +682,19 @@ TEST(Value, IsLosslessDouble) {
EXPECT_TRUE(Value(RAPIDJSON_UINT64_C2(0xA0000000, 0x00000000)).IsLosslessDouble());
#endif
- EXPECT_FALSE(Value(-static_cast<int64_t>(RAPIDJSON_UINT64_C2(0x7FFFFFFF, 0xFFFFFFFF))).IsLosslessDouble());
- EXPECT_FALSE(Value(RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0xFFFFFFFF)).IsLosslessDouble());
+ EXPECT_FALSE(Value(static_cast<int64_t>(RAPIDJSON_UINT64_C2(0x7FFFFFFF, 0xFFFFFFFF))).IsLosslessDouble()); // INT64_MAX
+ EXPECT_FALSE(Value(-static_cast<int64_t>(RAPIDJSON_UINT64_C2(0x7FFFFFFF, 0xFFFFFFFF))).IsLosslessDouble()); // -INT64_MAX
+ EXPECT_TRUE(Value(-static_cast<int64_t>(RAPIDJSON_UINT64_C2(0x7FFFFFFF, 0xFFFFFFFF)) - 1).IsLosslessDouble()); // INT64_MIN
+ EXPECT_FALSE(Value(RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0xFFFFFFFF)).IsLosslessDouble()); // UINT64_MAX
+
+ EXPECT_TRUE(Value(3.4028234e38f).IsLosslessDouble()); // FLT_MAX
+ EXPECT_TRUE(Value(-3.4028234e38f).IsLosslessDouble()); // -FLT_MAX
+ EXPECT_TRUE(Value(1.17549435e-38f).IsLosslessDouble()); // FLT_MIN
+ EXPECT_TRUE(Value(-1.17549435e-38f).IsLosslessDouble()); // -FLT_MIN
+ EXPECT_TRUE(Value(1.7976931348623157e+308).IsLosslessDouble()); // DBL_MAX
+ EXPECT_TRUE(Value(-1.7976931348623157e+308).IsLosslessDouble()); // -DBL_MAX
+ EXPECT_TRUE(Value(2.2250738585072014e-308).IsLosslessDouble()); // DBL_MIN
+ EXPECT_TRUE(Value(-2.2250738585072014e-308).IsLosslessDouble()); // -DBL_MIN
}
TEST(Value, IsLosslessFloat) {
@@ -1119,14 +1133,18 @@ TEST(Value, ArrayHelperRangeFor) {
{
int i = 0;
- for (auto& v : x.GetArray())
- EXPECT_EQ(i++, v.GetInt());
+ for (auto& v : x.GetArray()) {
+ EXPECT_EQ(i, v.GetInt());
+ i++;
+ }
EXPECT_EQ(i, 10);
}
{
int i = 0;
- for (const auto& v : const_cast<const Value&>(x).GetArray())
- EXPECT_EQ(i++, v.GetInt());
+ for (const auto& v : const_cast<const Value&>(x).GetArray()) {
+ EXPECT_EQ(i, v.GetInt());
+ i++;
+ }
EXPECT_EQ(i, 10);
}
diff --git a/test/unittest/writertest.cpp b/test/unittest/writertest.cpp
index 22f428e0..29f76260 100644
--- a/test/unittest/writertest.cpp
+++ b/test/unittest/writertest.cpp
@@ -439,11 +439,9 @@ TEST(Writer, InvalidEventSequence) {
}
}
-extern double zero; // clang -Wmissing-variable-declarations
-double zero = 0.0; // Use global variable to prevent compiler warning
-
TEST(Writer, NaN) {
- double nan = zero / zero;
+ double nan = std::numeric_limits<double>::quiet_NaN();
+
EXPECT_TRUE(internal::Double(nan).IsNan());
StringBuffer buffer;
{
@@ -461,7 +459,8 @@ TEST(Writer, NaN) {
}
TEST(Writer, Inf) {
- double inf = 1.0 / zero;
+ double inf = std::numeric_limits<double>::infinity();
+
EXPECT_TRUE(internal::Double(inf).IsInf());
StringBuffer buffer;
{