diff --git a/Zend/tests/gh10810.phpt b/Zend/tests/gh10810.phpt new file mode 100644 index 000000000000..2539d5c7b40d --- /dev/null +++ b/Zend/tests/gh10810.phpt @@ -0,0 +1,10 @@ +--TEST-- +GH-10810: Fix NUL byte terminating Exception::__toString() +--FILE-- + +--EXPECTF-- +Exception: Hello%0World in %s:%d +Stack trace: +#0 {main} diff --git a/Zend/zend_exceptions.c b/Zend/zend_exceptions.c index 7ccdaeea4431..89d75627e3f2 100644 --- a/Zend/zend_exceptions.c +++ b/Zend/zend_exceptions.c @@ -680,24 +680,36 @@ ZEND_METHOD(Exception, __toString) } if ((Z_OBJCE_P(exception) == zend_ce_type_error || Z_OBJCE_P(exception) == zend_ce_argument_count_error) && strstr(ZSTR_VAL(message), ", called in ")) { - zend_string *real_message = zend_strpprintf(0, "%s and defined", ZSTR_VAL(message)); + zval message_zv; + ZVAL_STR(&message_zv, message); + zend_string *real_message = zend_strpprintf_unchecked(0, "%Z and defined", &message_zv); zend_string_release_ex(message, 0); message = real_message; } + zend_string *tmp_trace = (Z_TYPE(trace) == IS_STRING && Z_STRLEN(trace)) + ? zend_string_copy(Z_STR(trace)) + : ZSTR_INIT_LITERAL("#0 {main}\n", false); + + zval name_zv, trace_zv, file_zv, prev_str_zv; + ZVAL_STR(&name_zv, Z_OBJCE_P(exception)->name); + ZVAL_STR(&trace_zv, tmp_trace); + ZVAL_STR(&file_zv, file); + ZVAL_STR(&prev_str_zv, prev_str); + if (ZSTR_LEN(message) > 0) { - str = zend_strpprintf(0, "%s: %s in %s:" ZEND_LONG_FMT - "\nStack trace:\n%s%s%s", - ZSTR_VAL(Z_OBJCE_P(exception)->name), ZSTR_VAL(message), ZSTR_VAL(file), line, - (Z_TYPE(trace) == IS_STRING && Z_STRLEN(trace)) ? Z_STRVAL(trace) : "#0 {main}\n", - ZSTR_LEN(prev_str) ? "\n\nNext " : "", ZSTR_VAL(prev_str)); + zval message_zv; + ZVAL_STR(&message_zv, message); + + str = zend_strpprintf_unchecked(0, "%Z: %Z in %Z:" ZEND_LONG_FMT "\nStack trace:\n%Z%s%Z", + &name_zv, &message_zv, &file_zv, line, + &trace_zv, ZSTR_LEN(prev_str) ? "\n\nNext " : "", &prev_str_zv); } else { - str = zend_strpprintf(0, "%s in %s:" ZEND_LONG_FMT - "\nStack trace:\n%s%s%s", - ZSTR_VAL(Z_OBJCE_P(exception)->name), ZSTR_VAL(file), line, - (Z_TYPE(trace) == IS_STRING && Z_STRLEN(trace)) ? Z_STRVAL(trace) : "#0 {main}\n", - ZSTR_LEN(prev_str) ? "\n\nNext " : "", ZSTR_VAL(prev_str)); + str = zend_strpprintf_unchecked(0, "%Z in %Z:" ZEND_LONG_FMT "\nStack trace:\n%Z%s%Z", + &name_zv, &file_zv, line, + &trace_zv, ZSTR_LEN(prev_str) ? "\n\nNext " : "", &prev_str_zv); } + zend_string_release_ex(tmp_trace, false); zend_string_release_ex(prev_str, 0); zend_string_release_ex(message, 0); diff --git a/Zend/zend_string.h b/Zend/zend_string.h index 472bf6bbfde6..32acba9f21ce 100644 --- a/Zend/zend_string.h +++ b/Zend/zend_string.h @@ -108,6 +108,8 @@ END_EXTERN_C() #define ZSTR_ALLOCA_FREE(str, use_heap) free_alloca(str, use_heap) +#define ZSTR_INIT_LITERAL(s, persistent) (zend_string_init((s), strlen(s), (persistent))) + /*---*/ static zend_always_inline zend_ulong zend_string_hash_val(zend_string *s)