Changeset View
Changeset View
Standalone View
Standalone View
lib/libc/gen/posix_spawn.c
| Show All 22 Lines | |||||
| * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | ||||
| * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | ||||
| * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | ||||
| * SUCH DAMAGE. | * SUCH DAMAGE. | ||||
| */ | */ | ||||
| #include "namespace.h" | #include "namespace.h" | ||||
| #include <sys/param.h> | #include <sys/param.h> | ||||
| #include <sys/mman.h> | |||||
| #include <sys/procctl.h> | #include <sys/procctl.h> | ||||
| #include <sys/procdesc.h> | #include <sys/procdesc.h> | ||||
| #include <sys/queue.h> | #include <sys/queue.h> | ||||
| #include <sys/wait.h> | #include <sys/wait.h> | ||||
| #include <errno.h> | #include <errno.h> | ||||
| #include <fcntl.h> | #include <fcntl.h> | ||||
| #include <sched.h> | #include <sched.h> | ||||
| ▲ Show 20 Lines • Show All 187 Lines • ▼ Show 20 Lines | struct posix_spawn_args { | ||||
| 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 * argv; | ||||
| char * const * envp; | char * const * envp; | ||||
| int use_env_path; | int use_env_path; | ||||
| volatile int error; | volatile int error; | ||||
| }; | }; | ||||
| #define PSPAWN_STACK_ALIGNMENT 16 | |||||
| #define PSPAWN_STACK_ALIGNBYTES (PSPAWN_STACK_ALIGNMENT - 1) | |||||
| #define PSPAWN_STACK_ALIGN(sz) \ | |||||
| (((sz) + PSPAWN_STACK_ALIGNBYTES) & ~PSPAWN_STACK_ALIGNBYTES) | |||||
| /* | |||||
| * Below we'll assume that _RFORK_THREAD_STACK_SIZE is appropriately aligned for | |||||
| * the posix_spawn() case where we do not end up calling execvpe and won't ever | |||||
| * try to allocate space on the stack for argv[]. | |||||
| */ | |||||
| #define _RFORK_THREAD_STACK_SIZE 4096 | #define _RFORK_THREAD_STACK_SIZE 4096 | ||||
| _Static_assert((_RFORK_THREAD_STACK_SIZE % PSPAWN_STACK_ALIGNMENT) == 0, | |||||
| "Inappropriate stack size alignment"); | |||||
| static int | static int | ||||
| _posix_spawn_thr(void *data) | _posix_spawn_thr(void *data) | ||||
| { | { | ||||
| struct posix_spawn_args *psa; | struct posix_spawn_args *psa; | ||||
| char * const *envp; | char * const *envp; | ||||
| psa = data; | psa = data; | ||||
| Show All 25 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[], bool use_env_path) | char * const argv[], char * const envp[], bool use_env_path) | ||||
| { | { | ||||
| struct posix_spawn_args psa; | struct posix_spawn_args psa; | ||||
| pid_t p; | pid_t p; | ||||
| int pfd; | int pfd; | ||||
| bool do_pfd; | bool do_pfd; | ||||
| char *stack; | char *guard, *stack; | ||||
| size_t stacksz; | size_t stacksz, pagesz; | ||||
| #if defined(__i386__) || defined(__amd64__) | #if defined(__i386__) || defined(__amd64__) | ||||
| stacksz = _RFORK_THREAD_STACK_SIZE; | stacksz = _RFORK_THREAD_STACK_SIZE; | ||||
| pagesz = getpagesize(); | |||||
| if (use_env_path) { | if (use_env_path) { | ||||
| size_t cnt; | size_t cnt; | ||||
| /* | /* | ||||
| * We need to make sure we have enough room on the stack for the | * We need to make sure we have enough room on the stack for the | ||||
| * potential alloca() in execvPe if it gets kicked back an | * potential alloca() in execvPe if it gets kicked back an | ||||
| * ENOEXEC from execve(2), plus the original buffer we gave | * ENOEXEC from execve(2), plus the original buffer we gave | ||||
| * ourselves; this protects us in the event that the caller | * ourselves; this protects us in the event that the caller | ||||
| * intentionally or inadvertently supplies enough arguments to | * intentionally or inadvertently supplies enough arguments to | ||||
| * make us blow past the stack we've allocated from it. | * make us blow past the stack we've allocated from it. | ||||
| */ | */ | ||||
| for (cnt = 0; argv[cnt] != NULL; ++cnt) | for (cnt = 0; argv[cnt] != NULL; ++cnt) | ||||
| ; | ; | ||||
| stacksz += MAX(3, cnt + 2) * sizeof(char *); | stacksz += MAX(3, cnt + 2) * sizeof(char *); | ||||
| stacksz = PSPAWN_STACK_ALIGN(stacksz); | |||||
| } | } | ||||
| /* | stacksz = round_page(stacksz); | ||||
| * aligned_alloc is not safe to use here, because we can't guarantee | guard = mmap(NULL, stacksz + pagesz, 0, MAP_GUARD, -1, 0); | ||||
| * that aligned_alloc and free will be provided by the same | if (guard == MAP_FAILED) | ||||
| * implementation. We've actively hit at least one application that | |||||
| * will provide its own malloc/free but not aligned_alloc leading to | |||||
| * a free by the wrong allocator. | |||||
| */ | |||||
| stack = malloc(stacksz); | |||||
| if (stack == NULL) | |||||
| return (ENOMEM); | return (ENOMEM); | ||||
| stacksz = (((uintptr_t)stack + stacksz) & ~PSPAWN_STACK_ALIGNBYTES) - | stack = mmap(guard + pagesz, stacksz, PROT_READ | PROT_WRITE, | ||||
| (uintptr_t)stack; | MAP_PRIVATE | MAP_FIXED | MAP_ANON, -1, 0); | ||||
| if (stack == MAP_FAILED) { | |||||
kevans: What would prevent us from being able to map over the guard here? I wonder if this should be… | |||||
Done Inline ActionsI dislike asserts in libraries. It is not the library authors to establish the assert policy for the caller. The check is mostly for correctness. If I somehow missed the correct args to mmap, I can see it immediately. kib: I dislike asserts in libraries. It is not the library authors to establish the assert policy… | |||||
Done Inline ActionsRight, but in this case we're presumably hiding a kernel bug if it does actually fail at this particular point, which feels like a reasonable exception to make for assertions in libraries. I don't insist either way, though. kevans: Right, but in this case we're presumably hiding a kernel bug if it does actually fail at this… | |||||
| munmap(guard, stacksz + pagesz); | |||||
| return (ENOMEM); | |||||
| } | |||||
| #else | #else | ||||
| stack = NULL; | guard = stack = NULL; | ||||
| stacksz = 0; | pagesz = stacksz = 0; | ||||
| #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; | ||||
| psa.use_env_path = use_env_path; | psa.use_env_path = use_env_path; | ||||
| psa.error = 0; | psa.error = 0; | ||||
| Show All 24 Lines | #endif | ||||
| */ | */ | ||||
| if (do_pfd) { | if (do_pfd) { | ||||
| p = pdrfork_thread(&pfd, PD_CLOEXEC | (*sa)->sa_pdflags, | p = pdrfork_thread(&pfd, PD_CLOEXEC | (*sa)->sa_pdflags, | ||||
| RFSPAWN, stack + stacksz, _posix_spawn_thr, &psa); | RFSPAWN, stack + stacksz, _posix_spawn_thr, &psa); | ||||
| } else { | } else { | ||||
| p = rfork_thread(RFSPAWN, stack + stacksz, _posix_spawn_thr, | p = rfork_thread(RFSPAWN, stack + stacksz, _posix_spawn_thr, | ||||
| &psa); | &psa); | ||||
| } | } | ||||
| free(stack); | if (stack != NULL) | ||||
| munmap(guard, stacksz + pagesz); | |||||
| /* | /* | ||||
| * The above block should leave us in a state where we've either | * The above block should leave us in a state where we've either | ||||
| * succeeded and we're ready to process the results, or we need to | * succeeded and we're ready to process the results, or we need to | ||||
| * fallback to vfork() if the kernel didn't like RFSPAWN. | * fallback to vfork() if the kernel didn't like RFSPAWN. | ||||
| */ | */ | ||||
| if (p == -1 && errno == EINVAL) { | if (p == -1 && errno == EINVAL) { | ||||
| ▲ Show 20 Lines • Show All 383 Lines • Show Last 20 Lines | |||||
What would prevent us from being able to map over the guard here? I wonder if this should be an assert instead.