Skip to content

Commit dbadeb1

Browse files
committed
Fix ext/gmp
1 parent d8a3a41 commit dbadeb1

File tree

10 files changed

+208
-26
lines changed

10 files changed

+208
-26
lines changed

ext/gmp/gmp.c

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

@@ -129,7 +196,7 @@ static bool gmp_zend_parse_arg_into_mpz_ex(
129196
}
130197

131198
if (Z_TYPE_P(arg) == IS_LONG) {
132-
mpz_set_si(*destination_mpz_ptr, Z_LVAL_P(arg));
199+
gmp_set_zlong(*destination_mpz_ptr, Z_LVAL_P(arg));
133200
return true;
134201
}
135202

@@ -145,7 +212,7 @@ static bool gmp_zend_parse_arg_into_mpz_ex(
145212
return false;
146213
}
147214

148-
mpz_set_si(*destination_mpz_ptr, lval);
215+
gmp_set_zlong(*destination_mpz_ptr, lval);
149216

150217
return true;
151218
}
@@ -243,16 +310,16 @@ static zend_result gmp_cast_object(zend_object *readobj, zval *writeobj, int typ
243310
return SUCCESS;
244311
case IS_LONG:
245312
gmpnum = GET_GMP_OBJECT_FROM_OBJ(readobj)->num;
246-
ZVAL_LONG(writeobj, mpz_get_si(gmpnum));
313+
ZVAL_LONG(writeobj, gmp_get_zlong(gmpnum));
247314
return SUCCESS;
248315
case IS_DOUBLE:
249316
gmpnum = GET_GMP_OBJECT_FROM_OBJ(readobj)->num;
250317
ZVAL_DOUBLE(writeobj, mpz_get_d(gmpnum));
251318
return SUCCESS;
252319
case _IS_NUMBER:
253320
gmpnum = GET_GMP_OBJECT_FROM_OBJ(readobj)->num;
254-
if (mpz_fits_si_p(gmpnum)) {
255-
ZVAL_LONG(writeobj, mpz_get_si(gmpnum));
321+
if (gmp_fits_zlong_p(gmpnum)) {
322+
ZVAL_LONG(writeobj, gmp_get_zlong(gmpnum));
256323
} else {
257324
ZVAL_DOUBLE(writeobj, mpz_get_d(gmpnum));
258325
}
@@ -712,7 +779,7 @@ static zend_result gmp_initialize_number(mpz_ptr gmp_number, const zend_string *
712779
return convert_zstr_to_gmp(gmp_number, arg_str, base, 1);
713780
}
714781

715-
mpz_set_si(gmp_number, arg_l);
782+
gmp_set_zlong(gmp_number, arg_l);
716783
return SUCCESS;
717784
}
718785

@@ -862,7 +929,7 @@ ZEND_FUNCTION(gmp_intval)
862929
GMP_Z_PARAM_INTO_MPZ_PTR(gmpnum)
863930
ZEND_PARSE_PARAMETERS_END();
864931

865-
RETVAL_LONG(mpz_get_si(gmpnum));
932+
RETVAL_LONG(gmp_get_zlong(gmpnum));
866933
}
867934
/* }}} */
868935

@@ -1526,8 +1593,13 @@ ZEND_FUNCTION(gmp_random_range)
15261593
}
15271594
/* }}} */
15281595

1596+
#if SIZEOF_SIZE_T >= SIZEOF_ZEND_LONG
1597+
# define GMP_SAFE_BITINDEX_MAX ((mp_bitcnt_t)INT_MAX * GMP_NUMB_BITS)
1598+
#else
1599+
# define GMP_SAFE_BITINDEX_MAX ((mp_bitcnt_t)INT_MAX * GMP_NUMB_BITS - 1)
1600+
#endif
15291601
static bool gmp_is_bit_index_valid(zend_long index) {
1530-
return index >= 0 && (index / GMP_NUMB_BITS < INT_MAX);
1602+
return index >= 0 && (zend_ulong)index <= GMP_SAFE_BITINDEX_MAX;
15311603
}
15321604

15331605
/* {{{ Sets or clear bit in a */
@@ -1543,7 +1615,7 @@ ZEND_FUNCTION(gmp_setbit)
15431615
}
15441616

15451617
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);
1618+
zend_argument_value_error(2, "must be between 0 and %lu", GMP_SAFE_BITINDEX_MAX);
15471619
RETURN_THROWS();
15481620
}
15491621

@@ -1569,7 +1641,7 @@ ZEND_FUNCTION(gmp_clrbit)
15691641
}
15701642

15711643
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);
1644+
zend_argument_value_error(2, "must be between 0 and %lu", GMP_SAFE_BITINDEX_MAX);
15731645
RETURN_THROWS();
15741646
}
15751647

@@ -1590,7 +1662,7 @@ ZEND_FUNCTION(gmp_testbit)
15901662
ZEND_PARSE_PARAMETERS_END();
15911663

