[php-src] PR #24478: ext/ftp: clear ftp->nb when ftp_nb_get() bails out

From: 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)

« previous php.git-pulls (#39657) next »