@@ -334,25 +334,89 @@ 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+ // For PHP 8.3 and up use zend_try_get_long()
343+ switch (Z_TYPE_P (op2 )) {
344+ case IS_DOUBLE :
345+ shift = zval_get_long (op2 );
346+ if (UNEXPECTED (EG (exception ))) {
347+ return FAILURE ;
348+ }
349+ break ;
350+ case IS_STRING :
351+ if (is_numeric_str_function (Z_STR_P (op2 ), & shift , NULL ) != IS_LONG ) {
352+ goto valueof_op_failure ;
353+ }
354+ break ;
355+ default :
356+ goto typeof_op_failure ;
357+ }
358+ } else {
359+ // TODO We shouldn't cast the GMP object to int here
360+ shift = zval_get_long (op2 );
361+ }
362+ } else {
363+ shift = Z_LVAL_P (op2 );
364+ }
339365
340366 if (shift < 0 ) {
341367 zend_throw_error (
342368 zend_ce_value_error , "%s must be greater than or equal to 0" ,
343369 opcode == ZEND_POW ? "Exponent" : "Shift"
344370 );
345371 ZVAL_UNDEF (return_value );
346- return ;
372+ return FAILURE ;
347373 } else {
348374 mpz_ptr gmpnum_op , gmpnum_result ;
349375 gmp_temp_t temp ;
350376
351- FETCH_GMP_ZVAL (gmpnum_op , op1 , temp , 1 );
377+ /* We do not use FETCH_GMP_ZVAL(...); here as we don't use convert_to_gmp()
378+ * as we want to handle the emitted exception ourself. */
379+ if (UNEXPECTED (!IS_GMP (op1 ))) {
380+ if (UNEXPECTED (Z_TYPE_P (op1 ) != IS_LONG )) {
381+ goto typeof_op_failure ;
382+ }
383+ mpz_init (temp .num );
384+ mpz_set_si (temp .num , Z_LVAL_P (op1 ));
385+ temp .is_used = 1 ;
386+ gmpnum_op = temp .num ;
387+ } else {
388+ gmpnum_op = GET_GMP_FROM_ZVAL (op1 );
389+ temp .is_used = 0 ;
390+ }
352391 INIT_GMP_RETVAL (gmpnum_result );
353392 op (gmpnum_result , gmpnum_op , (gmp_ulong ) shift );
354393 FREE_GMP_TEMP (temp );
394+ return SUCCESS ;
395+ }
396+
397+ typeof_op_failure : ;
398+ /* Returning FAILURE without throwing an exception would emit the
399+ * Unsupported operand types: GMP OP TypeOfOp2
400+ * However, this leads to the engine trying to interpret the GMP object as an integer
401+ * and doing the operation that way, which is not something we want. */
402+ const char * op_sigil ;
403+ switch (opcode ) {
404+ case ZEND_POW :
405+ op_sigil = "**" ;
406+ break ;
407+ case ZEND_SL :
408+ op_sigil = "<<" ;
409+ break ;
410+ case ZEND_SR :
411+ op_sigil = ">>" ;
412+ break ;
413+ EMPTY_SWITCH_DEFAULT_CASE ();
355414 }
415+ zend_type_error ("Unsupported operand types: %s %s %s" , zend_zval_type_name (op1 ), op_sigil , zend_zval_type_name (op2 ));
416+ return FAILURE ;
417+ valueof_op_failure :
418+ zend_value_error ("Number is not an integer string" );
419+ return FAILURE ;
356420}
357421
358422#define DO_BINARY_UI_OP_EX (op , uop , check_b_zero ) \
@@ -381,18 +445,15 @@ static zend_result gmp_do_operation_ex(zend_uchar opcode, zval *result, zval *op
381445 case ZEND_MUL :
382446 DO_BINARY_UI_OP (mpz_mul );
383447 case ZEND_POW :
384- shift_operator_helper (mpz_pow_ui , result , op1 , op2 , opcode );
385- return SUCCESS ;
448+ return shift_operator_helper (mpz_pow_ui , result , op1 , op2 , opcode );
386449 case ZEND_DIV :
387450 DO_BINARY_UI_OP_EX (mpz_tdiv_q , gmp_mpz_tdiv_q_ui , 1 );
388451 case ZEND_MOD :
389452 DO_BINARY_UI_OP_EX (mpz_mod , gmp_mpz_mod_ui , 1 );
390453 case ZEND_SL :
391- shift_operator_helper (mpz_mul_2exp , result , op1 , op2 , opcode );
392- return SUCCESS ;
454+ return shift_operator_helper (mpz_mul_2exp , result , op1 , op2 , opcode );
393455 case ZEND_SR :
394- shift_operator_helper (mpz_fdiv_q_2exp , result , op1 , op2 , opcode );
395- return SUCCESS ;
456+ return shift_operator_helper (mpz_fdiv_q_2exp , result , op1 , op2 , opcode );
396457 case ZEND_BW_OR :
397458 DO_BINARY_OP (mpz_ior );
398459 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