Fixed undefined behavior in chrono converting.

Replaced static_cast with boost::numeric_cast to detect invalid cast
easier.
This commit is contained in:
Takatoshi Kondo
2020-07-01 09:19:36 +09:00
parent da2fc25f87
commit ebb5e0ceca

View File

@@ -17,6 +17,8 @@
#include <chrono> #include <chrono>
#include <boost/numeric/conversion/cast.hpp>
namespace msgpack { namespace msgpack {
/// @cond /// @cond
@@ -25,13 +27,12 @@ MSGPACK_API_VERSION_NAMESPACE(v1) {
namespace adaptor { namespace adaptor {
template <typename Clock> template <typename Clock, typename Duration>
struct as<std::chrono::time_point<Clock>> { struct as<std::chrono::time_point<Clock, Duration>> {
typename std::chrono::time_point<Clock> operator()(msgpack::object const& o) const { typename std::chrono::time_point<Clock, Duration> operator()(msgpack::object const& o) const {
using duration_t = typename std::chrono::time_point<Clock>::duration;
if(o.type != msgpack::type::EXT) { throw msgpack::type_error(); } if(o.type != msgpack::type::EXT) { throw msgpack::type_error(); }
if(o.via.ext.type() != -1) { throw msgpack::type_error(); } if(o.via.ext.type() != -1) { throw msgpack::type_error(); }
std::chrono::time_point<Clock> tp; std::chrono::time_point<Clock, Duration> tp;
switch(o.via.ext.size) { switch(o.via.ext.size) {
case 4: { case 4: {
uint32_t sec; uint32_t sec;
@@ -41,9 +42,9 @@ struct as<std::chrono::time_point<Clock>> {
case 8: { case 8: {
uint64_t value; uint64_t value;
_msgpack_load64(uint64_t, o.via.ext.data(), &value); _msgpack_load64(uint64_t, o.via.ext.data(), &value);
uint32_t nanosec = static_cast<uint32_t>(value >> 34); uint32_t nanosec = boost::numeric_cast<uint32_t>(value >> 34);
uint64_t sec = value & 0x00000003ffffffffLL; uint64_t sec = value & 0x00000003ffffffffLL;
tp += std::chrono::duration_cast<duration_t>( tp += std::chrono::duration_cast<Duration>(
std::chrono::nanoseconds(nanosec)); std::chrono::nanoseconds(nanosec));
tp += std::chrono::seconds(sec); tp += std::chrono::seconds(sec);
} break; } break;
@@ -52,9 +53,24 @@ struct as<std::chrono::time_point<Clock>> {
_msgpack_load32(uint32_t, o.via.ext.data(), &nanosec); _msgpack_load32(uint32_t, o.via.ext.data(), &nanosec);
int64_t sec; int64_t sec;
_msgpack_load64(int64_t, o.via.ext.data() + 4, &sec); _msgpack_load64(int64_t, o.via.ext.data() + 4, &sec);
tp += std::chrono::duration_cast<duration_t>(
std::chrono::nanoseconds(nanosec)); if (sec > 0) {
tp += std::chrono::seconds(sec); tp += std::chrono::seconds(sec);
tp += std::chrono::duration_cast<Duration>(
std::chrono::nanoseconds(nanosec));
}
else {
if (nanosec == 0) {
tp += std::chrono::seconds(sec);
}
else {
++sec;
tp += std::chrono::seconds(sec);
int64_t ns = boost::numeric_cast<int64_t>(nanosec) - 1000000000L;
tp += std::chrono::duration_cast<Duration>(
std::chrono::nanoseconds(ns));
}
}
} break; } break;
default: default:
throw msgpack::type_error(); throw msgpack::type_error();
@@ -63,13 +79,12 @@ struct as<std::chrono::time_point<Clock>> {
} }
}; };
template <typename Clock> template <typename Clock, typename Duration>
struct convert<std::chrono::time_point<Clock>> { struct convert<std::chrono::time_point<Clock, Duration>> {
msgpack::object const& operator()(msgpack::object const& o, std::chrono::time_point<Clock>& v) const { msgpack::object const& operator()(msgpack::object const& o, std::chrono::time_point<Clock, Duration>& v) const {
using duration_t = typename std::chrono::time_point<Clock>::duration;
if(o.type != msgpack::type::EXT) { throw msgpack::type_error(); } if(o.type != msgpack::type::EXT) { throw msgpack::type_error(); }
if(o.via.ext.type() != -1) { throw msgpack::type_error(); } if(o.via.ext.type() != -1) { throw msgpack::type_error(); }
std::chrono::time_point<Clock> tp; std::chrono::time_point<Clock, Duration> tp;
switch(o.via.ext.size) { switch(o.via.ext.size) {
case 4: { case 4: {
uint32_t sec; uint32_t sec;
@@ -80,9 +95,9 @@ struct convert<std::chrono::time_point<Clock>> {
case 8: { case 8: {
uint64_t value; uint64_t value;
_msgpack_load64(uint64_t, o.via.ext.data(), &value); _msgpack_load64(uint64_t, o.via.ext.data(), &value);
uint32_t nanosec = static_cast<uint32_t>(value >> 34); uint32_t nanosec = boost::numeric_cast<uint32_t>(value >> 34);
uint64_t sec = value & 0x00000003ffffffffLL; uint64_t sec = value & 0x00000003ffffffffLL;
tp += std::chrono::duration_cast<duration_t>( tp += std::chrono::duration_cast<Duration>(
std::chrono::nanoseconds(nanosec)); std::chrono::nanoseconds(nanosec));
tp += std::chrono::seconds(sec); tp += std::chrono::seconds(sec);
v = tp; v = tp;
@@ -92,9 +107,25 @@ struct convert<std::chrono::time_point<Clock>> {
_msgpack_load32(uint32_t, o.via.ext.data(), &nanosec); _msgpack_load32(uint32_t, o.via.ext.data(), &nanosec);
int64_t sec; int64_t sec;
_msgpack_load64(int64_t, o.via.ext.data() + 4, &sec); _msgpack_load64(int64_t, o.via.ext.data() + 4, &sec);
tp += std::chrono::duration_cast<duration_t>(
std::chrono::nanoseconds(nanosec)); if (sec > 0) {
tp += std::chrono::seconds(sec); tp += std::chrono::seconds(sec);
tp += std::chrono::duration_cast<Duration>(
std::chrono::nanoseconds(nanosec));
}
else {
if (nanosec == 0) {
tp += std::chrono::seconds(sec);
}
else {
++sec;
tp += std::chrono::seconds(sec);
int64_t ns = boost::numeric_cast<int64_t>(nanosec) - 1000000000L;
tp += std::chrono::duration_cast<Duration>(
std::chrono::nanoseconds(ns));
}
}
v = tp; v = tp;
} break; } break;
default: default:
@@ -104,32 +135,31 @@ struct convert<std::chrono::time_point<Clock>> {
} }
}; };
template <typename Clock> template <typename Clock, typename Duration>
struct pack<std::chrono::time_point<Clock>> { struct pack<std::chrono::time_point<Clock, Duration>> {
template <typename Stream> template <typename Stream>
msgpack::packer<Stream>& operator()(msgpack::packer<Stream>& o, std::chrono::time_point<Clock> const& v) const { msgpack::packer<Stream>& operator()(msgpack::packer<Stream>& o, std::chrono::time_point<Clock, Duration> const& v) const {
using duration_t = typename std::chrono::time_point<Clock>::duration; int64_t count = boost::numeric_cast<int64_t>(v.time_since_epoch().count());
int64_t count = static_cast<int64_t>(v.time_since_epoch().count());
int64_t nano_num = int64_t nano_num =
duration_t::period::ratio::num * Duration::period::ratio::num *
(1000000000 / duration_t::period::ratio::den); (1000000000L / Duration::period::ratio::den);
int64_t nanosec = count % (1000000000 / nano_num) * nano_num; int64_t nanosec = count % (1000000000L / nano_num) * nano_num;
int64_t sec = 0; int64_t sec = 0;
if (nanosec < 0) { if (nanosec < 0) {
nanosec = 1000000000 + nanosec; nanosec = 1000000000L + nanosec;
--sec; --sec;
} }
sec += count sec += count
* duration_t::period::ratio::num * Duration::period::ratio::num
/ duration_t::period::ratio::den; / Duration::period::ratio::den;
if ((sec >> 34) == 0) { if ((sec >> 34) == 0) {
uint64_t data64 = (static_cast<uint64_t>(nanosec) << 34) | static_cast<uint64_t>(sec); uint64_t data64 = (boost::numeric_cast<uint64_t>(nanosec) << 34) | boost::numeric_cast<uint64_t>(sec);
if ((data64 & 0xffffffff00000000L) == 0) { if ((data64 & 0xffffffff00000000L) == 0) {
// timestamp 32 // timestamp 32
o.pack_ext(4, -1); o.pack_ext(4, -1);
uint32_t data32 = static_cast<uint32_t>(data64); uint32_t data32 = boost::numeric_cast<uint32_t>(data64);
char buf[4]; char buf[4];
_msgpack_store32(buf, data32); _msgpack_store32(buf, data32);
o.pack_ext_body(buf, 4); o.pack_ext_body(buf, 4);
@@ -146,7 +176,9 @@ struct pack<std::chrono::time_point<Clock>> {
// timestamp 96 // timestamp 96
o.pack_ext(12, -1); o.pack_ext(12, -1);
char buf[12]; char buf[12];
_msgpack_store32(&buf[0], static_cast<uint32_t>(nanosec));
_msgpack_store32(&buf[0], boost::numeric_cast<uint32_t>(nanosec));
_msgpack_store64(&buf[4], sec); _msgpack_store64(&buf[4], sec);
o.pack_ext_body(buf, 12); o.pack_ext_body(buf, 12);
} }
@@ -154,34 +186,33 @@ struct pack<std::chrono::time_point<Clock>> {
} }
}; };
template <typename Clock> template <typename Clock, typename Duration>
struct object_with_zone<std::chrono::time_point<Clock>> { struct object_with_zone<std::chrono::time_point<Clock, Duration>> {
void operator()(msgpack::object::with_zone& o, const std::chrono::time_point<Clock>& v) const { void operator()(msgpack::object::with_zone& o, const std::chrono::time_point<Clock, Duration>& v) const {
using duration_t = typename std::chrono::time_point<Clock>::duration; int64_t count = boost::numeric_cast<int64_t>(v.time_since_epoch().count());
int64_t count = static_cast<int64_t>(v.time_since_epoch().count());
int64_t nano_num = int64_t nano_num =
duration_t::period::ratio::num * Duration::period::ratio::num *
(1000000000 / duration_t::period::ratio::den); (1000000000L / Duration::period::ratio::den);
int64_t nanosec = count % (1000000000 / nano_num) * nano_num; int64_t nanosec = count % (1000000000L / nano_num) * nano_num;
int64_t sec = 0; int64_t sec = 0;
if (nanosec < 0) { if (nanosec < 0) {
nanosec = 1000000000 + nanosec; nanosec = 1000000000L + nanosec;
--sec; --sec;
} }
sec += count sec += count
* duration_t::period::ratio::num * Duration::period::ratio::num
/ duration_t::period::ratio::den; / Duration::period::ratio::den;
if ((sec >> 34) == 0) { if ((sec >> 34) == 0) {
uint64_t data64 = (static_cast<uint64_t>(nanosec) << 34) | static_cast<uint64_t>(sec); uint64_t data64 = (boost::numeric_cast<uint64_t>(nanosec) << 34) | boost::numeric_cast<uint64_t>(sec);
if ((data64 & 0xffffffff00000000L) == 0) { if ((data64 & 0xffffffff00000000L) == 0) {
// timestamp 32 // timestamp 32
o.type = msgpack::type::EXT; o.type = msgpack::type::EXT;
o.via.ext.size = 4; o.via.ext.size = 4;
char* p = static_cast<char*>(o.zone.allocate_no_align(o.via.ext.size + 1)); char* p = static_cast<char*>(o.zone.allocate_no_align(o.via.ext.size + 1));
p[0] = static_cast<char>(-1); p[0] = static_cast<char>(-1);
uint32_t data32 = static_cast<uint32_t>(data64); uint32_t data32 = boost::numeric_cast<uint32_t>(data64);
_msgpack_store32(&p[1], data32); _msgpack_store32(&p[1], data32);
o.via.ext.ptr = p; o.via.ext.ptr = p;
} }
@@ -201,7 +232,7 @@ struct object_with_zone<std::chrono::time_point<Clock>> {
o.via.ext.size = 12; o.via.ext.size = 12;
char* p = static_cast<char*>(o.zone.allocate_no_align(o.via.ext.size + 1)); char* p = static_cast<char*>(o.zone.allocate_no_align(o.via.ext.size + 1));
p[0] = static_cast<char>(-1); p[0] = static_cast<char>(-1);
_msgpack_store32(&p[1], static_cast<uint32_t>(nanosec)); _msgpack_store32(&p[1], boost::numeric_cast<uint32_t>(nanosec));
_msgpack_store64(&p[1 + 4], sec); _msgpack_store64(&p[1 + 4], sec);
o.via.ext.ptr = p; o.via.ext.ptr = p;
} }