Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Misleading error message on popen() when /bin/sh is missing #8240

Open
dilyanpalauzov opened this issue Mar 22, 2022 · 5 comments
Open

Misleading error message on popen() when /bin/sh is missing #8240

dilyanpalauzov opened this issue Mar 22, 2022 · 5 comments

Comments

@dilyanpalauzov
Copy link
Contributor

Description

I use PHP 7.4.28, but I do not expect that the described problem has changed in newer versions.

I run PHP-FPM in chrooted environment. That environment had no /bin/sh. The php-fpm.conf file contans:

php_admin_value[sendmail_path]=/chr/sbin/mini_sendmail -t -i

so I would expect that the file is executed, whenever PHP's mail() function is called. As it turned out, at least when calling WordPress’ wp_mail(), and running php-fpm under strace, php calls /bin/sh /chr/sbin/mini_sendmail. The same happens, when I call just popen('/ldconfig', 'r'). The error message in the latter case is “Cannot allocate memory”, in the former case the error message was that the process cannot be forked (I do not remember the precise text).

Now, when PHP relies on /bin/sh to call exec() or system() and /bin/sh is missing, PHP shall neither report, that it cannot fork, nor that it cannot allocate enough memory. It shall report, that /bin/sh is missing in the chroot-ed environment.

PHP Version

PHP 7.4.28,

Operating System

Linux from Scratch

@arnaud-lb
Copy link
Member

arnaud-lb commented Apr 1, 2022

Hello

I confirm. The glibc popen() function sets errno to ENOMEM (Cannot allocate memory) when it fails to spawn the command.

This can be seen here: https://sourceware.org/git/?p=glibc.git;a=blob;f=libio/iopopen.c;h=06778cf110ef9b2745664df00bfd37f291e23ff3;hb=HEAD#l210

This can be reproduced with this C code:

// test.c

#include <stdio.h>
#include <errno.h>
#include <string.h>

int main() {
    errno = 0;
    FILE * f = popen("/bin/true", "r");

    fprintf(stderr, "f: %px, errno: %d, strerror: %s\n", f, errno, strerror(errno));
}
; gcc -static -o test test.c
; sudo chroot . /test
f: (nil)x, errno: 12, strerror: Cannot allocate memory

The gnu man page mentions something about memory allocations and errno:

The popen() function does not set errno if memory allocation fails.

So it may be done on purpose. However, this doesn't appears to be standard behavior.

Compiling against musl libc has different results:

; musl-gcc -static -o test test.c
; sudo chroot . /test
f: 0x, errno: 2, strerror: No such file or directory

We could trigger a different error when errno is ENOMEM, but other libcs appear to set errno to ENOMEM to indicate memory allocation failures.

@arnaud-lb
Copy link
Member

Reported a glibc issue: https://sourceware.org/bugzilla/show_bug.cgi?id=29016

@dilyanpalauzov
Copy link
Contributor Author

My opinion is that when php calls popen('/bin/sh') and it fails, then php shall verify if /bin/sh exists and if it does not exist, then report the non-existence. Of course fixing this in addidion in LIBC is a good idea.

The documentation at https://www.php.net/manual/en/function.popen.php does not emphasize enough that the popen() call is wrapped by /bin/sh call and this has the advantage, that < and > redirections are supposed to work. The disadvantage is, that /bin/sh must exist.

Given the spare documentation, < and > redirections are probably not needed, and the invocation of /bin/sh can be removed from php’s code.

@arnaud-lb
Copy link
Member

Yes, this could be a way to fix this. I want to hear from the glibc people before taking a decision.

Agreed that the documentation doesn't emphasize enough that popen() actually invokes a shell. As to changing the behavior I don't think it would be a good idea: it could break people's code.

I think that currently all exec() variants invoke a shell, except pcntl_exec().

@arnaud-lb
Copy link
Member

The issue has been fixed in glibc: https://sourceware.org/bugzilla/show_bug.cgi?id=29016

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

No branches or pull requests

2 participants