Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
29 changes: 29 additions & 0 deletions NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ PHP NEWS
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
?? ??? ????, PHP 8.4.27

- CLI
. Fix GH-22567 (Windows ZTS CLI SAPI should refresh its TSRMLS cache during
request activation). (matyhtf)

- DOM:
. Fixed use-after-free when re-constructing a DOMXPath whose php:function
registrations are freed while still reachable from the cycle collector.
Expand All @@ -13,6 +17,8 @@ PHP NEWS
. Fixed Dom\HTMLDocument giving attributes the namespace of their element
when a fragment is parsed with an xlink, xml or xmlns context element.
(Ilia Alshanetsky)
. Fixed bug GH-23729 (DOMXPath::__construct() use-after-free during an
evaluation). (David Carlier)

- Intl:
. Fixed cloning IntlDateFormatter and MessageFormatter losing PHP-side state
Expand All @@ -28,10 +34,20 @@ PHP NEWS
. Fixed bug GH-23106 (mb_strpos() reads past the end of a haystack ending in
a truncated UTF-8 sequence). (Lazizbek Ergashev)

- Opcache:
. Fixed OSS-Fuzz #546798343 (Heap-buffer-overflow in optimizer with
FCCs and inlining). (ndossche)
. Fixed bug GH-23693 (Tracing JIT produces wrong results for a guard on a
loop-invariant addition). (Ilia Alshanetsky)

- PDO:
. Fixed PDOStatement::getColumnMeta() reading out of bounds for an invalid
column index. (Ilia Alshanetsky)

- Readline:
. Fixed a heap over-read in the interactive shell prompt when cli.prompt is
set to an empty string. (Ilia Alshanetsky)

- Sockets:
. Fixed socket_select() silently truncating sets larger than FD_SETSIZE on
Windows. (David Carlier)
Expand All @@ -40,9 +56,22 @@ PHP NEWS
. Fixed a crash when SQLite3::close() is called from a userland callback.
(Ilia Alshanetsky)

- Standard:
. Fixed three Windows-only proc_open() defects: an uninitialized
PROCESS_INFORMATION, an indeterminate comspec pointer after a failed
lookup, and an unchecked CreateFileA() failure. (Ilia Alshanetsky)
. Fixed out-of-bounds read when parsing a malformed MX answer in
dns_get_mx(). (Ilia Alshanetsky)

- XSL:
. Fixed bug GH-23730 (use-after-free when XSLTProcessor::importStylesheet()
is called during a transformation). (David Carlier)

- Zip:
. Fixed ZipArchive::extractTo() ignoring files given in a non-list array.
(David Carlier)
. Fixed bug GH-23747 (ZipArchive::close() use-after-free from a progress or
cancel callback). (David Carlier)


