Fix GH-13142: Undefined variable name is shortened when contains \0

Uses the new %S formatter and introduces the necessary changes and
helpers.
This commit is contained in:
Niels Dossche 2024-01-20 00:46:34 +01:00
parent a651ae86aa
commit fe064d7f12
14 changed files with 118 additions and 80 deletions

2
NEWS
View file

@ -7,6 +7,8 @@ Core:
Solaris and Haiku. (David Carlier)
. Enabled ifunc checks on FreeBSD from the 12.x releases. (Freaky)
. Changed the type of PHP_DEBUG and PHP_ZTS constants to bool. (haszi)
. Fixed bug GH-13142 (Undefined variable name is shortened when contains \0).
(nielsdos)
Curl:
. Deprecated the CURLOPT_BINARYTRANSFER constant. (divinity76)

29
Zend/tests/gh13142.phpt Normal file
View file

@ -0,0 +1,29 @@
--TEST--
GH-13142 (Undefined variable name is shortened when contains \0)
--FILE--
<?php
$a = "test\0test";
$$a;
$a = "\0test";
$$a;
$a = "test\0";
$$a;
compact("a\0b");
compact("\0ab");
compact("ab\0");
?>
--EXPECTF--
Warning: Undefined variable $test%0test in %s on line %d
Warning: Undefined variable $%0test in %s on line %d
Warning: Undefined variable $test%0 in %s on line %d
Warning: compact(): Undefined variable $a%0b in %s on line %d
Warning: compact(): Undefined variable $%0ab in %s on line %d
Warning: compact(): Undefined variable $ab%0 in %s on line %d

View file

@ -16,4 +16,4 @@ Warning: Octal escape sequence overflow \400 is greater than \377 in %s on line
Deprecated: Using ${expr} (variable variables) in strings is deprecated, use {${expr}} instead in %s on line %d
Warning: Undefined variable $ in %s on line %d
Warning: Undefined variable $%0 in %s on line %d

View file

@ -1597,26 +1597,22 @@ ZEND_API ZEND_COLD void zend_error_at(
va_end(args);
}
ZEND_API ZEND_COLD void zend_error(int type, const char *format, ...) {
zend_string *filename;
uint32_t lineno;
va_list args;
#define zend_error_impl(type, format) do { \
zend_string *filename; \
uint32_t lineno; \
va_list args; \
get_filename_lineno(type, &filename, &lineno); \
va_start(args, format); \
zend_error_va_list(type, filename, lineno, format, args); \
va_end(args); \
} while (0)
get_filename_lineno(type, &filename, &lineno);
va_start(args, format);
zend_error_va_list(type, filename, lineno, format, args);
va_end(args);
ZEND_API ZEND_COLD void zend_error(int type, const char *format, ...) {
zend_error_impl(type, format);
}
ZEND_API ZEND_COLD void zend_error_unchecked(int type, const char *format, ...) {
zend_string *filename;
uint32_t lineno;
va_list args;
get_filename_lineno(type, &filename, &lineno);
va_start(args, format);
zend_error_va_list(type, filename, lineno, format, args);
va_end(args);
zend_error_impl(type, format);
}
ZEND_API ZEND_COLD ZEND_NORETURN void zend_error_at_noreturn(
@ -1636,18 +1632,26 @@ ZEND_API ZEND_COLD ZEND_NORETURN void zend_error_at_noreturn(
abort();
}
#define zend_error_noreturn_impl(type, format) do { \
zend_string *filename; \
uint32_t lineno; \
va_list args; \
get_filename_lineno(type, &filename, &lineno); \
va_start(args, format); \
zend_error_va_list(type, filename, lineno, format, args); \
va_end(args); \
/* Should never reach this. */ \
abort(); \
} while (0)
ZEND_API ZEND_COLD ZEND_NORETURN void zend_error_noreturn(int type, const char *format, ...)
{
zend_string *filename;
uint32_t lineno;
va_list args;
zend_error_noreturn_impl(type, format);
}
get_filename_lineno(type, &filename, &lineno);
va_start(args, format);
zend_error_va_list(type, filename, lineno, format, args);
va_end(args);
/* Should never reach this. */
abort();
ZEND_API ZEND_COLD ZEND_NORETURN void zend_error_noreturn_unchecked(int type, const char *format, ...)
{
zend_error_noreturn_impl(type, format);
}
ZEND_API ZEND_COLD ZEND_NORETURN void zend_strerror_noreturn(int type, int errn, const char *message)

View file

@ -345,6 +345,7 @@ extern ZEND_API void (*zend_post_shutdown_cb)(void);
ZEND_API ZEND_COLD void zend_error(int type, const char *format, ...) ZEND_ATTRIBUTE_FORMAT(printf, 2, 3);
ZEND_API ZEND_COLD ZEND_NORETURN void zend_error_noreturn(int type, const char *format, ...) ZEND_ATTRIBUTE_FORMAT(printf, 2, 3);
ZEND_API ZEND_COLD ZEND_NORETURN void zend_error_noreturn_unchecked(int type, const char *format, ...);
/* For custom format specifiers like H */
ZEND_API ZEND_COLD void zend_error_unchecked(int type, const char *format, ...);
/* If filename is NULL the default filename is used. */

View file

@ -4970,7 +4970,7 @@ static void zend_compile_static_var(zend_ast *ast) /* {{{ */
}
if (zend_hash_exists(CG(active_op_array)->static_variables, var_name)) {
zend_error_noreturn(E_COMPILE_ERROR, "Duplicate declaration of static variable $%s", ZSTR_VAL(var_name));
zend_error_noreturn_unchecked(E_COMPILE_ERROR, "Duplicate declaration of static variable $%S", var_name);
}
zend_eval_const_expr(&ast->child[1]);
@ -7245,8 +7245,8 @@ static void zend_compile_closure_binding(znode *closure, zend_op_array *op_array
value = zend_hash_add(op_array->static_variables, var_name, &EG(uninitialized_zval));
if (!value) {
zend_error_noreturn(E_COMPILE_ERROR,
"Cannot use variable $%s twice", ZSTR_VAL(var_name));
zend_error_noreturn_unchecked(E_COMPILE_ERROR,
"Cannot use variable $%S twice", var_name);
}
CG(zend_lineno) = zend_ast_get_lineno(var_name_ast);
@ -7378,8 +7378,8 @@ static void zend_compile_closure_uses(zend_ast *ast) /* {{{ */
int i;
for (i = 0; i < op_array->last_var; i++) {
if (zend_string_equals(op_array->vars[i], var_name)) {
zend_error_noreturn(E_COMPILE_ERROR,
"Cannot use lexical variable $%s as a parameter name", ZSTR_VAL(var_name));
zend_error_noreturn_unchecked(E_COMPILE_ERROR,
"Cannot use lexical variable $%S as a parameter name", var_name);
}
}
}

View file

