Skip to content

Commit de7a0df

Browse files
committed
Fixed use-after-free in tracing JIT when recording closures and top-level op_arrays.
1 parent 32a1ebb commit de7a0df

File tree

4 files changed

+81
-49
lines changed

4 files changed

+81
-49
lines changed

ext/opcache/jit/zend_jit_internal.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,6 @@ int ZEND_FASTCALL zend_jit_check_constant(const zval *key);
159159
_(BLACK_LIST, "trace blacklisted") \
160160
_(INNER_LOOP, "inner loop") /* trace it */ \
161161
_(COMPILED_LOOP, "compiled loop") \
162-
_(TOPLEVEL, "toplevel") \
163162
_(TRAMPOLINE, "trampoline call") \
164163
_(BAD_FUNC, "bad function call") \
165164
_(HALT, "exit from interpreter") \
@@ -219,6 +218,7 @@ typedef union _zend_op_trace_info {
219218

220219
typedef struct _zend_jit_op_array_trace_extension {
221220
zend_func_info func_info;
221+
const zend_op_array *op_array;
222222
size_t offset; /* offset from "zend_op" to corresponding "op_info" */
223223
zend_op_trace_info trace_info[1];
224224
} zend_jit_op_array_trace_extension;

ext/opcache/jit/zend_jit_trace.c

Lines changed: 1 addition & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -4668,16 +4668,6 @@ int ZEND_FASTCALL zend_jit_trace_hot_root(zend_execute_data *execute_data, const
46684668
stop = zend_jit_trace_execute(execute_data, opline, trace_buffer,
46694669
ZEND_OP_TRACE_INFO(opline, offset)->trace_flags & ZEND_JIT_TRACE_START_MASK);
46704670

4671-
if (stop == ZEND_JIT_TRACE_STOP_TOPLEVEL) {
4672-
/* op_array may be already deallocated */
4673-
if (JIT_G(debug) & ZEND_JIT_DEBUG_TRACE_ABORT) {
4674-
fprintf(stderr, "---- TRACE %d abort (%s)\n",
4675-
trace_num,
4676-
zend_jit_trace_stop_description[stop]);
4677-
}
4678-
goto blacklist;
4679-
}
4680-
46814671
if (UNEXPECTED(JIT_G(debug) & ZEND_JIT_DEBUG_TRACE_BYTECODE)) {
46824672
zend_jit_dump_trace(trace_buffer, NULL);
46834673
}
@@ -4713,7 +4703,6 @@ int ZEND_FASTCALL zend_jit_trace_hot_root(zend_execute_data *execute_data, const
47134703
trace_num,
47144704
zend_jit_trace_stop_description[stop]);
47154705
}
4716-
blacklist:
47174706
if (!ZEND_JIT_TRACE_STOP_MAY_RECOVER(stop)
47184707
|| zend_jit_trace_is_bad_root(orig_opline, stop, offset)) {
47194708
if (JIT_G(debug) & ZEND_JIT_DEBUG_TRACE_BLACKLIST) {
@@ -4938,16 +4927,6 @@ int ZEND_FASTCALL zend_jit_trace_hot_side(zend_execute_data *execute_data, uint3
49384927

49394928
stop = zend_jit_trace_execute(execute_data, EX(opline), trace_buffer, ZEND_JIT_TRACE_START_SIDE);
49404929

4941-
if (stop == ZEND_JIT_TRACE_STOP_TOPLEVEL) {
4942-
/* op_array may be already deallocated */
4943-
if (JIT_G(debug) & ZEND_JIT_DEBUG_TRACE_ABORT) {
4944-
fprintf(stderr, "---- TRACE %d abort (%s)\n",
4945-
trace_num,
4946-
zend_jit_trace_stop_description[stop]);
4947-
}
4948-
goto blacklist;
4949-
}
4950-
49514930
if (UNEXPECTED(JIT_G(debug) & ZEND_JIT_DEBUG_TRACE_BYTECODE)) {
49524931
zend_jit_dump_trace(trace_buffer, NULL);
49534932
}
@@ -4992,7 +4971,6 @@ int ZEND_FASTCALL zend_jit_trace_hot_side(zend_execute_data *execute_data, uint3
49924971
trace_num,
49934972
zend_jit_trace_stop_description[stop]);
49944973
}
4995-
blacklist:
49964974
if (!ZEND_JIT_TRACE_STOP_MAY_RECOVER(stop)
49974975
|| zend_jit_trace_exit_is_bad(parent_num, exit_num)) {
49984976
zend_jit_blacklist_trace_exit(parent_num, exit_num);
@@ -5140,6 +5118,7 @@ static int zend_jit_setup_hot_trace_counters(zend_op_array *op_array)
51405118
memset(&jit_extension->func_info, 0, sizeof(zend_func_info));
51415119
jit_extension->func_info.num_args = -1;
51425120
jit_extension->func_info.return_value_used = -1;
5121+
jit_extension->op_array = op_array;
51435122
jit_extension->offset = (char*)jit_extension->trace_info - (char*)op_array->opcodes;
51445123
for (i = 0; i < op_array->last; i++) {
51455124
jit_extension->trace_info[i].orig_handler = op_array->opcodes[i].handler;

ext/opcache/jit/zend_jit_vm_helpers.c

Lines changed: 75 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -462,10 +462,28 @@ static int zend_jit_trace_record_fake_init_call(zend_execute_data *call, zend_ji
462462
zend_jit_trace_stop stop ZEND_ATTRIBUTE_UNUSED = ZEND_JIT_TRACE_STOP_ERROR;
463463

464464
do {
465+
zend_function *func;
466+
zend_jit_op_array_trace_extension *jit_extension;
467+
465468
if (call->prev_execute_data) {
466469
idx = zend_jit_trace_record_fake_init_call(call->prev_execute_data, trace_buffer, idx);
467470
}
468-
TRACE_RECORD(ZEND_JIT_TRACE_INIT_CALL, ZEND_JIT_TRACE_FAKE_INIT_CALL, call->func);
471+
472+
func = call->func;
473+
if (func->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE) {
474+
/* TODO: Can we continue recording ??? */
475+
return -1;
476+
}
477+
if (func->type == ZEND_USER_FUNCTION
478+
&& (func->op_array.fn_flags & ZEND_ACC_CLOSURE)) {
479+
jit_extension =
480+
(zend_jit_op_array_trace_extension*)ZEND_FUNC_INFO(&func->op_array);
481+
if (UNEXPECTED(!jit_extension)) {
482+
return -1;
483+
}
484+
func = (zend_function*)jit_extension->op_array;
485+
}
486+
TRACE_RECORD(ZEND_JIT_TRACE_INIT_CALL, ZEND_JIT_TRACE_FAKE_INIT_CALL, func);
469487
} while (0);
470488
return idx;
471489
}
@@ -511,6 +529,7 @@ zend_jit_trace_stop ZEND_FASTCALL zend_jit_trace_execute(zend_execute_data *ex,
511529
int level = 0;
512530
int ret_level = 0;
513531
zend_vm_opcode_handler_t handler;
532+
const zend_op_array *op_array;
514533
zend_jit_op_array_trace_extension *jit_extension;
515534
size_t offset;
516535
int idx, count;
@@ -521,7 +540,6 @@ zend_jit_trace_stop ZEND_FASTCALL zend_jit_trace_execute(zend_execute_data *ex,
521540
int backtrack_ret_recursion_level = 0;
522541
int loop_unroll_limit = 0;
523542
const zend_op_array *unrolled_calls[ZEND_JIT_TRACE_MAX_CALL_DEPTH + ZEND_JIT_TRACE_MAX_RET_DEPTH];
524-
zend_bool is_toplevel;
525543
#ifdef HAVE_GCC_GLOBAL_REGS
526544
zend_execute_data *prev_execute_data = ex;
527545

@@ -541,15 +559,23 @@ zend_jit_trace_stop ZEND_FASTCALL zend_jit_trace_execute(zend_execute_data *ex,
541559

542560
orig_opline = opline;
543561

562+
op_array = &EX(func)->op_array;
544563
jit_extension =
545-
(zend_jit_op_array_trace_extension*)ZEND_FUNC_INFO(&EX(func)->op_array);
564+
(zend_jit_op_array_trace_extension*)ZEND_FUNC_INFO(op_array);
546565
offset = jit_extension->offset;
566+
if (!op_array->function_name
567+
|| (op_array->fn_flags & ZEND_ACC_CLOSURE)) {
568+
op_array = jit_extension->op_array;
569+
}
547570

548-
TRACE_START(ZEND_JIT_TRACE_START, start, &EX(func)->op_array, opline);
549-
is_toplevel = EX(func)->op_array.function_name == NULL;
571+
TRACE_START(ZEND_JIT_TRACE_START, start, op_array, opline);
550572

551573
if (prev_call) {
552-
idx = zend_jit_trace_record_fake_init_call(prev_call, trace_buffer, idx);
574+
int ret = zend_jit_trace_record_fake_init_call(prev_call, trace_buffer, idx);
575+
if (ret < 0) {
576+
return ZEND_JIT_TRACE_STOP_BAD_FUNC;
577+
}
578+
idx = ret;
553579
}
554580

555581
while (1) {
@@ -660,7 +686,6 @@ zend_jit_trace_stop ZEND_FASTCALL zend_jit_trace_execute(zend_execute_data *ex,
660686
break;
661687
}
662688
if (UNEXPECTED(execute_data != prev_execute_data)) {
663-
if (execute_data->prev_execute_data == prev_execute_data) {
664689
#else
665690
rc = handler(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
666691
if (rc != 0) {
@@ -670,6 +695,24 @@ zend_jit_trace_stop ZEND_FASTCALL zend_jit_trace_execute(zend_execute_data *ex,
670695
}
671696
execute_data = EG(current_execute_data);
672697
opline = EX(opline);
698+
#endif
699+
700+
op_array = &EX(func)->op_array;
701+
jit_extension =
702+
(zend_jit_op_array_trace_extension*)ZEND_FUNC_INFO(op_array);
703+
if (UNEXPECTED(!jit_extension)) {
704+
stop = ZEND_JIT_TRACE_STOP_BAD_FUNC;
705+
break;
706+
}
707+
offset = jit_extension->offset;
708+
if (!op_array->function_name
709+
|| (op_array->fn_flags & ZEND_ACC_CLOSURE)) {
710+
op_array = jit_extension->op_array;
711+
}
712+
713+
#ifdef HAVE_GCC_GLOBAL_REGS
714+
if (execute_data->prev_execute_data == prev_execute_data) {
715+
#else
673716
if (rc == 1) {
674717
#endif
675718
/* Enter into function */
@@ -687,7 +730,7 @@ zend_jit_trace_stop ZEND_FASTCALL zend_jit_trace_execute(zend_execute_data *ex,
687730

688731
TRACE_RECORD(ZEND_JIT_TRACE_ENTER,
689732
EX(return_value) != NULL ? ZEND_JIT_TRACE_RETRUN_VALUE_USED : 0,
690-
&EX(func)->op_array);
733+
op_array);
691734

692735
count = zend_jit_trace_recursive_call_count(&EX(func)->op_array, unrolled_calls, ret_level, level);
693736

@@ -708,10 +751,7 @@ zend_jit_trace_stop ZEND_FASTCALL zend_jit_trace_execute(zend_execute_data *ex,
708751
/* Return from function */
709752
prev_call = EX(call);
710753
if (level == 0) {
711-
if (is_toplevel) {
712-
stop = ZEND_JIT_TRACE_STOP_TOPLEVEL;
713-
break;
714-
} else if (start == ZEND_JIT_TRACE_START_RETURN
754+
if (start == ZEND_JIT_TRACE_START_RETURN
715755
&& JIT_G(max_recursive_returns) > 0
716756
&& execute_data->prev_execute_data
717757
&& execute_data->prev_execute_data->func
@@ -721,7 +761,7 @@ zend_jit_trace_stop ZEND_FASTCALL zend_jit_trace_execute(zend_execute_data *ex,
721761
stop = ZEND_JIT_TRACE_STOP_TOO_DEEP_RET;
722762
break;
723763
}
724-
TRACE_RECORD(ZEND_JIT_TRACE_BACK, 0, &EX(func)->op_array);
764+
TRACE_RECORD(ZEND_JIT_TRACE_BACK, 0, op_array);
725765
count = zend_jit_trace_recursive_ret_count(&EX(func)->op_array, unrolled_calls, ret_level);
726766
if (opline == orig_opline) {
727767
if (count + 1 >= JIT_G(max_recursive_returns)) {
@@ -737,10 +777,14 @@ zend_jit_trace_stop ZEND_FASTCALL zend_jit_trace_execute(zend_execute_data *ex,
737777

738778
unrolled_calls[ret_level] = &EX(func)->op_array;
739779
ret_level++;
740-
is_toplevel = EX(func)->op_array.function_name == NULL;
741780

742781
if (prev_call) {
743-
idx = zend_jit_trace_record_fake_init_call(prev_call, trace_buffer, idx);
782+
int ret = zend_jit_trace_record_fake_init_call(prev_call, trace_buffer, idx);
783+
if (ret < 0) {
784+
stop = ZEND_JIT_TRACE_STOP_BAD_FUNC;
785+
break;
786+
}
787+
idx = ret;
744788
}
745789
} else if (start & ZEND_JIT_TRACE_START_LOOP
746790
&& !zend_jit_trace_bad_loop_exit(orig_opline)) {
@@ -754,29 +798,36 @@ zend_jit_trace_stop ZEND_FASTCALL zend_jit_trace_execute(zend_execute_data *ex,
754798
}
755799
} else {
756800
level--;
757-
TRACE_RECORD(ZEND_JIT_TRACE_BACK, 0, &EX(func)->op_array);
801+
TRACE_RECORD(ZEND_JIT_TRACE_BACK, 0, op_array);
758802
}
759803
}
760804
#ifdef HAVE_GCC_GLOBAL_REGS
761805
prev_execute_data = execute_data;
762806
#endif
763-
jit_extension =
764-
(zend_jit_op_array_trace_extension*)ZEND_FUNC_INFO(&EX(func)->op_array);
765-
if (UNEXPECTED(!jit_extension)) {
766-
stop = ZEND_JIT_TRACE_STOP_BAD_FUNC;
767-
break;
768-
}
769-
offset = jit_extension->offset;
770807
}
771808
if (EX(call) != prev_call) {
772809
if (EX(call)
773810
&& EX(call)->prev_execute_data == prev_call) {
811+
zend_function *func;
812+
zend_jit_op_array_trace_extension *jit_extension;
813+
774814
if (EX(call)->func->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE) {
775815
/* TODO: Can we continue recording ??? */
776816
stop = ZEND_JIT_TRACE_STOP_TRAMPOLINE;
777817
break;
778818
}
779-
TRACE_RECORD(ZEND_JIT_TRACE_INIT_CALL, 0, EX(call)->func);
819+
func = EX(call)->func;
820+
if (func->type == ZEND_USER_FUNCTION
821+
&& (func->op_array.fn_flags & ZEND_ACC_CLOSURE)) {
822+
jit_extension =
823+
(zend_jit_op_array_trace_extension*)ZEND_FUNC_INFO(&func->op_array);
824+
if (UNEXPECTED(!jit_extension)) {
825+
stop = ZEND_JIT_TRACE_STOP_BAD_FUNC;
826+
break;
827+
}
828+
func = (zend_function*)jit_extension->op_array;
829+
}
830+
TRACE_RECORD(ZEND_JIT_TRACE_INIT_CALL, 0, func);
780831
}
781832
prev_call = EX(call);
782833
}

ext/opcache/jit/zend_jit_x86.dasc

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8009,8 +8009,10 @@ static int zend_jit_init_fcall_guard(dasm_State **Dst, const zend_op *opline, co
80098009
return 0;
80108010
}
80118011

8012-
if (func->type == ZEND_USER_FUNCTION
8013-
&& !(func->common.fn_flags & ZEND_ACC_IMMUTABLE)) {
8012+
if (func->type == ZEND_USER_FUNCTION &&
8013+
(!(func->common.fn_flags & ZEND_ACC_IMMUTABLE) ||
8014+
(func->common.fn_flags & ZEND_ACC_CLOSURE) ||
8015+
!func->common.function_name)) {
80148016
const zend_op *opcodes = func->op_array.opcodes;
80158017

80168018
| // call = EX(call);

0 commit comments

Comments
 (0)