Bug 25715 - system() returns wrong errors when posix_spawn fails
Summary: system() returns wrong errors when posix_spawn fails
Status: RESOLVED FIXED
Alias: None
Product: glibc
Classification: Unclassified
Component: libc (show other bugs)
Version: 2.30
: P2 normal
Target Milestone: 2.32
Assignee: Adhemerval Zanella
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2020-03-23 13:48 UTC by pokogiv215
Modified: 2020-07-31 09:09 UTC (History)
2 users (show)

See Also:
Host:
Target:
Build:
Last reconfirmed: 2020-03-23 00:00:00
fweimer: security-


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description pokogiv215 2020-03-23 13:48:17 UTC
On linux, I observe a call to system() claiming it's killed by sigbus when really the underlying execv failed with E2BIG. See reproduction in [1].

What I expected to see is system() returning with exit code 127, as per `man system`:
       *  If a shell could not be executed in the child process, then the return value is as though the child shell terminated by calling _exit(2) with the status 127.

Less important, but what I also expected is for posix_spawn to return 0 in this case and to fill in the pid, but instead it returns E2BIG and doesn't fill in the pid, which is not the behavior described by posix_spawn's man page:
       The posix_spawn() and posix_spawnp() functions fail only in the case where the underlying fork(2) or vfork(2) call fails;  in these cases, these functions return an error number,  which  will
       be one of the errors described for fork(2) or vfork(2).

The bad return value from system() was introduced in 5fb7fc96350575c9adb1316833e48ca11553be49, which returns posix_spawn's result (an errno) from system (which returns a wait status).

