aboutsummaryrefslogtreecommitdiffstats
path: root/erts/emulator/beam/big.c
diff options
context:
space:
mode:
authorLukas Larsson <[email protected]>2014-08-04 12:04:45 +0200
committerLukas Larsson <[email protected]>2014-08-08 17:17:18 +0200
commit9d3c22934491afe85fbcf8543ae43fb2eb1ab387 (patch)
tree4cfbd2f87d60b7a143426140984a3ae0b93d9252 /erts/emulator/beam/big.c
parent62081266545df8f5eda8e2043f33055cfe575126 (diff)
downloadotp-9d3c22934491afe85fbcf8543ae43fb2eb1ab387.tar.gz
otp-9d3c22934491afe85fbcf8543ae43fb2eb1ab387.tar.bz2
otp-9d3c22934491afe85fbcf8543ae43fb2eb1ab387.zip
erts: Fix neg int overflow when sint is min size
When INT64_MIN is the value of a Sint64 we have to first cast it to an Uint64 before negating it. Otherwise we get an integer overflow which is undefined behaviour and in gcc 4.9 this results in -0 instead of -9223372036854775808 in gcc 4.8.
Diffstat (limited to 'erts/emulator/beam/big.c')
-rw-r--r--erts/emulator/beam/big.c18
1 files changed, 12 insertions, 6 deletions
diff --git a/erts/emulator/beam/big.c b/erts/emulator/beam/big.c
index 41a041eba6..520034d68a 100644
--- a/erts/emulator/beam/big.c
+++ b/erts/emulator/beam/big.c
@@ -274,6 +274,9 @@
_b = _b << _s; \
_vn1 = _b >> H_EXP; \
_vn0 = _b & LO_MASK; \
+ /* Sometimes _s is 0 which triggers undefined behaviour for the \
+ (_a0>>(D_EXP-_s)) shift, but this is ok because the \
+ & -s will make it all to 0 later anyways. */ \
_un32 = (_a1 << _s) | ((_a0>>(D_EXP-_s)) & (-_s >> (D_EXP-1))); \
_un10 = _a0 << _s; \
_un1 = _un10 >> H_EXP; \
@@ -1540,21 +1543,24 @@ Eterm erts_uint64_to_big(Uint64 x, Eterm **hpp)
Eterm erts_sint64_to_big(Sint64 x, Eterm **hpp)
{
Eterm *hp = *hpp;
+ Uint64 ux;
int neg;
- if (x >= 0)
+ if (x >= 0) {
neg = 0;
+ ux = x;
+ }
else {
neg = 1;
- x = -x;
+ ux = -(Uint64)x;
}
#if defined(ARCH_32) || HALFWORD_HEAP
- if (x >= (((Uint64) 1) << 32)) {
+ if (ux >= (((Uint64) 1) << 32)) {
if (neg)
*hp = make_neg_bignum_header(2);
else
*hp = make_pos_bignum_header(2);
- BIG_DIGIT(hp, 0) = (Uint) (x & ((Uint) 0xffffffff));
- BIG_DIGIT(hp, 1) = (Uint) ((x >> 32) & ((Uint) 0xffffffff));
+ BIG_DIGIT(hp, 0) = (Uint) (ux & ((Uint) 0xffffffff));
+ BIG_DIGIT(hp, 1) = (Uint) ((ux >> 32) & ((Uint) 0xffffffff));
*hpp += 3;
}
else
@@ -1564,7 +1570,7 @@ Eterm erts_sint64_to_big(Sint64 x, Eterm **hpp)
*hp = make_neg_bignum_header(1);
else
*hp = make_pos_bignum_header(1);
- BIG_DIGIT(hp, 0) = (Uint) x;
+ BIG_DIGIT(hp, 0) = (Uint) ux;
*hpp += 2;
}
return make_big(hp);