Skip to content

Commit fadd51d

Browse files
committed
ext/gmp: Throw TypeError for unusable types for <<, >>, and ** ops
1 parent 64b2b18 commit fadd51d

6 files changed

+69
-83
lines changed

ext/gmp/gmp.c

Lines changed: 53 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -334,25 +334,71 @@ static zend_object *gmp_clone_obj(zend_object *obj) /* {{{ */
334334
}
335335
/* }}} */
336336

337-
static void shift_operator_helper(gmp_binary_ui_op_t op, zval *return_value, zval *op1, zval *op2, zend_uchar opcode) {
338-
zend_long shift = zval_get_long(op2);
337+
static zend_result shift_operator_helper(gmp_binary_ui_op_t op, zval *return_value, zval *op1, zval *op2, zend_uchar opcode) {
338+
zend_long shift = 0;
339+
340+
if (UNEXPECTED(Z_TYPE_P(op2) != IS_LONG)) {
341+
if (UNEXPECTED(!IS_GMP(op2))) {
342+
goto typeof_op_failure;
343+
} else {
344+
// TODO We shouldn't cast the GMP object to int here
345+
shift = zval_get_long(op2);
346+
}
347+
} else {
348+
shift = Z_LVAL_P(op2);
349+
}
339350

340351
if (shift < 0) {
341352
zend_throw_error(
342353
zend_ce_value_error, "%s must be greater than or equal to 0",
343354
opcode == ZEND_POW ? "Exponent" : "Shift"
344355
);
345356
ZVAL_UNDEF(return_value);
346-
return;
357+
return FAILURE;
347358
} else {
348359
mpz_ptr gmpnum_op, gmpnum_result;
349360
gmp_temp_t temp;
350361

351-
FETCH_GMP_ZVAL(gmpnum_op, op1, temp, 1);
362+
/* Inline FETCH_GMP_ZVAL(gmpnum_op, op1, temp, 1); as cannot use RETURN_THROWS() */
363+
if (UNEXPECTED(!IS_GMP(op1))) {
364+
if (UNEXPECTED(Z_TYPE_P(op1) != IS_LONG)) {
365+
goto typeof_op_failure;
366+
}
367+
mpz_init(temp.num);
368+
mpz_set_si(temp.num, Z_LVAL_P(op1));
369+
temp.is_used = 1;
370+
gmpnum_op = temp.num;
371+
} else {
372+
gmpnum_op = GET_GMP_FROM_ZVAL(op1);
373+
temp.is_used = 0;
374+
}
352375
INIT_GMP_RETVAL(gmpnum_result);
353376
op(gmpnum_result, gmpnum_op, (gmp_ulong) shift);
354377
FREE_GMP_TEMP(temp);
378+
return SUCCESS;
355379
}
380+
381+
typeof_op_failure:
382+
; /* Blank statement */
383+
/* Returning FAILURE without throwing an exception would emit the
384+
* Unsupported operand types: GMP OP TypeOfOp2
385+
* However, this leads to the engine trying to interpret the GMP object as an integer
386+
* and doing the operation that way, which is not something we want. */
387+
const char *op_sigil;
388+
switch (opcode) {
389+
case ZEND_POW:
390+
op_sigil = "**";
391+
break;
392+
case ZEND_SL:
393+
op_sigil = "<<";
394+
break;
395+
case ZEND_SR:
396+
op_sigil = ">>";
397+
break;
398+
EMPTY_SWITCH_DEFAULT_CASE();
399+
}
400+
zend_type_error("Unsupported operand types: %s %s %s", zend_zval_type_name(op1), op_sigil, zend_zval_type_name(op2));
401+
return FAILURE;
356402
}
357403

358404
#define DO_BINARY_UI_OP_EX(op, uop, check_b_zero) \
@@ -381,18 +427,15 @@ static zend_result gmp_do_operation_ex(zend_uchar opcode, zval *result, zval *op
381427
case ZEND_MUL:
382428
DO_BINARY_UI_OP(mpz_mul);
383429
case ZEND_POW:
384-
shift_operator_helper(mpz_pow_ui, result, op1, op2, opcode);
385-
return SUCCESS;
430+
return shift_operator_helper(mpz_pow_ui, result, op1, op2, opcode);
386431
case ZEND_DIV:
387432
DO_BINARY_UI_OP_EX(mpz_tdiv_q, gmp_mpz_tdiv_q_ui, 1);
388433
case ZEND_MOD:
389434
DO_BINARY_UI_OP_EX(mpz_mod, gmp_mpz_mod_ui, 1);
390435
case ZEND_SL:
391-
shift_operator_helper(mpz_mul_2exp, result, op1, op2, opcode);
392-
return SUCCESS;
436+
return shift_operator_helper(mpz_mul_2exp, result, op1, op2, opcode);
393437
case ZEND_SR:
394-
shift_operator_helper(mpz_fdiv_q_2exp, result, op1, op2, opcode);
395-
return SUCCESS;
438+
return shift_operator_helper(mpz_fdiv_q_2exp, result, op1, op2, opcode);
396439
case ZEND_BW_OR:
397440
DO_BINARY_OP(mpz_ior);
398441
case ZEND_BW_AND:

ext/gmp/tests/overloading_with_array.phpt

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -76,18 +76,9 @@ TypeError: Number must be of type GMP|string|int, array given
7676
TypeError: Number must be of type GMP|string|int, array given
7777
TypeError: Number must be of type GMP|string|int, array given
7878
TypeError: Number must be of type GMP|string|int, array given
79-
object(GMP)#3 (1) {
80-
["num"]=>
81-
string(1) "1"
82-
}
79+
TypeError: Unsupported operand types: GMP ** array
8380
TypeError: Number must be of type GMP|string|int, array given
8481
TypeError: Number must be of type GMP|string|int, array given
8582
TypeError: Number must be of type GMP|string|int, array given
86-
object(GMP)#2 (1) {
87-
["num"]=>
88-
string(2) "42"
89-
}
90-
object(GMP)#2 (1) {
91-
["num"]=>
92-
string(2) "42"
93-
}
83+
TypeError: Unsupported operand types: GMP << array
84+
TypeError: Unsupported operand types: GMP >> array

