#include <unistd.h>
#include <dispatch/dispatch.h>
#include <sys/mman.h>
+#include <stdatomic.h>
-#include <darwintest.h>
+#include "darwintest_defaults.h"
+#include <darwintest_multiprocess.h>
+#include <darwintest_utils.h>
#define T_LOG_VERBOSE(...)
+#ifndef T_MAYFAIL_WITH_REASON
+#define T_MAYFAIL_WITH_REASON(x) T_MAYFAIL
+#endif
+
#ifdef __LP64__
#define STACK_LOCATIONS 16
#else
#define STACK_LOCATIONS 8
#endif
+static const int attempts = 128, attempt_rounds = 3;
+
static void*
thread_routine(void *loc)
{
return a > b ? 1 : a < b ? -1 : 0;
}
+typedef struct shmem_s {
+ _Atomic int ctr, done;
+ uintptr_t addr_array[attempts];
+} *shmem_t;
+
+static shmem_t
+test_shmem_open(const char* shmem_name, int creatflags)
+{
+ int fd = open(shmem_name, O_RDWR | creatflags, 0600);
+ T_QUIET; T_ASSERT_POSIX_SUCCESS(fd, "open temp file");
+ if (creatflags) {
+ T_QUIET; T_ASSERT_POSIX_SUCCESS(ftruncate(fd,
+ sizeof(struct shmem_s)), "resize temp file");
+ }
+ shmem_t shmem = mmap(NULL, sizeof(struct shmem_s),
+ PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
+ T_QUIET; T_ASSERT_NOTNULL(shmem, "mmap shmem");
+ T_QUIET; T_ASSERT_POSIX_SUCCESS(close(fd), "close temp file");
+ return shmem;
+}
+
+static uintptr_t*
+test_shmem_start(shmem_t shmem)
+{
+ int idx = atomic_fetch_add(&shmem->ctr, 1);
+ return &shmem->addr_array[idx];
+}
+
static void
-test_stack_aslr(bool workqueue_thread)
+test_shmem_end(shmem_t shmem)
+{
+ atomic_fetch_add(&shmem->done, 1);
+}
+
+T_HELPER_DECL(wq_stack_aslr_helper,
+ "Confirm that workqueue stacks are ASLRed (Helper)")
{
- const int attempts = 128;
- int attempt_round = 0;
+ shmem_t shmem = test_shmem_open(argv[0], 0);
+ uintptr_t *addr = test_shmem_start(shmem);
+ dispatch_group_t g = dispatch_group_create();
+ dispatch_group_async_f(g, dispatch_get_global_queue(0,0), addr,
+ (dispatch_function_t)thread_routine);
+ dispatch_group_wait(g, DISPATCH_TIME_FOREVER);
+ dispatch_release(g);
+ test_shmem_end(shmem);
+}
- uintptr_t *addr_array = mmap(NULL, sizeof(uintptr_t) * attempts,
- PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANON, -1, 0);
- T_QUIET; T_ASSERT_NOTNULL(addr_array, NULL);
+T_HELPER_DECL(pthread_stack_aslr_helper,
+ "Confirm that stacks are ASLRed (Helper)")
+{
+ shmem_t shmem = test_shmem_open(argv[0], 0);
+ uintptr_t *addr = test_shmem_start(shmem);
+ pthread_t th;
+ int ret = pthread_create(&th, NULL, thread_routine, addr);
+ assert(ret == 0);
+ ret = pthread_join(th, NULL);
+ assert(ret == 0);
+ test_shmem_end(shmem);
+}
+static void
+test_stack_aslr(bool workqueue_thread)
+{
+ const char *tmpdir = dt_tmpdir();
+ char *tmp;
+ asprintf(&tmp, "%s/pthread_stack_aslr_XXXXX", tmpdir);
+ T_QUIET; T_ASSERT_NOTNULL(mkdtemp(tmp), "mkdtemp");
+
+ char *shmem_name;
+ asprintf(&shmem_name, "%s/shmem", tmp);
+ shmem_t shmem = test_shmem_open(shmem_name, O_CREAT|O_EXCL);
+ uintptr_t *addr_array = shmem->addr_array;
+
+ dt_helper_t helpers[attempts * attempt_rounds];
+ const char* helper = workqueue_thread ? "wq_stack_aslr_helper" :
+ "pthread_stack_aslr_helper";
+ char *helper_args[] = {shmem_name, NULL};
+ size_t helper_idx = 0;
+
+ struct rlimit l;
+ if (!getrlimit(RLIMIT_NOFILE, &l)) {
+ l.rlim_cur += 3 * attempts * attempt_rounds; // 3 fifos per helper
+ T_QUIET; T_ASSERT_POSIX_SUCCESS(setrlimit(RLIMIT_NOFILE, &l),
+ "setrlimit");
+ }
+ signal(SIGCHLD, SIG_IGN);
+
+ int attempt_round = attempt_rounds;
again:
- bzero(addr_array, sizeof(uintptr_t) * attempts);
+ bzero(shmem, sizeof(struct shmem_s));
for (int i = 0; i < attempts; i++) {
- pid_t pid = fork();
- T_QUIET; T_ASSERT_POSIX_SUCCESS(pid, "[%d] fork()", i);
-
- if (pid) { // parent
- pid = waitpid(pid, NULL, 0);
- T_QUIET; T_ASSERT_POSIX_SUCCESS(pid, "[%d] waitpid()", i);
- } else if (workqueue_thread) { // child
- dispatch_async(dispatch_get_global_queue(0,0), ^{
- int foo;
- addr_array[i] = (uintptr_t)&foo;
- exit(0);
- });
- while (true) sleep(1);
- } else { // child
- pthread_t th;
- int ret = pthread_create(&th, NULL, thread_routine, &addr_array[i]);
- assert(ret == 0);
- ret = pthread_join(th, NULL);
- assert(ret == 0);
- exit(0);
- }
+ char *t;
+ asprintf(&t, "%s/%d", tmp, i);
+ T_QUIET; T_ASSERT_POSIX_SUCCESS(mkdir(t, 0700), "mkdir");
+ setenv("BATS_TMP_DIR", t, 1); // hack to workaround rdar://33443485
+ free(t);
+ helpers[helper_idx++] = dt_child_helper_args(helper, helper_args);
+ int w = 100;
+ do {
+ if (!w--) {
+ T_QUIET; T_FAIL("Helper should complete in <.1s");
+ goto timeout;
+ }
+ usleep(1000 * 100);
+ } while (shmem->done <= i);
}
+ setenv("BATS_TMP_DIR", tmpdir, 1);
qsort(addr_array, attempts, sizeof(uintptr_t), pointer_compare);
-
- T_LOG("Stack address range: %p - %p (+%lx)", (void*)addr_array[0], (void*)addr_array[attempts-1],
+ T_LOG("Stack address range: %p - %p (+%lx)", (void*)addr_array[0],
+ (void*)addr_array[attempts-1],
addr_array[attempts-1] - addr_array[0]);
int unique_values = 0;
}
}
- if (attempt_round < 3) T_MAYFAIL;
- T_EXPECT_GE(unique_values, STACK_LOCATIONS, "Should have more than %d unique stack locations", STACK_LOCATIONS);
- if (attempt_round++ < 3 && unique_values < STACK_LOCATIONS) goto again;
+ if (--attempt_round) T_MAYFAIL_WITH_REASON("ASLR");
+ T_EXPECT_GE(unique_values, STACK_LOCATIONS,
+ "Should have more than %d unique stack locations", STACK_LOCATIONS);
+ if (attempt_round && unique_values < STACK_LOCATIONS) goto again;
+
+timeout:
+ T_QUIET; T_EXPECT_POSIX_SUCCESS(unlink(shmem_name), "unlink temp file");
+ free(shmem_name);
+ free(tmp);
+ dt_run_helpers(helpers, helper_idx, 5);
}
-T_DECL(pthread_stack_aslr, "Confirm that stacks are ASLRed", T_META_CHECK_LEAKS(NO),
- T_META_ALL_VALID_ARCHS(YES))
+T_DECL(pthread_stack_aslr, "Confirm that stacks are ASLRed",
+ T_META_CHECK_LEAKS(NO), T_META_ALL_VALID_ARCHS(YES))
{
test_stack_aslr(false);
}
-T_DECL(wq_stack_aslr, "Confirm that workqueue stacks are ASLRed", T_META_CHECK_LEAKS(NO),
- T_META_ALL_VALID_ARCHS(YES))
+T_DECL(wq_stack_aslr, "Confirm that workqueue stacks are ASLRed",
+ T_META_CHECK_LEAKS(NO), T_META_ALL_VALID_ARCHS(YES))
{
test_stack_aslr(true);
}