You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Internal exceptions may misinterpret the null byte \0 #10810
Fatal error: Uncaught Exception: Hello in /tmp/preview:3
Stack trace:
#0 {main}
thrown in /tmp/preview on line 3
But I expected this output instead:
Fatal error: Uncaught Exception: HelloWorld in /tmp/preview:3
Stack trace:
#0 {main}
thrown in /tmp/preview on line 3
or
Fatal error: Uncaught Exception: Hello\x00World in /tmp/preview:3
Stack trace:
#0 {main}
thrown in /tmp/preview on line 3
Notes
Thanks to this strange behavior, I discovered that Zend strings are perfectly able to handle the \0 character.
But to call the function zend_throw_exception(zend_class_entry *exception_ce, const char *message, zend_long code), some callers may convert a zend string to a raw C string, losing the actual size. Then, the newly created zend string will stop at the first \0, loosing the end of the string.
I didn't found any risk with this bug, since sensitive functions seem to look explicitly for this kind of abuse. Just to notice that some error messages may be truncated if they contain this null byte.
@TimWolla you're right, I didn't spot this issue with __toString, but I'm not sure it's the exact same problem, because zend_throw_exception doesn't call __toString 🤔
But it's similar, when an exception is converted to string, the message is transmitted as a char* in zend_strpprintf, losing the length stored in the zend string 😕
This is actually relatively annoying to fix. zend_strpprintf provides only one way to not stop on \0 characters, namely %Z. However, %Z is not a standard printf argument (obviously) and thus compilers will complain about mismatched placeholder/arg counts. Changing behavior for width (%*s) or precision (%.*s) are both dangerously breaking, potentially leading to a buffer overflow. Using php_formatted_print isn't impossible. However, the code isn't used anywhere else internally, and it operates on zvals. The same problem can also occur in most logs that use zend_string, so a specific fix for just this case doesn't seem sensible.
PHP supports "\0" in double-quoted strings, not because it is handled as a special character, but because it is an octal representation.
"\0" is the octal equivalent of the hexadecimal "\x00". (nonsignificant zeroes can be omitted, so \x0 is also valid, but I wouldn't recommend it, as we represent bytes, which consist of 2 hexadecimal digits)
I would not recommend using the octal "\0", as it may be confusing.
For example, "\042" results to the same as "\42" or "\x22", not to the "\0" . "42" that some people may have been expecting.
Activity
TimWolla commentedon Mar 8, 2023
As I've noted on phpc.social, the issue is with
__toString(). The$messageproperty holds the correct value:https://3v4l.org/Kt5QS
b-viguier commentedon Mar 9, 2023
@TimWolla you're right, I didn't spot this issue with
__toString, but I'm not sure it's the exact same problem, becausezend_throw_exceptiondoesn't call__toString🤔But it's similar, when an exception is converted to string, the message is transmitted as a
char*inzend_strpprintf, losing the length stored in the zend string 😕php-src/Zend/zend_exceptions.c
Line 682 in afd8695
mvorisek commentedon Mar 9, 2023
userland
sprintfcan handle zend_string with null byte - https://3v4l.org/dGqpR, can zend_string be used withzend_strpprintfdirectly?iluuu1994 commentedon Mar 13, 2023
This is actually relatively annoying to fix.
zend_strpprintfprovides only one way to not stop on\0characters, namely%Z. However,%Zis not a standard printf argument (obviously) and thus compilers will complain about mismatched placeholder/arg counts. Changing behavior for width (%*s) or precision (%.*s) are both dangerously breaking, potentially leading to a buffer overflow. Usingphp_formatted_printisn't impossible. However, the code isn't used anywhere else internally, and it operates onzvals. The same problem can also occur in most logs that usezend_string, so a specific fix for just this case doesn't seem sensible.iluuu1994 commentedon Mar 17, 2023
I missed that there's already an
_uncheckedvariant for these cases. #10873Fix NUL byte in exception string terminating Exception::__toString()
vlakoff commentedon Jul 12, 2024
PHP supports
"\0"in double-quoted strings, not because it is handled as a special character, but because it is an octal representation."\0"is the octal equivalent of the hexadecimal"\x00". (nonsignificant zeroes can be omitted, so\x0is also valid, but I wouldn't recommend it, as we represent bytes, which consist of 2 hexadecimal digits)I would not recommend using the octal
"\0", as it may be confusing.For example,
"\042"results to the same as"\42"or"\x22", not to the"\0" . "42"that some people may have been expecting.