summaryrefslogtreecommitdiffstats
path: root/Objects/intobject.c
diff options
context:
space:
mode:
authorMark Dickinson <mdickinson@enthought.com>2011-09-19 15:38:08 (GMT)
committerMark Dickinson <mdickinson@enthought.com>2011-09-19 15:38:08 (GMT)
commitdbbed0494113ab7631777c4996a7971770f2dcc1 (patch)
tree5552a3f82f9d658c28fb460ec67bdd69717432ca /Objects/intobject.c
parentd256ca12c13b20d243a27e143986426018b57877 (diff)
downloadcpython-dbbed0494113ab7631777c4996a7971770f2dcc1.zip
cpython-dbbed0494113ab7631777c4996a7971770f2dcc1.tar.gz
cpython-dbbed0494113ab7631777c4996a7971770f2dcc1.tar.bz2
Issue #12973: Fix int.__pow__ overflow checks that invoked undefined behaviour, thereby producing incorrect results on Clang.
Diffstat (limited to 'Objects/intobject.c')
-rw-r--r--Objects/intobject.c10
1 files changed, 8 insertions, 2 deletions
diff --git a/Objects/intobject.c b/Objects/intobject.c
index 7d70bfb..e518e74 100644
--- a/Objects/intobject.c
+++ b/Objects/intobject.c
@@ -751,7 +751,13 @@ int_pow(PyIntObject *v, PyIntObject *w, PyIntObject *z)
while (iw > 0) {
prev = ix; /* Save value for overflow check */
if (iw & 1) {
- ix = ix*temp;
+ /*
+ * The (unsigned long) cast below ensures that the multiplication
+ * is interpreted as an unsigned operation rather than a signed one
+ * (C99 6.3.1.8p1), thus avoiding the perils of undefined behaviour
+ * from signed arithmetic overflow (C99 6.5p5). See issue #12973.
+ */
+ ix = (unsigned long)ix * temp;
if (temp == 0)
break; /* Avoid ix / 0 */
if (ix / temp != prev) {
@@ -764,7 +770,7 @@ int_pow(PyIntObject *v, PyIntObject *w, PyIntObject *z)
iw >>= 1; /* Shift exponent down by 1 bit */
if (iw==0) break;
prev = temp;
- temp *= temp; /* Square the value of temp */
+ temp = (unsigned long)temp * temp; /* Square the value of temp */
if (prev != 0 && temp / prev != prev) {
return PyLong_Type.tp_as_number->nb_power(
(PyObject *)v, (PyObject *)w, (PyObject *)z);