Skip to content

Commit f58ec39

Browse files
authored
butil: fix undefined behaviors (#3135)
There are two kinds of problems: 1. signed number overflow is undefined behavior; 2. vsnprintfT may return E2BIG instead of EOVERFLOW.
1 parent 8734f06 commit f58ec39

4 files changed

Lines changed: 46 additions & 34 deletions

File tree

src/butil/fast_rand.cpp

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include "butil/macros.h"
2424
#include "butil/time.h" // gettimeofday_us()
2525
#include "butil/fast_rand.h"
26+
#include "butil/numerics/safe_conversions.h" // safe_abs
2627

2728
namespace butil {
2829

@@ -110,20 +111,31 @@ int64_t fast_rand_in_64(int64_t min, int64_t max) {
110111
if (need_init(_tls_seed)) {
111112
init_fast_rand_seed(&_tls_seed);
112113
}
113-
if (min >= max) {
114+
if (BAIDU_UNLIKELY(min >= max)) {
114115
if (min == max) {
115116
return min;
116117
}
117-
const int64_t tmp = min;
118-
min = max;
119-
max = tmp;
118+
std::swap(min, max);
119+
}
120+
uint64_t range;
121+
if (min >= 0) {
122+
// Always safe to do subtraction.
123+
range = (uint64_t)(max - min) + 1;
124+
return min + (int64_t)fast_rand_impl(range, &_tls_seed);
125+
}
126+
127+
uint64_t abs_min = safe_abs(min);
128+
if (max >= 0) {
129+
range = abs_min + (uint64_t)(max) + 1;
130+
} else {
131+
range = abs_min - safe_abs(max) + 1;
120132
}
121-
int64_t range = max - min + 1;
122133
if (range == 0) {
123134
// max = INT64_MAX, min = INT64_MIN
124135
return (int64_t)xorshift128_next(&_tls_seed);
125136
}
126-
return min + (int64_t)fast_rand_impl(max - min + 1, &_tls_seed);
137+
uint64_t r = fast_rand_impl(range, &_tls_seed);
138+
return r >= abs_min ? (int64_t)(r - abs_min) : -((int64_t)(abs_min - r));
127139
}
128140

129141
uint64_t fast_rand_in_u64(uint64_t min, uint64_t max) {

src/butil/numerics/safe_conversions.h

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,31 @@ inline Dst saturated_cast(Src value) {
5858
return static_cast<Dst>(value);
5959
}
6060

61+
inline uint64_t safe_abs(uint64_t x) {
62+
return x;
63+
}
64+
65+
inline uint64_t safe_abs(int64_t x) {
66+
return (x >= 0) ? (uint64_t)x : ((~(uint64_t)(x)) + 1);
67+
}
68+
69+
inline uint32_t safe_abs(uint32_t x) {
70+
return x;
71+
}
72+
73+
inline uint32_t safe_abs(int32_t x) {
74+
return (uint32_t)safe_abs((int64_t)x);
75+
}
76+
77+
#if defined(__APPLE__)
78+
inline unsigned long safe_abs(unsigned long x) {
79+
return x;
80+
}
81+
inline unsigned long safe_abs(long x) {
82+
return (x >= 0) ? (unsigned long)x : ((~(unsigned long)(x)) + 1);
83+
}
84+
#endif
85+
6186
} // namespace butil
6287

6388
#endif // BUTIL_SAFE_CONVERSIONS_H_

src/butil/strings/string_number_conversions.cc

Lines changed: 2 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include <limits>
1313

1414
#include "butil/logging.h"
15+
#include "butil/numerics/safe_conversions.h" // safe_abs
1516
#include "butil/scoped_clear_errno.h"
1617
#include "butil/strings/utf_string_conversions.h"
1718
#include "butil/third_party/dmg_fp/dmg_fp.h"
@@ -22,30 +23,6 @@ namespace {
2223

2324
template <typename STR, typename INT, typename UINT, bool NEG>
2425
struct IntToStringT {
25-
// This is to avoid a compiler warning about unary minus on unsigned type.
26-
// For example, say you had the following code:
27-
// template <typename INT>
28-
// INT abs(INT value) { return value < 0 ? -value : value; }
29-
// Even though if INT is unsigned, it's impossible for value < 0, so the
30-
// unary minus will never be taken, the compiler will still generate a
31-
// warning. We do a little specialization dance...
32-
template <typename INT2, typename UINT2, bool NEG2>
33-
struct ToUnsignedT {};
34-
35-
template <typename INT2, typename UINT2>
36-
struct ToUnsignedT<INT2, UINT2, false> {
37-
static UINT2 ToUnsigned(INT2 value) {
38-
return static_cast<UINT2>(value);
39-
}
40-
};
41-
42-
template <typename INT2, typename UINT2>
43-
struct ToUnsignedT<INT2, UINT2, true> {
44-
static UINT2 ToUnsigned(INT2 value) {
45-
return static_cast<UINT2>(value < 0 ? -value : value);
46-
}
47-
};
48-
4926
// This set of templates is very similar to the above templates, but
5027
// for testing whether an integer is negative.
5128
template <typename INT2, bool NEG2>
@@ -74,9 +51,7 @@ struct IntToStringT {
7451
STR outbuf(kOutputBufSize, 0);
7552

7653
bool is_neg = TestNegT<INT, NEG>::TestNeg(value);
77-
// Even though is_neg will never be true when INT is parameterized as
78-
// unsigned, even the presence of the unary operation causes a warning.
79-
UINT res = ToUnsignedT<INT, UINT, NEG>::ToUnsigned(value);
54+
UINT res = safe_abs(value);
8055

8156
typename STR::iterator it(outbuf.end());
8257
do {

src/butil/strings/stringprintf.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ static void StringAppendVT(StringType* dst,
7878
// wrong and no amount of buffer-doubling is going to fix it.
7979
return;
8080
#else
81-
if (errno != 0 && errno != EOVERFLOW)
81+
if (errno != 0 && errno != EOVERFLOW && errno != E2BIG)
8282
return;
8383
// Try doubling the buffer size.
8484
mem_length *= 2;

0 commit comments

Comments
 (0)