15921664
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);
1665+
zend_argument_value_error(2, "must be between 0 and %lu", GMP_SAFE_BITINDEX_MAX);
15941666
RETURN_THROWS();
15951667
}
15961668

@@ -1602,26 +1674,44 @@ ZEND_FUNCTION(gmp_testbit)
16021674
ZEND_FUNCTION(gmp_popcount)
16031675
{
16041676
mpz_ptr gmpnum_a;
1677+
mp_bitcnt_t result;
16051678

16061679
ZEND_PARSE_PARAMETERS_START(1, 1)
16071680
GMP_Z_PARAM_INTO_MPZ_PTR(gmpnum_a)
16081681
ZEND_PARSE_PARAMETERS_END();
16091682

1610-
RETURN_LONG(mpz_popcount(gmpnum_a));
1683+
result = mpz_popcount(gmpnum_a);
1684+
1685+
#if SIZEOF_SIZE_T <= SIZEOF_ZEND_LONG
1686+
if (SIZE_MAX == result) {
1687+
RETURN_LONG(-1);
1688+
}
1689+
#endif
1690+
1691+
RETURN_LONG(result);
16111692
}
16121693
/* }}} */
16131694

16141695
/* {{{ Calculates hamming distance between a and b */
16151696
ZEND_FUNCTION(gmp_hamdist)
16161697
{
16171698
mpz_ptr gmpnum_a, gmpnum_b;
1699+
mp_bitcnt_t result;
16181700

16191701
ZEND_PARSE_PARAMETERS_START(2, 2)
16201702
GMP_Z_PARAM_INTO_MPZ_PTR(gmpnum_a)
16211703
GMP_Z_PARAM_INTO_MPZ_PTR(gmpnum_b)
16221704
ZEND_PARSE_PARAMETERS_END();
16231705

1624-
RETURN_LONG(mpz_hamdist(gmpnum_a, gmpnum_b));
1706+
result = mpz_hamdist(gmpnum_a, gmpnum_b);
1707+
1708+
#if SIZEOF_SIZE_T <= SIZEOF_ZEND_LONG
1709+
if (SIZE_MAX == result) {
1710+
RETURN_LONG(-1);
1711+
}
1712+
#endif
1713+
1714+
RETURN_LONG(result);
16251715
}
16261716
/* }}} */
16271717

@@ -1630,18 +1720,27 @@ ZEND_FUNCTION(gmp_scan0)
16301720
{
16311721
mpz_ptr gmpnum_a;
16321722
zend_long start;
1723+
mp_bitcnt_t result;
16331724

16341725
ZEND_PARSE_PARAMETERS_START(2, 2)
16351726
GMP_Z_PARAM_INTO_MPZ_PTR(gmpnum_a)
16361727
Z_PARAM_LONG(start)
16371728
ZEND_PARSE_PARAMETERS_END();
16381729

16391730
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);
1731+
zend_argument_value_error(2, "must be between 0 and %lu", GMP_SAFE_BITINDEX_MAX);
16411732
RETURN_THROWS();
16421733
}
16431734

1644-
RETURN_LONG(mpz_scan0(gmpnum_a, start));
1735+
result = mpz_scan0(gmpnum_a, start);
1736+
1737+
#if SIZEOF_SIZE_T <= SIZEOF_ZEND_LONG
1738+
if (SIZE_MAX == result) {
1739+
RETURN_LONG(-1);
1740+
}
1741+
#endif
1742+
1743+
RETURN_LONG(result);
16451744
}
16461745
/* }}} */
16471746

@@ -1650,18 +1749,28 @@ ZEND_FUNCTION(gmp_scan1)
16501749
{
16511750
mpz_ptr gmpnum_a;
16521751
zend_long start;
1752+
mp_bitcnt_t result;
1753+
16531754

16541755
ZEND_PARSE_PARAMETERS_START(2, 2)
16551756
GMP_Z_PARAM_INTO_MPZ_PTR(gmpnum_a)
16561757
Z_PARAM_LONG(start)
16571758
ZEND_PARSE_PARAMETERS_END();
16581759

16591760
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);
1761+
zend_argument_value_error(2, "must be between 0 and %lu", GMP_SAFE_BITINDEX_MAX);
16611762
RETURN_THROWS();
16621763
}
16631764

1664-
RETURN_LONG(mpz_scan1(gmpnum_a, start));
1765+
result = mpz_scan1(gmpnum_a, start);
1766+
1767+
#if SIZEOF_SIZE_T <= SIZEOF_ZEND_LONG
1768+
if (SIZE_MAX == result) {
1769+
RETURN_LONG(-1);
1770+
}
1771+
#endif
1772+
1773+
RETURN_LONG(result);
16651774
}
16661775
/* }}} */
16671776

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)

0 commit comments

Comments
 (0)