-
-
Notifications
You must be signed in to change notification settings - Fork 8.3k
objint_mpz: Fix pow3(1,2,0). #17716
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
objint_mpz: Fix pow3(1,2,0). #17716
Conversation
Code size report:
|
Codecov ReportAll modified and coverable lines are covered by tests ✅
Additional details and impacted files@@ Coverage Diff @@
## master #17716 +/- ##
=======================================
Coverage 98.41% 98.41%
=======================================
Files 171 171
Lines 22210 22211 +1
=======================================
+ Hits 21857 21858 +1
Misses 353 353 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
py/objint_mpz.c
Outdated
@@ -364,6 +364,9 @@ mp_obj_t mp_obj_int_pow3(mp_obj_t base, mp_obj_t exponent, mp_obj_t modulus) { | |||
mpz_t *lhs = mp_mpz_for_int(base, &l_temp); | |||
mpz_t *rhs = mp_mpz_for_int(exponent, &r_temp); | |||
mpz_t *mod = mp_mpz_for_int(modulus, &m_temp); | |||
if (mpz_is_zero(mod)) { | |||
mp_raise_msg(&mp_type_ZeroDivisionError, MP_ERROR_TEXT("divide by zero")); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If CPython raises a ValueError
here, why mp_raise_msg(&mp_type_ZeroDivisionError
instead of just mp_raise_ValueError(
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I changed it, including introducing a new MP_ERROR_TEXT.
py/objint_mpz.c
Outdated
@@ -364,6 +364,9 @@ mp_obj_t mp_obj_int_pow3(mp_obj_t base, mp_obj_t exponent, mp_obj_t modulus) { | |||
mpz_t *lhs = mp_mpz_for_int(base, &l_temp); | |||
mpz_t *rhs = mp_mpz_for_int(exponent, &r_temp); | |||
mpz_t *mod = mp_mpz_for_int(modulus, &m_temp); | |||
if (mpz_is_zero(mod)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be checked right after the typeerror check, rather than after it (potentially) already allocated memory for the mpz buffers for its arguments.
Note that integer 0 is never a concrete object when represented as a mp_obj_t
, it's always a small int --- so the condition to check for zero is just mp_obj_is_small_int(modulus) && MP_OBJ_SMALL_INT_VALUE(modulus) == 0
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Makes sense, done.
Thinking about this led to the discovery of #17730.
8b4de89
to
4c4582b
Compare
py/objint_mpz.c
Outdated
} else { | ||
mp_obj_t result = mp_obj_new_int_from_ull(0); // Use the _from_ull version as this forces an mpz int | ||
mp_obj_int_t *res_p = (mp_obj_int_t *)MP_OBJ_TO_PTR(result); | ||
if (modulus == MP_OBJ_NEW_SMALL_INT(0)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
De-nest this so it's parallel as an else if
with the line above.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done.
py/objint_mpz.c
Outdated
mp_obj_t result = mp_obj_new_int_from_ull(0); // Use the _from_ull version as this forces an mpz int | ||
mp_obj_int_t *res_p = (mp_obj_int_t *)MP_OBJ_TO_PTR(result); | ||
if (modulus == MP_OBJ_NEW_SMALL_INT(0)) { | ||
mp_raise_msg(&mp_type_ValueError, MP_ERROR_TEXT("pow() 3rd argument cannot be 0")); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use mp_raise_ValueError
for smaller code size, that's the whole reason those shortcut raise methods exist.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done, thanks.
This finding is based on fuzzing micropython. I manually minimized the test case it provided. Signed-off-by: Jeff Epler <jepler@gmail.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Something must be wrong with the change, rv32 failed (timeout) specifically on basics_builtin_pow3_intbig. I can't figure why, and I failed at performing the rv32 build locally on my debian bookworm system to reproduce it here. |
I ran the rv32 tests locally with this PR, and did not get any failure. That |
I'll see if I can corroborate whether or not it works on my Pico2 in Hazard3 mode. |
Can confirm that the tests pass on my Pico2 running in Hazard3 mode, not sure what the cause for the failure in qemu rv32 might be. |
Summary
This finding is based on fuzzing micropython. I manually minimized the test case it provided.
Testing
A fuzzer found a crash in 3-arg pow where the 3rd argument was zero. I added a test case for it, and fixed it.
Trade-offs and Alternatives
CPython throws ValueError in this case, while the code in this PR throws ZeroDivisionError.