php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #78231 Segmentation fault upon stream_socket_accept of exported socket-to-stream
Submitted: 2019-06-29 06:05 UTC Modified: 2019-07-03 10:38 UTC
From: asmqb7 at gmail dot com Assigned: nikic (profile)
Status: Closed Package: Sockets related
PHP Version: 7.2.20, 7.3.6 OS: Linux
Private report: No CVE-ID: None
 [2019-06-29 06:05 UTC] asmqb7 at gmail dot com
Description:
------------
Due to lack of documentation about the fact that SO_REUSEADDR is unconditionally set when stream_socket_server() is used, I thought I would need to use the socket_* functions to set the option on a stream I'd already created.

I figured I could then export the newly-bound socket to a stream, and proceed from there without needing to use any other socket_* functions, which I try to avoid using as I've been bitten by the sockets extension in the past (other bugs etc).

Well, I'm not quite sure what I broke this time, but the attached test script segfaults on my system with 100% consistency. I suspect the code path that handles "stream bound by the sockets library, exported, then selected on by the streams subsystem" is probably un[der?]implemented.

It's not visible yet, but I added some info to the notes section at http://php.net/socket_set_option, which is what shows up most prominently when googling SO_REUSEADDR in the context of PHP.

Test script:
---------------
<?php
$port = 55555;

$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
print '$socket: '; var_dump($socket);

print 'socket_bind(): ';
var_dump(socket_bind($socket, '127.0.0.1', $port));

$listening_fd = socket_export_stream($socket);
print 'socket_export_stream(): ';
var_dump($listening_fd);

for (;;) {
  $read = $write = $except = [];
  $read[] = $listening_fd;
  
  stream_select($read, $write, $except, null);
  
  foreach ($read as $fd) {
    switch ($fd) {
      case $listening_fd:
        print "stream_socket_accept() on: "; var_dump($fd);
        $new_fd = stream_socket_accept($fd);
    }
  }
}


Expected result:
----------------
For a stream exported from a socket to behave identically to a stream created by the streams functionality, and particularly for untested codepaths to not cause runtime segfaults.

Actual result:
--------------
$socket: resource(4) of type (Socket)
socket_bind(): bool(true)
socket_export_stream(): resource(5) of type (stream)
stream_socket_accept() on: resource(5) of type (stream)

Program received signal SIGSEGV, Segmentation fault.
0x00005555558ce3b6 in php_stream_context_get_option ()
(gdb) bt
#0  0x00005555558ce3b6 in php_stream_context_get_option ()
#1  0x00005555556df1bc in ?? ()
#2  0x00005555558cc72d in _php_stream_set_option ()
#3  0x00005555558d6560 in php_stream_xport_accept ()
#4  0x0000555555884378 in ?? ()
#5  0x0000555555994a85 in execute_ex ()
#6  0x000055555599aaf6 in zend_execute ()
#7  0x00005555559136aa in zend_execute_scripts ()
#8  0x00005555558b35c9 in php_execute_script ()
#9  0x000055555599d0f6 in ?? ()
#10 0x000055555569e0a7 in ?? ()
#11 0x00007ffff72abce3 in __libc_start_

Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2019-07-03 09:58 UTC] nikic@php.net
-Status: Open +Status: Verified -PHP Version: 7.3.6 +PHP Version: 7.2.20, 7.3.6
 [2019-07-03 09:58 UTC] nikic@php.net
Confirming segfault, also on 7.2.
 [2019-07-03 10:37 UTC] nikic@php.net
Automatic comment on behalf of nikita.ppv@gmail.com
Revision: http://git.php.net/?p=php-src.git;a=commit;h=0e48e35e0485bc6d6458a45ecab5b19a0c2ec001
Log: Fixed bug #78231
 [2019-07-03 10:37 UTC] nikic@php.net
-Status: Verified +Status: Closed
 [2019-07-03 10:38 UTC] nikic@php.net
-Assigned To: +Assigned To: nikic
 [2019-07-03 10:38 UTC] nikic@php.net
Segfault fixed... Note that the script is missing the listen step, so the accept will still fail.
 [2019-07-05 10:41 UTC] asmqb7 at gmail dot com
Wow - that was a very quick fix!

(Could not possibly have expected it was from the openssl extension checking if tcp_nodelay was enabled :) interesting.)

Good catch about the missing `socket_listen($socket)`, which should have gone immediately after the `socket_bind()`. Didn't realize this because the segfault got in the way.

Also, my apologies for the unneeded edges in my initial bug report - my feathers were ruffled, but not to the extent I let on.
 
PHP Copyright © 2001-2019 The PHP Group
All rights reserved.
Last updated: Tue Aug 20 14:01:47 2019 UTC