Austin Group Defect Tracker

Aardvark Mark IV


Viewing Issue Simple Details Jump to Notes ] Issue History ] Print ]
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
Reporter eblake View Status public  
Assigned To
Priority normal Resolution Accepted As Marked  
Status Applied  
Name Eric Blake
Organization Red Hat
User Reference ebb.posix_spawnp
Section XSH posix_spawnp
Page Number 1455
Line Number 48328
Interp Status Approved
Final Accepted Text Note: 0006411
Summary 0001674: may posix_spawnp() fail with ENOEXEC?
Description 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).
Desired Action 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).
Tags applied_after_i8d3, issue8
Attached Files

- Relationships
related to 0001645Applied 1003.1(2016/18)/Issue7+TC2 execvp( ) requirements on arg0 are too strict 
related to 0001208Applied 1003.1(2016/18)/Issue7+TC2 calling chdir as part of posix_spawn 
related to 0001044Applied 1003.1(2013)/Issue7+TC1 Calling setsid as part of posix_spawn 

-  Notes
(0006264)
eblake (manager)
2023-04-19 12:36
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.


(0006265)
eblake (manager)
2023-04-19 17:12

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.
(0006411)
geoffclare (manager)
2023-07-31 16:06

Interpretation response
------------------------

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.

Rationale:
-------------
None.

Notes to the Editor (not part of this interpretation):
-------------------------------------------------------
Make the changes in the Desired Action, and also the changes in Note: 0006264.
(0006414)
agadmin (administrator)
2023-07-31 16:44

Interpretation proposed: 31 July 2023
(0006455)
ajosey (manager)
2023-09-04 10:46

Interpretation approved: 4 September 2023

- Issue History
Date Modified Username Field Change
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
Powered by Mantis Bugtracker