[php-src] PR #24340: Fix out-of-bounds reads parsing ftp:// wrapper server replies
| From: | alepacheco | Date: | Thu, 15 Oct 2026 02:50:31 +0000 |
| Subject: | [php-src] PR #24340: Fix out-of-bounds reads parsing ftp:// wrapper server replies | ||
| Groups: | php.git-pulls | ||
| Request: | Send a blank email to git-pulls+get-39454@lists.php.net to get a copy of this message | ||
Pull Request: https://github.com/php/php-src/pull/24340
Author: alepacheco
The ftp:// wrapper reads server replies into a 512-byte
tmp_line
and then parses them at fixed offsets and sizes that are not tied to the line actually received:
- get_ftp_result() returns the code of whatever line it read last when the stream ends,
even if that line is not a complete ddd status line. The callers then parse from
tmp_line + 4 (PASV/EPSV, SIZE,
MDTM), which for a short line such as 213 is past its terminating NUL.
- php_fopen_do_pasv() copies a fixed ip_size bytes from the dotted address
inside tmp_line, so an address near the end of the buffer is read past
tmp_line, and an over-long one is silently truncated to a different address.
- The MDTM handler in php_stream_ftp_url_stat() skips non-digits without
stopping at the NUL, and its bound check uses > instead of >=, so
sscanf() can start at tmp_line + sizeof(tmp_line).
This change makes get_ftp_result() return a code only for a complete status line (and 0
otherwise), which keeps every tmp_line + 4 reader inside the received line;
php_fopen_do_pasv() now copies exactly the parsed address and rejects one that does not
fit; and the MDTM scan stops at the end of the line.
Every caller already treats 0 as a failure (it is what get_ftp_result() returned when
nothing could be read at all). The only visible change is for a server that ends the connection
after a reply without the space after the code, such as a bare 226, which RFC 959 does
not allow; that reply used to be accepted from the stale buffer after the read timed out.
#13717 enlarges the ip buffers; this change bounds the copy itself, so it is
independent of the buffer size.
The tests add server.inc replies for two cases: a PASV reply whose address
ends at the buffer limit, which fopen() now rejects (the old read past the buffer shows
under ASan); and an MDTM reply without digits following a longer SIZE
reply, for which stat() now reports an unknown mtime instead of the timestamp left in
the buffer by the previous reply.
Targeting PHP-8.4 as the oldest branch with the bug in active support.