Skip to content

ext/standard: reject a dechunk chunk size that overflows size_t#171

Closed
iliaal wants to merge 4 commits into
PHP-8.4from
std-ss007-dechunk-size-overflow
Closed

ext/standard: reject a dechunk chunk size that overflows size_t#171
iliaal wants to merge 4 commits into
PHP-8.4from
std-ss007-dechunk-size-overflow

Conversation

@iliaal

@iliaal iliaal commented Jul 16, 2026

Copy link
Copy Markdown
Owner

php_dechunk() accumulated the hex chunk size with chunk_size * 16 + digit and never checked the multiply. A size of 10000000000000000 is 2^64, which wraps to 0, and 0 reads as the terminating chunk, so the filter stops and drops the body it was handed:

// before
string(0) ""                     // 10000000000000000\nBODYDATA\n0\n
// after
string(13) "0\nBODYDATA\n0\n"    // CHUNK_ERROR, passed through like other malformed sizes

The guard is hoisted out of the three digit branches rather than repeated in each, which needs the digit value computed first. SIZE_MAX / 16 is sufficient for the add as well: the largest value that survives it is SIZE_MAX / 16, and SIZE_MAX / 16 * 16 + 15 is exactly SIZE_MAX.

Floor: PHP-8.4.

@iliaal
iliaal force-pushed the std-ss007-dechunk-size-overflow branch from e742195 to 70bcfdb Compare July 16, 2026 18:56
@iliaal iliaal changed the title ext/standard: reject oversized hex sizes in dechunk filter ext/standard: reject a dechunk chunk size that overflows size_t Jul 16, 2026
@iliaal
iliaal force-pushed the std-ss007-dechunk-size-overflow branch from 70bcfdb to 48610c3 Compare July 16, 2026 20:10
iliaal added 4 commits July 16, 2026 21:58
make_http_soap_request() looked for the reason phrase with strstr(tmp, " ")
even when the first strstr() found no space, which happens for a status line
carrying a version but no status code, such as "HTTP/1.1". tmp is NULL there,
so strstr() dereferenced it and the client crashed.

Only parse the reason phrase when a status code field was present.

Closes phpGH-22761
php_ftp_dirstream_read() sized the basename copy with
MIN(sizeof(ent->d_name), ZSTR_LEN(basename) - 1) and wrote the terminator at
[tmp_len - 1]. An empty listing line leaves a basename of length 1, so
tmp_len is 0 and the NUL lands one byte before d_name; a slash-only line
leaves length 0, so the subtraction underflows to SIZE_MAX and memcpy() reads
4096 bytes past the interned empty string. The same off-by-one truncated
entry names that do not end in CRLF.

Closes phpGH-22768
odbc_sqlconnect() stripped a trailing semicolon by writing a NUL into
the Z_PARAM_STRING buffer, which is a zend_string. Shared variables,
interned literals, and const values used as the DSN were permanently
altered (strlen unchanged, last byte became NUL). Compute the base
length instead and pass it to spprintf with %.*s, matching the
non-mutating approach used when assembling UID/PWD into the connect
string.

Closes phpGH-22776
php_dechunk() accumulated the hex chunk size with chunk_size * 16 + digit and
never checked the multiply, so a size of 2^64 wrapped to 0. A zero size reads
as the terminating chunk, so the filter stopped there and dropped the body it
had been handed. Error out instead, as the other malformed-size cases already
do.
@iliaal
iliaal force-pushed the std-ss007-dechunk-size-overflow branch from 48610c3 to 36ba49e Compare July 17, 2026 14:04
@iliaal

iliaal commented Jul 17, 2026

Copy link
Copy Markdown
Owner Author

Promoted upstream: php#22786.

@iliaal iliaal closed this Jul 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant