Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ PHP NEWS

- FTP:
. Fix double free in ext/ftp data_accept() on accept failure. (alepacheco)
. Fixed ftp_nb_put() not aborting a pending non-blocking transfer, which
leaked its data connection. (Jakub Kozub)

- MBString:
. Fixed bug GH-23958 (truncated encoding names resolving to full names). (Kamil Tekiela)
Expand Down
8 changes: 8 additions & 0 deletions ext/ftp/ftp.c
Original file line number Diff line number Diff line change
Expand Up @@ -2329,6 +2329,14 @@ ftp_nb_put(ftpbuf_t *ftp, const char *path, const size_t path_len, php_stream *i
php_error_docref(NULL, E_WARNING, "FTP\\Connection is already in use");
return PHP_FTP_FAILED;
}
if (ftp->data != NULL) {
/* If there is a transfer in action, abort it.
* If we don't, we get an invalid state and memory leaks when the new connection gets opened. */
data_close(ftp);
if (!ftp_getresp(ftp) || (ftp->resp != 226 && ftp->resp != 250)) {
goto bail;
}
}
if (!ftp_type(ftp, type)) {
goto bail;
}
Expand Down
42 changes: 42 additions & 0 deletions ext/ftp/tests/ftp_nb_put_during_nb_transfer.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
--TEST--
ftp_nb_put() must abort a pending non-blocking transfer, like ftp_nb_get() does
--EXTENSIONS--
ftp
pcntl
--FILE--
<?php
require 'server.inc';

$ftp = ftp_connect('127.0.0.1', $port);
if (!$ftp) die("Couldn't connect to the server");
var_dump(ftp_login($ftp, 'user', 'pass'));

$local_file = __DIR__ . DIRECTORY_SEPARATOR . "ftp_nb_put_during_nb_transfer.txt";
$fout = fopen($local_file, "w");

/* Requests more data, but we deliberately do not loop to fetch it, so a
* transfer is still pending when the upload below starts. */
var_dump(ftp_nb_fget($ftp, $fout, "fget", FTP_BINARY, 0) === FTP_MOREDATA);
fclose($fout);

/* Without the guard in ftp_nb_put() this reuses ftp->data while the previous
* databuf_t is still owned by it, and reads the pending transfer's response
* as the answer to its own command. */
$ret = ftp_nb_put($ftp, basename(__FILE__), __FILE__, FTP_ASCII);
while ($ret == FTP_MOREDATA) {
$ret = ftp_nb_continue($ftp);
}
var_dump($ret === FTP_FINISHED);

ftp_close($ftp);
echo "closed\n";
?>
--CLEAN--
<?php
@unlink(__DIR__ . DIRECTORY_SEPARATOR . "ftp_nb_put_during_nb_transfer.txt");
?>
--EXPECT--
bool(true)
bool(true)
bool(true)
closed
Loading