Skip to content

Commit 9cea1a2

Browse files
committed
ext/gmp: Fix segfault when null is encountered on an overloaded operator
And improve the behaviour for other not supported types
1 parent 74bb0f3 commit 9cea1a2

6 files changed

+103
-46
lines changed

ext/gmp/gmp.c

Lines changed: 51 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,7 @@ typedef void (*gmp_binary_ui_op_t)(mpz_ptr, mpz_srcptr, gmp_ulong);
199199
typedef void (*gmp_binary_op2_t)(mpz_ptr, mpz_ptr, mpz_srcptr, mpz_srcptr);
200200
typedef gmp_ulong (*gmp_binary_ui_op2_t)(mpz_ptr, mpz_ptr, mpz_srcptr, gmp_ulong);
201201

202-
static inline void gmp_zval_binary_ui_op(zval *return_value, zval *a_arg, zval *b_arg, gmp_binary_op_t gmp_op, gmp_binary_ui_op_t gmp_ui_op, bool check_b_zero, bool is_operator);
202+
static inline zend_result gmp_zval_binary_ui_op(zval *return_value, zval *a_arg, zval *b_arg, gmp_binary_op_t gmp_op, gmp_binary_ui_op_t gmp_ui_op, bool check_b_zero, bool is_operator);
203203
static inline void gmp_zval_binary_ui_op2(zval *return_value, zval *a_arg, zval *b_arg, gmp_binary_op2_t gmp_op, gmp_binary_ui_op2_t gmp_ui_op, int check_b_zero);
204204
static inline void gmp_zval_unary_op(zval *return_value, zval *a_arg, gmp_unary_op_t gmp_op);
205205

@@ -356,10 +356,7 @@ static void shift_operator_helper(gmp_binary_ui_op_t op, zval *return_value, zva
356356
}
357357

358358
#define DO_BINARY_UI_OP_EX(op, uop, check_b_zero) \
359-
gmp_zval_binary_ui_op( \
360-
result, op1, op2, op, uop, check_b_zero, /* is_operator */ true); \
361-
if (UNEXPECTED(EG(exception))) { return FAILURE; } \
362-
return SUCCESS;
359+
return gmp_zval_binary_ui_op(result, op1, op2, op, uop, check_b_zero, /* is_operator */ true);
363360

