Commit ba8c7aa4772 for php
commit ba8c7aa4772fb9852c4b413cee55d30bc416c2c3
Author: Weilin Du <weilindu@php.net>
Date: Tue Nov 3 21:01:40 2026 +0800
ext/standard: Decode quoted-printable input after NUL bytes
diff --git a/NEWS b/NEWS
index 673437eec83..4cc663d5a7e 100644
--- a/NEWS
+++ b/NEWS
@@ -157,6 +157,7 @@ PHP NEWS
rmdir(), touch(), chmod() or stat(). (Ilia Alshanetsky)
- Standard:
+ . Fixed quoted_printable_decode() truncating input at NUL bytes. (Weilin Du)
. Fixed bug #60110 (fclose(), file_put_contents(), copy() do not return false
properly). (Jakub Zelenka, Ilija Tovilo)
. Fixed three Windows-only proc_open() defects: an uninitialized
diff --git a/ext/standard/quot_print.c b/ext/standard/quot_print.c
index e9d8fafb998..2c478842b80 100644
--- a/ext/standard/quot_print.c
+++ b/ext/standard/quot_print.c
@@ -194,22 +194,24 @@ PHP_FUNCTION(quoted_printable_decode)
char *str_in;
zend_string *str_out;
size_t i = 0, j = 0, k;
+ size_t len;
ZEND_PARSE_PARAMETERS_START(1, 1)
Z_PARAM_STR(arg1)
ZEND_PARSE_PARAMETERS_END();
- if (ZSTR_LEN(arg1) == 0) {
+ len = ZSTR_LEN(arg1);
+ if (len == 0) {
/* shortcut */
RETURN_EMPTY_STRING();
}
str_in = ZSTR_VAL(arg1);
- str_out = zend_string_alloc(ZSTR_LEN(arg1), 0);
- while (str_in[i]) {
+ str_out = zend_string_alloc(len, 0);
+ while (i < len) {
switch (str_in[i]) {
case '=':
- if (str_in[i + 1] && str_in[i + 2] &&
+ if (len - i > 2 &&
isxdigit((unsigned char)str_in[i + 1]) &&
isxdigit((unsigned char)str_in[i + 2]))
{
@@ -218,15 +220,15 @@ PHP_FUNCTION(quoted_printable_decode)
i += 3;
} else /* check for soft line break according to RFC 2045*/ {
k = 1;
- while (str_in[i + k] && ((str_in[i + k] == 32) || (str_in[i + k] == 9))) {
+ while (k < len - i && ((str_in[i + k] == 32) || (str_in[i + k] == 9))) {
/* Possibly, skip spaces/tabs at the end of line */
k++;
}
- if (!str_in[i + k]) {
+ if (k == len - i) {
/* End of line reached */
i += k;
}
- else if ((str_in[i + k] == 13) && (str_in[i + k + 1] == 10)) {
+ else if ((str_in[i + k] == 13) && k + 1 < len - i && (str_in[i + k + 1] == 10)) {
/* CRLF */
i += k + 2;
}
diff --git a/ext/standard/tests/strings/quoted_printable_decode_null_bytes.phpt b/ext/standard/tests/strings/quoted_printable_decode_null_bytes.phpt
new file mode 100644
index 00000000000..0f5724bb987
--- /dev/null
+++ b/ext/standard/tests/strings/quoted_printable_decode_null_bytes.phpt
@@ -0,0 +1,37 @@
+--TEST--
+quoted_printable_decode() processes input after NUL bytes
+--FILE--
+<?php
+$input = "abc\0d=41f";
+$stream = fopen('php://temp', 'w+');
+fwrite($stream, $input);
+rewind($stream);
+stream_filter_append($stream, 'convert.quoted-printable-decode', STREAM_FILTER_READ);
+var_dump(quoted_printable_decode($input) === stream_get_contents($stream));
+fclose($stream);
+
+foreach ([
+ "abc\0def",
+ 'A=00B',
+ "A\0=42",
+ "A=\0B",
+ "A= \0B",
+ "A=\r\n\0B",
+ "A=0\0B",
+ 'A=',
+ 'A= ',
+] as $input) {
+ echo bin2hex(quoted_printable_decode($input)), "\n";
+}
+?>
+--EXPECT--
+bool(true)
+61626300646566
+410042
+410042
+413d0042
+413d200042
+410042
+413d300042
+41
+41