php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #65667 ftp_nb_continue produces segfault
Submitted: 2013-09-13 16:11 UTC Modified: 2013-09-17 08:36 UTC
Votes:1
Avg. Score:4.0 ± 0.0
Reproduced:1 of 1 (100.0%)
Same Version:0 (0.0%)
Same OS:0 (0.0%)
From: imprec at gmail dot com Assigned:
Status: Closed Package: FTP related
PHP Version: 5.5.3 OS: OSX
Private report: No CVE-ID:
 [2013-09-13 16:11 UTC] imprec at gmail dot com
Description:
------------
Running the following code on my OSX produces a segfault, see backtrace 

(gdb) (gdb) Starting program: /usr/local/bin/php /Users/romain/ftp-script.php
Reading symbols for shared libraries 
+++++++++++++++++++++............................................... done
Reading symbols for shared libraries ...................... done
Reading symbols for shared libraries .. done
Reading symbols for shared libraries .. done
Reading symbols for shared libraries ....... done
Reading symbols for shared libraries ..... done
Reading symbols for shared libraries . done
Reading symbols for shared libraries . done

Program received signal EXC_BAD_ACCESS, Could not access memory.
Reason: 13 at address: 0x0000000000000000
0x0000000100387a75 in _php_stream_write ()
(gdb) #0  0x0000000100387a75 in _php_stream_write ()
No symbol table info available.
#1  0x000000010013ab6f in ftp_nb_continue_read ()
No symbol table info available.
#2  0x0000000100137c2c in zif_ftp_nb_continue ()
No symbol table info available.
#3  0x00000001003bf524 in dtrace_execute_internal ()
No symbol table info available.
#4  0x00000001004430c2 in zend_do_fcall_common_helper_SPEC ()
No symbol table info available.
#5  0x00000001003f310a in execute_ex ()
No symbol table info available.
#6  0x00000001003bf458 in dtrace_execute_ex ()
No symbol table info available.
#7  0x00000001003ce7ac in zend_execute_scripts ()
No symbol table info available.
#8  0x0000000100374602 in php_execute_script ()
No symbol table info available.
#9  0x0000000100467075 in do_cli ()
No symbol table info available.
#10 0x0000000100465e3d in main ()
No symbol table info available.
(gdb) %                          

Test script:
---------------
$connection = ftp_connect('hostname', 21);
ftp_login($connection, 'login', 'password');

$localfile = __DIR__ . '/ftpfile';
$start = file_exists($localfile) ? FTP_AUTORESUME : 0;

$result = ftp_nb_get($connection, $localfile, '/remotedir/remotefile', FTP_BINARY, $start);

while ($result == FTP_MOREDATA) {
    $result = ftp_nb_continue($connection);
}

ftp_close($connection);


Expected result:
----------------
No seg fault

Actual result:
--------------
A seg fault

Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2013-09-17 00:58 UTC] felipe@php.net
-Status: Open +Status: Feedback
 [2013-09-17 00:58 UTC] felipe@php.net
Hi, when seeing the backtrace on gdb, please run the following commands (and post the results):
p ftp
p data
p rcvd

Thanks.
 [2013-09-17 08:36 UTC] imprec at gmail dot com
-Status: Feedback +Status: Open
 [2013-09-17 08:36 UTC] imprec at gmail dot com
