Skip to content

Commit 194d1a4

Browse files
authored
Merge pull request #893 from redboltz/fix_chrono_ub
Fixed undefined behavior in chrono converting.
2 parents f72c6c4 + ebb5e0c commit 194d1a4

File tree

1 file changed

+80
-49
lines changed

1 file changed

+80
-49
lines changed

include/msgpack/v1/adaptor/cpp11/chrono.hpp

Lines changed: 80 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717

1818
#include <chrono>
1919

20+
#include <boost/numeric/conversion/cast.hpp>
21+
2022
namespace msgpack {
2123

2224
/// @cond
@@ -25,13 +27,12 @@ MSGPACK_API_VERSION_NAMESPACE(v1) {
2527

2628
namespace adaptor {
2729

28-
template <typename Clock>
29-
struct as<std::chrono::time_point<Clock>> {
30-
typename std::chrono::time_point<Clock> operator()(msgpack::object const& o) const {
31-
using duration_t = typename std::chrono::time_point<Clock>::duration;
30+
template <typename Clock, typename Duration>
31+
struct as<std::chrono::time_point<Clock, Duration>> {
32+
typename std::chrono::time_point<Clock, Duration> operator()(msgpack::object const& o) const {
3233
if(o.type != msgpack::type::EXT) { throw msgpack::type_error(); }
3334
if(o.via.ext.type() != -1) { throw msgpack::type_error(); }
34-
std::chrono::time_point<Clock> tp;
35+
std::chrono::time_point<Clock, Duration> tp;
3536
switch(o.via.ext.size) {
3637
case 4: {
3738
uint32_t sec;
@@ -41,9 +42,9 @@ struct as<std::chrono::time_point<Clock>> {
4142
case 8: {
4243
uint64_t value;
4344
_msgpack_load64(uint64_t, o.via.ext.data(), &value);
44-
uint32_t nanosec = static_cast<uint32_t>(value >> 34);
45+
uint32_t nanosec = boost::numeric_cast<uint32_t>(value >> 34);
4546
uint64_t sec = value & 0x00000003ffffffffLL;
46-
tp += std::chrono::duration_cast<duration_t>(
47+
tp += std::chrono::duration_cast<Duration>(
4748
std::chrono::nanoseconds(nanosec));
4849
tp += std::chrono::seconds(sec);
4950
} break;
@@ -52,9 +53,24 @@ struct as<std::chrono::time_point<Clock>> {
5253
_msgpack_load32(uint32_t, o.via.ext.data(), &nanosec);
5354
int64_t sec;
5455
_msgpack_load64(int64_t, o.via.ext.data() + 4, &sec);
55-
tp += std::chrono::duration_cast<duration_t>(
56-
std::chrono::nanoseconds(nanosec));
57-
tp += std::chrono::seconds(sec);
56+
57+
if (sec > 0) {
58+
tp += std::chrono::seconds(sec);
59+
tp += std::chrono::duration_cast<Duration>(
60+
std::chrono::nanoseconds(nanosec));
61+
}
62+
else {
63+
if (nanosec == 0) {
64+
tp += std::chrono::seconds(sec);
65+
}
66+
else {
67+
++sec;
68+
tp += std::chrono::seconds(sec);
69+
int64_t ns = boost::numeric_cast<int64_t>(nanosec) - 1000000000L;
70+
tp += std::chrono::duration_cast<Duration>(
71+
std::chrono::nanoseconds(ns));
72+
}
73+
}
5874
} break;
5975
default:
6076
throw msgpack::type_error();
@@ -63,13 +79,12 @@ struct as<std::chrono::time_point<Clock>> {
6379
}
6480
};
6581

66-
template <typename Clock>
67-
struct convert<std::chrono::time_point<Clock>> {
68-
msgpack::object const& operator()(msgpack::object const& o, std::chrono::time_point<Clock>& v) const {
69-
using duration_t = typename std::chrono::time_point<Clock>::duration;
82+
template <typename Clock, typename Duration>
83+
struct convert<std::chrono::time_point<Clock, Duration>> {
84+
msgpack::object const& operator()(msgpack::object const& o, std::chrono::time_point<Clock, Duration>& v) const {
7085
if(o.type != msgpack::type::EXT) { throw msgpack::type_error(); }
7186
if(o.via.ext.type() != -1) { throw msgpack::type_error(); }
72-
std::chrono::time_point<Clock> tp;
87+
std::chrono::time_point<Clock, Duration> tp;
7388
switch(o.via.ext.size) {
7489
case 4: {
7590
uint32_t sec;
@@ -80,9 +95,9 @@ struct convert<std::chrono::time_point<Clock>> {
8095
case 8: {
8196
uint64_t value;
8297
_msgpack_load64(uint64_t, o.via.ext.data(), &value);
83-
uint32_t nanosec = static_cast<uint32_t>(value >> 34);
98+
uint32_t nanosec = boost::numeric_cast<uint32_t>(value >> 34);
8499
uint64_t sec = value & 0x00000003ffffffffLL;
85-
tp += std::chrono::duration_cast<duration_t>(
100+
tp += std::chrono::duration_cast<Duration>(
86101
std::chrono::nanoseconds(nanosec));
87102
tp += std::chrono::seconds(sec);
88103
v = tp;
@@ -92,9 +107,25 @@ struct convert<std::chrono::time_point<Clock>> {
92107
_msgpack_load32(uint32_t, o.via.ext.data(), &nanosec);
93108
int64_t sec;
94109
_msgpack_load64(int64_t, o.via.ext.data() + 4, &sec);
95-
tp += std::chrono::duration_cast<duration_t>(
96-
std::chrono::nanoseconds(nanosec));
97-
tp += std::chrono::seconds(sec);
110+
111+
if (sec > 0) {
112+
tp += std::chrono::seconds(sec);
113+
tp += std::chrono::duration_cast<Duration>(
114+
std::chrono::nanoseconds(nanosec));
115+
}
116+
else {
117+
if (nanosec == 0) {
118+
tp += std::chrono::seconds(sec);
119+
}
120+
else {
121+
++sec;
122+
tp += std::chrono::seconds(sec);
123+
int64_t ns = boost::numeric_cast<int64_t>(nanosec) - 1000000000L;
124+
tp += std::chrono::duration_cast<Duration>(
125+
std::chrono::nanoseconds(ns));
126+
}
127+
}
128+
98129
v = tp;
99130
} break;
100131
default:
@@ -104,32 +135,31 @@ struct convert<std::chrono::time_point<Clock>> {
104135
}
105136
};
106137

107-
template <typename Clock>
108-
struct pack<std::chrono::time_point<Clock>> {
138+
template <typename Clock, typename Duration>
139+
struct pack<std::chrono::time_point<Clock, Duration>> {
109140
template <typename Stream>
110-
msgpack::packer<Stream>& operator()(msgpack::packer<Stream>& o, std::chrono::time_point<Clock> const& v) const {
111-
using duration_t = typename std::chrono::time_point<Clock>::duration;
112-
int64_t count = static_cast<int64_t>(v.time_since_epoch().count());
113-
141+
msgpack::packer<Stream>& operator()(msgpack::packer<Stream>& o, std::chrono::time_point<Clock, Duration> const& v) const {
142+
int64_t count = boost::numeric_cast<int64_t>(v.time_since_epoch().count());
114143
int64_t nano_num =
115-
duration_t::period::ratio::num *
116-
(1000000000 / duration_t::period::ratio::den);
144+
Duration::period::ratio::num *
145+
(1000000000L / Duration::period::ratio::den);
117146

118-
int64_t nanosec = count % (1000000000 / nano_num) * nano_num;
147+
int64_t nanosec = count % (1000000000L / nano_num) * nano_num;
119148
int64_t sec = 0;
120149
if (nanosec < 0) {
121-
nanosec = 1000000000 + nanosec;
150+
nanosec = 1000000000L + nanosec;
122151
--sec;
123152
}
124153
sec += count
125-
* duration_t::period::ratio::num
126-
/ duration_t::period::ratio::den;
154+
* Duration::period::ratio::num
155+
/ Duration::period::ratio::den;
156+
127157
if ((sec >> 34) == 0) {
128-
uint64_t data64 = (static_cast<uint64_t>(nanosec) << 34) | static_cast<uint64_t>(sec);
158+
uint64_t data64 = (boost::numeric_cast<uint64_t>(nanosec) << 34) | boost::numeric_cast<uint64_t>(sec);
129159
if ((data64 & 0xffffffff00000000L) == 0) {
130160
// timestamp 32
131161
o.pack_ext(4, -1);
132-
uint32_t data32 = static_cast<uint32_t>(data64);
162+
uint32_t data32 = boost::numeric_cast<uint32_t>(data64);
133163
char buf[4];
134164
_msgpack_store32(buf, data32);
135165
o.pack_ext_body(buf, 4);
@@ -146,42 +176,43 @@ struct pack<std::chrono::time_point<Clock>> {
146176
// timestamp 96
147177
o.pack_ext(12, -1);
148178
char buf[12];
149-
_msgpack_store32(&buf[0], static_cast<uint32_t>(nanosec));
179+
180+
181+
_msgpack_store32(&buf[0], boost::numeric_cast<uint32_t>(nanosec));
150182
_msgpack_store64(&buf[4], sec);
151183
o.pack_ext_body(buf, 12);
152184
}
153185
return o;
154186
}
155187
};
156188

157-
template <typename Clock>
158-
struct object_with_zone<std::chrono::time_point<Clock>> {
159-
void operator()(msgpack::object::with_zone& o, const std::chrono::time_point<Clock>& v) const {
160-
using duration_t = typename std::chrono::time_point<Clock>::duration;
161-
int64_t count = static_cast<int64_t>(v.time_since_epoch().count());
189+
template <typename Clock, typename Duration>
190+
struct object_with_zone<std::chrono::time_point<Clock, Duration>> {
191+
void operator()(msgpack::object::with_zone& o, const std::chrono::time_point<Clock, Duration>& v) const {
192+
int64_t count = boost::numeric_cast<int64_t>(v.time_since_epoch().count());
162193

163194
int64_t nano_num =
164-
duration_t::period::ratio::num *
165-
(1000000000 / duration_t::period::ratio::den);
195+
Duration::period::ratio::num *
196+
(1000000000L / Duration::period::ratio::den);
166197

167-
int64_t nanosec = count % (1000000000 / nano_num) * nano_num;
198+
int64_t nanosec = count % (1000000000L / nano_num) * nano_num;
168199
int64_t sec = 0;
169200
if (nanosec < 0) {
170-
nanosec = 1000000000 + nanosec;
201+
nanosec = 1000000000L + nanosec;
171202
--sec;
172203
}
173204
sec += count
174-
* duration_t::period::ratio::num
175-
/ duration_t::period::ratio::den;
205+
* Duration::period::ratio::num
206+
/ Duration::period::ratio::den;
176207
if ((sec >> 34) == 0) {
177-
uint64_t data64 = (static_cast<uint64_t>(nanosec) << 34) | static_cast<uint64_t>(sec);
208+
uint64_t data64 = (boost::numeric_cast<uint64_t>(nanosec) << 34) | boost::numeric_cast<uint64_t>(sec);
178209
if ((data64 & 0xffffffff00000000L) == 0) {
179210
// timestamp 32
180211
o.type = msgpack::type::EXT;
181212
o.via.ext.size = 4;
182213
char* p = static_cast<char*>(o.zone.allocate_no_align(o.via.ext.size + 1));
183214
p[0] = static_cast<char>(-1);
184-
uint32_t data32 = static_cast<uint32_t>(data64);
215+
uint32_t data32 = boost::numeric_cast<uint32_t>(data64);
185216
_msgpack_store32(&p[1], data32);
186217
o.via.ext.ptr = p;
187218
}
@@ -201,7 +232,7 @@ struct object_with_zone<std::chrono::time_point<Clock>> {
201232
o.via.ext.size = 12;
202233
char* p = static_cast<char*>(o.zone.allocate_no_align(o.via.ext.size + 1));
203234
p[0] = static_cast<char>(-1);
204-
_msgpack_store32(&p[1], static_cast<uint32_t>(nanosec));
235+
_msgpack_store32(&p[1], boost::numeric_cast<uint32_t>(nanosec));
205236
_msgpack_store64(&p[1 + 4], sec);
206237
o.via.ext.ptr = p;
207238
}

0 commit comments

Comments
 (0)