24 Sep 2026, PHP 8.4.26
Expand Down
1 change: 1 addition & 0 deletions Zend/Optimizer/optimize_func_calls.c
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ static void zend_delete_call_instructions(zend_op_array *op_array, zend_op *opli
case ZEND_DO_ICALL:
case ZEND_DO_UCALL:
case ZEND_DO_FCALL_BY_NAME:
case ZEND_CALLABLE_CONVERT:
call++;
break;
case ZEND_SEND_VAL:
Expand Down
1 change: 1 addition & 0 deletions ext/dom/php_dom.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ extern zend_module_entry dom_module_entry;

typedef struct dom_xpath_object {
php_dom_xpath_callbacks xpath_callbacks;
uint32_t evaluation_depth;
bool register_node_ns;
dom_object dom;
} dom_xpath_object;
Expand Down
73 changes: 73 additions & 0 deletions ext/dom/tests/gh23729.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
--TEST--
GH-23729 (Use-after-free when DOMXPath is reconstructed during an evaluation)
--CREDITS--
djarfluka
--EXTENSIONS--
dom
--FILE--
<?php

function reconstruct() {
try {
$GLOBALS['xpath']->__construct($GLOBALS['other']);
} catch (Error $e) {
echo $e::class, ': ', $e->getMessage(), PHP_EOL;
}
return 'r';
}

function nested() {
echo 'nested: ', $GLOBALS['xpath']->evaluate('string(/root/b)'), PHP_EOL;
return 'n';
}

function test(string $class, object $doc, object $other) {
$xpath = new $class($doc);
$xpath->registerNamespace('php', 'http://php.net/xpath');
$xpath->registerPhpFunctions();

$GLOBALS['xpath'] = $xpath;
$GLOBALS['other'] = $other;

var_dump($xpath->evaluate('string(php:function("reconstruct"))'));
/* The evaluation the callback tried to destroy must still be usable. */
var_dump($xpath->evaluate('string(/root/a)'));
/* A nested evaluation must not lift the guard of the outer one. */
var_dump($xpath->evaluate('concat(php:function("nested"), php:function("reconstruct"))'));
var_dump($xpath->query('//b[php:function("reconstruct")]')->length);

/* Reconstructing outside of an evaluation is still allowed. */
$xpath->__construct($other);
var_dump($xpath->document->documentElement->nodeName);
}

$doc = new DOMDocument();
$doc->loadXML('<root><a>1</a><b>2</b></root>');
$other = new DOMDocument();
$other->loadXML('<other/>');
test(DOMXPath::class, $doc, $other);

$doc = Dom\XMLDocument::createFromString('<root><a>1</a><b>2</b></root>');
$other = Dom\XMLDocument::createFromString('<other/>');
test(Dom\XPath::class, $doc, $other);

?>
--EXPECT--
Error: Cannot call DOMXPath::__construct() while an XPath evaluation is in progress
string(1) "r"
string(1) "1"
nested: 2
Error: Cannot call DOMXPath::__construct() while an XPath evaluation is in progress
string(2) "nr"
Error: Cannot call DOMXPath::__construct() while an XPath evaluation is in progress
int(1)
string(5) "other"
Error: Cannot call Dom\XPath::__construct() while an XPath evaluation is in progress
string(1) "r"
string(1) "1"
nested: 2
Error: Cannot call Dom\XPath::__construct() while an XPath evaluation is in progress
string(2) "nr"
Error: Cannot call Dom\XPath::__construct() while an XPath evaluation is in progress
int(1)
string(5) "other"
10 changes: 9 additions & 1 deletion ext/dom/xpath.c
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,13 @@ static void dom_xpath_construct(INTERNAL_FUNCTION_PARAMETERS, zend_class_entry *
RETURN_THROWS();
}

dom_xpath_object *intern = Z_XPATHOBJ_P(ZEND_THIS);
if (UNEXPECTED(intern->evaluation_depth > 0)) {
zend_throw_error(NULL, "Cannot call %s::__construct() while an XPath evaluation is in progress",
ZSTR_VAL(Z_OBJCE_P(ZEND_THIS)->name));
RETURN_THROWS();
}

DOM_GET_OBJ(docp, doc, xmlDocPtr, docobj);

xmlXPathContextPtr ctx = xmlXPathNewContext(docp);
Expand All @@ -134,7 +141,6 @@ static void dom_xpath_construct(INTERNAL_FUNCTION_PARAMETERS, zend_class_entry *
RETURN_THROWS();
}

dom_xpath_object *intern = Z_XPATHOBJ_P(ZEND_THIS);
xmlXPathContextPtr oldctx = intern->dom.ptr;
if (oldctx != NULL) {
php_libxml_decrement_doc_ref((php_libxml_node_object *) &intern->dom);
Expand Down Expand Up @@ -301,7 +307,9 @@ static void php_xpath_eval(INTERNAL_FUNCTION_PARAMETERS, int type, bool modern)
ctxp->nsNr = in_scope_ns.count;
}

intern->evaluation_depth++;
xmlXPathObjectPtr xpathobjp = xmlXPathEvalExpression(BAD_CAST expr, ctxp);
intern->evaluation_depth--;
ctxp->node = NULL;

if (register_node_ns && nodep != NULL) {
Expand Down
19 changes: 18 additions & 1 deletion ext/opcache/jit/ir/ir_x86.dasc
Original file line number Diff line number Diff line change
Expand Up @@ -1923,6 +1923,21 @@ static bool ir_match_has_mem_deps(ir_ctx *ctx, ir_ref ref, ir_ref root)
return 0;
}

/* A naive check if anything that emits code, and so clobbers the flags, is
* scheduled between the flags setting instruction and the fusion root */
static bool ir_match_has_flags_deps(ir_ctx *ctx, ir_ref ref, ir_ref root)
{
ir_ref pos = ctx->prev_ref[root];

while (pos > ref) {
if (ctx->ir_base[pos].op != IR_SNAPSHOT) {
return 1;
}
pos = ctx->prev_ref[pos];
}
return pos != ref;
}

static void ir_match_fuse_load(ir_ctx *ctx, ir_ref ref, ir_ref root)
{
if (ir_in_same_block(ctx, ref) &&
Expand Down Expand Up @@ -3089,7 +3104,9 @@ store_int:
if (IR_IS_CONST_REF(op2_insn->op2)
&& !IR_IS_SYM_CONST(ctx->ir_base[op2_insn->op2].op)
&& ctx->ir_base[op2_insn->op2].val.i64 == 0) {
if (op2_insn->op1 == insn->op2 - 1) { /* previous instruction */
if (op2_insn->op1 == insn->op2 - 1 /* previous instruction */
&& ir_in_same_block(ctx, op2_insn->op1)
&& !ir_match_has_flags_deps(ctx, insn->op2, ref)) {
ir_insn *op1_insn = &ctx->ir_base[op2_insn->op1];

if ((op1_insn->op == IR_OR || op1_insn->op == IR_AND || op1_insn->op == IR_XOR) ||
Expand Down
41 changes: 41 additions & 0 deletions ext/opcache/tests/jit/gh23693.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
--TEST--
GH-23693: Tracing JIT reads stale flags for a guard on a hoisted addition
--INI--
opcache.enable=1
opcache.enable_cli=1
opcache.jit_buffer_size=64M
opcache.jit=tracing
opcache.jit_hot_func=1
--EXTENSIONS--
opcache
--FILE--
<?php
function f(int $pos, int $n): int {
if ($pos < 0) {
return -1;
}
$y = $pos >> 2;
$s = 0;
for ($dy = -1; $dy <= 1; ++$dy) {
for ($i = 0; $i < $n; ++$i) {
$ny = $y + $dy;
if ($ny < 0) {
continue;
}
if ($ny >= 4) {
continue;
}
$s += ($ny << 2) | ($i & 3);
}
}
return $s;
}
for ($k = 0; $k < 30; ++$k) {
f(16, 200);
}
var_dump(f(16, 200));
var_dump(f(0, 200));
?>
--EXPECT--
int(2700)
int(1400)
19 changes: 19 additions & 0 deletions ext/opcache/tests/opt/oss_fuzz_546798343.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
--TEST--
OSS-Fuzz #546798343 (Heap-buffer-overflow in zend_delete_call_instructions with callable conversion)
--EXTENSIONS--
opcache
--INI--
opcache.enable=1
opcache.enable_cli=1
--FILE--
<?php

$x = function() {};
gonnaBeInlined($x(...));
function gonnaBeInlined($foo) {
}

echo "Done";
?>
--EXPECT--
Done
8 changes: 4 additions & 4 deletions ext/readline/readline_cli.c
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ static zend_string *cli_get_prompt(char *block, char prompt) /* {{{ */
char *prompt_spec = CLIR_G(prompt) ? CLIR_G(prompt) : DEFAULT_PROMPT;
bool unicode_warned = false;

do {
while (*prompt_spec) {
if (*prompt_spec == '\\') {
switch (prompt_spec[1]) {
case '\\':
Expand Down Expand Up @@ -198,9 +198,9 @@ static zend_string *cli_get_prompt(char *block, char prompt) /* {{{ */
smart_str_appendc(&retval, '?');
}
}
} while (++prompt_spec && *prompt_spec);
smart_str_0(&retval);
return retval.s;
++prompt_spec;
}
return smart_str_extract(&retval);
}
/* }}} */

Expand Down
8 changes: 8 additions & 0 deletions ext/standard/dns.c
Original file line number Diff line number Diff line change
Expand Up @@ -1120,13 +1120,21 @@ PHP_FUNCTION(dns_get_mx)
RETURN_FALSE;
}
cp += i;
if (cp + 10 > end) {
php_dns_free_handle(handle);
RETURN_FALSE;
}
GETSHORT(type, cp);
cp += INT16SZ + INT32SZ;
GETSHORT(i, cp);
if (type != DNS_T_MX) {
cp += i;
continue;
}
if (cp + 2 > end) {
php_dns_free_handle(handle);
RETURN_FALSE;
}
GETSHORT(weight, cp);
if ((i = dn_expand(answer.qb2, end, cp, buf, sizeof(buf)-1)) < 0) {
php_dns_free_handle(handle);
Expand Down
6 changes: 3 additions & 3 deletions ext/standard/proc_open.c
Original file line number Diff line number Diff line change
Expand Up @@ -695,7 +695,7 @@ static void init_startup_info(STARTUPINFOW *si, descriptorspec_item *descriptors

static void init_process_info(PROCESS_INFORMATION *pi)
{
memset(&pi, 0, sizeof(pi));
memset(pi, 0, sizeof(*pi));
}

/* on success, returns length of *comspec, which then needs to be efree'd by caller */
Expand Down Expand Up @@ -746,7 +746,7 @@ static size_t find_comspec_nt(wchar_t **comspec)

static zend_result convert_command_to_use_shell(wchar_t **cmdw, size_t cmdw_len)
{
wchar_t *comspec;
wchar_t *comspec = NULL;
size_t len = find_comspec_nt(&comspec);
if (len == 0) {
php_error_docref(NULL, E_WARNING, "Command conversion failed");
Expand Down Expand Up @@ -829,7 +829,7 @@ static zend_result set_proc_descriptor_to_blackhole(descriptorspec_item *desc)
#ifdef PHP_WIN32
desc->childend = CreateFileA("nul", GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
if (desc->childend == NULL) {
if (desc->childend == INVALID_HANDLE_VALUE) {
php_error_docref(NULL, E_WARNING, "Failed to open nul");
return FAILURE;
}
Expand Down
1 change: 1 addition & 0 deletions ext/xsl/php_xsl.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ extern zend_module_entry xsl_module_entry;
typedef struct xsl_object {
void *ptr;
HashTable *parameter;
uint32_t transform_depth;
bool hasKeys;
php_libxml_ref_obj *sheet_ref_obj;
zend_long securityPrefs;
Expand Down
Loading
Loading