Changeset View
Changeset View
Standalone View
Standalone View
lib/libc/gen/posix_spawn.c
Show First 20 Lines • Show All 240 Lines • ▼ Show 20 Lines | do_posix_spawn(pid_t *pid, const char *path, | ||||
const posix_spawn_file_actions_t *fa, | const posix_spawn_file_actions_t *fa, | ||||
const posix_spawnattr_t *sa, | const posix_spawnattr_t *sa, | ||||
char * const argv[], char * const envp[], int use_env_path) | char * const argv[], char * const envp[], int use_env_path) | ||||
{ | { | ||||
struct posix_spawn_args psa; | struct posix_spawn_args psa; | ||||
pid_t p; | pid_t p; | ||||
#ifdef _RFORK_THREAD_STACK_SIZE | #ifdef _RFORK_THREAD_STACK_SIZE | ||||
char *stack; | char *stack; | ||||
size_t cnt, stacksz; | |||||
stack = malloc(_RFORK_THREAD_STACK_SIZE); | stacksz = _RFORK_THREAD_STACK_SIZE; | ||||
if (use_env_path) { | |||||
/* | |||||
* We need to make sure we have enough room on the stack for the | |||||
* potential alloca() in execvPe if it gets kicked back an | |||||
* ENOEXEC from execve(2), plus the original buffer we gave | |||||
* ourselves; this protects us in the event that the caller | |||||
* intentionally or inadvertently supplies enough arguments to | |||||
* make us blow past the stack we've allocated from it. | |||||
*/ | |||||
stacksz += (2 * sizeof(char *)); | |||||
for (cnt = 0; argv[cnt] != NULL; ++cnt) | |||||
stacksz += sizeof(char *); | |||||
} | |||||
stack = malloc(stacksz); | |||||
if (stack == NULL) | if (stack == NULL) | ||||
return (ENOMEM); | return (ENOMEM); | ||||
#endif | #endif | ||||
psa.path = path; | psa.path = path; | ||||
psa.fa = fa; | psa.fa = fa; | ||||
psa.sa = sa; | psa.sa = sa; | ||||
psa.argv = argv; | psa.argv = argv; | ||||
psa.envp = envp; | psa.envp = envp; | ||||
Show All 9 Lines | #endif | ||||
*/ | */ | ||||
#ifdef _RFORK_THREAD_STACK_SIZE | #ifdef _RFORK_THREAD_STACK_SIZE | ||||
/* | /* | ||||
* x86 stores the return address on the stack, so rfork(2) cannot work | * x86 stores the return address on the stack, so rfork(2) cannot work | ||||
* as-is because the child would clobber the return address om the | * as-is because the child would clobber the return address om the | ||||
* parent. Because of this, we must use rfork_thread instead while | * parent. Because of this, we must use rfork_thread instead while | ||||
* almost every other arch stores the return address in a register. | * almost every other arch stores the return address in a register. | ||||
*/ | */ | ||||
p = rfork_thread(RFSPAWN, stack + _RFORK_THREAD_STACK_SIZE, | p = rfork_thread(RFSPAWN, stack + stacksz, _posix_spawn_thr, &psa); | ||||
andrew_tao173.riddles.org.uk: Does rfork_thread align the stack properly, or does it assume the caller did that? | |||||
Done Inline ActionsOn amd64, rfork_thread() loads the stack parameter into %rsp and then performs a call, so the stack will be properly aligned if stack is. Ensuring the stack size is a multiple of 16 and using aligned_alloc() will suffice. However, on i386, rfork_thread() pushes 4 bytes onto stack and then performs a call, so doing the obvious thing will lead to a misaligned stack. jilles: On amd64, `rfork_thread()` loads the `stack` parameter into `%rsp` and then performs a call, so… | |||||
Done Inline ActionsWe don't actually provide or assume any better than 4-byte stack alignment on i386, this is a source of issues with GCC-compiled ports that I've brought up in the past to no avail. andrew_tao173.riddles.org.uk: We don't actually provide or assume any better than 4-byte stack alignment on i386, this is a… | |||||
_posix_spawn_thr, &psa); | |||||
free(stack); | free(stack); | ||||
#else | #else | ||||
p = rfork(RFSPAWN); | p = rfork(RFSPAWN); | ||||
if (p == 0) | if (p == 0) | ||||
/* _posix_spawn_thr does not return */ | /* _posix_spawn_thr does not return */ | ||||
_posix_spawn_thr(&psa); | _posix_spawn_thr(&psa); | ||||
#endif | #endif | ||||
/* | /* | ||||
▲ Show 20 Lines • Show All 270 Lines • Show Last 20 Lines |
Does rfork_thread align the stack properly, or does it assume the caller did that?