Well, not so much chance :(


(gdb) run
Starting program: /usr/local/bin/php 
/Users/romain/Documents/workspace/Phraseanet/ftp.php
Reading symbols for shared libraries 
+++++++++++++++++++++............................................... done
Reading symbols for shared libraries ...................... done
Reading symbols for shared libraries .. done
Reading symbols for shared libraries .. done
Reading symbols for shared libraries ....... done
Reading symbols for shared libraries ..... done
Reading symbols for shared libraries . done
Reading symbols for shared libraries . done
bt full
Program received signal EXC_BAD_ACCESS, Could not access memory.
Reason: 13 at address: 0x0000000000000000
0x0000000100387a75 in _php_stream_write ()
(gdb) bt full
#0  0x0000000100387a75 in _php_stream_write ()
No symbol table info available.
#1  0x000000010013ab6f in ftp_nb_continue_read ()
No symbol table info available.
#2  0x0000000100137c2c in zif_ftp_nb_continue ()
No symbol table info available.
#3  0x00000001003bf524 in dtrace_execute_internal ()
No symbol table info available.
#4  0x00000001004430c2 in zend_do_fcall_common_helper_SPEC ()
No symbol table info available.
#5  0x00000001003f310a in execute_ex ()
No symbol table info available.
#6  0x00000001003bf458 in dtrace_execute_ex ()
No symbol table info available.
#7  0x00000001003ce7ac in zend_execute_scripts ()
No symbol table info available.
#8  0x0000000100374602 in php_execute_script ()
No symbol table info available.
#9  0x0000000100467075 in do_cli ()
No symbol table info available.
#10 0x0000000100465e3d in main ()
No symbol table info available.
(gdb) p ftp
No symbol "ftp" in current context.
(gdb) p data
No symbol "data" in current context.
(gdb) p rcvd
No symbol "rcvd" in current context.
(gdb) 


Whereas my PHP is compiled with debug :

PHP 5.5.3 (cli) (built: Sep 12 2013 02:41:16) (DEBUG)
Copyright (c) 1997-2013 The PHP Group
Zend Engine v2.5.0, Copyright (c) 1998-2013 Zend Technologies
 [2013-10-02 05:52 UTC] phofstetter at sensational dot ch
I can confirm this to happen on Linux too.

Also in 5.4.20 (5.4.16 was fine) and 5.5.4
 [2013-10-02 06:00 UTC] phofstetter at sensational dot ch
Here's a bit of poking around in gdb:

Program received signal SIGSEGV, Segmentation fault.
0x000000000070080d in _php_stream_write (stream=0x18eecb8,
    buf=0x19511b4 "\243\060\060\060\060\060\060\060\061\243\r\nPAD\243\060\243\060\060\062\063\071\060\243\060\060\067\065\063\061\243\060\060\060\060\060\060\060\061\243\r\nPAD\243\060\243\060\060\062\063\071\060\243\060\060\067\063\066\061\243\060\060\060\060\060\060\060\062\243\r\nPAD\243\060\243\060\060\062\063\071\060\243\060\060\064\065\070\060\243\060\060\060\060\060\060\062\071\243\r\nPAD\243\060\243\060\060\062\063\071\060\243\060\060\067\060\060\066\243\060\060\060\060\060\060\060\063\243\r\nPAD\243\060\243\060\060\062\063\071\060\243\060\060\060\063\061\061\243\060\060\060\060\060\060\060\065\243\r\nPAD\243\060\243\060\060\062\063\071\060\243\060\060\066\063\061\065\243\060\060\060\060\060\060\060\066\243\r\nPA"..., count=1352) at /home/crazyhat/popscan-deb/downloads/php-5.5.4/main/streams/streams.c:1233
warning: Source file is more recent than executable.
1233        if (buf == NULL || count == 0 || stream->ops->write == NULL) {
(gdb) p count
$1 = 1352
(gdb) p stream
$2 = (php_stream *) 0x18eecb8
(gdb) p stream->ops
$3 = (php_stream_ops *) 0x0
(gdb)

stream->ops seems to be NULL
 [2013-10-02 06:14 UTC] phofstetter at sensational dot ch
and here's one stack frame higher (giving you the data you requested):

(gdb) p ftp
$1 = (ftpbuf_t *) 0x7ffff7fcf1f8
(gdb) p ftp->stream
$2 = (php_stream *) 0x7ffff7fceb78
(gdb) p data
$3 = (databuf_t *) 0x7ffff7fd1388
(gdb) p ftp->stream->ops
$4 = (php_stream_ops *) 0x0

Again, something is wrong with that stream.
 [2013-10-02 06:30 UTC] phofstetter at sensational dot ch
I think the bug was introduced in 

https://github.com/php/php-src/commit/a93a462dcefd62e07963dd2da506fbb3409c88b5

where php_stream_close(outstream); is called unconditionally. Thus any further ftp_nb_continue() will work on a stream that has already been closed.

When I restore the behaviour pre-patch, the segfault goes away. 

I will create a proper pull request and attach it to this bug.
 [2013-10-02 06:40 UTC] phofstetter at sensational dot ch
Ok. Official Pull-Request submitted here:

https://github.com/php/php-src/pull/478

Sorry for the spam. I initially just wanted to confirm the issue, but then I felt compelled to dig deeper and deeper, commenting more and more :-)
 [2013-10-04 15:28 UTC] nikic@php.net
Automatic comment on behalf of phofstetter@sensational.ch
Revision: http://git.php.net/?p=php-src.git;a=commit;h=96cc419924c38874f9e2f2e5ccf3cd0430d90f43
Log: Fix bug #65667: ftp_nb_continue produces segfault
 [2013-10-04 15:28 UTC] nikic@php.net
-Status: Open +Status: Closed
 [2013-12-30 01:38 UTC] Terry at ellisons dot org dot uk
The fix posted as https://github.com/php/php-src/commit/96cc419924c38874f9e2f2e5ccf3cd0430d90f43 stops the segfault, but doesn't fix the underlying bug.  This can be seen by running ext/ftp/tests/ftp_nb_get_large.phpt
    [Sun Dec 29 19:05:11 2013]  Script:  '.../ftp_nb_get_large.php'
    .../main/streams/streams.c(292) :  Freeing 0x7FF7EB20F9C0 (248 bytes),
        script=.../ext/ftp/tests/ftp_nb_get_large.php
    .../ext/ftp/php_ftp.c(938) : Actual location (location was relayed)
    === Total 1 memory leaks detected

The output stream is stored in ftp->stream by ftp_nb_get() called from PHP_FUNCTION(ftp_nb_get) at ext/ftp/php_ftp.c:964.  On the path ret == PHP_FTP_FINISHED the stream is then explicitly closed, but in the case where ret == PHP_FTP_MOREDATA it is now left open to allow further writes by subsequent PHP_FUNCTION(ftp_nb_continue) calls.

Explicitly closing the stream is a botch after it has been assigned to the ftp resource; it should be closed by the ftp DTOR ftp_destructor_ftpbuf(), that is ftp.c:ftp_close(ftpbuf_t *ftp) should include the test

	if (ftp->stream) {
		php_stream_close(ftp->stream);
		ftp->stream = NULL;
	}

and any early closes of the steam, eg. ftp.c:900, 906, 965, 972, 1005, 1157, 1161, 1216 should either also set ftp->stream to NULL or leave the close to the DTOR.
 
PHP Copyright © 2001-2014 The PHP Group
All rights reserved.
Last updated: Wed Apr 23 09:02:23 2014 UTC