Skip to content

Conversation

@redfoxli
Copy link
Contributor

@redfoxli redfoxli commented Dec 9, 2014

the bug is here: https://bugs.php.net/bug.php?id=68571

There is several conditions when core dump
1 don't use error_log in php.ini.
2 a lot of stderr log generate
3 webserver close the socket after send the request(request timeout will lead the action)

from the code of php, we know that
1 output is base a buffer(out_buf)
2 out_pos point to the last address, out_pos - out_buf <= 8192 (the buffer's length)
3 if out_hdr is NULL, we let out_pos add sizeof(fcgi_header)
4 before write data to client, we will reset out_hdr to NULL(in function close_packet)

but when write stderr to client failed, there are two mistake
1 we don't reset the pos of out_pos
2 we don't check the return value of fcgi_write

so out_pos add more and more sizeof(fcgi_header) until address out of bounds and core dump

I rest the pos of out_pos and check the return value of fcgi_write in my PR

@redfoxli
Copy link
Contributor Author

redfoxli commented Dec 9, 2014

According to the fastcgi protocol(http://www.fastcgi.com/devkit/doc/fcgi-spec.html), I write a php client to talk with php directly.
It can reproduce the core dump more easy.

server code(/home/users/xxx/test/index.php) is here:

<?php
  for($i = 0; $i < 70000; ++$i)
  {  
     strpos("", "");
  } 

fastcgi client code is here:

<?php
define('FCGI_REQUEST_ID', 1);
define('FCGI_VERSION_1', 1);
define('FCGI_BEGIN_REQUEST', 1);
define('FCGI_RESPONDER', 1);
define('FCGI_END_REQUEST', 3);
define('FCGI_PARAMS', 4);
define('FCGI_STDIN', 5);
define('FCGI_STDOUT', 6);
define('FCGI_STDERR', 7);

define('FCGI_HOST', '127.0.0.1');
define('FCGI_PORT', 19550);

make_core_main();

function make_core_main()
{
    $socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
    socket_connect($socket, FCGI_HOST, FCGI_PORT);

    $content_body = '';
    $fcgi_request_id = 1;
    $env = array(
        'SCRIPT_FILENAME' => '/home/users/xxx/test/index.php',
        'REQUEST_METHOD'  => 'GET',
        'CONTENT_LENGTH' => strlen($content_body),
    );

    send_request($socket, $content_body, $env, $fcgi_request_id); 
    //$result = get_response($socket);
    var_dump($result);
    socket_close($socket);
}


function send_request($socket, $content_body, $env, $fcgi_request_id)
{
    $content_body_len = strlen($content_body);

    //step1 FCGI_BEGIN_REQUEST
    $begin_request_body = get_begin_request_body();
    $begin_request_header = get_header(FCGI_BEGIN_REQUEST, $fcgi_request_id, 8, 0);
    $record = $begin_request_header . $begin_request_body;
    socket_write($socket, $record);

    //step2 FCGI_PARAMS
    $params_body = '';
    //$env['CONTENT_LENGTH'] = $content_body_len;
    foreach($env as $key => $value)
    {
        $params_body .= get_params_key_len($key) . get_params_key_len($value) . $key . $value;
    }
    $params_body_len = strlen($params_body);
    $params_pad_body_len = get_pad_len($params_body_len);
    $params_header = get_header(FCGI_PARAMS, $fcgi_request_id, $params_body_len, $params_pad_body_len);
    $params_pad_body = get_pad_data($params_pad_body_len);
    $record = $params_header . $params_body . $params_pad_body;
    socket_write($socket, $record);

    $params_empty_body_header = get_header(FCGI_PARAMS, $fcgi_request_id, 0, 0);
    socket_write($socket, $params_empty_body_header);

    //step3 FCGI_STDIN
    if($content_body_len > 0)
    {
        $stdin_pad_body_len = get_pad_len($content_body_len);
        $stdin_header = get_header(FCGI_STDIN, $fcgi_request_id, $content_body_len, $stdin_pad_body_len);
        $record = $stdin_header . $content_body . get_pad_data($stdin_pad_body_len);
        socket_write($socket, $record);
    }
    $stdin_empty_header = get_header(FCGI_STDIN, $fcgi_request_id, 0, 0);
    socket_write($socket, $stdin_empty_header);
}

function get_response($socket)
{
    $result = array();
    $stdout_response_body = '';
    $stderr_response_body = '';
    while(1)
    {
        $header = socket_read($socket, 8);
        $header = unpack("Cversion/Ctype/nrequestId/ncontentLength/CpaddingLength/Creserved", $header);
        $response_body_len = $header['contentLength'];
        $response_pad_len = $header['paddingLength'];
        $type = $header['type'];

        $response_body = read_body($socket, $response_body_len);    
        $response_pad_body = read_body($socket, $response_pad_len);   

        if($type == FCGI_END_REQUEST)
        {
            $result['end']['response_header'] = $header;
            $result['end']['response_body'] = unpack("NappStatus/CprotocolStatus/C3reserved", $response_body);

            break;
        }
        else if($type == FCGI_STDOUT) 
        {
            $stdout_response_body .= $response_body;
            $result['stdout']['last_response_header'] = $header;
        }
        else if($type == FCGI_STDERR)
        {
            $stderr_response_body .= $response_body;
            $result['stderr']['last_response_header'] = $header;
        }
    }

    if(empty($stdout_response_body) === false)
    {
        list($header, $body) = explode("\r\n\r\n", $stdout_response_body);
        $result['stdout']['response_body']['header'] = get_http_header_info($header);
        $result['stdout']['response_body']['body'] = $body;
    }

    if(empty($stderr_response_body) === false)
    {
        $result['stderr']['response_body'] = $stderr_response_body;
    }

    return $result;
}

function get_http_header_info($header)
{
    $arr = explode("\r\n", $header);
    foreach($arr as $item)
    {
        $arr_item = explode(":", $item);

        if(count($arr_item) == 2)
        {
            $key = trim($arr_item[0]);
            $value = trim($arr_item[1]);
            $arr_header[$key] = $value;
        }
    }
    return $arr_header;
}

function get_begin_request_body()
{
    return pack("nC6", FCGI_RESPONDER, 0, 0, 0, 0, 0, 0);
}

function get_header($type, $request_id, $content_len, $pad_len, $reserved = 0)
{
    return pack("C2n2C2", FCGI_VERSION_1, $type, $request_id, $content_len, $pad_len, $reserved);
}

function get_pad_len($body_len)
{
    $left = $body_len % 8;
    if ($left == 0)
    {
        return 0;
    }
    return 8 - $left;
}

function get_pad_data($pad_len)
{
    $data = '';
    for($i = 0; $i < $pad_len; ++$i)
    { 
        $data .= chr(0); 
    }
    return $data;
}

function get_params_key_len($key)
{
    $len = strlen($key);
    if($len > 0x7f)
    {
        $b0 = $len & 0xff;
        $b1 = ($len >> 8) & 0xff;
        $b2 = ($len >> 16) & 0xff;
        $b3 = ($len >> 24) & 0xff;

        $b3 = $b3 | 0x80;
        $bin = pack("C4", $b3, $b2, $b1, $b0);
    }
    else
    {
        $bin = pack("C", $len);
    }
    return $bin;
}

function read_body($socket, $len)
{
    $body = "";
    while($len)
    {
        $buf = socket_read($socket, $len);
        $len -= strlen($buf); 
        $body .= $buf;
    }
    return $body;
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

break C89 here... move the declaration to the top please.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I will fix it. thank niaoge

@smalyshev smalyshev added the Bug label Dec 11, 2014
@php-pulls php-pulls merged commit 7953d83 into php:PHP-5.5 Jan 27, 2015
@laruence
Copy link
Member

已经提交, 感谢你的Fix(Merged thanks) :)

@ghost
Copy link

ghost commented Jan 28, 2015

@laruence why not merged into PHP 5.4 branch?

@laruence
Copy link
Member

@netroby 5.4 is in only security fix status

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants