Skip to content

Commit 450f626

Browse files
committed
Fix ext/gmp
1 parent b728a8f commit 450f626

File tree

10 files changed

+204
-26
lines changed

10 files changed

+204
-26
lines changed

ext/gmp/gmp.c

Lines changed: 122 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,69 @@ PHP_GMP_API zend_class_entry *php_gmp_class_entry(void) {
106106
#define GET_GMP_FROM_ZVAL(zval) \
107107
GET_GMP_OBJECT_FROM_OBJ(Z_OBJ_P(zval))->num
108108

109+
#define GMP_SI_MAX (GMP_NUMB_MAX >> 1)
110+
#define GMP_SI_MIN (-(1LL << (GMP_NUMB_BITS - 1)))
111+
#if GMP_NUMB_BITS < SIZEOF_ZEND_LONG*8
112+
void gmp_set_zlong(mpz_t z, zend_long zlong) {
113+
if (zlong <= GMP_SI_MAX && zlong >= GMP_SI_MIN) {
114+
mpz_set_si(z, zlong);
115+
} else if (zlong >= 0) {
116+
mpz_import(z, 1, 1, sizeof(zend_long), 0, 0, &zlong);
117+
} else {
118+
mpz_import(z, 1, 1, sizeof(zend_long), 0, 0, &zlong);
119+
mpz_neg(z, z);
120+
}
121+
}
122+
123+
int gmp_fits_zlong_p(mpz_t z) {
124+
int result = 1;
125+
mpz_t z_min_max;
126+
zend_long min_max;
127+
128+
if (mpz_cmp_si(z, GMP_SI_MAX) > 0) {
129+
min_max = ZEND_LONG_MAX;
130+
mpz_init(z_min_max);
131+
mpz_import(z_min_max, 1, 1, sizeof(zend_long), 0, 0, &min_max);
132+
result = mpz_cmp(z, z_min_max) <= 0;
133+
mpz_clear(z_min_max);
134+
} else if (mpz_cmp_si(z, GMP_SI_MIN) < 0) {
135+
min_max = ZEND_LONG_MIN;
136+
mpz_init(z_min_max);
137+
mpz_import(z_min_max, 1, 1, sizeof(zend_long), 0, 0, &min_max);
138+
mpz_neg(z_min_max, z_min_max);
139+
result = mpz_cmp(z, z_min_max) >= 0;
140+
mpz_clear(z_min_max);
141+
}
142+
143+
return result;
144+
}
145+
146+
zend_long gmp_get_zlong(mpz_t z) {
147+
zend_long result;
148+
zend_long mask = -1;
149+
mpz_t z_tmp;
150+
mpz_t z_mask;
151+
152+
if (mpz_cmp_si(z, GMP_SI_MAX) > 0 || mpz_cmp_si(z, GMP_SI_MIN) < 0) {
153+
mpz_init(z_tmp);
154+
mpz_init(z_mask);
155+
mpz_import(z_mask, 1, 1, sizeof(zend_long), 0, 0, &mask);
156+
mpz_and(z_tmp, z, z_mask);
157+
mpz_export(&result, NULL, 0, sizeof(zend_long), 0, 0, z_tmp);
158+
mpz_clear(z_mask);
159+
mpz_clear(z_tmp);
160+
} else {
161+
result = mpz_get_si(z);
162+
}
163+
164+
return result;
165+
}
166+
#else
167+
# define gmp_set_zlong(z, l) mpz_set_si(z, l)
168+
# define gmp_fits_zlong_p(z) mpz_fits_si_p(z)
169+
# define gmp_get_zlong(z) mpz_get_si(z)
170+
#endif
171+
109172
static void gmp_strval(zval *result, mpz_t gmpnum, int base);
110173
static zend_result convert_zstr_to_gmp(mpz_t gmp_number, const zend_string *val, zend_long base, uint32_t arg_pos);
111174

@@ -129,7 +192,7 @@ static bool gmp_zend_parse_arg_into_mpz_ex(
129192
}
130193

131194
if (Z_TYPE_P(arg) == IS_LONG) {
132-
mpz_set_si(*destination_mpz_ptr, Z_LVAL_P(arg));
195+
gmp_set_zlong(*destination_mpz_ptr, Z_LVAL_P(arg));
133196
return true;
134197
}
135198

@@ -145,7 +208,7 @@ static bool gmp_zend_parse_arg_into_mpz_ex(
145208
return false;
146209
}
147210

148-
mpz_set_si(*destination_mpz_ptr, lval);
211+
gmp_set_zlong(*destination_mpz_ptr, lval);
149212

150213
return true;
151214
}
@@ -243,16 +306,16 @@ static zend_result gmp_cast_object(zend_object *readobj, zval *writeobj, int typ
243306
return SUCCESS;
244307
case IS_LONG:
245308
gmpnum = GET_GMP_OBJECT_FROM_OBJ(readobj)->num;
246-
ZVAL_LONG(writeobj, mpz_get_si(gmpnum));
309+
ZVAL_LONG(writeobj, gmp_get_zlong(gmpnum));
247310
return SUCCESS;
248311
case IS_DOUBLE:
249312
gmpnum = GET_GMP_OBJECT_FROM_OBJ(readobj)->num;
250313
ZVAL_DOUBLE(writeobj, mpz_get_d(gmpnum));
251314
return SUCCESS;
252315
case _IS_NUMBER:
253316
gmpnum = GET_GMP_OBJECT_FROM_OBJ(readobj)->num;
254-
if (mpz_fits_si_p(gmpnum)) {
255-
ZVAL_LONG(writeobj, mpz_get_si(gmpnum));
317+
if (gmp_fits_zlong_p(gmpnum)) {
318+
ZVAL_LONG(writeobj, gmp_get_zlong(gmpnum));
256319
} else {
257320
ZVAL_DOUBLE(writeobj, mpz_get_d(gmpnum));
258321
}
@@ -712,7 +775,7 @@ static zend_result gmp_initialize_number(mpz_ptr gmp_number, const zend_string *
712775
return convert_zstr_to_gmp(gmp_number, arg_str, base, 1);
713776
}
714777

715-
mpz_set_si(gmp_number, arg_l);
778+
gmp_set_zlong(gmp_number, arg_l);
716779
return SUCCESS;
717780
}
718781

@@ -862,7 +925,7 @@ ZEND_FUNCTION(gmp_intval)
862925
GMP_Z_PARAM_INTO_MPZ_PTR(gmpnum)
863926
ZEND_PARSE_PARAMETERS_END();
864927

865-
RETVAL_LONG(mpz_get_si(gmpnum));
928+
RETVAL_LONG(gmp_get_zlong(gmpnum));
866929
}
867930
/* }}} */
868931

@@ -1526,8 +1589,13 @@ ZEND_FUNCTION(gmp_random_range)
15261589
}
15271590
/* }}} */
15281591

1592+
#if SIZEOF_SIZE_T >= SIZEOF_ZEND_LONG
1593+
# define GMP_SAFE_BITINDEX_MAX ((mp_bitcnt_t)INT_MAX * GMP_NUMB_BITS)
1594+
#else
1595+
# define GMP_SAFE_BITINDEX_MAX ((mp_bitcnt_t)INT_MAX * GMP_NUMB_BITS - 1)
1596+
#endif
15291597
static bool gmp_is_bit_index_valid(zend_long index) {
1530-
return index >= 0 && (index / GMP_NUMB_BITS < INT_MAX);
1598+
return index >= 0 && (zend_ulong)index <= GMP_SAFE_BITINDEX_MAX;
15311599
}
15321600

15331601
/* {{{ Sets or clear bit in a */
@@ -1543,7 +1611,7 @@ ZEND_FUNCTION(gmp_setbit)
15431611
}
15441612

15451613
if (!gmp_is_bit_index_valid(index)) {
1546-
zend_argument_value_error(2, "must be between 0 and %d * %d", INT_MAX, GMP_NUMB_BITS);
1614+
zend_argument_value_error(2, "must be between 0 and %lu", GMP_SAFE_BITINDEX_MAX);
15471615
RETURN_THROWS();
15481616
}
15491617

@@ -1569,7 +1637,7 @@ ZEND_FUNCTION(gmp_clrbit)
15691637
}
15701638

15711639
if (!gmp_is_bit_index_valid(index)) {
1572-
zend_argument_value_error(2, "must be between 0 and %d * %d", INT_MAX, GMP_NUMB_BITS);
1640+
zend_argument_value_error(2, "must be between 0 and %lu", GMP_SAFE_BITINDEX_MAX);
15731641
RETURN_THROWS();
15741642
}
15751643

@@ -1590,7 +1658,7 @@ ZEND_FUNCTION(gmp_testbit)
15901658
ZEND_PARSE_PARAMETERS_END();
15911659

15921660
if (!gmp_is_bit_index_valid(index)) {
1593-
zend_argument_value_error(2, "must be between 0 and %d * %d", INT_MAX, GMP_NUMB_BITS);
1661+
zend_argument_value_error(2, "must be between 0 and %lu", GMP_SAFE_BITINDEX_MAX);
15941662
RETURN_THROWS();
15951663
}
15961664

@@ -1602,26 +1670,44 @@ ZEND_FUNCTION(gmp_testbit)
16021670
ZEND_FUNCTION(gmp_popcount)
16031671
{
16041672
mpz_ptr gmpnum_a;
1673+
mp_bitcnt_t result;
16051674

16061675
ZEND_PARSE_PARAMETERS_START(1, 1)
16071676
GMP_Z_PARAM_INTO_MPZ_PTR(gmpnum_a)
16081677
ZEND_PARSE_PARAMETERS_END();
16091678

1610-
RETURN_LONG(mpz_popcount(gmpnum_a));
1679+
result = mpz_popcount(gmpnum_a);
1680+
1681+
#if SIZEOF_SIZE_T <= SIZEOF_ZEND_LONG
1682+
if (SIZE_MAX == result) {
1683+
RETURN_LONG(-1);
1684+
}
1685+
#endif
1686+
1687+
RETURN_LONG(result);
16111688
}
16121689
/* }}} */
16131690

16141691
/* {{{ Calculates hamming distance between a and b */
16151692
ZEND_FUNCTION(gmp_hamdist)
16161693
{
16171694
mpz_ptr gmpnum_a, gmpnum_b;
1695+
mp_bitcnt_t result;
16181696

16191697
ZEND_PARSE_PARAMETERS_START(2, 2)
16201698
GMP_Z_PARAM_INTO_MPZ_PTR(gmpnum_a)
16211699
GMP_Z_PARAM_INTO_MPZ_PTR(gmpnum_b)
16221700
ZEND_PARSE_PARAMETERS_END();
16231701

1624-
RETURN_LONG(mpz_hamdist(gmpnum_a, gmpnum_b));
1702+
result = mpz_hamdist(gmpnum_a, gmpnum_b);
1703+
1704+
#if SIZEOF_SIZE_T <= SIZEOF_ZEND_LONG
1705+
if (SIZE_MAX == result) {
1706+
RETURN_LONG(-1);
1707+
}
1708+
#endif
1709+
1710+
RETURN_LONG(result);
16251711
}
16261712
/* }}} */
16271713

@@ -1630,18 +1716,27 @@ ZEND_FUNCTION(gmp_scan0)
16301716
{
16311717
mpz_ptr gmpnum_a;
16321718
zend_long start;
1719+
mp_bitcnt_t result;
16331720

16341721
ZEND_PARSE_PARAMETERS_START(2, 2)
16351722
GMP_Z_PARAM_INTO_MPZ_PTR(gmpnum_a)
16361723
Z_PARAM_LONG(start)
16371724
ZEND_PARSE_PARAMETERS_END();
16381725

16391726
if (!gmp_is_bit_index_valid(start)) {
1640-
zend_argument_value_error(2, "must be between 0 and %d * %d", INT_MAX, GMP_NUMB_BITS);
1727+
zend_argument_value_error(2, "must be between 0 and %lu", GMP_SAFE_BITINDEX_MAX);
16411728
RETURN_THROWS();
16421729
}
16431730

1644-
RETURN_LONG(mpz_scan0(gmpnum_a, start));
1731+
result = mpz_scan0(gmpnum_a, start);
1732+
1733+
#if SIZEOF_SIZE_T <= SIZEOF_ZEND_LONG
1734+
if (SIZE_MAX == result) {
1735+
RETURN_LONG(-1);
1736+
}
1737+
#endif
1738+
1739+
RETURN_LONG(result);
16451740
}
16461741
/* }}} */
16471742

@@ -1650,18 +1745,28 @@ ZEND_FUNCTION(gmp_scan1)
16501745
{
16511746
mpz_ptr gmpnum_a;
16521747
zend_long start;
1748+
mp_bitcnt_t result;
1749+
16531750

16541751
ZEND_PARSE_PARAMETERS_START(2, 2)
16551752
GMP_Z_PARAM_INTO_MPZ_PTR(gmpnum_a)
16561753
Z_PARAM_LONG(start)
16571754
ZEND_PARSE_PARAMETERS_END();
16581755

16591756
if (!gmp_is_bit_index_valid(start)) {
1660-
zend_argument_value_error(2, "must be between 0 and %d * %d", INT_MAX, GMP_NUMB_BITS);
1757+
zend_argument_value_error(2, "must be between 0 and %lu", GMP_SAFE_BITINDEX_MAX);
16611758
RETURN_THROWS();
16621759
}
16631760

1664-
RETURN_LONG(mpz_scan1(gmpnum_a, start));
1761+
result = mpz_scan1(gmpnum_a, start);
1762+
1763+
#if SIZEOF_SIZE_T <= SIZEOF_ZEND_LONG
1764+
if (SIZE_MAX == result) {
1765+
RETURN_LONG(-1);
1766+
}
1767+
#endif
1768+
1769+
RETURN_LONG(result);
16651770
}
16661771
/* }}} */
16671772

ext/gmp/tests/construct_32bit.phpt

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
--TEST--
2+
Constructor for GMP on 32bit int
3+
--SKIPIF--
4+
<?php
5+
if (PHP_INT_SIZE != 4) print "skip, this test is for 32bit int only";
6+
?>
7+
--EXTENSIONS--
8+
gmp
9+
--FILE--
10+
<?php
11+
var_dump(new GMP(PHP_INT_MAX));
12+
var_dump(new GMP((string)PHP_INT_MAX));
13+
var_dump(new GMP('0x7FFFFFFF'));
14+
var_dump(new GMP(PHP_INT_MIN));
15+
var_dump(new GMP((string)PHP_INT_MIN));
16+
--EXPECT--
17+
object(GMP)#1 (1) {
18+
["num"]=>
19+
string(10) "2147483647"
20+
}
21+
object(GMP)#1 (1) {
22+
["num"]=>
23+
string(10) "2147483647"
24+
}
25+
object(GMP)#1 (1) {
26+
["num"]=>
27+
string(10) "2147483647"
28+
}
29+
object(GMP)#1 (1) {
30+
["num"]=>
31+
string(11) "-2147483648"
32+
}
33+
object(GMP)#1 (1) {
34+
["num"]=>
35+
string(11) "-2147483648"
36+
}

ext/gmp/tests/construct_64bit.phpt

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
--TEST--
2+
Constructor for GMP on 64bit int
3+
--SKIPIF--
4+
<?php
5+
if (PHP_INT_SIZE != 8) print "skip, this test is for 64bit int only";
6+
?>
7+
--EXTENSIONS--
8+
gmp
9+
--FILE--
10+
<?php
11+
var_dump(new GMP(PHP_INT_MAX));
12+
var_dump(new GMP((string)PHP_INT_MAX));
13+
var_dump(new GMP('0x7FFFFFFFFFFFFFFF'));
14+
var_dump(new GMP(PHP_INT_MIN));
15+
var_dump(new GMP((string)PHP_INT_MIN));
16+
--EXPECT--
17+
object(GMP)#1 (1) {
18+
["num"]=>
19+
string(19) "9223372036854775807"
20+
}
21+
object(GMP)#1 (1) {
22+
["num"]=>
23+
string(19) "9223372036854775807"
24+
}
25+
object(GMP)#1 (1) {
26+
["num"]=>
27+
string(19) "9223372036854775807"
28+
}
29+
object(GMP)#1 (1) {
30+
["num"]=>
31+
string(20) "-9223372036854775808"
32+
}
33+
object(GMP)#1 (1) {
34+
["num"]=>
35+
string(20) "-9223372036854775808"
36+
}

ext/gmp/tests/gmp_clrbit.phpt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,9 @@ echo "Done\n";
4646
?>
4747
--EXPECTF--
4848
string(1) "0"
49-
gmp_clrbit(): Argument #2 ($index) must be between 0 and %d * %d
49+
gmp_clrbit(): Argument #2 ($index) must be between 0 and %d
5050
string(2) "-1"
51-
gmp_clrbit(): Argument #2 ($index) must be between 0 and %d * %d
51+
gmp_clrbit(): Argument #2 ($index) must be between 0 and %d
5252
string(7) "1000000"
5353
string(7) "1000000"
5454
string(30) "238462734628347239571822592658"

ext/gmp/tests/gmp_popcount.phpt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ gmp_popcount() basic tests
44
gmp
55
--FILE--
66
<?php
7-
87
var_dump(gmp_popcount(-1));
98
var_dump(gmp_popcount(0));
109
var_dump(gmp_popcount(12123));

ext/gmp/tests/gmp_scan0.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ try {
2828
echo "Done\n";
2929
?>
3030
--EXPECTF--
31-
gmp_scan0(): Argument #2 ($start) must be between 0 and %d * %d
31+
gmp_scan0(): Argument #2 ($start) must be between 0 and %d
3232
int(2)
3333
int(0)
3434
int(5)

ext/gmp/tests/gmp_scan1.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ try {
2828
echo "Done\n";
2929
?>
3030
--EXPECTF--
31-
gmp_scan1(): Argument #2 ($start) must be between 0 and %d * %d
31+
gmp_scan1(): Argument #2 ($start) must be between 0 and %d
3232
int(1)
3333
int(12)
3434
int(9)

ext/gmp/tests/gmp_setbit.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ echo "Done\n";
5252
?>
5353
--EXPECTF--
5454
string(2) "-1"
55-
gmp_setbit(): Argument #2 ($index) must be between 0 and %d * %d
55+
gmp_setbit(): Argument #2 ($index) must be between 0 and %d
5656
string(1) "5"
5757
string(1) "1"
5858
string(1) "7"

0 commit comments

Comments
 (0)