Skip to content

Fix GH-16411: gmp_export() can cause overflow #16418

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

Closed
wants to merge 2 commits into from

Conversation

cmb69
Copy link
Member

@cmb69 cmb69 commented Oct 13, 2024

We need not only to avoid the signed overflow while calculating bits_per_word (reported issue), but also the unsigned overflow when calculating count. While the former has a fixed threshold, the latter does not, since it also depends on the size in base 2. Thus we use a somewhat unconventional error message.

We need not only to avoid the signed overflow while calculating
`bits_per_word` (reported issue), but also the unsigned overflow when
calculating `count`.  While the former has a fixed threshold, the
latter does not, since it also depends on the size in base 2.  Thus we
use a somewhat unconventional error message.
@cmb69 cmb69 requested a review from Girgias as a code owner October 13, 2024 17:11
@cmb69 cmb69 linked an issue Oct 13, 2024 that may be closed by this pull request
Copy link
Member

@Girgias Girgias left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems reasonable, but I don't exactly understand what this function does.

gmp
--FILE--
<?php
gmp_export(-9223372036854775808, 9223372036854775807, -9223372036854775808);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like we don't validate the $option argument properly?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, we're only checking for the relevant flags:

php-src/ext/gmp/gmp.c

Lines 929 to 958 in 79c71c9

switch (options & (GMP_LSW_FIRST | GMP_MSW_FIRST)) {
case GMP_LSW_FIRST:
*order = -1;
break;
case GMP_MSW_FIRST:
case 0: /* default */
*order = 1;
break;
default:
/* options argument is in third position */
zend_argument_value_error(3, "cannot use multiple word order options");
return false;
}
switch (options & (GMP_LITTLE_ENDIAN | GMP_BIG_ENDIAN | GMP_NATIVE_ENDIAN)) {
case GMP_LITTLE_ENDIAN:
*endian = -1;
break;
case GMP_BIG_ENDIAN:
*endian = 1;
break;
case GMP_NATIVE_ENDIAN:
case 0: /* default */
*endian = 0;
break;
default:
/* options argument is in third position */
zend_argument_value_error(3, "cannot use multiple endian options");
return false;
}

So basically $options % 32. I think this okay (wouldn't want to touch it for a stable release), but could be improved for master (reject >= 32).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, improving this for master is what I had in mind, I was just surprised that large int wasn't caught by the option checking.

@Girgias
Copy link
Member

Girgias commented Oct 13, 2024

Also, is this not an issue for gmp_import?

@cmb69
Copy link
Member Author

cmb69 commented Oct 13, 2024

I don't exactly understand what this function does.

Do you mean gmp_export() or the new overflow check. If the latter, I guess I should add a comment (already thought about that; but not quite sure how detailed).

Also, is this not an issue for gmp_import?

No, since there are no calculations, and from what I can tell, we're not calling mpz_import() in a way we truncation may occur.

Anyway, as CI tells me, I need to cater to 32bit.

@Girgias
Copy link
Member

Girgias commented Oct 13, 2024

I don't exactly understand what this function does.

Do you mean gmp_export() or the new overflow check. If the latter, I guess I should add a comment (already thought about that; but not quite sure how detailed).

I meant gmp_export()

Also, is this not an issue for gmp_import?

No, since there are no calculations, and from what I can tell, we're not calling mpz_import() in a way we truncation may occur.

OK

Anyway, as CI tells me, I need to cater to 32bit.

Indeed.

@cmb69
Copy link
Member Author

cmb69 commented Oct 13, 2024

The documentation is pretty clear about gmp_export(): "Export a GMP number to a binary string". ;)

It's actually an elaborate pack(); besides that it obviously works for large numbers, you cannot only choose endianess, but also the $word_size. Oh, works somewhat different from what I had expected:

php > echo bin2hex(pack("N", 0x12345678));
12345678
php > echo bin2hex(pack("V", 0x12345678));
78563412
php > echo bin2hex(gmp_export(0x12345678, 1, GMP_BIG_ENDIAN));
12345678
php > echo bin2hex(gmp_export(0x12345678, 1, GMP_LITTLE_ENDIAN));
12345678

Need to check what's going on there.

@cmb69
Copy link
Member Author

cmb69 commented Oct 13, 2024

The documentation is pretty clear about gmp_export(): "Export a GMP number to a binary string". ;)

It's actually an elaborate pack(); besides that it obviously works for large numbers, you cannot only choose endianess, but also the $word_size. Oh, works somewhat different from what I had expected:

php > echo bin2hex(pack("N", 0x12345678));
12345678
php > echo bin2hex(pack("V", 0x12345678));
78563412
php > echo bin2hex(gmp_export(0x12345678, 1, GMP_BIG_ENDIAN));
12345678
php > echo bin2hex(gmp_export(0x12345678, 1, GMP_LITTLE_ENDIAN));
12345678

Need to check what's going on there.

PS: all good. For $word_size=1 endianess is actually determined by GMP_(LSW|MSW)_FIRST:

php > echo bin2hex(gmp_export(0x12345678, 1, GMP_MSW_FIRST));
12345678
php > echo bin2hex(gmp_export(0x12345678, 1, GMP_LSW_FIRST));
78563412

Only for $word_size>1, GMP_(BIG|LITTE)_ENDIAN is relevant:

php > echo bin2hex(gmp_export(0x12345678, 2, GMP_LSW_FIRST|GMP_LITTLE_ENDIAN));
78563412
php > echo bin2hex(gmp_export(0x12345678, 2, GMP_LSW_FIRST|GMP_BIG_ENDIAN));
56781234

Copy link
Member

@Girgias Girgias left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Comment on lines +5 to +11
--FILE--
<?php
gmp_export("-9223372036854775808", PHP_INT_MAX, PHP_INT_MIN);
?>
--EXPECTF--
Fatal error: Uncaught ValueError: gmp_export(): Argument #2 ($word_size) is too large for argument #1 ($num) in %s:%d
%A
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could try/catch the ValueError and have the output just be an EXPECT section, but that is minor nit.

@cmb69 cmb69 closed this in ab595c0 Oct 15, 2024
@cmb69 cmb69 deleted the cmb/gh16411 branch October 15, 2024 14:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

gmp_export() can cause overflow
2 participants