summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Mitchell <davem@iabyn.com>2014-12-20 16:40:52 +0000
committerDavid Mitchell <davem@iabyn.com>2014-12-31 11:28:51 +0000
commit53e2bfb7c6a2e8a3171dabe7dbdc24eba77e4bf0 (patch)
tree0581d4da5f1bccbd72d0632eaa006e972800445d
parent382a7a77501a1e25895d78eca9cb6838c6d7e6a3 (diff)
downloadperl-53e2bfb7c6a2e8a3171dabe7dbdc24eba77e4bf0.tar.gz
fix -IV_MIN negations
Doing uv = -iv is undefined behaviour if iv happens to be IV_MIN. This occurs in several places in the perl sources. Found by -fsanitize=undefined. Here's a typical message: sv.c:2864:7: runtime error: negation of -9223372036854775808 cannot be represented in type 'IV' (aka 'long'); cast to an unsigned type to negate this value to itself
-rw-r--r--pp.c16
-rw-r--r--pp_hot.c9
-rw-r--r--sv.c10
3 files changed, 21 insertions, 14 deletions
diff --git a/pp.c b/pp.c
index 08e0999f3e..e24b5714fe 100644
--- a/pp.c
+++ b/pp.c
@@ -1298,7 +1298,8 @@ PP(pp_multiply)
alow = aiv;
auvok = TRUE; /* effectively it's a UV now */
} else {
- alow = -aiv; /* abs, auvok == false records sign */
+ /* abs, auvok == false records sign */
+ alow = (aiv == IV_MIN) ? (UV)aiv : (UV)(-aiv);
}
}
if (buvok) {
@@ -1309,7 +1310,8 @@ PP(pp_multiply)
blow = biv;
buvok = TRUE; /* effectively it's a UV now */
} else {
- blow = -biv; /* abs, buvok == false records sign */
+ /* abs, buvok == false records sign */
+ blow = (biv == IV_MIN) ? (UV)biv : (UV)(-biv);
}
}
@@ -1372,7 +1374,8 @@ PP(pp_multiply)
/* 2s complement assumption again */
/* -ve result, which could overflow an IV */
SP--;
- SETi( -(IV)product_low );
+ SETi(product_low == (UV)IV_MIN
+ ? IV_MIN : -(IV)product_low);
RETURN;
} /* else drop to NVs below. */
}
@@ -1797,7 +1800,7 @@ PP(pp_subtract)
auv = aiv;
auvok = 1; /* Now acting as a sign flag. */
} else { /* 2s complement assumption for IV_MIN */
- auv = (UV)-aiv;
+ auv = (aiv == IV_MIN) ? (UV)aiv : (UV)-aiv;
}
}
a_valid = 1;
@@ -1817,7 +1820,7 @@ PP(pp_subtract)
buv = biv;
buvok = 1;
} else
- buv = (UV)-biv;
+ buv = (biv == IV_MIN) ? (UV)biv : (UV)-biv;
}
/* ?uvok if value is >= 0. basically, flagged as UV if it's +ve,
else "IV" now, independent of how it came in.
@@ -1858,7 +1861,8 @@ PP(pp_subtract)
else {
/* Negate result */
if (result <= (UV)IV_MIN)
- SETi( -(IV)result );
+ SETi(result == (UV)IV_MIN
+ ? IV_MIN : -(IV)result);
else {
/* result valid, but out of range for IV. */
SETn( -(NV)result );
diff --git a/pp_hot.c b/pp_hot.c
index 3ee48180d3..4072ab11e1 100644
--- a/pp_hot.c
+++ b/pp_hot.c
@@ -647,8 +647,8 @@ PP(pp_add)
if (aiv >= 0) {
auv = aiv;
auvok = 1; /* Now acting as a sign flag. */
- } else { /* 2s complement assumption for IV_MIN */
- auv = (UV)-aiv;
+ } else {
+ auv = (aiv == IV_MIN) ? (UV)aiv : (UV)(-aiv);
}
}
a_valid = 1;
@@ -668,7 +668,7 @@ PP(pp_add)
buv = biv;
buvok = 1;
} else
- buv = (UV)-biv;
+ buv = (biv == IV_MIN) ? (UV)biv : (UV)(-biv);
}
/* ?uvok if value is >= 0. basically, flagged as UV if it's +ve,
else "IV" now, independent of how it came in.
@@ -709,7 +709,8 @@ PP(pp_add)
else {
/* Negate result */
if (result <= (UV)IV_MIN)
- SETi( -(IV)result );
+ SETi(result == (UV)IV_MIN
+ ? IV_MIN : -(IV)result);
else {
/* result valid, but out of range for IV. */
SETn( -(NV)result );
diff --git a/sv.c b/sv.c
index 94740d3528..f33da0b5e9 100644
--- a/sv.c
+++ b/sv.c
@@ -2158,6 +2158,7 @@ S_sv_2iuv_common(pTHX_ SV *const sv)
SvIV_set(sv, I_V(SvNVX(sv)));
if (SvNVX(sv) == (NV) SvIVX(sv)
#ifndef NV_PRESERVES_UV
+ && SvIVX(sv) != IV_MIN /* avoid negating IV_MIN below */
&& (((UV)1 << NV_PRESERVES_UV_BITS) >
(UV)(SvIVX(sv) > 0 ? SvIVX(sv) : -SvIVX(sv)))
/* Don't flag it as "accurately an integer" if the number
@@ -2273,7 +2274,8 @@ S_sv_2iuv_common(pTHX_ SV *const sv)
} else {
/* 2s complement assumption */
if (value <= (UV)IV_MIN) {
- SvIV_set(sv, -(IV)value);
+ SvIV_set(sv, value == (UV)IV_MIN
+ ? IV_MIN : -(IV)value);
} else {
/* Too negative for an IV. This is a double upgrade, but
I'm assuming it will be rare. */
@@ -2732,7 +2734,7 @@ Perl_sv_2nv_flags(pTHX_ SV *const sv, const I32 flags)
SvNOK_on(sv);
} else {
/* value has been set. It may not be precise. */
- if ((numtype & IS_NUMBER_NEG) && (value > (UV)IV_MIN)) {
+ if ((numtype & IS_NUMBER_NEG) && (value >= (UV)IV_MIN)) {
/* 2s complement assumption for (UV)IV_MIN */
SvNOK_on(sv); /* Integer is too negative. */
} else {
@@ -2861,7 +2863,7 @@ S_uiv_2buf(char *const buf, const IV iv, UV uv, const int is_uv, char **const pe
uv = iv;
sign = 0;
} else {
- uv = -iv;
+ uv = (iv == IV_MIN) ? (UV)iv : (UV)(-iv);
sign = 1;
}
do {
@@ -11898,7 +11900,7 @@ Perl_sv_vcatpvfn_flags(pTHX_ SV *const sv, const char *const pat, const STRLEN p
esignbuf[esignlen++] = plus;
}
else {
- uv = -iv;
+ uv = (iv == IV_MIN) ? (UV)iv : (UV)(-iv);
esignbuf[esignlen++] = '-';
}
}