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