Skip to content

Reject NUL bytes in the SimpleXMLElement constructor - #23069

Merged
iliaal merged 1 commit into
php:masterfrom
iliaal:fix/sxe-ctor-nul-path
Aug 9, 2026
Merged

Reject NUL bytes in the SimpleXMLElement constructor#23069
iliaal merged 1 commit into
php:masterfrom
iliaal:fix/sxe-ctor-nul-path

Conversation

@iliaal

@iliaal iliaal commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

With dataIsURL set, SimpleXMLElement::__construct hands its first argument to xmlReadFile as a C string, so new SimpleXMLElement("/tmp/ok.xml\0anything", 0, true) truncates at the NUL and quietly loads /tmp/ok.xml. simplexml_load_file() and SimpleXMLElement::asXML() already declare their path argument as a path; the constructor took a plain string, so the check never ran.

Declaring it as a path applies to the non-URL form too. At default options current libxml already rejects an embedded NUL there, so that form gains a ValueError in place of a parse failure. With LIBXML_RECOVER, or on older libxml such as 2.9.14, the string was accepted and is now rejected.

@iliaal
iliaal requested a review from devnexen as a code owner August 5, 2026 13:27
iliaal added a commit to iliaal/php-src that referenced this pull request Aug 5, 2026
When dataIsURL is true, __construct parsed the path as a plain string
and passed it to xmlReadFile, so an embedded NUL truncated the path.
simplexml_load_file already rejects NULs via the path parameter type.
Match that check before xmlReadFile.

Closes phpGH-23069
@iliaal
iliaal force-pushed the fix/sxe-ctor-nul-path branch from 49c4d81 to 3f8efc2 Compare August 5, 2026 13:28
@devnexen

devnexen commented Aug 5, 2026

Copy link
Copy Markdown
Member

should target master (thus no more CHECK_NULL_PATH)

Comment thread ext/simplexml/simplexml.c Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This should just use a P ZPP specifier.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done in b423d89, with lowercase p to keep the existing char*/size_t locals, matching simplexml_load_file() and asXML() in the same file.

It covers the non-URL form too, where the NUL truncates just as visibly: "<r/>\0evil" parsed as <r/> and dropped the rest, while the same string without the NUL is rejected. UPGRADING entry added for that.

@iliaal
iliaal force-pushed the fix/sxe-ctor-nul-path branch from 3f8efc2 to c6d000b Compare August 9, 2026 13:55
iliaal added a commit to iliaal/php-src that referenced this pull request Aug 9, 2026
The first argument was parsed with the s specifier and handed to
xmlReadFile as a C string when dataIsURL is set, so
new SimpleXMLElement("/tmp/ok.xml\0evil", 0, true) quietly loaded
/tmp/ok.xml. simplexml_load_file() and SimpleXMLElement::asXML() already
declare their path argument with p; the constructor now does the same.

Closes phpGH-23069
@iliaal
iliaal changed the base branch from PHP-8.4 to master August 9, 2026 13:55
iliaal added a commit to iliaal/php-src that referenced this pull request Aug 9, 2026
The first argument was parsed with the s specifier, so a NUL byte
truncated it: with dataIsURL set xmlReadFile got a C string and
new SimpleXMLElement("/tmp/ok.xml\0evil", 0, true) quietly loaded
/tmp/ok.xml, and without it libxml stopped at the NUL and dropped the
rest of the document. simplexml_load_file() and
SimpleXMLElement::asXML() already declare their path argument with p;
the constructor now does the same.

Closes phpGH-23069
@iliaal
iliaal force-pushed the fix/sxe-ctor-nul-path branch from c6d000b to b423d89 Compare August 9, 2026 14:04
@iliaal

iliaal commented Aug 9, 2026

Copy link
Copy Markdown
Contributor Author

Retargeted and rebased onto master in b423d89.

@iliaal iliaal changed the title Reject embedded NUL in SimpleXMLElement path/URL constructor Reject NUL bytes in the SimpleXMLElement constructor Aug 9, 2026
@iliaal
iliaal removed the request for review from adoy August 9, 2026 14:10
@iliaal
iliaal requested review from Girgias and removed request for SakiTakamachi, TimWolla, arnaud-lb, dstogov and kocsismate August 9, 2026 14:10

@devnexen devnexen left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

UPGRADING is wrong about data mode. data_len goes to xmlReadMemory, so nothing truncated; on baseline "\0evil" and "evil" both throw Exception: String could not be parsed as XML. This changes Exception to ValueError, not silence to error.

And p can't key off $dataIsURL, so new SimpleXMLElement("\0evil") now throws while simplexml_load_string() still returns false.

iliaal added a commit to iliaal/php-src that referenced this pull request Aug 9, 2026
With dataIsURL set the first argument goes to xmlReadFile as a C string,
so new SimpleXMLElement("/tmp/ok.xml\0evil", 0, true) truncates at the NUL
and quietly loads /tmp/ok.xml. simplexml_load_file() and
SimpleXMLElement::asXML() already declare their path argument as a path;
the constructor took a plain string, so the check never ran.

Closes phpGH-23069
@iliaal
iliaal force-pushed the fix/sxe-ctor-nul-path branch from b423d89 to 7b464e7 Compare August 9, 2026 15:49
iliaal added a commit to iliaal/php-src that referenced this pull request Aug 9, 2026
With dataIsURL set the first argument goes to xmlReadFile as a C string,
so new SimpleXMLElement("/tmp/ok.xml\0evil", 0, true) truncates at the NUL
and quietly loads /tmp/ok.xml. simplexml_load_file() and
SimpleXMLElement::asXML() already declare their path argument as a path;
the constructor took a plain string, so the check never ran.

Closes phpGH-23069
@iliaal
iliaal force-pushed the fix/sxe-ctor-nul-path branch from 7b464e7 to 14a60a6 Compare August 9, 2026 16:02
@iliaal

iliaal commented Aug 9, 2026

Copy link
Copy Markdown
Contributor Author

Ugh, the UPGRADING wording was wrong, rewritten in 14a60a6.

We measured different things because it is libxml dependent: on 2.9.14 "<r/>\0evil" parses as <r/> while "<r/>evil" throws, on 2.13.9 both throw at default options. Your second point holds further than I first read it, though: with LIBXML_RECOVER even 2.13.9 accepts "<r/>\0evil", so the specifier does make the constructor reject a string that simplexml_load_string() still returns an element for, on current libxml.

If that divergence is the blocker, the alternative is checking only when dataIsURL is set, the way ext/dom guards DOM_LOAD_FILE.

@iliaal
iliaal requested a review from devnexen August 9, 2026 16:30
With dataIsURL set the first argument goes to xmlReadFile as a C string,
so new SimpleXMLElement("/tmp/ok.xml\0evil", 0, true) truncates at the NUL
and quietly loads /tmp/ok.xml. simplexml_load_file() and
SimpleXMLElement::asXML() already declare their path argument as a path;
the constructor took a plain string, so the check never ran.

Closes phpGH-23069
@iliaal
iliaal force-pushed the fix/sxe-ctor-nul-path branch from 14a60a6 to fbe772c Compare August 9, 2026 18:20
@iliaal
iliaal merged commit bda5a49 into php:master Aug 9, 2026
18 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants