On Wed, Oct 25, 2023 at 11:47 AM Gabriel Ravier <[email protected]> wrote: > > On 10/24/23 13:59, Oğuz via austin-group-l at The Open Group wrote: > > Oğuz > > > > On Tue, Oct 24, 2023 at 1:53 PM Jonathan Wakely <[email protected]> wrote: > >> > >> > >> On Tue, 24 Oct 2023 at 07:10, Oğuz via austin-group-l at The Open Group > >> <[email protected]> wrote: > >>> On Tuesday, October 24, 2023, enh via austin-group-l at The Open Group > >>> <[email protected]> wrote: > >>>> netbsd checks that _PATH_BSHELL is exectuable with access(2) > >>>> (but doesn't actually _execute_ anything). apple's copy of freebsd has > >>>> a local change similar to the netbsd one. glibc seems to actually try > >>>> to _run_ a shell: > >>>> ``` > >>>> [pid 3612818] execve("/bin/sh", ["sh", "-c", "exit 0"], 0x7fff98502fe8 > >>>> ``` > >>> > >>> None of those guarantee that the shell is actually gonna work. I think > >>> they all should just return 1. > >>> > >>>> but a > >>>> POSIX system can have those kinds of features that could mean > >>>> system(3) doesn't actually work _for this process_ > >>> > >>> Such a system should provide the means for testing if a process is in > >>> that state, and the means shouldn't involve auditing the shell executable. > >> > >> Why not? Checking if you can run /bin/sh seems like the most direct and > >> accurate way of checking if the process can run /bin/sh. POSIX might > >> require /bin/sh to be usable, so a "pure POSIX" system can safely just > >> return non-zero from system(NULL). But a system that supports features > >> like SELinux that aren't part of POSIX might want to do extra checks, > >> which wouldn't be required on a system where /bin/sh is always available. > >> > >> Why require those systems to provide some other non-standard API for > >> testing it when system(NULL) is specified by C to do that job? > > system(NULL) is specified by both C and POSIX to return whether the > > host environment features a command processor or not. It's merely a > > feature test, as evident from the following paragraphs from POSIX > > Issue 8 draft 3 P2196 L71808-71821: > > > >> Note that, system(NULL) is required to return non-zero, indicating that > >> there is a command > >> language interpreter. At first glance, this would seem to conflict with > >> the ISO C standard which > >> allows system(NULL) to return zero. There is no conflict, however. A > >> system must have a > >> command language interpreter, and is non-conforming if none is present. It > >> is therefore > >> permissible for the system() function on such a system to implement the > >> behavior specified by > >> the ISO C standard as long as it is understood that the implementation > >> does not conform to > >> POSIX.1-202x if system(NULL) returns zero. > >> > >> It was explicitly decided that when command is NULL, system() should not > >> be required to check > >> to make sure that the command language interpreter actually exists with > >> the correct mode, that > >> there are enough processes to execute it, and so on. The call system(NULL) > >> could, theoretically, > >> check for such problems as too many existing child processes, and return > >> zero. However, it > >> would be inappropriate to return zero due to such a (presumably) transient > >> condition. If some > >> condition exists that is not under the control of this application and > >> that would cause any > >> system() call to fail, that system has been rendered non-conforming. > > On a system where certain users/processes can have limited/no shell > > access, providing a separate interface like do_I_have_shell_access() > > would be more appropriate than overloading system(NULL). > > > Isn't this a similar situation to e.g. `getuid`/`geteuid`/etc. failures > ? I don't see the distinction between `getuid` and other associated > functions saying that they "shall always be successful and no return > value is reserved to indicate the error" and `system` stating "The > system( ) function shall always return non-zero when command is NULL." - > personally I would consider the note in `getuid`'s rationale stating: > > > It is possible for implementations to provide an extension where a > process in a non-conforming environment will not be associated with a > user or group ID. It is recommended that such implementations return > `(uid_t)−1` and set errno to indicate such an environment; doing so does > not violate this standard, since such an environment is already an > extension. > > to mean `system(NULL)` is allowed to check whether it is in a > non-conforming environment.
that sounds like a reasonable answer to me... ...but we'd need to remove "If some condition exists that is not under the control of this application and that would cause any system() call to fail, that system has been rendered non-conforming" from https://pubs.opengroup.org/onlinepubs/9699919799/functions/system.html to allow that, no? (which i think means plenty of existing systems are already non-conforming. arguably _all_ systems, since there doesn't appear to be an exception for _temporary_ failures.) (it's not clear to me that C23 _really_ says what POSIX says it says; it depends on your interpretation of "available" in "If the argument is a null pointer, the system function returns nonzero only if a command processor is available" --- available to whom, and when?) according to man7, glibc returned 1 in the 1990s but changed behavior in 2000: Before glibc 2.1.3, the check for the availability of /bin/sh was not actually performed if command was NULL; instead it was always assumed to be available, and system() always returned 1 in this case. Since glibc 2.1.3, this check is performed because, even though POSIX.1-2001 requires a conforming implementation to provide a shell, that shell may not be available or executable if the calling program has previously called chroot(2) (which is not specified by POSIX.1-2001). i'm guessing that the specific chroot() case is why NetBSD (and Apple's copy of the FreeBSD system.c) use access(2)...
