@@ -4639,6 +4639,16 @@ static void zend_swap_operands(zend_op *op) /* {{{ */
4639
4639
op -> op1_type = op -> op2_type ;
4640
4640
op -> op2 = tmp ;
4641
4641
op -> op2_type = tmp_type ;
4642
+
4643
+ #ifdef ZEND_VERIFY_TYPE_INFERENCE
4644
+ uint32_t tmp_info ;
4645
+ tmp_info = op -> op1_use_type ;
4646
+ op -> op1_use_type = op -> op2_use_type ;
4647
+ op -> op2_use_type = tmp_info ;
4648
+ tmp_info = op -> op1_def_type ;
4649
+ op -> op1_def_type = op -> op2_def_type ;
4650
+ op -> op2_def_type = tmp_info ;
4651
+ #endif
4642
4652
}
4643
4653
/* }}} */
4644
4654
#endif
@@ -5303,14 +5313,159 @@ static zend_always_inline zend_execute_data *_zend_vm_stack_push_call_frame(uint
5303
5313
# include "zend_vm_trace_map.h"
5304
5314
#endif
5305
5315
5316
+ #ifdef ZEND_VERIFY_TYPE_INFERENCE
5317
+
5318
+ static void zend_verify_type_inference (zval * value , uint32_t type_mask , uint8_t op_type , zend_execute_data * execute_data , const zend_op * opline , const char * msg )
5319
+ {
5320
+ if (type_mask == MAY_BE_CLASS ) {
5321
+ return ;
5322
+ }
5323
+
5324
+ if (Z_TYPE_P (value ) == IS_INDIRECT ) {
5325
+ if (!(type_mask & MAY_BE_INDIRECT )) {
5326
+ fprintf (stderr , "Inference verification failed at %04d %s (mask 0x%x mising MAY_BE_INDIRECT)\n" , (int )(opline - EX (func )-> op_array .opcodes ), msg , type_mask );
5327
+ }
5328
+ value = Z_INDIRECT_P (value );
5329
+ }
5330
+
5331
+ /* Verifying RC inference is currently not possible because type information is based on the SSA
5332
+ * built without ZEND_SSA_RC_INFERENCE, which is missing various definitions for RC-modifying
5333
+ * operations. Support could be added by repeating SSA-construction and type inference with the
5334
+ * given flag. */
5335
+ // if (Z_REFCOUNTED_P(value)) {
5336
+ // if (Z_REFCOUNT_P(value) == 1 && !(type_mask & MAY_BE_RC1)) {
5337
+ // fprintf(stderr, "Inference verification failed at %04d %s (mask 0x%x missing MAY_BE_RC1)\n", (int)(opline - EX(func)->op_array.opcodes), msg, type_mask);
5338
+ // }
5339
+ // if (Z_REFCOUNT_P(value) > 1 && !(type_mask & MAY_BE_RCN)) {
5340
+ // fprintf(stderr, "Inference verification failed at %04d %s (mask 0x%x missing MAY_BE_RCN)\n", (int)(opline - EX(func)->op_array.opcodes), msg, type_mask);
5341
+ // }
5342
+ // }
5343
+
5344
+ if (Z_TYPE_P (value ) == IS_REFERENCE ) {
5345
+ if (!(type_mask & MAY_BE_REF )) {
5346
+ fprintf (stderr , "Inference verification failed at %04d %s (mask 0x%x missing MAY_BE_REF)\n" , (int )(opline - EX (func )-> op_array .opcodes ), msg , type_mask );
5347
+ }
5348
+ value = Z_REFVAL_P (value );
5349
+ }
5350
+
5351
+ if (!(type_mask & (1u << Z_TYPE_P (value )))) {
5352
+ if (Z_TYPE_P (value ) == IS_UNUSED && op_type == IS_VAR && (type_mask & MAY_BE_NULL )) {
5353
+ /* FETCH_OBJ_* for typed property may return IS_UNDEF. This is an exception. */
5354
+ } else {
5355
+ fprintf (stderr , "Inference verification failed at %04d %s (mask 0x%x missing type %d)\n" , (int )(opline - EX (func )-> op_array .opcodes ), msg , type_mask , Z_TYPE_P (value ));
5356
+ }
5357
+ }
5358
+
5359
+ if (Z_TYPE_P (value ) == IS_ARRAY ) {
5360
+ HashTable * ht = Z_ARRVAL_P (value );
5361
+ uint32_t num_checked = 0 ;
5362
+ zend_string * str ;
5363
+ zval * val ;
5364
+ if (HT_IS_INITIALIZED (ht )) {
5365
+ if (HT_IS_PACKED (ht ) && !MAY_BE_PACKED (type_mask )) {
5366
+ fprintf (stderr , "Inference verification failed at %04d %s (mask 0x%x missing MAY_BE_ARRAY_PACKED)\n" , (int )(opline - EX (func )-> op_array .opcodes ), msg , type_mask );
5367
+ }
5368
+ if (!HT_IS_PACKED (ht ) && !MAY_BE_HASH (type_mask )) {
5369
+ fprintf (stderr , "Inference verification failed at %04d %s (mask 0x%x missing MAY_BE_ARRAY_HASH)\n" , (int )(opline - EX (func )-> op_array .opcodes ), msg , type_mask );
5370
+ }
5371
+ } else {
5372
+ if (!(type_mask & MAY_BE_ARRAY_EMPTY )) {
5373
+ fprintf (stderr , "Inference verification failed at %04d %s (mask 0x%x missing MAY_BE_ARRAY_EMPTY)\n" , (int )(opline - EX (func )-> op_array .opcodes ), msg , type_mask );
5374
+ }
5375
+ }
5376
+ ZEND_HASH_FOREACH_STR_KEY_VAL (ht , str , val ) {
5377
+ if (str ) {
5378
+ if (!(type_mask & MAY_BE_ARRAY_KEY_STRING )) {
5379
+ fprintf (stderr , "Inference verification failed at %04d %s (mask 0x%x missing MAY_BE_ARRAY_KEY_STRING)\n" , (int )(opline - EX (func )-> op_array .opcodes ), msg , type_mask );
5380
+ break ;
5381
+ }
5382
+ } else {
5383
+ if (!(type_mask & MAY_BE_ARRAY_KEY_LONG )) {
5384
+ fprintf (stderr , "Inference verification failed at %04d %s (mask 0x%x missing MAY_BE_ARRAY_KEY_LONG)\n" , (int )(opline - EX (func )-> op_array .opcodes ), msg , type_mask );
5385
+ break ;
5386
+ }
5387
+ }
5388
+
5389
+ uint32_t array_type = 1u << (Z_TYPE_P (val ) + MAY_BE_ARRAY_SHIFT );
5390
+ if (!(type_mask & array_type )) {
5391
+ fprintf (stderr , "Inference verification failed at %04d %s (mask 0x%x missing array type %d)\n" , (int )(opline - EX (func )-> op_array .opcodes ), msg , type_mask , Z_TYPE_P (val ));
5392
+ break ;
5393
+ }
5394
+
5395
+ /* Don't check all elements of large arrays. */
5396
+ if (++ num_checked > 16 ) {
5397
+ break ;
5398
+ }
5399
+ } ZEND_HASH_FOREACH_END ();
5400
+ }
5401
+ }
5402
+
5403
+ static void zend_verify_inference_use (zend_execute_data * execute_data , const zend_op * opline )
5404
+ {
5405
+ if (opline -> op1_use_type
5406
+ && (opline -> op1_type & (IS_TMP_VAR |IS_VAR |IS_CV ))
5407
+ && opline -> opcode != ZEND_ROPE_ADD
5408
+ && opline -> opcode != ZEND_ROPE_END ) {
5409
+ zend_verify_type_inference (EX_VAR (opline -> op1 .var ), opline -> op1_use_type , opline -> op1_type , execute_data , opline , "op1_use" );
5410
+ }
5411
+ if (opline -> op2_use_type
5412
+ && (opline -> op2_type & (IS_TMP_VAR |IS_VAR |IS_CV ))) {
5413
+ zend_verify_type_inference (EX_VAR (opline -> op2 .var ), opline -> op2_use_type , opline -> op2_type , execute_data , opline , "op2_use" );
5414
+ }
5415
+ if (opline -> result_use_type
5416
+ && (opline -> result_type & (IS_TMP_VAR |IS_VAR |IS_CV ))) {
5417
+ zend_verify_type_inference (EX_VAR (opline -> result .var ), opline -> result_use_type , opline -> result_type , execute_data , opline , "result_use" );
5418
+ }
5419
+ }
5420
+
5421
+ static void zend_verify_inference_def (zend_execute_data * execute_data , const zend_op * opline )
5422
+ {
5423
+ if (EG (exception )) {
5424
+ return ;
5425
+ }
5426
+ if (opline -> op1_def_type
5427
+ && (opline -> op1_type & (IS_TMP_VAR |IS_VAR |IS_CV ))
5428
+ // array is actually changed by the the following instruction(s)
5429
+ && opline -> opcode != ZEND_FETCH_DIM_W
5430
+ && opline -> opcode != ZEND_FETCH_DIM_RW
5431
+ && opline -> opcode != ZEND_FETCH_DIM_FUNC_ARG
5432
+ && opline -> opcode != ZEND_FETCH_LIST_W ) {
5433
+ zend_verify_type_inference (EX_VAR (opline -> op1 .var ), opline -> op1_def_type , opline -> op1_type , execute_data , opline , "op1_def" );
5434
+ }
5435
+ if (opline -> op2_def_type
5436
+ && (opline -> op2_type & (IS_TMP_VAR |IS_VAR |IS_CV ))) {
5437
+ zend_verify_type_inference (EX_VAR (opline -> op2 .var ), opline -> op2_def_type , opline -> op2_type , execute_data , opline , "op2_def" );
5438
+ }
5439
+ if (opline -> result_def_type
5440
+ && (opline -> result_type & (IS_TMP_VAR |IS_VAR |IS_CV ))
5441
+ && opline -> opcode != ZEND_ROPE_INIT
5442
+ && opline -> opcode != ZEND_ROPE_ADD
5443
+ // Some jump opcode handlers don't set result when it's never read
5444
+ && opline -> opcode != ZEND_JMP_SET
5445
+ && opline -> opcode != ZEND_JMP_NULL
5446
+ && opline -> opcode != ZEND_COALESCE
5447
+ && opline -> opcode != ZEND_ASSERT_CHECK ) {
5448
+ zend_verify_type_inference (EX_VAR (opline -> result .var ), opline -> result_def_type , opline -> result_type , execute_data , opline , "result_def" );
5449
+ }
5450
+ }
5451
+
5452
+ # define ZEND_VERIFY_INFERENCE_USE () zend_verify_inference_use(execute_data, OPLINE);
5453
+ # define ZEND_VERIFY_INFERENCE_DEF () zend_verify_inference_def(execute_data, OPLINE);
5454
+ #else
5455
+ # define ZEND_VERIFY_INFERENCE_USE ()
5456
+ # define ZEND_VERIFY_INFERENCE_DEF ()
5457
+ #endif
5458
+
5306
5459
#define ZEND_VM_NEXT_OPCODE_EX (check_exception , skip ) \
5460
+ ZEND_VERIFY_INFERENCE_DEF() \
5307
5461
CHECK_SYMBOL_TABLES() \
5308
5462
if (check_exception) { \
5309
5463
OPLINE = EX(opline) + (skip); \
5310
5464
} else { \
5311
5465
ZEND_ASSERT(!EG(exception)); \
5312
5466
OPLINE = opline + (skip); \
5313
5467
} \
5468
+ ZEND_VERIFY_INFERENCE_USE() \
5314
5469
ZEND_VM_CONTINUE()
5315
5470
5316
5471
#define ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION () \
0 commit comments