summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJeff Epler <jepler@gmail.com>2025-06-11 19:32:28 +0200
committerDamien George <damien@micropython.org>2025-06-16 12:11:07 +1000
commit0ef5ede3823ef00e95d1cc1a5b475d8d30c9caa4 (patch)
treeb14eec63b1b583f0396e63f874142a096330bb3c
parent42404b5588eb12c22e07283f2e62ee9bb80f1714 (diff)
py/mpz: Avoid undefined behavior decrementing NULL.
In the case where an mpz number is zero, its `len` is 0 and its `dig` is NULL. In that case, decrementing NULL via `d--` is undefined behavior according to the C specification. Restructuring the loops in this way avoids undefined behavior. Also, ensure that these cases are tested in the coverage test. This doesn't make much difference now, but would otherwise cause errors later when the undefined behavior sanitizer is employed in CI. Signed-off-by: Jeff Epler <jepler@gmail.com>
-rw-r--r--ports/unix/coverage.c12
-rw-r--r--py/mpz.c12
-rw-r--r--tests/ports/unix/extra_coverage.py.exp4
3 files changed, 24 insertions, 4 deletions
diff --git a/ports/unix/coverage.c b/ports/unix/coverage.c
index 29e1457cb..9201dab12 100644
--- a/ports/unix/coverage.c
+++ b/ports/unix/coverage.c
@@ -475,6 +475,18 @@ static mp_obj_t extra_coverage(void) {
mp_int_t value_signed;
mpz_as_int_checked(&mpz, &value_signed);
mp_printf(&mp_plat_print, "%d\n", (int)value_signed);
+
+ // hash the zero mpz integer
+ mpz_set_from_int(&mpz, 0);
+ mp_printf(&mp_plat_print, "%d\n", mpz_hash(&mpz));
+
+ // convert the mpz zero integer to int
+ mp_printf(&mp_plat_print, "%d\n", mpz_as_int_checked(&mpz, &value_signed));
+ mp_printf(&mp_plat_print, "%d\n", value_signed);
+
+ // mpz_set_from_float with 0 as argument
+ mpz_set_from_float(&mpz, 0);
+ mp_printf(&mp_plat_print, "%f\n", mpz_as_float(&mpz));
}
// runtime utils
diff --git a/py/mpz.c b/py/mpz.c
index 471bd1598..5a4d7d27d 100644
--- a/py/mpz.c
+++ b/py/mpz.c
@@ -1537,7 +1537,8 @@ mp_int_t mpz_hash(const mpz_t *z) {
mp_uint_t val = 0;
mpz_dig_t *d = z->dig + z->len;
- while (d-- > z->dig) {
+ while (d > z->dig) {
+ d--;
val = (val << DIG_SIZE) | *d;
}
@@ -1552,11 +1553,12 @@ bool mpz_as_int_checked(const mpz_t *i, mp_int_t *value) {
mp_uint_t val = 0;
mpz_dig_t *d = i->dig + i->len;
- while (d-- > i->dig) {
+ while (d > i->dig) {
if (val > (~(MP_OBJ_WORD_MSBIT_HIGH) >> DIG_SIZE)) {
// will overflow
return false;
}
+ d--;
val = (val << DIG_SIZE) | *d;
}
@@ -1577,11 +1579,12 @@ bool mpz_as_uint_checked(const mpz_t *i, mp_uint_t *value) {
mp_uint_t val = 0;
mpz_dig_t *d = i->dig + i->len;
- while (d-- > i->dig) {
+ while (d > i->dig) {
if (val > (~(MP_OBJ_WORD_MSBIT_HIGH) >> (DIG_SIZE - 1))) {
// will overflow
return false;
}
+ d--;
val = (val << DIG_SIZE) | *d;
}
@@ -1642,7 +1645,8 @@ mp_float_t mpz_as_float(const mpz_t *i) {
mp_float_t val = 0;
mpz_dig_t *d = i->dig + i->len;
- while (d-- > i->dig) {
+ while (d > i->dig) {
+ d--;
val = val * DIG_BASE + *d;
}
diff --git a/tests/ports/unix/extra_coverage.py.exp b/tests/ports/unix/extra_coverage.py.exp
index 19b32f1c9..154916822 100644
--- a/tests/ports/unix/extra_coverage.py.exp
+++ b/tests/ports/unix/extra_coverage.py.exp
@@ -89,6 +89,10 @@ data
12345
6
-1
+0
+1
+0
+0.000000
# runtime utils
TypeError: unsupported type for __abs__: 'str'
TypeError: unsupported types for __divmod__: 'str', 'str'