Skip to content

Commit af66b20

Browse files
committed
Merge branch 'PHP-5.6' of git.php.net:php-src into PHP-5.6
* 'PHP-5.6' of git.php.net:php-src: Use zend_error_noreturn here OCI8 build change: Fix source variable definition for C89 compatibility Add Tests for #65784 in 5.5 Disallowed JMP into a finally block. Update NEWS for 5.5.7 release Fixed bug #65784 (Segfault with finally).
2 parents 3bddcc1 + 6f95a07 commit af66b20

14 files changed

+228
-44
lines changed

NEWS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ PHP NEWS
2323
emalloc/efree/estrdup (Anatol, Dmitry)
2424
. Implemented constant scalar expressions (with support for constants)
2525
(RFC: https://wiki.php.net/rfc/const_scalar_exprs). (Bob)
26+
. Fixed bug #65784 (Segfault with finally). (Laruence, Dmitry)
2627

2728
- cURL:
2829
. Implemented FR #65646 (re-enable CURLOPT_FOLLOWLOCATION with open_basedir

Zend/tests/bug65784.phpt

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
--TEST--
2+
Fixed Bug #65784 (Segfault with finally)
3+
--FILE--
4+
<?php
5+
function foo1() {
6+
try {
7+
throw new Exception("not catch");
8+
return true;
9+
} finally {
10+
try {
11+
throw new Exception("catched");
12+
} catch (Exception $e) {
13+
}
14+
}
15+
}
16+
try {
17+
$foo = foo1();
18+
var_dump($foo);
19+
} catch (Exception $e) {
20+
do {
21+
var_dump($e->getMessage());
22+
} while ($e = $e->getPrevious());
23+
}
24+
25+
function foo2() {
26+
try {
27+
try {
28+
throw new Exception("catched");
29+
return true;
30+
} finally {
31+
try {
32+
throw new Exception("catched");
33+
} catch (Exception $e) {
34+
}
35+
}
36+
} catch (Exception $e) {
37+
}
38+
}
39+
40+
$foo = foo2();
41+
var_dump($foo);
42+
43+
function foo3() {
44+
try {
45+
throw new Exception("not catched");
46+
return true;
47+
} finally {
48+
try {
49+
throw new NotExists();
50+
} catch (Exception $e) {
51+
}
52+
}
53+
}
54+
55+
$bar = foo3();
56+
--EXPECTF--
57+
string(9) "not catch"
58+
NULL
59+
60+
Fatal error: Class 'NotExists' not found in %sbug65784.php on line %d

Zend/tests/finally_goto_001.phpt

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
--TEST--
2+
jmp into a finally block 01
3+
--FILE--
4+
<?php
5+
function foo() {
6+
goto test;
7+
try {
8+
} finally {
9+
test:
10+
}
11+
}
12+
?>
13+
--EXPECTF--
14+
Fatal error: jump into a finally block is disallowed in %sfinally_goto_001.php on line %d

Zend/tests/finally_goto_002.phpt

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
--TEST--
2+
jmp into a finally block 02
3+
--FILE--
4+
<?php
5+
function foo() {
6+
try {
7+
goto test;
8+
} finally {
9+
test:
10+
}
11+
}
12+
?>
13+
--EXPECTF--
14+
Fatal error: jump into a finally block is disallowed in %sfinally_goto_002.php on line %d

Zend/tests/finally_goto_003.phpt

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
--TEST--
2+
jmp into a finally block 03
3+
--FILE--
4+
<?php
5+
function foo() {
6+
try {
7+
} finally {
8+
goto test;
9+
test:
10+
}
11+
}
12+
echo "okey";
13+
?>
14+
--EXPECTF--
15+
okey

Zend/tests/finally_goto_004.phpt

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
--TEST--
2+
jmp into a finally block 03
3+
--FILE--
4+
<?php
5+
function foo() {
6+
try {
7+
} finally {
8+
test:
9+
}
10+
goto test;
11+
}
12+
?>
13+
--EXPECTF--
14+
Fatal error: jump into a finally block is disallowed in %sfinally_goto_004.php on line %d

Zend/zend_compile.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -401,6 +401,7 @@ struct _zend_execute_data {
401401
zend_class_entry *current_called_scope;
402402
zval *current_this;
403403
struct _zend_op *fast_ret; /* used by FAST_CALL/FAST_RET (finally keyword) */
404+
zval *delayed_exception;
404405
call_slot *call_slots;
405406
call_slot *call;
406407
};

Zend/zend_execute.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1650,6 +1650,7 @@ static zend_always_inline zend_execute_data *i_create_execute_data_from_op_array
16501650
EX(call) = NULL;
16511651
EG(current_execute_data) = execute_data;
16521652
EX(nested) = nested;
1653+
EX(delayed_exception) = NULL;
16531654

16541655
if (!op_array->run_time_cache && op_array->last_cache_slot) {
16551656
op_array->run_time_cache = ecalloc(op_array->last_cache_slot, sizeof(void*));

Zend/zend_opcode.c

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -489,10 +489,15 @@ static void zend_check_finally_breakout(zend_op_array *op_array, zend_uint op_nu
489489
zend_uint i;
490490

491491
for (i = 0; i < op_array->last_try_catch; i++) {
492-
if (op_array->try_catch_array[i].try_op > op_num) {
493-
break;
494-
}
495-
if ((op_num >= op_array->try_catch_array[i].finally_op
492+
if ((op_num < op_array->try_catch_array[i].finally_op ||
493+
op_num >= op_array->try_catch_array[i].finally_end)
494+
&& (dst_num >= op_array->try_catch_array[i].finally_op &&
495+
dst_num <= op_array->try_catch_array[i].finally_end)) {
496+
CG(in_compilation) = 1;
497+
CG(active_op_array) = op_array;
498+
CG(zend_lineno) = op_array->opcodes[op_num].lineno;
499+
zend_error_noreturn(E_COMPILE_ERROR, "jump into a finally block is disallowed");
500+
} else if ((op_num >= op_array->try_catch_array[i].finally_op
496501
&& op_num <= op_array->try_catch_array[i].finally_end)
497502
&& (dst_num > op_array->try_catch_array[i].finally_end
498503
|| dst_num < op_array->try_catch_array[i].finally_op)) {
@@ -541,11 +546,11 @@ static void zend_resolve_finally_call(zend_op_array *op_array, zend_uint op_num,
541546
while (i > 0) {
542547
i--;
543548
if (op_array->try_catch_array[i].finally_op &&
544-
op_num >= op_array->try_catch_array[i].try_op &&
545-
op_num < op_array->try_catch_array[i].finally_op - 1 &&
546-
(dst_num < op_array->try_catch_array[i].try_op ||
547-
dst_num > op_array->try_catch_array[i].finally_end)) {
548-
549+
op_num >= op_array->try_catch_array[i].try_op &&
550+
op_num < op_array->try_catch_array[i].finally_op - 1 &&
551+
(dst_num < op_array->try_catch_array[i].try_op ||
552+
dst_num > op_array->try_catch_array[i].finally_end)) {
553+
549554
opline = get_next_op(op_array TSRMLS_CC);
550555
opline->opcode = ZEND_FAST_CALL;
551556
SET_UNUSED(opline->op1);
@@ -565,7 +570,7 @@ static void zend_resolve_finally_call(zend_op_array *op_array, zend_uint op_num,
565570
SET_UNUSED(opline->op2);
566571
opline->op1.opline_num = start_op;
567572

568-
break;
573+
break;
569574
}
570575
}
571576
}

Zend/zend_vm_def.h

Lines changed: 35 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5021,7 +5021,7 @@ ZEND_VM_HANDLER(149, ZEND_HANDLE_EXCEPTION, ANY, ANY)
50215021
{
50225022
zend_uint op_num = EG(opline_before_exception)-EG(active_op_array)->opcodes;
50235023
int i;
5024-
zend_uint catch_op_num = 0, finally_op_num = 0;
5024+
zend_uint catch_op_num = 0, finally_op_num = 0, finally_op_end = 0;
50255025
void **stack_frame;
50265026

50275027
/* Figure out where the next stack frame (which maybe contains pushed
@@ -5046,6 +5046,10 @@ ZEND_VM_HANDLER(149, ZEND_HANDLE_EXCEPTION, ANY, ANY)
50465046
if (op_num < EG(active_op_array)->try_catch_array[i].finally_op) {
50475047
finally_op_num = EX(op_array)->try_catch_array[i].finally_op;
50485048
}
5049+
if (op_num >= EG(active_op_array)->try_catch_array[i].finally_op &&
5050+
op_num < EG(active_op_array)->try_catch_array[i].finally_end) {
5051+
finally_op_end = EG(active_op_array)->try_catch_array[i].finally_end;
5052+
}
50495053
}
50505054

50515055
if (EX(call) >= EX(call_slots)) {
@@ -5107,14 +5111,29 @@ ZEND_VM_HANDLER(149, ZEND_HANDLE_EXCEPTION, ANY, ANY)
51075111
EX(old_error_reporting) = NULL;
51085112

51095113
if (finally_op_num && (!catch_op_num || catch_op_num >= finally_op_num)) {
5110-
zend_exception_save(TSRMLS_C);
5114+
if (EX(delayed_exception)) {
5115+
zend_exception_set_previous(EG(exception), EX(delayed_exception) TSRMLS_CC);
5116+
}
5117+
EX(delayed_exception) = EG(exception);
5118+
EG(exception) = NULL;
51115119
EX(fast_ret) = NULL;
51125120
ZEND_VM_SET_OPCODE(&EX(op_array)->opcodes[finally_op_num]);
51135121
ZEND_VM_CONTINUE();
51145122
} else if (catch_op_num) {
5123+
if (finally_op_end && catch_op_num > finally_op_end) {
5124+
/* we are going out of current finally scope */
5125+
if (EX(delayed_exception)) {
5126+
zend_exception_set_previous(EG(exception), EX(delayed_exception) TSRMLS_CC);
5127+
EX(delayed_exception) = NULL;
5128+
}
5129+
}
51155130
ZEND_VM_SET_OPCODE(&EX(op_array)->opcodes[catch_op_num]);
51165131
ZEND_VM_CONTINUE();
51175132
} else {
5133+
if (EX(delayed_exception)) {
5134+
zend_exception_set_previous(EG(exception), EX(delayed_exception) TSRMLS_CC);
5135+
EX(delayed_exception) = NULL;
5136+
}
51185137
if (UNEXPECTED((EX(op_array)->fn_flags & ZEND_ACC_GENERATOR) != 0)) {
51195138
ZEND_VM_DISPATCH_TO_HANDLER(ZEND_GENERATOR_RETURN);
51205139
} else {
@@ -5405,10 +5424,10 @@ ZEND_VM_HANDLER(160, ZEND_YIELD, CONST|TMP|VAR|CV|UNUSED, CONST|TMP|VAR|CV|UNUSE
54055424

54065425
ZEND_VM_HANDLER(159, ZEND_DISCARD_EXCEPTION, ANY, ANY)
54075426
{
5408-
if (EG(prev_exception) != NULL) {
5427+
if (EX(delayed_exception) != NULL) {
54095428
/* discard the previously thrown exception */
5410-
zval_ptr_dtor(&EG(prev_exception));
5411-
EG(prev_exception) = NULL;
5429+
zval_ptr_dtor(&EX(delayed_exception));
5430+
EX(delayed_exception) = NULL;
54125431
}
54135432

54145433
ZEND_VM_NEXT_OPCODE();
@@ -5425,6 +5444,7 @@ ZEND_VM_HANDLER(162, ZEND_FAST_CALL, ANY, ANY)
54255444
ZEND_VM_CONTINUE();
54265445
}
54275446
EX(fast_ret) = opline + 1;
5447+
EX(delayed_exception) = NULL;
54285448
ZEND_VM_SET_OPCODE(opline->op1.jmp_addr);
54295449
ZEND_VM_CONTINUE();
54305450
}
@@ -5441,16 +5461,17 @@ ZEND_VM_HANDLER(163, ZEND_FAST_RET, ANY, ANY)
54415461
if (opline->extended_value == ZEND_FAST_RET_TO_FINALLY) {
54425462
ZEND_VM_SET_OPCODE(&EX(op_array)->opcodes[opline->op2.opline_num]);
54435463
ZEND_VM_CONTINUE();
5444-
} else if (opline->extended_value == ZEND_FAST_RET_TO_CATCH) {
5445-
zend_exception_restore(TSRMLS_C);
5446-
ZEND_VM_SET_OPCODE(&EX(op_array)->opcodes[opline->op2.opline_num]);
5447-
ZEND_VM_CONTINUE();
5448-
} else if (UNEXPECTED((EX(op_array)->fn_flags & ZEND_ACC_GENERATOR) != 0)) {
5449-
zend_exception_restore(TSRMLS_C);
5450-
ZEND_VM_DISPATCH_TO_HANDLER(ZEND_GENERATOR_RETURN);
54515464
} else {
5452-
zend_exception_restore(TSRMLS_C);
5453-
ZEND_VM_DISPATCH_TO_HELPER(zend_leave_helper);
5465+
EG(exception) = EX(delayed_exception);
5466+
EX(delayed_exception) = NULL;
5467+
if (opline->extended_value == ZEND_FAST_RET_TO_CATCH) {
5468+
ZEND_VM_SET_OPCODE(&EX(op_array)->opcodes[opline->op2.opline_num]);
5469+
ZEND_VM_CONTINUE();
5470+
} else if (UNEXPECTED((EX(op_array)->fn_flags & ZEND_ACC_GENERATOR) != 0)) {
5471+
ZEND_VM_DISPATCH_TO_HANDLER(ZEND_GENERATOR_RETURN);
5472+
} else {
5473+
ZEND_VM_DISPATCH_TO_HELPER(zend_leave_helper);
5474+
}
54545475
}
54555476
}
54565477
}

Zend/zend_vm_execute.h

Lines changed: 35 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1016,7 +1016,7 @@ static int ZEND_FASTCALL ZEND_HANDLE_EXCEPTION_SPEC_HANDLER(ZEND_OPCODE_HANDLER
10161016
{
10171017
zend_uint op_num = EG(opline_before_exception)-EG(active_op_array)->opcodes;
10181018
int i;
1019-
zend_uint catch_op_num = 0, finally_op_num = 0;
1019+
zend_uint catch_op_num = 0, finally_op_num = 0, finally_op_end = 0;
10201020
void **stack_frame;
10211021

10221022
/* Figure out where the next stack frame (which maybe contains pushed
@@ -1041,6 +1041,10 @@ static int ZEND_FASTCALL ZEND_HANDLE_EXCEPTION_SPEC_HANDLER(ZEND_OPCODE_HANDLER
10411041
if (op_num < EG(active_op_array)->try_catch_array[i].finally_op) {
10421042
finally_op_num = EX(op_array)->try_catch_array[i].finally_op;
10431043
}
1044+
if (op_num >= EG(active_op_array)->try_catch_array[i].finally_op &&
1045+
op_num < EG(active_op_array)->try_catch_array[i].finally_end) {
1046+
finally_op_end = EG(active_op_array)->try_catch_array[i].finally_end;
1047+
}
10441048
}
10451049

10461050
if (EX(call) >= EX(call_slots)) {
@@ -1102,14 +1106,29 @@ static int ZEND_FASTCALL ZEND_HANDLE_EXCEPTION_SPEC_HANDLER(ZEND_OPCODE_HANDLER
11021106
EX(old_error_reporting) = NULL;
11031107

11041108
if (finally_op_num && (!catch_op_num || catch_op_num >= finally_op_num)) {
1105-
zend_exception_save(TSRMLS_C);
1109+
if (EX(delayed_exception)) {
1110+
zend_exception_set_previous(EG(exception), EX(delayed_exception) TSRMLS_CC);
1111+
}
1112+
EX(delayed_exception) = EG(exception);
1113+
EG(exception) = NULL;
11061114
EX(fast_ret) = NULL;
11071115
ZEND_VM_SET_OPCODE(&EX(op_array)->opcodes[finally_op_num]);
11081116
ZEND_VM_CONTINUE();
11091117
} else if (catch_op_num) {
1118+
if (finally_op_end && catch_op_num > finally_op_end) {
1119+
/* we are going out of current finally scope */
1120+
if (EX(delayed_exception)) {
1121+
zend_exception_set_previous(EG(exception), EX(delayed_exception) TSRMLS_CC);
1122+
EX(delayed_exception) = NULL;
1123+
}
1124+
}
11101125
ZEND_VM_SET_OPCODE(&EX(op_array)->opcodes[catch_op_num]);
11111126
ZEND_VM_CONTINUE();
11121127
} else {
1128+
if (EX(delayed_exception)) {
1129+
zend_exception_set_previous(EG(exception), EX(delayed_exception) TSRMLS_CC);
1130+
EX(delayed_exception) = NULL;
1131+
}
11131132
if (UNEXPECTED((EX(op_array)->fn_flags & ZEND_ACC_GENERATOR) != 0)) {
11141133
return ZEND_GENERATOR_RETURN_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
11151134
} else {
@@ -1159,10 +1178,10 @@ static int ZEND_FASTCALL ZEND_USER_OPCODE_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS
11591178

11601179
static int ZEND_FASTCALL ZEND_DISCARD_EXCEPTION_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
11611180
{
1162-
if (EG(prev_exception) != NULL) {
1181+
if (EX(delayed_exception) != NULL) {
11631182
/* discard the previously thrown exception */
1164-
zval_ptr_dtor(&EG(prev_exception));
1165-
EG(prev_exception) = NULL;
1183+
zval_ptr_dtor(&EX(delayed_exception));
1184+
EX(delayed_exception) = NULL;
11661185
}
11671186

11681187
ZEND_VM_NEXT_OPCODE();
@@ -1179,6 +1198,7 @@ static int ZEND_FASTCALL ZEND_FAST_CALL_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
11791198
ZEND_VM_CONTINUE();
11801199
}
11811200
EX(fast_ret) = opline + 1;
1201+
EX(delayed_exception) = NULL;
11821202
ZEND_VM_SET_OPCODE(opline->op1.jmp_addr);
11831203
ZEND_VM_CONTINUE();
11841204
}
@@ -1195,16 +1215,17 @@ static int ZEND_FASTCALL ZEND_FAST_RET_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
11951215
if (opline->extended_value == ZEND_FAST_RET_TO_FINALLY) {
11961216
ZEND_VM_SET_OPCODE(&EX(op_array)->opcodes[opline->op2.opline_num]);
11971217
ZEND_VM_CONTINUE();
1198-
} else if (opline->extended_value == ZEND_FAST_RET_TO_CATCH) {
1199-
zend_exception_restore(TSRMLS_C);
1200-
ZEND_VM_SET_OPCODE(&EX(op_array)->opcodes[opline->op2.opline_num]);
1201-
ZEND_VM_CONTINUE();
1202-
} else if (UNEXPECTED((EX(op_array)->fn_flags & ZEND_ACC_GENERATOR) != 0)) {
1203-
zend_exception_restore(TSRMLS_C);
1204-
return ZEND_GENERATOR_RETURN_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
12051218
} else {
1206-
zend_exception_restore(TSRMLS_C);
1207-
return zend_leave_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
1219+
EG(exception) = EX(delayed_exception);
1220+
EX(delayed_exception) = NULL;
1221+
if (opline->extended_value == ZEND_FAST_RET_TO_CATCH) {
1222+
ZEND_VM_SET_OPCODE(&EX(op_array)->opcodes[opline->op2.opline_num]);
1223+
ZEND_VM_CONTINUE();
1224+
} else if (UNEXPECTED((EX(op_array)->fn_flags & ZEND_ACC_GENERATOR) != 0)) {
1225+
return ZEND_GENERATOR_RETURN_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
1226+
} else {
1227+
return zend_leave_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
1228+
}
12081229
}
12091230
}
12101231
}

0 commit comments

Comments
 (0)