|Anonymous | Login||2023-09-23 19:59 UTC|
|Main | My View | View Issues | Change Log | Docs|
|Viewing Issue Simple Details|
|ID||Category||Severity||Type||Date Submitted||Last Update|
|0001674||[1003.1(2016/18)/Issue7+TC2] Shell and Utilities||Objection||Omission||2023-04-19 02:35||2023-09-05 11:09|
|Priority||normal||Resolution||Accepted As Marked|
|Final Accepted Text||Note: 0006411|
|Summary||0001674: may posix_spawnp() fail with ENOEXEC?|
I'm raising this based on a thread on the Cygwin mailing list: https://cygwin.com/pipermail/cygwin/2023-April/253495.html [^]
The standard is clear that while execl(), execle(), execv(), execve(), and fexecve() can fail with ENOEXEC, execlp() and execvp() must instead fall back to an invocation of 'sh' (see page 784 line 26548, and page 788 line 26744). However, when it comes to posix_spawn() and posix_spawnp(), the standard is silent as to whether a fallback to 'sh' must be attempted.
At a first glance, one might assume that similarity in naming to the exec counterparts means that posix_spawn() matches execv() (no PATH search attempted, no sh fallback), and posix_spawnp() matches execvp() (utilize PATH search, attempt sh fallback). However, behavior differs between platforms (some fall back to sh for both spawn variants, Cygwin only for posix_spawnp, and glibc has refused the fallback for any file not starting with #! since 2.15 on Linux (patched in 2011, https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=d96de963), [^] and since 2.33 on Hurd (patched in 2020, https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=13adfa34; [^] musl also does not fall back on either function but also doesn't fall back to sh on execvp() so it is not a factor here).
But a more precise read of the current wording does not actually require what should happen on ENOEXEC - page 1452 line 48229 describes the PATH search aspect of posix_spawnp(), but has no mention of a fallback to sh; and page 1455 line 48329 states that posix_spawn[p]() may fail for the same reasons as one of the exec family without specifying any particular mapping between the 2 spawn functions and the 7 exec functions. That is, I could come up with a weirdnix where posix_spawn() uses execvp() with a filename containing '/' (no PATH search but does get a fallback to sh), while posix_spawnp() implements its own PATH search to open an fd and use fexecve() which can see ENOEXEC, and that would still be compliant given the current ambiguous wording).
More to the point, the non-normative XRAT B.3.3 starting on page 3694 line 126541 gives a sample implementation of posix_spawn() (but not posix_spawnp()) that uses execve() (line 126790) and therefore can fail with ENOEXEC in the child process but has exit status 127 (confusing, since everywhere else in the standard we try to turn ENOEXEC into exit status 126 - but consistent with the current requirements on posix_spawn). Furthermore, the standard is clear that posix_spawn[p] can utilize a different operating system hook than the exec family; where it may be impractical to even attempt a fallback to sh (although the standard shows an implementation using fork/exec where the child process could still attempt a second execve() after the ENOEXEC failure, the intent was that there may be some other system-specific mechanism for creating a child process that does NOT have any way of doing a second attempt short of reapplying the full set of file actions in the parent process).
Additionally, on systems with setuid binaries, application writers PREFER to use posix_spawn*() over exec*() to have more control over starting a child process with known characteristics. If a setuid binary can end up trying to invoke a child process on something that would give an ENOEXEC error, and blindly attempts to hand that to 'sh', but it is not a valid shell script, this could easily be exploited as a security hole when sh starts executing random data as a shell script (which is the rationale glibc gave for disabling the fallback to sh for non-#! files on both spawn variants). Of course, this standard doesn't specify #! behavior; that's implementation-defined (and on Linux, starting a file with #! is handled by the kernel as a known executable format that can't fail with ENOEXEC, so only the files without #! can even reach the point where glibc attempts an sh fallback when it wants to in execvp).
Although the XRAT is non-normative, I think we are better off mandating that posix_spawn and posix_spawnp do NOT attempt an sh fallback on ENOEXEC failure; but the group may decide that is at too much risk of breaking some existing implementations that depend on an sh fallback, and decide to water this down into instead declaring the sh fallback to be implementation-defined (where glibc would then define that there is no sh fallback).
At page 1452 line 48234 (XSH posix_spawn DESCRIPTION), add a sentence:
However, if at least one of the exec family of functions would fail with [ENOEXEC] because the process image contents are not executable, this shall cause posix_spawnp( ) to fail rather than attempting a fallback to invoking the process as a shell script passed to sh.
At page 1455 line 48328 (XSH posix_spawn ERRORS), change:
If posix_spawn( ) or posix_spawnp( ) fail for any of the reasons that would cause fork( ) or one of the exec family of functions to fail, an error value shall be returned as described by fork( ) and exec, respectively (or, if the error occurs after the calling process successfully returns, the child process shall exit with exit status 127).to:
If posix_spawn( ) or posix_spawnp( ) fail for any of the reasons that would cause fork( ) or one of the exec family of functions to fail, including when the corresponding exec function would attempt a fallback to sh instead of failing with [ENOEXEC], an error value shall be returned as described by fork( ) and exec, respectively (or, if the error occurs after the calling process successfully returns, the child process shall exit with exit status 127).
edited on: 2023-04-19 12:36
While we're touching this, it may be worth acknowledging that the XRAT example is not robust (among other things, it mishandles file names containing things like '*', and can easily overwrite past the bounds of an array when passed a long filename).
At page 3695 line 126556 (XRAT B.3.3), change:
The effective behavior of a successful invocation of posix_spawn( ) is as if the operation were implemented with POSIX operations as follows:to:
The example below demonstrates an initial approach to implementing posix_spawn( ) using other POSIX operations, although an actual implementation will need to be more robust at handling all possible file names.
|Another option might be to define a new POSIX_SPAWN_XXX flag to posix_spawnattr_setflags() that gives finer-grained control over whether or not to attempt a fallback to sh, and whether or not to attempt exit status 126 instead of 127 on ENOEXEC failures (when the failure is only detected after a child process is created); but that would be more invention than existing practice.|
The standard does not speak to this issue, and as such no conformance distinction can be made between alternative implementations based on this. This is being referred to the sponsor.
Notes to the Editor (not part of this interpretation):
Make the changes in the Desired Action, and also the changes in Note: 0006264.
|Interpretation proposed: 31 July 2023|
|Interpretation approved: 4 September 2023|
|2023-04-19 02:35||eblake||New Issue|
|2023-04-19 02:35||eblake||Name||=> Eric Blake|
|2023-04-19 02:35||eblake||Organization||=> Red Hat|
|2023-04-19 02:35||eblake||User Reference||=> ebb.posix_spawnp|
|2023-04-19 02:35||eblake||Section||=> XSH posix_spawnp|
|2023-04-19 02:35||eblake||Page Number||=> 1455|
|2023-04-19 02:35||eblake||Line Number||=> 48328|
|2023-04-19 02:35||eblake||Interp Status||=> ---|
|2023-04-19 12:16||eblake||Description Updated|
|2023-04-19 12:36||eblake||Note Added: 0006264|
|2023-04-19 12:36||eblake||Note Edited: 0006264|
|2023-04-19 13:25||eblake||Description Updated|
|2023-04-19 17:05||eblake||Relationship added||related to 0001208|
|2023-04-19 17:06||eblake||Relationship added||related to 0001044|
|2023-04-19 17:12||eblake||Note Added: 0006265|
|2023-04-19 17:26||eblake||Relationship added||related to 0001645|
|2023-07-31 16:06||geoffclare||Note Added: 0006411|
|2023-07-31 16:07||geoffclare||Interp Status||--- => Pending|
|2023-07-31 16:07||geoffclare||Final Accepted Text||=> Note: 0006411|
|2023-07-31 16:07||geoffclare||Status||New => Interpretation Required|
|2023-07-31 16:07||geoffclare||Resolution||Open => Accepted As Marked|
|2023-07-31 16:08||geoffclare||Tag Attached: issue8|
|2023-07-31 16:44||agadmin||Interp Status||Pending => Proposed|
|2023-07-31 16:44||agadmin||Note Added: 0006414|
|2023-09-04 10:46||ajosey||Interp Status||Proposed => Approved|
|2023-09-04 10:46||ajosey||Note Added: 0006455|
|2023-09-05 11:09||geoffclare||Status||Interpretation Required => Applied|
|2023-09-05 11:10||geoffclare||Tag Attached: applied_after_i8d3|
|Mantis 1.1.6[^] Copyright © 2000 - 2008 Mantis Group|