php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #69121 Segfault in get_current_user when script owner is not in passwd with ZTS build
Submitted: 2015-02-25 21:10 UTC Modified: 2015-02-27 04:00 UTC
From: dan at syneto dot net Assigned:
Status: Closed Package: Reproducible crash
PHP Version: 5.6.6 OS:
Private report: No CVE-ID:
 [2015-02-25 21:10 UTC] dan at syneto dot net
Description:
------------
This is a bug in function php_get_current_user (main.c) when compiling in a standards compliant environment on Illumos or Solaris based systems. It might also be happening on Linux or other Unix systems that follow the below semantics of getpwuid.

This is the relevant section from the getpwnam_r() manual from Illumos:
=== (illumos) man getpwuid_r ===
...
       The standard-conforming functions getpwnam_r() and getpwuid_r() can
       return 0 even on an error, particularly in the case where the requested
       entry is not found. The application needs to check the return value and
       that the pwd pointer is non-null. Otherwise, an error value is returned
       to indicate the error.
...

The same issue is pointed to by the equivalent section on Linux systems:
=== (linux) man getpwuid_r ===
...
       If no matching password record was found, these functions return 0 and store NULL in *result
...

When running the test script, make sure you are root and able to run sudo to change its owner to a non-existent UID using:
    $ sudo chown 31415 get_current_user.php
You will not need to run the script as root. Just make sure its owner UID does not exist in /etc/passwd.

I also attached a patch that properly checks for a NULL result.


Test script:
---------------
=== First code:
<?php
echo "Owner: " . get_current_user() . "\n";

=== Second code:
<?php
array_fill(5, 6, 'banana');
echo "Owner: " . get_current_user() . "\n";


Expected result:
----------------
=== First code output:
Owner: 
=== Second code output:
Owner: 


Actual result:
--------------
=== First code output:
Owner: d��

=== Second code output:
Segmentation Fault (core dumped)
admin@vmbox-desk:/tmp/test$ pstack core
core 'core' of 8560:	php ./get_current_user.php
 fffffd7fff130474 strlen () + 14
 000000000065df1b zif_get_current_user () + 2b
 000000000072eb91 dtrace_execute_internal () + 91
 00000000007f6e21 zend_do_fcall_common_helper_SPEC () + 661
 000000000077d51b execute_ex () + 4b
 000000000072ea6b dtrace_execute_ex () + 13b
 00000000007442b2 zend_execute_scripts () + 1d2
 00000000006d03af php_execute_script () + 1ef
 00000000007f92cc do_cli () + 12bc
 00000000008092aa main () + 63a
 00000000004d8a7c _start () + 6c


Patches

php-5.6.6-getpwuid_r-segfault.patch (last revision 2015-02-25 21:11 UTC) by dan at syneto dot net)

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2015-02-25 22:46 UTC] requinix@php.net
-Status: Open +Status: Verified
 [2015-02-25 22:46 UTC] requinix@php.net
On Ubuntu 14.04 x64, getpwuid_r behaves the same way: returns 0 with result=NULL, but my PHP doesn't crash so it's probably using the getpwuid fallback.

Compare php_get_current_user [1] with posix_getpwuid [2] which has

ret = getpwuid_r(uid, &_pw, pwbuf, pwbuflen, &retpwptr);
if (ret || retpwptr == NULL) {
	POSIX_G(last_error) = ret;
	efree(pwbuf);
	RETURN_FALSE;
}

I certainly wouldn't call myself a C dev, but

#include <sys/types.h>
#include <pwd.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>

int main(int argc, char* argv[]) {
        int ret;
        uid_t uid;
        struct passwd pwd;
        char* buf;
        size_t buflen;
        struct passwd* result;

        if (argc != 2) {
                printf("usage: %s uid\n", argv[0]);
                return 0;
        }

        uid = atoi(argv[1]);
        printf("uid=%d\n", uid);

        buf = (char*)malloc(100 * sizeof(char));
        buflen = 99;
        result = NULL;

        ret = getpwuid_r(uid, &pwd, buf, buflen, &result);
        printf("ret=%d\n", ret);
        printf("errno=%d\n", errno);
        if (ret != 0) goto end;

        printf("result=%p\n", result);
        if (result == NULL) goto end;

        printf("pwd=%p\n", &pwd);
        printf("pwd.pw_name=%s\n", pwd.pw_name);
        printf("pwd.pw_passwd=%s\n", pwd.pw_passwd);
        printf("pwd.pw_uid=%d\n", pwd.pw_uid);
        printf("pwd.pw_gid=%d\n", pwd.pw_gid);
        printf("pwd.pw_gecos=%s\n", pwd.pw_gecos);
        printf("pwd.pw_dir=%s\n", pwd.pw_dir);
        printf("pwd.pw_shell=%s\n", pwd.pw_shell);
        printf("buf=%s\n", buf);

end:
        free(buf);
        return 0;
}

which produces (eg,)

uid=9999
ret=0
errno=0
result=(nil)

[1] https://github.com/php/php-src/blob/4629f8978e86aa20e6254e7282e6c5a4fb26796c/main/main.c#L1258
[2] https://github.com/php/php-src/blob/4629f8978e86aa20e6254e7282e6c5a4fb26796c/ext/posix/posix.c#L1157
 [2015-02-25 23:48 UTC] dan at syneto dot net
I also checked on CentOS 7, and even though it should crash it does not.
It might take a different route, or different compiler flags might have an impact on this.
The point is that the code is still wrong, an in fact crashes on illumos/solaris.
 [2015-02-27 04:00 UTC] laruence@php.net
-Summary: Segfault in get_current_user when script owner is not in passwd +Summary: Segfault in get_current_user when script owner is not in passwd with ZTS build
 [2015-02-27 04:00 UTC] laruence@php.net
change title (only in ZTS build)
 [2015-02-27 04:03 UTC] laruence@php.net
Automatic comment on behalf of laruence
Revision: http://git.php.net/?p=php-src.git;a=commit;h=ebfc49aa918044e65b3704933224a807d98b4e68
Log: Fixed bug #69121 (Segfault in get_current_user when script owner is not in passwd with ZTS build)
 [2015-02-27 04:03 UTC] laruence@php.net
-Status: Verified +Status: Closed
 
PHP Copyright © 2001-2017 The PHP Group
All rights reserved.
Last updated: Tue Aug 29 15:01:52 2017 UTC