364361
#define DO_BINARY_UI_OP(op) DO_BINARY_UI_OP_EX(op, op ## _ui, 0)
365362
#define DO_BINARY_OP(op) DO_BINARY_UI_OP_EX(op, NULL, 0)
@@ -437,7 +434,7 @@ static int gmp_compare(zval *op1, zval *op2) /* {{{ */
437434
/* An error/exception occurs if one of the operands is not a numeric string
438435
* or an object which is different from GMP */
439436
if (EG(exception)) {
440-
return 1;
437+
return ZEND_UNCOMPARABLE;
441438
}
442439
/* result can only be a zend_long if gmp_cmp hasn't thrown an Error */
443440
ZEND_ASSERT(Z_TYPE(result) == IS_LONG);
@@ -619,7 +616,16 @@ static zend_result convert_to_gmp(mpz_t gmpnumber, zval *val, zend_long base, ui
619616
case IS_STRING: {
620617
return convert_zstr_to_gmp(gmpnumber, Z_STR_P(val), base, arg_pos);
621618
}
622-
default: {
619+
case IS_NULL:
620+
/* For operator overloading just reject null */
621+
if (arg_pos == 0) {
622+
return FAILURE;
623+
}
624+
ZEND_FALLTHROUGH;
625+
case IS_DOUBLE:
626+
case IS_FALSE:
627+
case IS_TRUE:
628+
{
623629
zend_long lval;
624630
if (!zend_parse_arg_long_slow(val, &lval, arg_pos)) {
625631
if (arg_pos == 0) {
@@ -635,6 +641,12 @@ static zend_result convert_to_gmp(mpz_t gmpnumber, zval *val, zend_long base, ui
635641
mpz_set_si(gmpnumber, lval);
636642
return SUCCESS;
637643
}
644+
default:
645+
if (arg_pos != 0) {
646+
zend_argument_type_error(arg_pos,
647+
"must be of type GMP|string|int, %s given", zend_zval_type_name(val));
648+
}
649+
return FAILURE;
638650
}
639651
}
640652
/* }}} */
@@ -702,18 +714,45 @@ static void gmp_cmp(zval *return_value, zval *a_arg, zval *b_arg, bool is_operat
702714
/* {{{ gmp_zval_binary_ui_op
703715
Execute GMP binary operation.
704716
*/
705-
static inline void gmp_zval_binary_ui_op(zval *return_value, zval *a_arg, zval *b_arg, gmp_binary_op_t gmp_op, gmp_binary_ui_op_t gmp_ui_op, bool check_b_zero, bool is_operator)
717+
static inline zend_result gmp_zval_binary_ui_op(zval *return_value, zval *a_arg, zval *b_arg, gmp_binary_op_t gmp_op, gmp_binary_ui_op_t gmp_ui_op, bool check_b_zero, bool is_operator)
706718
{
707719
mpz_ptr gmpnum_a, gmpnum_b, gmpnum_result;
708720
gmp_temp_t temp_a, temp_b;
709721

710-
FETCH_GMP_ZVAL(gmpnum_a, a_arg, temp_a, is_operator ? 0 : 1);
722+
/* Inline version of FETCH_GMP_ZVAL(gmpnum_a, a_arg, temp_a, is_operator ? 0 : 1);
723+
* because we cannot use RETURN_THROWS() here */
724+
if (IS_GMP(a_arg)) {
725+
gmpnum_a = GET_GMP_FROM_ZVAL(a_arg);
726+
temp_a.is_used = 0;
727+
} else {
728+
mpz_init(temp_a.num);
729+
if (convert_to_gmp(temp_a.num, a_arg, 0, is_operator ? 0 : 1) == FAILURE) {
730+
mpz_clear(temp_a.num);
731+
return FAILURE;
732+
}
733+
temp_a.is_used = 1;
734+
gmpnum_a = temp_a.num;
735+
}
711736

712737
if (gmp_ui_op && Z_TYPE_P(b_arg) == IS_LONG && Z_LVAL_P(b_arg) >= 0) {
713738
gmpnum_b = NULL;
714739
temp_b.is_used = 0;
715740
} else {
716-
FETCH_GMP_ZVAL_DEP(gmpnum_b, b_arg, temp_b, temp_a, is_operator ? 0 : 2);
741+
/* Inline version of FETCH_GMP_ZVAL_DEP(gmpnum_b, b_arg, temp_b, temp_a, is_operator ? 0 : 2);
742+
* because we cannot use RETURN_THROWS() here */
743+
if (IS_GMP(b_arg)) {
744+
gmpnum_b = GET_GMP_FROM_ZVAL(b_arg);
745+
temp_b.is_used = 0;
746+
} else {
747+
mpz_init(temp_b.num);
748+
if (convert_to_gmp(temp_b.num, b_arg, 0, is_operator ? 0 : 2) == FAILURE) {
749+
mpz_clear(temp_b.num);
750+
FREE_GMP_TEMP(temp_a);
751+
return FAILURE;
752+
}
753+
temp_b.is_used = 1;
754+
gmpnum_b = temp_b.num;
755+
}
717756
}
718757

719758
if (check_b_zero) {
@@ -732,7 +771,7 @@ static inline void gmp_zval_binary_ui_op(zval *return_value, zval *a_arg, zval *
732771
}
733772
FREE_GMP_TEMP(temp_a);
734773
FREE_GMP_TEMP(temp_b);
735-
RETURN_THROWS();
774+
return FAILURE;
736775
}
737776
}
738777

@@ -746,6 +785,7 @@ static inline void gmp_zval_binary_ui_op(zval *return_value, zval *a_arg, zval *
746785

747786
FREE_GMP_TEMP(temp_a);
748787
FREE_GMP_TEMP(temp_b);
788+
return SUCCESS;
749789
}
750790
/* }}} */
751791

ext/gmp/tests/overloading_with_array.phpt

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -71,18 +71,18 @@ try {
7171

7272
?>
7373
--EXPECT--
74-
TypeError: Number must be of type GMP|string|int, array given
75-
TypeError: Number must be of type GMP|string|int, array given
76-
TypeError: Number must be of type GMP|string|int, array given
77-
TypeError: Number must be of type GMP|string|int, array given
78-
TypeError: Number must be of type GMP|string|int, array given
74+
TypeError: Unsupported operand types: GMP + array
75+
TypeError: Unsupported operand types: GMP - array
76+
TypeError: Unsupported operand types: GMP * array
77+
TypeError: Unsupported operand types: GMP / array
78+
TypeError: Unsupported operand types: GMP % array
7979
object(GMP)#3 (1) {
8080
["num"]=>
8181
string(1) "1"
8282
}
83-
TypeError: Number must be of type GMP|string|int, array given
84-
TypeError: Number must be of type GMP|string|int, array given
85-
TypeError: Number must be of type GMP|string|int, array given
83+
TypeError: Unsupported operand types: GMP | array
84+
TypeError: Unsupported operand types: GMP & array
85+
TypeError: Unsupported operand types: GMP ^ array
8686
object(GMP)#2 (1) {
8787
["num"]=>
8888
string(2) "42"

ext/gmp/tests/overloading_with_null.phpt

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@
22
GMP operator overloading does not support null
33
--EXTENSIONS--
44
gmp
5-
--XFAIL--
6-
Test showcasing segfaulting behaviour
75
--FILE--
86
<?php
97

@@ -73,4 +71,23 @@ try {
7371

7472
?>
7573
--EXPECT--
76-
SEGFAULT
74+
int(42)
75+
int(42)
76+
int(0)
77+
DivisionByZeroError: Division by zero
78+
DivisionByZeroError: Modulo by zero
79+
object(GMP)#2 (1) {
80+
["num"]=>
81+
string(1) "1"
82+
}
83+
int(42)
84+
int(0)
85+
int(42)
86+
object(GMP)#2 (1) {
87+
["num"]=>
88+
string(2) "42"
89+
}
90+
object(GMP)#2 (1) {
91+
["num"]=>
92+
string(2) "42"
93+
}

ext/gmp/tests/overloading_with_object_not_stringable.phpt

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -72,20 +72,20 @@ try {
7272

7373
?>
7474
--EXPECTF--
75-
TypeError: Number must be of type GMP|string|int, stdClass given
76-
TypeError: Number must be of type GMP|string|int, stdClass given
77-
TypeError: Number must be of type GMP|string|int, stdClass given
78-
TypeError: Number must be of type GMP|string|int, stdClass given
79-
TypeError: Number must be of type GMP|string|int, stdClass given
75+
TypeError: Unsupported operand types: GMP + stdClass
76+
TypeError: Unsupported operand types: GMP - stdClass
77+
TypeError: Unsupported operand types: GMP * stdClass
78+
TypeError: Unsupported operand types: GMP / stdClass
79+
TypeError: Unsupported operand types: GMP % stdClass
8080

8181
Warning: Object of class stdClass could not be converted to int in %s on line %d
8282
object(GMP)#4 (1) {
8383
["num"]=>
8484
string(2) "42"
8585
}
86-
TypeError: Number must be of type GMP|string|int, stdClass given
87-
TypeError: Number must be of type GMP|string|int, stdClass given
88-
TypeError: Number must be of type GMP|string|int, stdClass given
86+
TypeError: Unsupported operand types: GMP | stdClass
87+
TypeError: Unsupported operand types: GMP & stdClass
88+
TypeError: Unsupported operand types: GMP ^ stdClass
8989

9090
Warning: Object of class stdClass could not be converted to int in %s on line %d
9191
object(GMP)#3 (1) {

ext/gmp/tests/overloading_with_object_stringable.phpt

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -78,20 +78,20 @@ try {
7878

7979
?>
8080
--EXPECTF--
81-
TypeError: Number must be of type GMP|string|int, T given
82-
TypeError: Number must be of type GMP|string|int, T given
83-
TypeError: Number must be of type GMP|string|int, T given
84-
TypeError: Number must be of type GMP|string|int, T given
85-
TypeError: Number must be of type GMP|string|int, T given
81+
TypeError: Unsupported operand types: GMP + T
82+
TypeError: Unsupported operand types: GMP - T
83+
TypeError: Unsupported operand types: GMP * T
84+
TypeError: Unsupported operand types: GMP / T
85+
TypeError: Unsupported operand types: GMP % T
8686

8787
Warning: Object of class T could not be converted to int in %s on line %d
8888
object(GMP)#4 (1) {
8989
["num"]=>
9090
string(2) "42"
9191
}
92-
TypeError: Number must be of type GMP|string|int, T given
93-
TypeError: Number must be of type GMP|string|int, T given
94-
TypeError: Number must be of type GMP|string|int, T given
92+
TypeError: Unsupported operand types: GMP | T
93+
TypeError: Unsupported operand types: GMP & T
94+
TypeError: Unsupported operand types: GMP ^ T
9595

9696
Warning: Object of class T could not be converted to int in %s on line %d
9797
object(GMP)#3 (1) {

ext/gmp/tests/overloading_with_resource.phpt

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -71,18 +71,18 @@ try {
7171

7272
?>
7373
--EXPECT--
74-
TypeError: Number must be of type GMP|string|int, resource given
75-
TypeError: Number must be of type GMP|string|int, resource given
76-
TypeError: Number must be of type GMP|string|int, resource given
77-
TypeError: Number must be of type GMP|string|int, resource given
78-
TypeError: Number must be of type GMP|string|int, resource given
74+
TypeError: Unsupported operand types: GMP + resource
75+
TypeError: Unsupported operand types: GMP - resource
76+
TypeError: Unsupported operand types: GMP * resource
77+
TypeError: Unsupported operand types: GMP / resource
78+
TypeError: Unsupported operand types: GMP % resource
7979
object(GMP)#3 (1) {
8080
["num"]=>
8181
string(5) "74088"
8282
}
83-
TypeError: Number must be of type GMP|string|int, resource given
84-
TypeError: Number must be of type GMP|string|int, resource given
85-
TypeError: Number must be of type GMP|string|int, resource given
83+
TypeError: Unsupported operand types: GMP | resource
84+
TypeError: Unsupported operand types: GMP & resource
85+
TypeError: Unsupported operand types: GMP ^ resource
8686
object(GMP)#2 (1) {
8787
["num"]=>
8888
string(3) "336"

0 commit comments

Comments
 (0)