The posix_spawn handling of errors in the child seems to have changed unintentionally in 4b4d4056bb154603f36c6f8845757c1012758158 (because ec didn't use to be set).


[1]

$ cat a.c
#include <stdio.h>
#include <string.h>
#include <signal.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <spawn.h>

int main(int argc, char* argv[], char** env) {
  char cmd[150000];
  memset(cmd, 'a', sizeof(cmd));
  cmd[sizeof(cmd) - 1] = 0;
  int ret = posix_spawn(NULL, "/bin/sh", 0, NULL,
			(char *[]){"sh", "-c", (char *)cmd, 0}, env);
  printf("posix_spawn returns: %d = %s\n", ret, strerror(ret));
  int wstatus = system(cmd);
  if (WIFEXITED(wstatus)) {
    printf("exited, status=%d\n", WEXITSTATUS(wstatus));
  } else if (WIFSIGNALED(wstatus)) {
    printf("killed by signal %d = %s\n", WTERMSIG(wstatus), strsignal(WTERMSIG(wstatus)));
  } else if (WIFSTOPPED(wstatus)) {
    printf("stopped by signal %d\n", WSTOPSIG(wstatus));
  } else if (WIFCONTINUED(wstatus)) {
    printf("continued\n");
  }
  return 0;
}
$ gcc a.c && ./a.out
posix_spawn returns: 7 = Argument list too long
killed by signal 7 = Bus error
Comment 1 Adhemerval Zanella 2020-03-23 16:24:54 UTC
(In reply to pokogiv215 from comment #0)
> On linux, I observe a call to system() claiming it's killed by sigbus when
> really the underlying execv failed with E2BIG. See reproduction in [1].
> 
> What I expected to see is system() returning with exit code 127, as per `man
> system`:
>        *  If a shell could not be executed in the child process, then the
> return value is as though the child shell terminated by calling _exit(2)
> with the status 127.

Yes, this is an issue introduced by 5fb7fc9635057. I am currently preparing a 
patch to fix it.

> 
> Less important, but what I also expected is for posix_spawn to return 0 in
> this case and to fill in the pid, but instead it returns E2BIG and doesn't
> fill in the pid, which is not the behavior described by posix_spawn's man
> page:
>        The posix_spawn() and posix_spawnp() functions fail only in the case
> where the underlying fork(2) or vfork(2) call fails;  in these cases, these
> functions return an error number,  which  will
>        be one of the errors described for fork(2) or vfork(2).

POSIX [1] and man-pages (which is not the canonical documentation of glibc) [2] 
documents that if there is an error before or during the fork(2) (which in glibc
Linux implementation of posix_spawn is done by a clone call with CLONE_VFORK), 
then no child  is created, the contents of *pid are unspecified, and these 
functions return an error number. And this is what is happening since what is
throwing the error is the execve syscall (using your example):

  $ strace -f ./elf/ld-linux-x86-64.so.2 [...] ./bz25715 
  [...]
  [pid 16313] rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
  pid 16313] execve("/bin/sh", ["sh", "-c", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"...], 0x7ffcf2800f60 /* 78 vars */) = -1 E2BIG (Argument list too long)
  [pid 16313] exit_group(127)             = ?
  [pid 16311] <... clone resumed> child_stack=0x7f35d0eb1ff0, 
flags=CLONE_VM|CLONE_VFORK|SIGCHLD) = 16313
  [pid 16313] +++ exited with 127 +++

Returning anything different than an error, in this case, does not make sense.

[1] https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawn.html

> 
> The bad return value from system() was introduced in
> 5fb7fc96350575c9adb1316833e48ca11553be49, which returns posix_spawn's result
> (an errno) from system (which returns a wait status).
> 
> The posix_spawn handling of errors in the child seems to have changed
> unintentionally in 4b4d4056bb154603f36c6f8845757c1012758158 (because ec
> didn't use to be set).
> 
> 
> [1]
> 
> $ cat a.c
> #include <stdio.h>
> #include <string.h>
> #include <signal.h>
> #include <stdlib.h>
> #include <sys/types.h>
> #include <sys/stat.h>
> #include <spawn.h>
> 
> int main(int argc, char* argv[], char** env) {
>   char cmd[150000];
>   memset(cmd, 'a', sizeof(cmd));
>   cmd[sizeof(cmd) - 1] = 0;
>   int ret = posix_spawn(NULL, "/bin/sh", 0, NULL,
> 			(char *[]){"sh", "-c", (char *)cmd, 0}, env);
>   printf("posix_spawn returns: %d = %s\n", ret, strerror(ret));
>   int wstatus = system(cmd);
>   if (WIFEXITED(wstatus)) {
>     printf("exited, status=%d\n", WEXITSTATUS(wstatus));
>   } else if (WIFSIGNALED(wstatus)) {
>     printf("killed by signal %d = %s\n", WTERMSIG(wstatus),
> strsignal(WTERMSIG(wstatus)));
>   } else if (WIFSTOPPED(wstatus)) {
>     printf("stopped by signal %d\n", WSTOPSIG(wstatus));
>   } else if (WIFCONTINUED(wstatus)) {
>     printf("continued\n");
>   }
>   return 0;
> }
> $ gcc a.c && ./a.out
> posix_spawn returns: 7 = Argument list too long
> killed by signal 7 = Bus error
Comment 2 Sourceware Commits 2020-03-23 21:05:47 UTC
The master branch has been updated by Adhemerval Zanella <azanella@sourceware.org>:

https://sourceware.org/git/gitweb.cgi?p=glibc.git;h=f09542c584b121da0322fde4b55306d512b85d93

commit f09542c584b121da0322fde4b55306d512b85d93
Author: Adhemerval Zanella <adhemerval.zanella@linaro.org>
Date:   Mon Mar 23 15:23:20 2020 -0300

    posix: Fix system error return value [BZ #25715]
    
    It fixes 5fb7fc9635 when posix_spawn fails.
    
    Checked on x86_64-linux-gnu and i686-linux-gnu.
    
    Reviewed-by: Carlos O'Donell <carlos@redhat.com>
Comment 3 Adhemerval Zanella 2020-03-23 21:06:54 UTC
Fixed on 2.32.
Comment 4 Sourceware Commits 2020-03-24 11:51:12 UTC
The release/2.30/master branch has been updated by Adhemerval Zanella <azanella@sourceware.org>:

https://sourceware.org/git/gitweb.cgi?p=glibc.git;h=9cd774568999873bec34b3db228c2ee914692dbd

commit 9cd774568999873bec34b3db228c2ee914692dbd
Author: Adhemerval Zanella <adhemerval.zanella@linaro.org>
Date:   Mon Mar 23 15:23:20 2020 -0300

    posix: Fix system error return value [BZ #25715]
    
    It fixes 5fb7fc9635 when posix_spawn fails.
    
    Checked on x86_64-linux-gnu and i686-linux-gnu.
    
    Reviewed-by: Carlos O'Donell <carlos@redhat.com>
    
    (cherry picked from commit f09542c584b121da0322fde4b55306d512b85d93)
Comment 5 Sourceware Commits 2020-03-24 12:16:31 UTC
The release/2.31/master branch has been updated by Adhemerval Zanella <azanella@sourceware.org>:

https://sourceware.org/git/gitweb.cgi?p=glibc.git;h=46bbbd4622351528ae22c2d3397ab090f5099e82

commit 46bbbd4622351528ae22c2d3397ab090f5099e82
Author: Adhemerval Zanella <adhemerval.zanella@linaro.org>
Date:   Mon Mar 23 15:23:20 2020 -0300

    posix: Fix system error return value [BZ #25715]
    
    It fixes 5fb7fc9635 when posix_spawn fails.
    
    Checked on x86_64-linux-gnu and i686-linux-gnu.
    
    Reviewed-by: Carlos O'Donell <carlos@redhat.com>
    
    (cheery picked from commit f09542c584b121da0322fde4b55306d512b85d93)