@ -272,7 +272,7 @@ static zend_never_inline ZEND_COLD zval* zval_undefined_cv(uint32_t var EXECUTE_
{
if (EXPECTED(EG(exception) == NULL)) {
zend_string *cv = CV_DEF_OF(EX_VAR_TO_NUM(var));
zend_error(E_WARNING, "Undefined variable $%s", ZSTR_VAL(cv));
zend_error_unchecked(E_WARNING, "Undefined variable $%S", cv);
}
return &EG(uninitialized_zval);
}
@ -3788,7 +3788,7 @@ static zend_never_inline void zend_fetch_this_var(int type OPLINE_DC EXECUTE_DAT
Z_ADDREF_P(result);
} else {
ZVAL_NULL(result);
zend_error(E_WARNING, "Undefined variable $this");
zend_error_unchecked(E_WARNING, "Undefined variable $this");
}
break;
case BP_VAR_IS:

View file

@ -1755,8 +1755,8 @@ ZEND_VM_C_LABEL(fetch_this):
/* Keep name alive in case an error handler tries to free it. */
zend_string_addref(name);
}
zend_error(E_WARNING, "Undefined %svariable $%s",
(opline->extended_value & ZEND_FETCH_GLOBAL ? "global " : ""), ZSTR_VAL(name));
zend_error_unchecked(E_WARNING, "Undefined %svariable $%S",
(opline->extended_value & ZEND_FETCH_GLOBAL ? "global " : ""), name);
if (type == BP_VAR_RW && !EG(exception)) {
retval = zend_hash_update(target_symbol_table, name, &EG(uninitialized_zval));
} else {
@ -1778,8 +1778,8 @@ ZEND_VM_C_LABEL(fetch_this):
} else if (type == BP_VAR_IS || type == BP_VAR_UNSET) {
retval = &EG(uninitialized_zval);
} else {
zend_error(E_WARNING, "Undefined %svariable $%s",
(opline->extended_value & ZEND_FETCH_GLOBAL ? "global " : ""), ZSTR_VAL(name));
zend_error_unchecked(E_WARNING, "Undefined %svariable $%S",
(opline->extended_value & ZEND_FETCH_GLOBAL ? "global " : ""), name);
if (type == BP_VAR_RW && !EG(exception)) {
ZVAL_NULL(retval);
} else {

24
Zend/zend_vm_execute.h generated
View file

@ -10109,8 +10109,8 @@ fetch_this:
/* Keep name alive in case an error handler tries to free it. */
zend_string_addref(name);
}
zend_error(E_WARNING, "Undefined %svariable $%s",
(opline->extended_value & ZEND_FETCH_GLOBAL ? "global " : ""), ZSTR_VAL(name));
zend_error_unchecked(E_WARNING, "Undefined %svariable $%S",
(opline->extended_value & ZEND_FETCH_GLOBAL ? "global " : ""), name);
if (type == BP_VAR_RW && !EG(exception)) {
retval = zend_hash_update(target_symbol_table, name, &EG(uninitialized_zval));
} else {
@ -10132,8 +10132,8 @@ fetch_this:
} else if (type == BP_VAR_IS || type == BP_VAR_UNSET) {
retval = &EG(uninitialized_zval);
} else {
zend_error(E_WARNING, "Undefined %svariable $%s",
(opline->extended_value & ZEND_FETCH_GLOBAL ? "global " : ""), ZSTR_VAL(name));
zend_error_unchecked(E_WARNING, "Undefined %svariable $%S",
(opline->extended_value & ZEND_FETCH_GLOBAL ? "global " : ""), name);
if (type == BP_VAR_RW && !EG(exception)) {
ZVAL_NULL(retval);
} else {
@ -17954,8 +17954,8 @@ fetch_this:
/* Keep name alive in case an error handler tries to free it. */
zend_string_addref(name);
}
zend_error(E_WARNING, "Undefined %svariable $%s",
(opline->extended_value & ZEND_FETCH_GLOBAL ? "global " : ""), ZSTR_VAL(name));
zend_error_unchecked(E_WARNING, "Undefined %svariable $%S",
(opline->extended_value & ZEND_FETCH_GLOBAL ? "global " : ""), name);
if (type == BP_VAR_RW && !EG(exception)) {
retval = zend_hash_update(target_symbol_table, name, &EG(uninitialized_zval));
} else {
@ -17977,8 +17977,8 @@ fetch_this:
} else if (type == BP_VAR_IS || type == BP_VAR_UNSET) {
retval = &EG(uninitialized_zval);
} else {
zend_error(E_WARNING, "Undefined %svariable $%s",
(opline->extended_value & ZEND_FETCH_GLOBAL ? "global " : ""), ZSTR_VAL(name));
zend_error_unchecked(E_WARNING, "Undefined %svariable $%S",
(opline->extended_value & ZEND_FETCH_GLOBAL ? "global " : ""), name);
if (type == BP_VAR_RW && !EG(exception)) {
ZVAL_NULL(retval);
} else {
@ -48342,8 +48342,8 @@ fetch_this:
/* Keep name alive in case an error handler tries to free it. */
zend_string_addref(name);
}
zend_error(E_WARNING, "Undefined %svariable $%s",
(opline->extended_value & ZEND_FETCH_GLOBAL ? "global " : ""), ZSTR_VAL(name));
zend_error_unchecked(E_WARNING, "Undefined %svariable $%S",
(opline->extended_value & ZEND_FETCH_GLOBAL ? "global " : ""), name);
if (type == BP_VAR_RW && !EG(exception)) {
retval = zend_hash_update(target_symbol_table, name, &EG(uninitialized_zval));
} else {
@ -48365,8 +48365,8 @@ fetch_this:
} else if (type == BP_VAR_IS || type == BP_VAR_UNSET) {
retval = &EG(uninitialized_zval);
} else {
zend_error(E_WARNING, "Undefined %svariable $%s",
(opline->extended_value & ZEND_FETCH_GLOBAL ? "global " : ""), ZSTR_VAL(name));
zend_error_unchecked(E_WARNING, "Undefined %svariable $%S",
(opline->extended_value & ZEND_FETCH_GLOBAL ? "global " : ""), name);
if (type == BP_VAR_RW && !EG(exception)) {
ZVAL_NULL(retval);
} else {

View file

@ -95,7 +95,7 @@ static ZEND_COLD void ZEND_FASTCALL zend_jit_invalid_method_call(zval *object)
if (Z_TYPE_P(object) == IS_UNDEF && opline->op1_type == IS_CV) {
zend_string *cv = EX(func)->op_array.vars[EX_VAR_TO_NUM(opline->op1.var)];
zend_error(E_WARNING, "Undefined variable $%s", ZSTR_VAL(cv));
zend_error_unchecked(E_WARNING, "Undefined variable $%S", cv);
if (UNEXPECTED(EG(exception) != NULL)) {
return;
}
@ -340,7 +340,7 @@ static int ZEND_FASTCALL zend_jit_undefined_op_helper(uint32_t var)
const zend_execute_data *execute_data = EG(current_execute_data);
zend_string *cv = EX(func)->op_array.vars[EX_VAR_TO_NUM(var)];
zend_error(E_WARNING, "Undefined variable $%s", ZSTR_VAL(cv));
zend_error_unchecked(E_WARNING, "Undefined variable $%S", cv);
return EG(exception) == NULL;
}
@ -354,7 +354,7 @@ static int ZEND_FASTCALL zend_jit_undefined_op_helper_write(HashTable *ht, uint3
if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE)) {
GC_ADDREF(ht);
}
zend_error(E_WARNING, "Undefined variable $%s", ZSTR_VAL(cv));
zend_error_unchecked(E_WARNING, "Undefined variable $%S", cv);
if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE) && GC_DELREF(ht) != 1) {
if (!GC_REFCOUNT(ht)) {
zend_array_destroy(ht);
@ -2418,7 +2418,7 @@ static void ZEND_FASTCALL zend_jit_invalid_property_incdec(zval *container, cons
if (Z_TYPE_P(container) == IS_UNDEF && opline->op1_type == IS_CV) {
zend_string *cv = EX(func)->op_array.vars[EX_VAR_TO_NUM(opline->op1.var)];
zend_error(E_WARNING, "Undefined variable $%s", ZSTR_VAL(cv));
zend_error_unchecked(E_WARNING, "Undefined variable $%S", cv);
}
if (opline->result_type & (IS_VAR|IS_TMP_VAR)) {
ZVAL_UNDEF(EX_VAR(opline->result.var));

View file

@ -4646,7 +4646,7 @@ static int zend_jit_inc_dec(zend_jit_ctx *jit, const zend_op *opline, uint32_t o
if_def = jit_if_not_Z_TYPE(jit, op1_addr, IS_UNDEF);
ir_IF_FALSE_cold(if_def);
// zend_error(E_WARNING, "Undefined variable $%s", ZSTR_VAL(CV_DEF_OF(EX_VAR_TO_NUM(opline->op1.var))));
// zend_error_unchecked(E_WARNING, "Undefined variable $%S", CV_DEF_OF(EX_VAR_TO_NUM(opline->op1.var)));
ir_CALL_1(IR_VOID, ir_CONST_FC_FUNC(zend_jit_undefined_op_helper), ir_CONST_U32(opline->op1.var));
jit_set_Z_TYPE_INFO(jit, op1_def_addr, IS_NULL);
@ -5588,7 +5588,7 @@ static int zend_jit_long_math_helper(zend_jit_ctx *jit,
if_def = jit_if_not_Z_TYPE(jit, op1_addr, IS_UNDEF);
ir_IF_FALSE_cold(if_def);
// zend_error(E_WARNING, "Undefined variable $%s", ZSTR_VAL(CV_DEF_OF(EX_VAR_TO_NUM(opline->op1.var))));
// zend_error_unchecked(E_WARNING, "Undefined variable $%S", CV_DEF_OF(EX_VAR_TO_NUM(opline->op1.var)));
ir_CALL_1(IR_VOID, ir_CONST_FC_FUNC(zend_jit_undefined_op_helper), ir_CONST_U32(opline->op1.var));
ref2 = jit_EG(uninitialized_zval);
@ -5604,7 +5604,7 @@ static int zend_jit_long_math_helper(zend_jit_ctx *jit,
if_def = jit_if_not_Z_TYPE(jit, op2_addr, IS_UNDEF);
ir_IF_FALSE_cold(if_def);
// zend_error(E_WARNING, "Undefined variable $%s", ZSTR_VAL(CV_DEF_OF(EX_VAR_TO_NUM(opline->op2.var))));
// zend_error_unchecked(E_WARNING, "Undefined variable $%S", CV_DEF_OF(EX_VAR_TO_NUM(opline->op2.var)));
ir_CALL_1(IR_VOID, ir_CONST_FC_FUNC(zend_jit_undefined_op_helper), ir_CONST_U32(opline->op2.var));
ref2 = jit_EG(uninitialized_zval);
@ -5980,7 +5980,7 @@ static int zend_jit_simple_assign(zend_jit_ctx *jit,
jit_SET_EX_OPLINE(jit, opline);
ZEND_ASSERT(Z_MODE(val_addr) == IS_MEM_ZVAL);
// zend_error(E_WARNING, "Undefined variable $%s", ZSTR_VAL(CV_DEF_OF(EX_VAR_TO_NUM(opline->op1.var))));
// zend_error_unchecked(E_WARNING, "Undefined variable $%S", CV_DEF_OF(EX_VAR_TO_NUM(opline->op1.var)));
ret = ir_CALL_1(IR_I32, ir_CONST_FC_FUNC(zend_jit_undefined_op_helper), ir_CONST_U32(Z_OFFSET(val_addr)));
if (check_exception) {

View file

@ -2608,7 +2608,7 @@ static void php_compact_var(HashTable *eg_active_symbol_table, zval *return_valu
zend_hash_update(Z_ARRVAL_P(return_value), Z_STR_P(entry), &data);
}
} else {
php_error_docref(NULL, E_WARNING, "Undefined variable $%s", ZSTR_VAL(Z_STR_P(entry)));
php_error_docref_unchecked(NULL, E_WARNING, "Undefined variable $%S", Z_STR_P(entry));
}
} else if (Z_TYPE_P(entry) == IS_ARRAY) {
if (Z_REFCOUNTED_P(entry)) {

View file

@ -944,11 +944,10 @@ static zend_string *escape_html(const char *buffer, size_t buffer_len) {
*/
PHPAPI ZEND_COLD void php_verror(const char *docref, const char *params, int type, const char *format, va_list args)
{
zend_string *replace_buffer = NULL, *replace_origin = NULL;
char *buffer = NULL, *docref_buf = NULL, *target = NULL;
zend_string *replace_origin = NULL;
char *docref_buf = NULL, *target = NULL;
char *docref_target = "", *docref_root = "";
char *p;
int buffer_len = 0;
const char *space = "";
const char *class_name = "";
const char *function;
@ -958,18 +957,16 @@ PHPAPI ZEND_COLD void php_verror(const char *docref, const char *params, int typ
int is_function = 0;
/* get error text into buffer and escape for html if necessary */
buffer_len = (int)vspprintf(&buffer, 0, format, args);
zend_string *buffer = vstrpprintf(0, format, args);
if (PG(html_errors)) {
replace_buffer = escape_html(buffer, buffer_len);
efree(buffer);
zend_string *replace_buffer = escape_html(ZSTR_VAL(buffer), ZSTR_LEN(buffer));
zend_string_free(buffer);
if (replace_buffer) {
buffer = ZSTR_VAL(replace_buffer);
buffer_len = (int)ZSTR_LEN(replace_buffer);
buffer = replace_buffer;
} else {
buffer = "";
buffer_len = 0;
buffer = zend_empty_string;
}
}
@ -1091,15 +1088,15 @@ PHPAPI ZEND_COLD void php_verror(const char *docref, const char *params, int typ
}
/* display html formatted or only show the additional links */
if (PG(html_errors)) {
message = zend_strpprintf(0, "%s [<a href='%s%s%s'>%s</a>]: %s", origin, docref_root, docref, docref_target, docref, buffer);
message = zend_strpprintf_unchecked(0, "%s [<a href='%s%s%s'>%s</a>]: %S", origin, docref_root, docref, docref_target, docref, buffer);
} else {
message = zend_strpprintf(0, "%s [%s%s%s]: %s", origin, docref_root, docref, docref_target, buffer);
message = zend_strpprintf_unchecked(0, "%s [%s%s%s]: %S", origin, docref_root, docref, docref_target, buffer);
}
if (target) {
efree(target);
}
} else {
message = zend_strpprintf(0, "%s: %s", origin, buffer);
message = zend_strpprintf_unchecked(0, "%s: %S", origin, buffer);
}
if (replace_origin) {
zend_string_free(replace_origin);
@ -1110,11 +1107,7 @@ PHPAPI ZEND_COLD void php_verror(const char *docref, const char *params, int typ
efree(docref_buf);
}
if (replace_buffer) {
zend_string_free(replace_buffer);
} else {
efree(buffer);
}
zend_string_free(buffer);
zend_error_zstr(type, message);
zend_string_release(message);
@ -1123,13 +1116,21 @@ PHPAPI ZEND_COLD void php_verror(const char *docref, const char *params, int typ
/* {{{ php_error_docref */
/* Generate an error which links to docref or the php.net documentation if docref is NULL */
#define php_error_docref_impl(docref, type, format) do {\
va_list args; \
va_start(args, format); \
php_verror(docref, "", type, format, args); \
va_end(args); \
} while (0)
PHPAPI ZEND_COLD void php_error_docref(const char *docref, int type, const char *format, ...)
{
va_list args;
php_error_docref_impl(docref, type, format);
}
va_start(args, format);
php_verror(docref, "", type, format, args);
va_end(args);
PHPAPI ZEND_COLD void php_error_docref_unchecked(const char *docref, int type, const char *format, ...)
{
php_error_docref_impl(docref, type, format);
}
/* }}} */

View file

@ -328,6 +328,7 @@ PHPAPI ZEND_COLD void php_verror(const char *docref, const char *params, int typ
/* PHPAPI void php_error(int type, const char *format, ...); */
PHPAPI ZEND_COLD void php_error_docref(const char *docref, int type, const char *format, ...)
PHP_ATTRIBUTE_FORMAT(printf, 3, 4);
PHPAPI ZEND_COLD void php_error_docref_unchecked(const char *docref, int type, const char *format, ...);
PHPAPI ZEND_COLD void php_error_docref1(const char *docref, const char *param1, int type, const char *format, ...)
PHP_ATTRIBUTE_FORMAT(printf, 4, 5);
PHPAPI ZEND_COLD void php_error_docref2(const char *docref, const char *param1, const char *param2, int type, const char *format, ...)