-
Notifications
You must be signed in to change notification settings - Fork 7.8k
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
Fix GH-16870: gmp_pow(64, 11) throws overflow exception #16884
base: master
Are you sure you want to change the base?
Conversation
The current guard to prevent FPEs is way too restrictive; `64 ** 11` is a perfectly reasonable operation. Instead, we now estimate the number of bytes of the resulting GMP (assuming that numbers are stored base 256 encoded), and fail if that exceeds a given threshold. The chosen threshold is somewhat arbitrary. We also ensure that we do not prematurely convert a given non int base to an int to avoid overflow which could circumvent our early check.
The threshold logic should be unified with #16880 and other places in gmp.c. |
ext/gmp/gmp.c
Outdated
|
||
if (Z_TYPE_P(base_arg) == IS_LONG && Z_LVAL_P(base_arg) >= 0) { | ||
INIT_GMP_RETVAL(gmpnum_result); | ||
if ((log(Z_LVAL_P(base_arg)) * exp) > powmax) { | ||
if ((log(Z_LVAL_P(base_arg)) / log(256) * exp) > powmax) { |
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.
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 is basically what is already happening in this patch. The difference is that I'm counting bytes, not bits. The logic is to estimate the number of bytes the result would have, and if this is greater than some threshold (powmax
is badly named; should be rather max_size_in_bytes
or so), trigger a value error.
mpz_sizeinbase()
is only used in the else branch, because if the input is a zend_long
, that wouldn't work. So I'm calculating log256(base_arg) * exp
instead.
I'll update to counting bytes.
Oh, I had only checked with 64bit builds, but now noticed that two test cases are failing on 32bit. I could further reduce |
The current guard to prevent FPEs is way too restrictive;
64 ** 11
is a perfectly reasonable operation. Instead, we now estimate the number of bytes of the resulting GMP (assuming that numbers are stored base 256 encoded), and fail if that exceeds a given threshold. The chosen threshold is somewhat arbitrary.We also ensure that we do not prematurely convert a given non int base to an int to avoid overflow which could circumvent our early check.
Note that the threshold of 2000 seems way too small. To avoid triggering overflow handling during allocations it should rather be close to
SIZE_MAX
(or whatever libgmp/mpir assume there), but even 10,000 would causephp-src/ext/gmp/tests/gmp_pow_fpe.phpt
Lines 20 to 29 in ff3b4ec
to no longer throw, but instead yield reasonable results: numbers with a bit less than 10,000 and 5,000 digits, respectively. It's hard for me to tell whether these examples would actually cause libgmp's overflow check to trigger, since mpir doesn't even have these overflow checks.
In any way, it might not be the best idea to push the limits of our early overflow check just to avoid overflow checks to be triggered by libgmp, since we're likely hitting OOM, which causes abort.