Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can string literals be passed in posix_spawn's argv?

For the posix_spawn function its prototype is:

int posix_spawn(pid_t *restrict pid, const char *restrict path,
   const posix_spawn_file_actions_t *file_actions,
   const posix_spawnattr_t *restrict attrp,
   char *const argv[restrict], char *const envp[restrict]);

Notably, the argv parameter points to an array of char * pointers (i.e. pointers to mutable characters). Further, the documentation does not seem to give any guarantee that the data will not be written to.

My question is: is there a guarantee somewhere that it is OK to pass a string literal? Or are we risking a segfault?

Sample code:

char *v[] = { "foo.exe", "bar", NULL };
posix_spawn( NULL, "foo.exe", NULL, NULL, v, NULL );
like image 573
M.M Avatar asked May 30 '18 04:05

M.M


3 Answers

Using string literals is perfectly fine here.

Whether a pointer argument (or pointer data pointed to by an argument) points to const-qualified type has nothing to do with whether the function can modify a pointed-to object. This is purely a matter of the contract of the function in question. As a convention, it's usually preferable to use const-qualified pointers in arguments when the object won't be modified:

  1. to allow passing pointers to const-qualified objects without casts, and
  2. as an indication that the object won't be modified.

but there is no requirement to do so in the C language. And for functions that use double-pointer types in their interfaces, there's often a tradeoff here. Since T * and const T * cannot alias one another, the interface has to choose the form more likely for the caller to want; if the caller wants the other form, it has to make a temporary copy to pass to the function. This is the case for posix_spawn.

In general, when it comes to standard functions (C or POSIX), they cannot have any observable side effects except as specified. Unless the DESCRIPTION for the function documents that it will modify an object "belonging to" the application, or which the application has access to, it cannot modify it; doing so is non-conforming. This is why functions which return pointers to static storage explicitly document it. For example, POSIX documents for strerror:

The returned string pointer might be invalidated or the string content might be overwritten by a subsequent call to strerror(),

Short of such documentation, an application could assume that the string returned by strerror is never modified by the implementation.

Since posix_spawn is not documented to modify the strings pointed to by its argv array, it does not modify them.

Further, note that posix_spawn is required to be thread-safe, and does not place any explicit constraint on applications for concurrent access to the argv strings. As such, any modification would introduce data races, thereby rendering posix_spawn non-thread-safe, contrary to specification.

like image 68
R.. GitHub STOP HELPING ICE Avatar answered Oct 13 '22 00:10

R.. GitHub STOP HELPING ICE


I'm pretty sure the type was chosen for compatibility with the char **argv argument to main (and execve). (Although in a traditional implementation with proper process separation, the kernel eventually has to make a copy.)

POSIX does not seem to say that these arrays are modified, but I'm pretty confident that no existing implementation will modify them. There could be some reasons for using different arguments (and executable names), but those will be longer, so posix_spawn has to allocate memory for a copy anyway, and cannot perform the modification in-place.

like image 35
Florian Weimer Avatar answered Oct 13 '22 00:10

Florian Weimer


is there a guarantee somewhere that it is OK to pass a string literal? Or are we risking a segfault?

Given, there is a technical segfault risk as argv expects a non-const array of char* and providing a string literal can lead to UB. With a function perhaps modeling main(int argc, char *argv[]), code can write to argv[0].

int main(int argc, char *argv[])
...
The parameters argc and argv and the strings pointed to by the argv array shall be modifiable by the program, and retain their last-stored values between program startup and program termination. C11 §5.1.2.2.1 2

int foo(......., char *const argv[restrict], char *const envp[restrict]);

char *v[] = { "foo.exe", "bar", NULL };
foo( NULL, "foo.exe", NULL, NULL, v, NULL );

Alternative

Although with posix_spawn(), I doubt a write will occur, a solution with C99 employs a compound literal rather than a string literal and so avoids UB potential.

// char *v[] = { "foo.exe", "bar", NULL };
char *v2[] = { (char [8]){"foo.exe"}, (char [4]){"bar"}, NULL };
posix_spawn( NULL, "foo.exe", NULL, NULL, v2, NULL );

Now posix_spawn() can write to v2[0]

like image 30
chux - Reinstate Monica Avatar answered Oct 12 '22 23:10

chux - Reinstate Monica