Skip to content

Commit ecf5b77

Browse files
committed
py: This time, real proper overflow checking of small int power.
Previous overflow test was inadequate.
1 parent 6902eed commit ecf5b77

8 files changed

Lines changed: 83 additions & 77 deletions

File tree

py/compile.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
#include "obj.h"
1818
#include "compile.h"
1919
#include "runtime.h"
20-
#include "intdivmod.h"
20+
#include "smallint.h"
2121

2222
// TODO need to mangle __attr names
2323

@@ -143,10 +143,10 @@ mp_parse_node_t fold_constants(mp_parse_node_t pn) {
143143
} else if (MP_PARSE_NODE_IS_TOKEN_KIND(pns->nodes[1], MP_TOKEN_OP_SLASH)) {
144144
; // pass
145145
} else if (MP_PARSE_NODE_IS_TOKEN_KIND(pns->nodes[1], MP_TOKEN_OP_PERCENT)) {
146-
pn = mp_parse_node_new_leaf(MP_PARSE_NODE_SMALL_INT, python_modulo(arg0, arg1));
146+
pn = mp_parse_node_new_leaf(MP_PARSE_NODE_SMALL_INT, mp_small_int_modulo(arg0, arg1));
147147
} else if (MP_PARSE_NODE_IS_TOKEN_KIND(pns->nodes[1], MP_TOKEN_OP_DBL_SLASH)) {
148148
if (arg1 != 0) {
149-
pn = mp_parse_node_new_leaf(MP_PARSE_NODE_SMALL_INT, python_floor_divide(arg0, arg1));
149+
pn = mp_parse_node_new_leaf(MP_PARSE_NODE_SMALL_INT, mp_small_int_floor_divide(arg0, arg1));
150150
}
151151
} else {
152152
// shouldn't happen

py/intdivmod.c

Lines changed: 0 additions & 24 deletions
This file was deleted.

py/intdivmod.h

Lines changed: 0 additions & 4 deletions
This file was deleted.

py/objint_mpz.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ mp_obj_t int_binary_op(int op, mp_obj_t lhs_in, mp_obj_t rhs_in) {
113113
case MP_BINARY_OP_INPLACE_FLOOR_DIVIDE: {
114114
mpz_t rem; mpz_init_zero(&rem);
115115
mpz_divmod_inpl(&res->mpz, &rem, zlhs, zrhs);
116-
if (zlhs->neg != zrhs->neg) {
116+
if (zlhs->neg != zrhs->neg) {
117117
if (!mpz_is_zero(&rem)) {
118118
mpz_t mpzone; mpz_init_from_int(&mpzone, -1);
119119
mpz_add_inpl(&res->mpz, &res->mpz, &mpzone);
@@ -127,8 +127,8 @@ mp_obj_t int_binary_op(int op, mp_obj_t lhs_in, mp_obj_t rhs_in) {
127127
mpz_t quo; mpz_init_zero(&quo);
128128
mpz_divmod_inpl(&quo, &res->mpz, zlhs, zrhs);
129129
mpz_deinit(&quo);
130-
// Check signs and do Python style modulo
131-
if (zlhs->neg != zrhs->neg) {
130+
// Check signs and do Python style modulo
131+
if (zlhs->neg != zrhs->neg) {
132132
mpz_add_inpl(&res->mpz, &res->mpz, zrhs);
133133
}
134134
break;

py/py.mk

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ PY_O_BASENAME = \
8484
vm.o \
8585
showbc.o \
8686
repl.o \
87-
intdivmod.o \
87+
smallint.o \
8888
pfenv.o \
8989

9090
# prepend the build destination prefix to the py object files

py/runtime.c

Lines changed: 18 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
#include "builtin.h"
1717
#include "builtintables.h"
1818
#include "bc.h"
19-
#include "intdivmod.h"
19+
#include "smallint.h"
2020
#include "objgenerator.h"
2121

2222
#if 0 // print debugging info
@@ -289,7 +289,7 @@ mp_obj_t mp_binary_op(int op, mp_obj_t lhs, mp_obj_t rhs) {
289289

290290
// If long long type exists and is larger than machine_int_t, then
291291
// we can use the following code to perform overflow-checked multiplication.
292-
// Otherwise (eg in x64 case) we must use the branching code below.
292+
// Otherwise (eg in x64 case) we must use mp_small_int_mul_overflow.
293293
#if 0
294294
// compute result using long long precision
295295
long long res = (long long)lhs_val * (long long)rhs_val;
@@ -302,44 +302,22 @@ mp_obj_t mp_binary_op(int op, mp_obj_t lhs, mp_obj_t rhs) {
302302
}
303303
#endif
304304

305-
if (lhs_val > 0) { // lhs_val is positive
306-
if (rhs_val > 0) { // lhs_val and rhs_val are positive
307-
if (lhs_val > (MP_SMALL_INT_MAX / rhs_val)) {
308-
goto mul_overflow;
309-
}
310-
} else { // lhs_val positive, rhs_val nonpositive
311-
if (rhs_val < (MP_SMALL_INT_MIN / lhs_val)) {
312-
goto mul_overflow;
313-
}
314-
} // lhs_val positive, rhs_val nonpositive
315-
} else { // lhs_val is nonpositive
316-
if (rhs_val > 0) { // lhs_val is nonpositive, rhs_val is positive
317-
if (lhs_val < (MP_SMALL_INT_MIN / rhs_val)) {
318-
goto mul_overflow;
319-
}
320-
} else { // lhs_val and rhs_val are nonpositive
321-
if (lhs_val != 0 && rhs_val < (MP_SMALL_INT_MAX / lhs_val)) {
322-
goto mul_overflow;
323-
}
324-
} // End if lhs_val and rhs_val are nonpositive
325-
} // End if lhs_val is nonpositive
326-
327-
// use standard precision
328-
return MP_OBJ_NEW_SMALL_INT(lhs_val * rhs_val);
329-
330-
mul_overflow:
331-
// use higher precision
332-
lhs = mp_obj_new_int_from_ll(lhs_val);
333-
goto generic_binary_op;
334-
305+
if (mp_small_int_mul_overflow(lhs_val, rhs_val)) {
306+
// use higher precision
307+
lhs = mp_obj_new_int_from_ll(lhs_val);
308+
goto generic_binary_op;
309+
} else {
310+
// use standard precision
311+
return MP_OBJ_NEW_SMALL_INT(lhs_val * rhs_val);
312+
}
335313
break;
336314
}
337315
case MP_BINARY_OP_FLOOR_DIVIDE:
338316
case MP_BINARY_OP_INPLACE_FLOOR_DIVIDE:
339317
if (rhs_val == 0) {
340318
goto zero_division;
341319
}
342-
lhs_val = python_floor_divide(lhs_val, rhs_val);
320+
lhs_val = mp_small_int_floor_divide(lhs_val, rhs_val);
343321
break;
344322

345323
#if MICROPY_ENABLE_FLOAT
@@ -352,11 +330,11 @@ mp_obj_t mp_binary_op(int op, mp_obj_t lhs, mp_obj_t rhs) {
352330
#endif
353331

354332
case MP_BINARY_OP_MODULO:
355-
case MP_BINARY_OP_INPLACE_MODULO:
356-
{
357-
lhs_val = python_modulo(lhs_val, rhs_val);
333+
case MP_BINARY_OP_INPLACE_MODULO: {
334+
lhs_val = mp_small_int_modulo(lhs_val, rhs_val);
358335
break;
359336
}
337+
360338
case MP_BINARY_OP_POWER:
361339
case MP_BINARY_OP_INPLACE_POWER:
362340
if (rhs_val < 0) {
@@ -370,21 +348,19 @@ mp_obj_t mp_binary_op(int op, mp_obj_t lhs, mp_obj_t rhs) {
370348
machine_int_t ans = 1;
371349
while (rhs_val > 0) {
372350
if (rhs_val & 1) {
373-
machine_int_t old = ans;
374-
ans *= lhs_val;
375-
if (ans < old) {
351+
if (mp_small_int_mul_overflow(ans, lhs_val)) {
376352
goto power_overflow;
377353
}
354+
ans *= lhs_val;
378355
}
379356
if (rhs_val == 1) {
380357
break;
381358
}
382359
rhs_val /= 2;
383-
machine_int_t old = lhs_val;
384-
lhs_val *= lhs_val;
385-
if (lhs_val < old) {
360+
if (mp_small_int_mul_overflow(lhs_val, lhs_val)) {
386361
goto power_overflow;
387362
}
363+
lhs_val *= lhs_val;
388364
}
389365
lhs_val = ans;
390366
}

py/smallint.c

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
#include "misc.h"
2+
#include "mpconfig.h"
3+
#include "qstr.h"
4+
#include "obj.h"
5+
6+
bool mp_small_int_mul_overflow(machine_int_t x, machine_int_t y) {
7+
// Check for multiply overflow; see CERT INT32-C
8+
if (x > 0) { // x is positive
9+
if (y > 0) { // x and y are positive
10+
if (x > (MP_SMALL_INT_MAX / y)) {
11+
return true;
12+
}
13+
} else { // x positive, y nonpositive
14+
if (y < (MP_SMALL_INT_MIN / x)) {
15+
return true;
16+
}
17+
} // x positive, y nonpositive
18+
} else { // x is nonpositive
19+
if (y > 0) { // x is nonpositive, y is positive
20+
if (x < (MP_SMALL_INT_MIN / y)) {
21+
return true;
22+
}
23+
} else { // x and y are nonpositive
24+
if (x != 0 && y < (MP_SMALL_INT_MAX / x)) {
25+
return true;
26+
}
27+
} // End if x and y are nonpositive
28+
} // End if x is nonpositive
29+
return false;
30+
}
31+
32+
machine_int_t mp_small_int_modulo(machine_int_t dividend, machine_int_t divisor) {
33+
machine_int_t lsign = (dividend >= 0) ? 1 :-1;
34+
machine_int_t rsign = (divisor >= 0) ? 1 :-1;
35+
dividend %= divisor;
36+
if (lsign != rsign) {
37+
dividend += divisor;
38+
}
39+
return dividend;
40+
}
41+
42+
43+
machine_int_t mp_small_int_floor_divide(machine_int_t num, machine_int_t denom) {
44+
machine_int_t lsign = num > 0 ? 1 : -1;
45+
machine_int_t rsign = denom > 0 ? 1 : -1;
46+
if (lsign == -1) {num *= -1;}
47+
if (rsign == -1) {denom *= -1;}
48+
if (lsign != rsign){
49+
return - ( num + denom - 1) / denom;
50+
} else {
51+
return num / denom;
52+
}
53+
}

py/smallint.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
// Functions for small integer arithmetic
2+
3+
bool mp_small_int_mul_overflow(machine_int_t x, machine_int_t y);
4+
machine_int_t mp_small_int_modulo(machine_int_t dividend, machine_int_t divisor);
5+
machine_int_t mp_small_int_floor_divide(machine_int_t num, machine_int_t denom);

0 commit comments

Comments
 (0)