aboutsummaryrefslogtreecommitdiffstats
path: root/lib/libc/gen/posix_spawn.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/libc/gen/posix_spawn.c')
-rw-r--r--lib/libc/gen/posix_spawn.c44
1 files changed, 41 insertions, 3 deletions
diff --git a/lib/libc/gen/posix_spawn.c b/lib/libc/gen/posix_spawn.c
index 5f4e74825362..805e42780be3 100644
--- a/lib/libc/gen/posix_spawn.c
+++ b/lib/libc/gen/posix_spawn.c
@@ -28,6 +28,7 @@
__FBSDID("$FreeBSD$");
#include "namespace.h"
+#include <sys/param.h>
#include <sys/queue.h>
#include <sys/wait.h>
@@ -202,8 +203,20 @@ struct posix_spawn_args {
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)
+
#if defined(__i386__) || defined(__amd64__)
+/*
+ * 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
+_Static_assert((_RFORK_THREAD_STACK_SIZE % PSPAWN_STACK_ALIGNMENT) == 0,
+ "Inappropriate stack size alignment");
#endif
static int
@@ -244,10 +257,36 @@ do_posix_spawn(pid_t *pid, const char *path,
pid_t p;
#ifdef _RFORK_THREAD_STACK_SIZE
char *stack;
+ size_t cnt, stacksz;
+
+ 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.
+ */
+ for (cnt = 0; argv[cnt] != NULL; ++cnt)
+ ;
+ stacksz += MAX(3, cnt + 2) * sizeof(char *);
+ stacksz = PSPAWN_STACK_ALIGN(stacksz);
+ }
- stack = malloc(_RFORK_THREAD_STACK_SIZE);
+ /*
+ * aligned_alloc is not safe to use here, because we can't guarantee
+ * that aligned_alloc and free will be provided by the same
+ * 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);
+ stacksz = (((uintptr_t)stack + stacksz) & ~PSPAWN_STACK_ALIGNBYTES) -
+ (uintptr_t)stack;
#endif
psa.path = path;
psa.fa = fa;
@@ -271,8 +310,7 @@ do_posix_spawn(pid_t *pid, const char *path,
* parent. Because of this, we must use rfork_thread instead while
* almost every other arch stores the return address in a register.
*/
- p = rfork_thread(RFSPAWN, stack + _RFORK_THREAD_STACK_SIZE,
- _posix_spawn_thr, &psa);
+ p = rfork_thread(RFSPAWN, stack + stacksz, _posix_spawn_thr, &psa);
free(stack);
#else
p = rfork(RFSPAWN);