ext/gmp/tests/overloading_with_null.phpt

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -76,18 +76,9 @@ TypeError: Number must be of type GMP|string|int, null given
7676
TypeError: Number must be of type GMP|string|int, null given
7777
TypeError: Number must be of type GMP|string|int, null given
7878
TypeError: Number must be of type GMP|string|int, null given
79-
object(GMP)#3 (1) {
80-
["num"]=>
81-
string(1) "1"
82-
}
79+
TypeError: Unsupported operand types: GMP ** null
8380
TypeError: Number must be of type GMP|string|int, null given
8481
TypeError: Number must be of type GMP|string|int, null given
8582
TypeError: Number must be of type GMP|string|int, null given
86-
object(GMP)#2 (1) {
87-
["num"]=>
88-
string(2) "42"
89-
}
90-
object(GMP)#2 (1) {
91-
["num"]=>
92-
string(2) "42"
93-
}
83+
TypeError: Unsupported operand types: GMP << null
84+
TypeError: Unsupported operand types: GMP >> null

ext/gmp/tests/overloading_with_object_not_stringable.phpt

Lines changed: 4 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -71,30 +71,15 @@ try {
7171
}
7272

7373
?>
74-
--EXPECTF--
74+
--EXPECT--
7575
TypeError: Number must be of type GMP|string|int, stdClass given
7676
TypeError: Number must be of type GMP|string|int, stdClass given
7777
TypeError: Number must be of type GMP|string|int, stdClass given
7878
TypeError: Number must be of type GMP|string|int, stdClass given
7979
TypeError: Number must be of type GMP|string|int, stdClass given
80-
81-
Warning: Object of class stdClass could not be converted to int in %s on line %d
82-
object(GMP)#4 (1) {
83-
["num"]=>
84-
string(2) "42"
85-
}
80+
TypeError: Unsupported operand types: GMP ** stdClass
8681
TypeError: Number must be of type GMP|string|int, stdClass given
8782
TypeError: Number must be of type GMP|string|int, stdClass given
8883
TypeError: Number must be of type GMP|string|int, stdClass given
89-
90-
Warning: Object of class stdClass could not be converted to int in %s on line %d
91-
object(GMP)#3 (1) {
92-
["num"]=>
93-
string(2) "84"
94-
}
95-
96-
Warning: Object of class stdClass could not be converted to int in %s on line %d
97-
object(GMP)#3 (1) {
98-
["num"]=>
99-
string(2) "21"
100-
}
84+
TypeError: Unsupported operand types: GMP << stdClass
85+
TypeError: Unsupported operand types: GMP >> stdClass

ext/gmp/tests/overloading_with_object_stringable.phpt

Lines changed: 3 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -83,24 +83,9 @@ TypeError: Number must be of type GMP|string|int, T given
8383
TypeError: Number must be of type GMP|string|int, T given
8484
TypeError: Number must be of type GMP|string|int, T given
8585
TypeError: Number must be of type GMP|string|int, T given
86-
87-
Warning: Object of class T could not be converted to int in %s on line %d
88-
object(GMP)#4 (1) {
89-
["num"]=>
90-
string(2) "42"
91-
}
86+
TypeError: Unsupported operand types: GMP ** T
9287
TypeError: Number must be of type GMP|string|int, T given
9388
TypeError: Number must be of type GMP|string|int, T given
9489
TypeError: Number must be of type GMP|string|int, T given
95-
96-
Warning: Object of class T could not be converted to int in %s on line %d
97-
object(GMP)#3 (1) {
98-
["num"]=>
99-
string(2) "84"
100-
}
101-
102-
Warning: Object of class T could not be converted to int in %s on line %d
103-
object(GMP)#3 (1) {
104-
["num"]=>
105-
string(2) "21"
106-
}
90+
TypeError: Unsupported operand types: GMP << T
91+
TypeError: Unsupported operand types: GMP >> T

ext/gmp/tests/overloading_with_resource.phpt

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -76,18 +76,9 @@ TypeError: Number must be of type GMP|string|int, resource given
7676
TypeError: Number must be of type GMP|string|int, resource given
7777
TypeError: Number must be of type GMP|string|int, resource given
7878
TypeError: Number must be of type GMP|string|int, resource given
79-
object(GMP)#3 (1) {
80-
["num"]=>
81-
string(5) "74088"
82-
}
79+
TypeError: Unsupported operand types: GMP ** resource
8380
TypeError: Number must be of type GMP|string|int, resource given
8481
TypeError: Number must be of type GMP|string|int, resource given
8582
TypeError: Number must be of type GMP|string|int, resource given
86-
object(GMP)#2 (1) {
87-
["num"]=>
88-
string(3) "336"
89-
}
90-
object(GMP)#2 (1) {
91-
["num"]=>
92-
string(1) "5"
93-
}
83+
TypeError: Unsupported operand types: GMP << resource
84+
TypeError: Unsupported operand types: GMP >> resource

0 commit comments

Comments
 (0)