@@ -333,25 +333,89 @@ static zend_object *gmp_clone_obj(zend_object *obj) /* {{{ */
333333}
334334/* }}} */
335335
336- static void shift_operator_helper (gmp_binary_ui_op_t op , zval * return_value , zval * op1 , zval * op2 , uint8_t opcode ) {
337- zend_long shift = zval_get_long (op2 );
336+ static zend_result shift_operator_helper (gmp_binary_ui_op_t op , zval * return_value , zval * op1 , zval * op2 , uint8_t opcode ) {
337+ zend_long shift = 0 ;
338+
339+ if (UNEXPECTED (Z_TYPE_P (op2 ) != IS_LONG )) {
340+ if (UNEXPECTED (!IS_GMP (op2 ))) {
341+ // For PHP 8.3 and up use zend_try_get_long()
342+ switch (Z_TYPE_P (op2 )) {
343+ case IS_DOUBLE :
344+ shift = zval_get_long (op2 );
345+ if (UNEXPECTED (EG (exception ))) {
346+ return FAILURE ;
347+ }
348+ break ;
349+ case IS_STRING :
350+ if (is_numeric_str_function (Z_STR_P (op2 ), & shift , NULL ) != IS_LONG ) {
351+ goto valueof_op_failure ;
352+ }
353+ break ;
354+ default :
355+ goto typeof_op_failure ;
356+ }
357+ } else {
358+ // TODO We shouldn't cast the GMP object to int here
359+ shift = zval_get_long (op2 );
360+ }
361+ } else {
362+ shift = Z_LVAL_P (op2 );
363+ }
338364
339365 if (shift < 0 ) {
340366 zend_throw_error (
341367 zend_ce_value_error , "%s must be greater than or equal to 0" ,
342368 opcode == ZEND_POW ? "Exponent" : "Shift"
343369 );
344370 ZVAL_UNDEF (return_value );
345- return ;
371+ return FAILURE ;
346372 } else {
347373 mpz_ptr gmpnum_op , gmpnum_result ;
348374 gmp_temp_t temp ;
349375
350- FETCH_GMP_ZVAL (gmpnum_op , op1 , temp , 1 );
376+ /* We do not use FETCH_GMP_ZVAL(...); here as we don't use convert_to_gmp()
377+ * as we want to handle the emitted exception ourself. */
378+ if (UNEXPECTED (!IS_GMP (op1 ))) {
379+ if (UNEXPECTED (Z_TYPE_P (op1 ) != IS_LONG )) {
380+ goto typeof_op_failure ;
381+ }
382+ mpz_init (temp .num );
383+ mpz_set_si (temp .num , Z_LVAL_P (op1 ));
384+ temp .is_used = 1 ;
385+ gmpnum_op = temp .num ;
386+ } else {
387+ gmpnum_op = GET_GMP_FROM_ZVAL (op1 );
388+ temp .is_used = 0 ;
389+ }
351390 INIT_GMP_RETVAL (gmpnum_result );
352391 op (gmpnum_result , gmpnum_op , (gmp_ulong ) shift );
353392 FREE_GMP_TEMP (temp );
393+ return SUCCESS ;
394+ }
395+
396+ typeof_op_failure : ;
397+ /* Returning FAILURE without throwing an exception would emit the
398+ * Unsupported operand types: GMP OP TypeOfOp2
399+ * However, this leads to the engine trying to interpret the GMP object as an integer
400+ * and doing the operation that way, which is not something we want. */
401+ const char * op_sigil ;
402+ switch (opcode ) {
403+ case ZEND_POW :
404+ op_sigil = "**" ;
405+ break ;
406+ case ZEND_SL :
407+ op_sigil = "<<" ;
408+ break ;
409+ case ZEND_SR :
410+ op_sigil = ">>" ;
411+ break ;
412+ EMPTY_SWITCH_DEFAULT_CASE ();
354413 }
414+ zend_type_error ("Unsupported operand types: %s %s %s" , zend_zval_type_name (op1 ), op_sigil , zend_zval_type_name (op2 ));
415+ return FAILURE ;
416+ valueof_op_failure :
417+ zend_value_error ("Number is not an integer string" );
418+ return FAILURE ;
355419}
356420
357421#define DO_BINARY_UI_OP_EX (op , uop , check_b_zero ) \
@@ -380,18 +444,15 @@ static zend_result gmp_do_operation_ex(uint8_t opcode, zval *result, zval *op1,
380444 case ZEND_MUL :
381445 DO_BINARY_UI_OP (mpz_mul );
382446 case ZEND_POW :
383- shift_operator_helper (mpz_pow_ui , result , op1 , op2 , opcode );
384- return SUCCESS ;
447+ return shift_operator_helper (mpz_pow_ui , result , op1 , op2 , opcode );
385448 case ZEND_DIV :
386449 DO_BINARY_UI_OP_EX (mpz_tdiv_q , gmp_mpz_tdiv_q_ui , 1 );
387450 case ZEND_MOD :
388451 DO_BINARY_UI_OP_EX (mpz_mod , gmp_mpz_mod_ui , 1 );
389452 case ZEND_SL :
390- shift_operator_helper (mpz_mul_2exp , result , op1 , op2 , opcode );
391- return SUCCESS ;
453+ return shift_operator_helper (mpz_mul_2exp , result , op1 , op2 , opcode );
392454 case ZEND_SR :
393- shift_operator_helper (mpz_fdiv_q_2exp , result , op1 , op2 , opcode );
394- return SUCCESS ;
455+ return shift_operator_helper (mpz_fdiv_q_2exp , result , op1 , op2 , opcode );
395456 case ZEND_BW_OR :
396457 DO_BINARY_OP (mpz_ior );
397458 case ZEND_BW_AND :
@@ -619,6 +680,13 @@ static zend_result convert_to_gmp(mpz_t gmpnumber, zval *val, zend_long base, ui
619680 case IS_STRING : {
620681 return convert_zstr_to_gmp (gmpnumber , Z_STR_P (val ), base , arg_pos );
621682 }
683+ case IS_NULL :
684+ /* Just reject null for operator overloading */
685+ if (arg_pos == 0 ) {
686+ zend_type_error ("Number must be of type GMP|string|int, %s given" , zend_zval_type_name (val ));
687+ return FAILURE ;
688+ }
689+ ZEND_FALLTHROUGH ;
622690 default : {
623691 zend_long lval ;
624692 if (!zend_parse_arg_long_slow (val , & lval , arg_pos )) {
0 commit comments