@@ -338,25 +338,89 @@ static zend_object *gmp_clone_obj(zend_object *obj) /* {{{ */
338338}
339339/* }}} */
340340
341- static void shift_operator_helper (gmp_binary_ui_op_t op , zval * return_value , zval * op1 , zval * op2 , uint8_t opcode ) {
342- zend_long shift = zval_get_long (op2 );
341+ static zend_result shift_operator_helper (gmp_binary_ui_op_t op , zval * return_value , zval * op1 , zval * op2 , uint8_t opcode ) {
342+ zend_long shift = 0 ;
343+
344+ if (UNEXPECTED (Z_TYPE_P (op2 ) != IS_LONG )) {
345+ if (UNEXPECTED (!IS_GMP (op2 ))) {
346+ // For PHP 8.3 and up use zend_try_get_long()
347+ switch (Z_TYPE_P (op2 )) {
348+ case IS_DOUBLE :
349+ shift = zval_get_long (op2 );
350+ if (UNEXPECTED (EG (exception ))) {
351+ return FAILURE ;
352+ }
353+ break ;
354+ case IS_STRING :
355+ if (is_numeric_str_function (Z_STR_P (op2 ), & shift , NULL ) != IS_LONG ) {
356+ goto valueof_op_failure ;
357+ }
358+ break ;
359+ default :
360+ goto typeof_op_failure ;
361+ }
362+ } else {
363+ // TODO We shouldn't cast the GMP object to int here
364+ shift = zval_get_long (op2 );
365+ }
366+ } else {
367+ shift = Z_LVAL_P (op2 );
368+ }
343369
344370 if (shift < 0 ) {
345371 zend_throw_error (
346372 zend_ce_value_error , "%s must be greater than or equal to 0" ,
347373 opcode == ZEND_POW ? "Exponent" : "Shift"
348374 );
349375 ZVAL_UNDEF (return_value );
350- return ;
376+ return FAILURE ;
351377 } else {
352378 mpz_ptr gmpnum_op , gmpnum_result ;
353379 gmp_temp_t temp ;
354380
355- FETCH_GMP_ZVAL (gmpnum_op , op1 , temp , 1 );
381+ /* We do not use FETCH_GMP_ZVAL(...); here as we don't use convert_to_gmp()
382+ * as we want to handle the emitted exception ourself. */
383+ if (UNEXPECTED (!IS_GMP (op1 ))) {
384+ if (UNEXPECTED (Z_TYPE_P (op1 ) != IS_LONG )) {
385+ goto typeof_op_failure ;
386+ }
387+ mpz_init (temp .num );
388+ mpz_set_si (temp .num , Z_LVAL_P (op1 ));
389+ temp .is_used = 1 ;
390+ gmpnum_op = temp .num ;
391+ } else {
392+ gmpnum_op = GET_GMP_FROM_ZVAL (op1 );
393+ temp .is_used = 0 ;
394+ }
356395 INIT_GMP_RETVAL (gmpnum_result );
357396 op (gmpnum_result , gmpnum_op , (gmp_ulong ) shift );
358397 FREE_GMP_TEMP (temp );
398+ return SUCCESS ;
399+ }
400+
401+ typeof_op_failure : ;
402+ /* Returning FAILURE without throwing an exception would emit the
403+ * Unsupported operand types: GMP OP TypeOfOp2
404+ * However, this leads to the engine trying to interpret the GMP object as an integer
405+ * and doing the operation that way, which is not something we want. */
406+ const char * op_sigil ;
407+ switch (opcode ) {
408+ case ZEND_POW :
409+ op_sigil = "**" ;
410+ break ;
411+ case ZEND_SL :
412+ op_sigil = "<<" ;
413+ break ;
414+ case ZEND_SR :
415+ op_sigil = ">>" ;
416+ break ;
417+ EMPTY_SWITCH_DEFAULT_CASE ();
359418 }
419+ zend_type_error ("Unsupported operand types: %s %s %s" , zend_zval_type_name (op1 ), op_sigil , zend_zval_type_name (op2 ));
420+ return FAILURE ;
421+ valueof_op_failure :
422+ zend_value_error ("Number is not an integer string" );
423+ return FAILURE ;
360424}
361425
362426#define DO_BINARY_UI_OP_EX (op , uop , check_b_zero ) \
@@ -385,18 +449,15 @@ static zend_result gmp_do_operation_ex(uint8_t opcode, zval *result, zval *op1,
385449 case ZEND_MUL :
386450 DO_BINARY_UI_OP (mpz_mul );
387451 case ZEND_POW :
388- shift_operator_helper (mpz_pow_ui , result , op1 , op2 , opcode );
389- return SUCCESS ;
452+ return shift_operator_helper (mpz_pow_ui , result , op1 , op2 , opcode );
390453 case ZEND_DIV :
391454 DO_BINARY_UI_OP_EX (mpz_tdiv_q , gmp_mpz_tdiv_q_ui , 1 );
392455 case ZEND_MOD :
393456 DO_BINARY_UI_OP_EX (mpz_mod , gmp_mpz_mod_ui , 1 );
394457 case ZEND_SL :
395- shift_operator_helper (mpz_mul_2exp , result , op1 , op2 , opcode );
396- return SUCCESS ;
458+ return shift_operator_helper (mpz_mul_2exp , result , op1 , op2 , opcode );
397459 case ZEND_SR :
398- shift_operator_helper (mpz_fdiv_q_2exp , result , op1 , op2 , opcode );
399- return SUCCESS ;
460+ return shift_operator_helper (mpz_fdiv_q_2exp , result , op1 , op2 , opcode );
400461 case ZEND_BW_OR :
401462 DO_BINARY_OP (mpz_ior );
402463 case ZEND_BW_AND :
@@ -629,6 +690,13 @@ static zend_result convert_to_gmp(mpz_t gmpnumber, zval *val, zend_long base, ui
629690 case IS_STRING : {
630691 return convert_zstr_to_gmp (gmpnumber , Z_STR_P (val ), base , arg_pos );
631692 }
693+ case IS_NULL :
694+ /* Just reject null for operator overloading */
695+ if (arg_pos == 0 ) {
696+ zend_type_error ("Number must be of type GMP|string|int, %s given" , zend_zval_type_name (val ));
697+ return FAILURE ;
698+ }
699+ ZEND_FALLTHROUGH ;
632700 default : {
633701 zend_long lval ;
634702 if (!zend_parse_arg_long_slow (val , & lval , arg_pos )) {
0 commit comments