-
Notifications
You must be signed in to change notification settings - Fork 3.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix undefined behaviour #646
Conversation
@@ -952,12 +956,16 @@ class GenericValue { | |||
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()/2 + 1)) * 2) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you explain about this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Casting a double that's out-of-range for the uint64_t is undefined behaviour, so this equality test has undefined results. I'm not 100% clear on what exactly IsLosslessDouble promises if it returns true, but it's certainly possible that you could get true from the equality test when you expect false.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not sure about the calculation (max / 2 + 1) * 2
. I think it can simply construct a maximum double that can be converted to uint64_t
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The issue is UINT64_MAX can't be exactly represented in a double. I changed it to be strictly less than UINT64_MAX as a double and added some more tests.
any more concerns? is this ready to merge? |
Travis reported that clang build were failed. Can you try to solve that? Thanks. |
it looks like travis is failing to create the clang toolchain in the VM. I don't think it has anything to do with the code itself. |
Yes, LLVM removed their apt mirror, which broken travis builds for tons of projects. We could move to the Trusty Travis environment and use the built-in clang, which is 3.5. |
Thank you. I just realized this. I will try to fix the build before merging for safety. |
Hi, I tried on OSX and seems the compiler version of clang is different. I tried to fixed with this:
Besides, there are problems in MSVC compilation in appveyor. Can you check if they can be fixed? |
…e compiling with -Werror
…chable-code clang advises: "note: silence by adding parentheses to mark code as explicitly dead"
specifically, "expression with side effects has no effect in an unevaluated context"
UBSAN gave "runtime error: index 13 out of bounds for type 'const uint32_t [10]'"
UBSAN gave issues with the typeless Schema: runtime error: reference binding to null pointer of type 'rapidjson::GenericSchemaDocument<rapidjson::GenericValue<rapidjson::UTF16<wchar_t>, rapidjson::MemoryPoolAllocator<rapidjson::CrtAllocator> >, rapidjson::CrtAllocator>' and runtime error: member access within null pointer of type 'AllocatorType' (aka 'rapidjson::CrtAllocator')
UBSAN on Clang/Linux gave: runtime error: null pointer passed as argument 2, which is declared to never be null /usr/include/string.h:43:45: note: nonnull attribute specified here
I merged #654 and fixed some compiling issues with that. |
UBSAN gave for test/unittest/itoatest.cpp:87: runtime error: signed integer overflow: 4611686018427387904 * 2 cannot be represented in type 'long'
maybe these tests should just be deleted? UBSAN gave: runtime error: signed integer overflow: -9223372036854775808 - 1 cannot be represented in type 'long' runtime error: signed integer overflow: -9223372036854775808 - 2 cannot be represented in type 'long'
UBSAN gave: runtime error: division by zero
UBSAN gave during Reader.ParseNumber_FullPrecisionDouble test: include/rapidjson/internal/strtod.h:149:11: runtime error: shift exponent 46 is too large for 32-bit type 'int'
UBSAN gave in Regex.Unicode test: include/rapidjson/encodings.h:157:28: runtime error: shift exponent 32 is too large for 32-bit type 'int'
note that std::numeric_limits<uint64_t>::max() and std::numeric_limits<int64_t>::max() aren't exactly representable in a double, so we need to be strictly less to be definitely lossless UBSAN gave during Value.IsLosslessDouble test: include/rapidjson/document.h:955:42: runtime error: value 1.84467e+19 is outside the range of representable values of type 'unsigned long'
UBSAN gave in Value.IsLosslessFloat: include/rapidjson/document.h:981:38: runtime error: value 3.40282e+38 is outside the range of representable values of type 'float'
I just pushed an update. Sorry for the slow progress; I'm travelling. |
ok, build passed |
Thank you very much. |
Fix issues with undefined behaviour, mostly found by running the unittests under UBSAN on Clang/OSX and GCC5/Linux.
Issue #644