Skip to content

Drop usages of E_RECOVERABLE_ERROR #6106

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 32 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
97cf236
Transform failure to convert object to bool a TypeError
Girgias Sep 22, 2020
e543540
Review zval_is_true() usage
Girgias Sep 23, 2020
7902c43
Review zend_is_true() usage in OpCache
Girgias Sep 23, 2020
9dc6bb2
Review zend_is_true() usage for Streams
Girgias Sep 23, 2020
b915dfa
Review zend_is_true() usage in BZ2 filter
Girgias Sep 23, 2020
8b9235c
Review zend_is_true() usage in DOM extension
Girgias Sep 23, 2020
bfb44ac
Review zend_is_true() usage in OpenSSL extension
Girgias Sep 23, 2020
3166d6a
Review zend_is_true() usage in standard extension
Girgias Sep 23, 2020
d0acc40
Review zend_is_true() usage in Readline extension
Girgias Sep 23, 2020
5a0ff89
Review zend_is_true() usage in SPL extension
Girgias Sep 23, 2020
b076737
Review zend_is_true() usage in PHPDBG SAPI
Girgias Sep 23, 2020
de6c99e
Review zend_is_true() usage in PDO parser
Girgias Sep 23, 2020
ff15543
Review zend_is_true() usage in PGSQL PDO driver
Girgias Sep 23, 2020
cf4b750
Review zend_is_true() usage in cURL extension
Girgias Sep 23, 2020
e84c542
Firebird PDO handler only used zend_is_true() with in, float, or boolean
Girgias Sep 23, 2020
566752c
Review zend_is_true() usage in LDAP extension
Girgias Sep 23, 2020
0764e59
Review zend_is_true() usage in ZIP extension
Girgias Sep 23, 2020
1c141ff
'Review' zend_is_true() usage in SOAP extension
Girgias Sep 23, 2020
ad2457c
Review zend_is_true() usage in FFI extension
Girgias Sep 23, 2020
0ed06d7
Review zend_is_true() usage in Date extension
Girgias Sep 23, 2020
750ea90
Review zend_is_true() usage in JIT
Girgias Sep 23, 2020
8592167
Review zend_is_true() usage in zend_ast.c
Girgias Sep 23, 2020
8ee2f61
Review zend_is_true() usage in zend_API.c
Girgias Sep 23, 2020
62f17c1
Review zend_is_true() usage in zend_compile.c
Girgias Sep 23, 2020
32d6566
Review zend_is_true() usage in zend_object_handlers.c
Girgias Sep 23, 2020
401230f
fix PDO
Girgias Sep 23, 2020
9c47de0
LDAP Indent fix
Girgias Jul 10, 2021
70c2e4c
Opcache JIT indent
Girgias Jul 10, 2021
a0091b9
Add test for PDO
Girgias Jul 10, 2021
e657b50
Add some tests for functions in ext/standard
Girgias Jul 10, 2021
16e8867
Adjust unary compile expectation
Girgias Jul 10, 2021
dc804dc
Try to fix new PDO test
Girgias Jul 10, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Zend/Optimizer/block_pass.c
Original file line number Diff line number Diff line change
Expand Up @@ -568,6 +568,7 @@ static void zend_optimize_block(zend_basic_block *block, zend_op_array *op_array
if (opline->op1_type == IS_CONST) {
++(*opt_count);
block->successors_count = 1;
/* zend_is_true() cannot fail here as op1 cannot be an object */
if (zend_is_true(&ZEND_OP1_LITERAL(opline)) ==
(opline->opcode == ZEND_JMPZ)) {

Expand Down Expand Up @@ -617,6 +618,7 @@ static void zend_optimize_block(zend_basic_block *block, zend_op_array *op_array
while (1) {
if (opline->op1_type == IS_CONST) {
++(*opt_count);
/* zend_is_true() cannot fail here as op1 cannot be an object */
if (zend_is_true(&ZEND_OP1_LITERAL(opline))) {
zend_op *target_opline = ZEND_OFFSET_TO_OPLINE(opline, opline->extended_value);
ZEND_SET_OP_JMP_ADDR(opline, opline->op1, target_opline);
Expand Down
1 change: 1 addition & 0 deletions Zend/Optimizer/dfa_pass.c
Original file line number Diff line number Diff line change
Expand Up @@ -397,6 +397,7 @@ int zend_dfa_optimize_calls(zend_op_array *op_array, zend_ssa *ssa)
send_array = call_info->caller_call_opline - 1;
send_needly = call_info->caller_call_opline - 2;
} else {
/* zend_is_true() cannot fail here as op1 is not an object */
if (zend_is_true(CT_CONSTANT_EX(op_array, (call_info->caller_call_opline - 1)->op1.constant))) {
strict = 1;
}
Expand Down
3 changes: 3 additions & 0 deletions Zend/Optimizer/pass1.c
Original file line number Diff line number Diff line change
Expand Up @@ -595,6 +595,7 @@ void zend_optimizer_pass1(zend_op_array *op_array, zend_optimizer_ctx *ctx)
/* convert Ti = JMPZ_EX(C, L) => Ti = QM_ASSIGN(C)
in case we know it wouldn't jump */
if (opline->op1_type == IS_CONST) {
/* zend_is_true() cannot fail here as op1 is not an object */
if (zend_is_true(&ZEND_OP1_LITERAL(opline))) {
if (opline->opcode == ZEND_JMPZ_EX) {
opline->opcode = ZEND_QM_ASSIGN;
Expand All @@ -619,6 +620,7 @@ void zend_optimizer_pass1(zend_op_array *op_array, zend_optimizer_ctx *ctx)
case ZEND_JMPZ:
case ZEND_JMPNZ:
if (opline->op1_type == IS_CONST) {
/* zend_is_true() cannot fail here as op1 is not an object */
int should_jmp = zend_is_true(&ZEND_OP1_LITERAL(opline));

if (opline->opcode == ZEND_JMPZ) {
Expand All @@ -642,6 +644,7 @@ void zend_optimizer_pass1(zend_op_array *op_array, zend_optimizer_ctx *ctx)
if (opline->op1_type == IS_CONST) {
zend_op *target_opline;

/* zend_is_true() cannot fail here as op1 is not an object */
if (zend_is_true(&ZEND_OP1_LITERAL(opline))) {
target_opline = ZEND_OFFSET_TO_OPLINE(opline, opline->extended_value); /* JMPNZ */
} else {
Expand Down
1 change: 1 addition & 0 deletions Zend/Optimizer/sccp.c
Original file line number Diff line number Diff line change
Expand Up @@ -391,6 +391,7 @@ static inline int ct_eval_bool_cast(zval *result, zval *op) {
return SUCCESS;
}

/* op cannot be an object so zend_is_true() cannot fail */
ZVAL_BOOL(result, zend_is_true(op));
return SUCCESS;
}
Expand Down
3 changes: 3 additions & 0 deletions Zend/Optimizer/zend_optimizer.c
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,9 @@ int zend_optimizer_eval_unary_op(zval *result, zend_uchar opcode, zval *op1) /*
return unary_op(result, op1);
} else { /* ZEND_BOOL */
ZVAL_BOOL(result, zend_is_true(op1));
if (UNEXPECTED(EG(exception))) {
return FAILURE;
}
return SUCCESS;
}
}
Expand Down
4 changes: 2 additions & 2 deletions Zend/tests/temporary_cleaning_014.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ Leak in JMP_SET
gmp
--FILE--
<?php
set_error_handler(function() { throw new Exception; });
set_exception_handler(function() { throw new Exception; });
try {
new GMP ?: null;
} catch (Exception $e) {
} catch (\TypeError $e) {
}
?>
DONE
Expand Down
4 changes: 4 additions & 0 deletions Zend/zend_API.c
Original file line number Diff line number Diff line change
Expand Up @@ -474,6 +474,10 @@ ZEND_API bool ZEND_FASTCALL zend_parse_arg_bool_weak(zval *arg, bool *dest, uint
return 0;
}
*dest = zend_is_true(arg);
/* arg is an object which cannot be converted to bool */
if (UNEXPECTED(EG(exception))) {
return 0;
}
} else {
return 0;
}
Expand Down
24 changes: 24 additions & 0 deletions Zend/zend_ast.c
Original file line number Diff line number Diff line change
Expand Up @@ -604,8 +604,18 @@ ZEND_API zend_result ZEND_FASTCALL zend_ast_evaluate(zval *result, zend_ast *ast
break;
}
ZVAL_BOOL(result, zend_is_true(&op2));
/* op2 is an object which cannot be converted to bool */
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Objects definitely can't occur in this context.

if (UNEXPECTED(EG(exception))) {
ZVAL_UNDEF(result);
return FAILURE;
}
zval_ptr_dtor_nogc(&op2);
} else {
/* op1 is an object which cannot be converted to bool */
if (UNEXPECTED(EG(exception))) {
ZVAL_UNDEF(result);
return FAILURE;
}
ZVAL_FALSE(result);
}
zval_ptr_dtor_nogc(&op1);
Expand All @@ -618,12 +628,22 @@ ZEND_API zend_result ZEND_FASTCALL zend_ast_evaluate(zval *result, zend_ast *ast
if (zend_is_true(&op1)) {
ZVAL_TRUE(result);
} else {
/* op1 is an object which cannot be converted to bool */
if (UNEXPECTED(EG(exception))) {
ZVAL_UNDEF(result);
return FAILURE;
}
if (UNEXPECTED(zend_ast_evaluate(&op2, ast->child[1], scope) != SUCCESS)) {
zval_ptr_dtor_nogc(&op1);
ret = FAILURE;
break;
}
ZVAL_BOOL(result, zend_is_true(&op2));
/* op2 is an object which cannot be converted to bool */
if (UNEXPECTED(EG(exception))) {
ZVAL_UNDEF(result);
return FAILURE;
}
zval_ptr_dtor_nogc(&op2);
}
zval_ptr_dtor_nogc(&op1);
Expand All @@ -645,6 +665,10 @@ ZEND_API zend_result ZEND_FASTCALL zend_ast_evaluate(zval *result, zend_ast *ast
zval_ptr_dtor_nogc(&op1);
}
} else {
/* op1 is an object which cannot be converted to bool */
if (UNEXPECTED(EG(exception))) {
return FAILURE;
}
if (UNEXPECTED(zend_ast_evaluate(result, ast->child[2], scope) != SUCCESS)) {
zval_ptr_dtor_nogc(&op1);
ret = FAILURE;
Expand Down
6 changes: 5 additions & 1 deletion Zend/zend_compile.c
Original file line number Diff line number Diff line change
Expand Up @@ -8320,7 +8320,8 @@ ZEND_API bool zend_unary_op_produces_error(uint32_t opcode, zval *op)
return Z_TYPE_P(op) <= IS_TRUE || !zend_is_op_long_compatible(op);
}

return 0;
/* Objects might throw a type error if they cannot be converted to bool */
return Z_TYPE_P(op) == IS_OBJECT;
}

static inline bool zend_try_ct_eval_unary_op(zval *result, uint32_t opcode, zval *op) /* {{{ */
Expand Down Expand Up @@ -10165,6 +10166,7 @@ void zend_eval_const_expr(zend_ast **ast_ptr) /* {{{ */
}

child0_is_true = zend_is_true(zend_ast_get_zval(ast->child[0]));
/* TODO Check if need to handle potential TypeError if child[0] is an object which cannot be converted to bool */
if (child0_is_true == (ast->kind == ZEND_AST_OR)) {
ZVAL_BOOL(&result, ast->kind == ZEND_AST_OR);
break;
Expand All @@ -10175,6 +10177,7 @@ void zend_eval_const_expr(zend_ast **ast_ptr) /* {{{ */
}

child1_is_true = zend_is_true(zend_ast_get_zval(ast->child[1]));
/* TODO Check if need to handle potential TypeError if child[1] is an object which cannot be converted to bool */
if (ast->kind == ZEND_AST_OR) {
ZVAL_BOOL(&result, child0_is_true || child1_is_true);
} else {
Expand Down Expand Up @@ -10241,6 +10244,7 @@ void zend_eval_const_expr(zend_ast **ast_ptr) /* {{{ */
}

child = &ast->child[2 - zend_is_true(zend_ast_get_zval(ast->child[0]))];
/* TODO Check if need to handle potential TypeError if child[0] is an object which cannot be converted to bool */
if (*child == NULL) {
child--;
}
Expand Down
7 changes: 7 additions & 0 deletions Zend/zend_object_handlers.c
Original file line number Diff line number Diff line change
Expand Up @@ -634,6 +634,7 @@ ZEND_API zval *zend_std_read_property(zend_object *zobj, zend_string *name, int
*guard &= ~IN_ISSET;

if (!zend_is_true(&tmp_result)) {
/* handles case when tmp_result is an object which cannot be converted to bool */
retval = &EG(uninitialized_zval);
OBJ_RELEASE(zobj);
zval_ptr_dtor(&tmp_result);
Expand Down Expand Up @@ -851,6 +852,7 @@ ZEND_API zval *zend_std_read_dimension(zend_object *object, zval *offset, int ty
return NULL;
}
if (!i_zend_is_true(rv)) {
/* handles case when tmp_result is an object which cannot be converted to bool */
OBJ_RELEASE(object);
zval_ptr_dtor(&tmp_offset);
zval_ptr_dtor(rv);
Expand Down Expand Up @@ -909,12 +911,14 @@ ZEND_API int zend_std_has_dimension(zend_object *object, zval *offset, int check
ZVAL_COPY_DEREF(&tmp_offset, offset);
GC_ADDREF(object);
zend_call_method_with_1_params(object, ce, NULL, "offsetexists", &retval, &tmp_offset);
ZEND_ASSERT(Z_TYPE(retval) != IS_OBJECT);
result = i_zend_is_true(&retval);
zval_ptr_dtor(&retval);
if (check_empty && result && EXPECTED(!EG(exception))) {
zend_call_method_with_1_params(object, ce, NULL, "offsetget", &retval, &tmp_offset);
result = i_zend_is_true(&retval);
zval_ptr_dtor(&retval);
/* TODO: need to do something if retval is an object which cannot be converted to bool? */
}
OBJ_RELEASE(object);
zval_ptr_dtor(&tmp_offset);
Expand Down Expand Up @@ -1676,6 +1680,7 @@ ZEND_API int zend_std_has_property(zend_object *zobj, zend_string *name, int has
found:
if (has_set_exists == ZEND_PROPERTY_NOT_EMPTY) {
result = zend_is_true(value);
/* TODO: need to do something if retval is an object which cannot be converted to bool? */
} else if (has_set_exists < ZEND_PROPERTY_NOT_EMPTY) {
ZEND_ASSERT(has_set_exists == ZEND_PROPERTY_ISSET);
ZVAL_DEREF(value);
Expand Down Expand Up @@ -1708,13 +1713,15 @@ ZEND_API int zend_std_has_property(zend_object *zobj, zend_string *name, int has
zend_std_call_issetter(zobj, name, &rv);
result = zend_is_true(&rv);
zval_ptr_dtor(&rv);
/* TODO: need to do something if retval is an object which cannot be converted to bool? */
if (has_set_exists == ZEND_PROPERTY_NOT_EMPTY && result) {
if (EXPECTED(!EG(exception)) && zobj->ce->__get && !((*guard) & IN_GET)) {
(*guard) |= IN_GET;
zend_std_call_getter(zobj, name, &rv);
(*guard) &= ~IN_GET;
result = i_zend_is_true(&rv);
zval_ptr_dtor(&rv);
/* TODO: need to do something if retval is an object which cannot be converted to bool? */
} else {
result = 0;
}
Expand Down
45 changes: 38 additions & 7 deletions Zend/zend_operators.c
Original file line number Diff line number Diff line change
Expand Up @@ -1423,7 +1423,7 @@ ZEND_API zend_result ZEND_FASTCALL mod_function(zval *result, zval *op1, zval *o

ZEND_API zend_result ZEND_FASTCALL boolean_xor_function(zval *result, zval *op1, zval *op2) /* {{{ */
{
int op1_val, op2_val;
bool op1_val, op2_val;

do {
if (Z_TYPE_P(op1) == IS_FALSE) {
Expand All @@ -1443,6 +1443,10 @@ ZEND_API zend_result ZEND_FASTCALL boolean_xor_function(zval *result, zval *op1,
}
ZEND_TRY_BINARY_OP1_OBJECT_OPERATION(ZEND_BOOL_XOR);
op1_val = zval_is_true(op1);
if (UNEXPECTED(EG(exception))) {
ZVAL_UNDEF(result);
return FAILURE;
}
}
} while (0);
do {
Expand All @@ -1463,6 +1467,10 @@ ZEND_API zend_result ZEND_FASTCALL boolean_xor_function(zval *result, zval *op1,
}
ZEND_TRY_BINARY_OP2_OBJECT_OPERATION(ZEND_BOOL_XOR);
op2_val = zval_is_true(op2);
if (UNEXPECTED(EG(exception))) {
ZVAL_UNDEF(result);
return FAILURE;
}
}
} while (0);

Expand All @@ -1478,6 +1486,7 @@ ZEND_API zend_result ZEND_FASTCALL boolean_not_function(zval *result, zval *op1)
} else if (EXPECTED(Z_TYPE_P(op1) == IS_TRUE)) {
ZVAL_FALSE(result);
} else {
bool not;
if (Z_ISREF_P(op1)) {
op1 = Z_REFVAL_P(op1);
if (Z_TYPE_P(op1) < IS_TRUE) {
Expand All @@ -1490,7 +1499,12 @@ ZEND_API zend_result ZEND_FASTCALL boolean_not_function(zval *result, zval *op1)
}
ZEND_TRY_UNARY_OBJECT_OPERATION(ZEND_BOOL_NOT);

ZVAL_BOOL(result, !zval_is_true(op1));
not = !zval_is_true(op1);
if (UNEXPECTED(EG(exception))) {
ZVAL_UNDEF(result);
return FAILURE;
}
ZVAL_BOOL(result, not);
}
return SUCCESS;
}
Expand Down Expand Up @@ -2214,13 +2228,29 @@ ZEND_API int ZEND_FASTCALL zend_compare(zval *op1, zval *op2) /* {{{ */

if (!converted) {
if (Z_TYPE_P(op1) < IS_TRUE) {
return zval_is_true(op2) ? -1 : 0;
bool is_true = zval_is_true(op2);
if (UNEXPECTED(EG(exception))) {
return 1;
}
return is_true ? -1 : 0;
} else if (Z_TYPE_P(op1) == IS_TRUE) {
return zval_is_true(op2) ? 0 : 1;
bool is_true = zval_is_true(op2);
if (UNEXPECTED(EG(exception))) {
return 1;
}
return is_true ? 0 : 1;
} else if (Z_TYPE_P(op2) < IS_TRUE) {
return zval_is_true(op1) ? 1 : 0;
bool is_true = zval_is_true(op1);
if (UNEXPECTED(EG(exception))) {
return 1;
}
return is_true ? 1 : 0;
} else if (Z_TYPE_P(op2) == IS_TRUE) {
return zval_is_true(op1) ? 0 : -1;
bool is_true = zval_is_true(op1);
if (UNEXPECTED(EG(exception))) {
return 1;
}
return is_true ? 0 : -1;
} else {
op1 = _zendi_convert_scalar_to_number_silent(op1, &op1_copy);
op2 = _zendi_convert_scalar_to_number_silent(op2, &op2_copy);
Expand Down Expand Up @@ -2594,6 +2624,7 @@ ZEND_API zend_result ZEND_FASTCALL decrement_function(zval *op1) /* {{{ */
}
/* }}} */

/* TODO make JIT compatible with a bool return */
ZEND_API int ZEND_FASTCALL zend_is_true(zval *op) /* {{{ */
{
return (int) i_zend_is_true(op);
Expand All @@ -2607,7 +2638,7 @@ ZEND_API bool ZEND_FASTCALL zend_object_is_true(zval *op) /* {{{ */
if (zobj->handlers->cast_object(zobj, &tmp, _IS_BOOL) == SUCCESS) {
return Z_TYPE(tmp) == IS_TRUE;
}
zend_error(E_RECOVERABLE_ERROR, "Object of class %s could not be converted to bool", ZSTR_VAL(zobj->ce->name));
zend_type_error("Object of class %s could not be converted to bool", ZSTR_VAL(zobj->ce->name));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is generally fine, but we will need to perform an exception-safety audit on all users for zend_is_true/zval_is_true.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure how feasible it is do perform this before next Tuesday when RC1 gets tagged as there does seems to be a couple of crucial usages (zend_compare, OpCache) looking at: https://heap.space/search?project=php-src&refs=zval_is_true and https://heap.space/search?project=php-src&refs=zend_is_true

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I've checked all of them but I'm very unsure about how to go about it with some cases.

return false;
}
/* }}} */
Expand Down
8 changes: 8 additions & 0 deletions ext/bz2/bz2_filter.c
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,10 @@ static php_stream_filter *php_bz2_filter_create(const char *filtername, zval *fi
if (Z_TYPE_P(filterparams) == IS_ARRAY || Z_TYPE_P(filterparams) == IS_OBJECT) {
if ((tmpzval = zend_hash_str_find(HASH_OF(filterparams), "concatenated", sizeof("concatenated")-1))) {
data->expect_concatenated = zend_is_true(tmpzval);
if (EG(exception)) {
status = BZ_DATA_ERROR;
// TODO Goto cleanup?
}
tmpzval = NULL;
}

Expand All @@ -349,6 +353,10 @@ static php_stream_filter *php_bz2_filter_create(const char *filtername, zval *fi

if (tmpzval) {
data->small_footprint = zend_is_true(tmpzval);
if (EG(exception)) {
status = BZ_DATA_ERROR;
// TODO Goto cleanup?
}
}
}

Expand Down
Loading