[php-src] PR #24478: ext/ftp: clear ftp->nb when ftp_nb_get() bails out
| From: | kubakozub | Date: | Wed, 21 Oct 2026 11:45:00 +0000 |
| Subject: | [php-src] PR #24478: ext/ftp: clear ftp->nb when ftp_nb_get() bails out | ||
| Groups: | php.git-pulls | ||
| Request: | Send a blank email to git-pulls+get-39657@lists.php.net to get a copy of this message | ||
Pull Request: https://github.com/php/php-src/pull/24478
Author: kubakozub
### What
ftp_nb_get() sets ftp->nb = 1 only on success, but its
bail: path left the flag set.
Whether that path is taken is decided by the server: the guard at the top of the function
aborts a pending transfer and then requires response 226 or 250.
After such a bail, PHP_FUNCTION(ftp_nb_get) has already set ftp->stream
to NULL and closed
the stream it opened, while ftp->closestream stays true.
ftp_nb_continue() then passes its
!ftp->nb gate and reaches
```c
if (ret != PHP_FTP_MOREDATA && ftp->closestream) {
php_stream_close(ftp->stream);
```
dereferencing the cleared pointer.
ftp_nb_continue_read()'s own bail: already clears the flag; this
makes ftp_nb_get()
consistent with it.
### Crash
```
nb_get(a) => FTP_MOREDATA
nb_get(b) => FTP_FAILED
now calling ftp_nb_continue()
Segmentation fault
```
```
_php_stream_free (...) at main/streams/streams.c:407
407 context = PHP_STREAM_CONTEXT(stream);
#1 zif_ftp_nb_continue (...) at ext/ftp/php_ftp.c:863
```
On a stripped build it faults earlier, in ftp_nb_continue_read() on
data->fd
(mov 0x4(%r12),%esi with r12 == 0; offsetof(databuf_t, fd) is
4). Adding a NULL check
there alone only moves the crash to php_ftp.c:863 - I tried that first and it still
exited
139 - so the flag is the right place to fix it.
### Test
ext/ftp/tests/ftp_nb_get_rejected_abort.phpt, with a server.inc case that
answers the
aborted transfer with 451. Without the change the test process dies at the
ftp_nb_continue()
call; with it the call reports there is nothing to continue. Full ext/ftp suite: 62
passed,
0 failed, 4 skipped.
### Note
php_ftp.c:862 could also take the ftp->stream && test that
ftp.c:167 already uses, and
on this path the first transfer's stream is dropped without being closed. I left both alone to
keep the change minimal; happy to add either.
🤖 Generated with [Claude Code](https://claude.com/claude-code)