]> git.saurik.com Git - apple/libpthread.git/blob - tests/stack_aslr.c
libpthread-330.230.1.tar.gz
[apple/libpthread.git] / tests / stack_aslr.c
1 #include <assert.h>
2 #include <stdlib.h>
3 #include <pthread.h>
4 #include <unistd.h>
5 #include <dispatch/dispatch.h>
6 #include <sys/mman.h>
7 #include <stdatomic.h>
8
9 #include "darwintest_defaults.h"
10 #include <darwintest_multiprocess.h>
11 #include <darwintest_utils.h>
12
13 #define T_LOG_VERBOSE(...)
14
15 #ifndef T_MAYFAIL_WITH_REASON
16 #define T_MAYFAIL_WITH_REASON(x) T_MAYFAIL
17 #endif
18
19 #ifdef __LP64__
20 #define STACK_LOCATIONS 16
21 #else
22 #define STACK_LOCATIONS 8
23 #endif
24
25 static const int attempts = 128, attempt_rounds = 3;
26
27 static void*
28 thread_routine(void *loc)
29 {
30 int foo;
31 *(uintptr_t*)loc = (uintptr_t)&foo;
32 return NULL;
33 }
34
35 static int
36 pointer_compare(const void *ap, const void *bp)
37 {
38 uintptr_t a = *(const uintptr_t*)ap;
39 uintptr_t b = *(const uintptr_t*)bp;
40 return a > b ? 1 : a < b ? -1 : 0;
41 }
42
43 typedef struct shmem_s {
44 _Atomic int ctr, done;
45 uintptr_t addr_array[attempts];
46 } *shmem_t;
47
48 static shmem_t
49 test_shmem_open(const char* shmem_name, int creatflags)
50 {
51 int fd = open(shmem_name, O_RDWR | creatflags, 0600);
52 T_QUIET; T_ASSERT_POSIX_SUCCESS(fd, "open temp file");
53 if (creatflags) {
54 T_QUIET; T_ASSERT_POSIX_SUCCESS(ftruncate(fd,
55 sizeof(struct shmem_s)), "resize temp file");
56 }
57 shmem_t shmem = mmap(NULL, sizeof(struct shmem_s),
58 PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
59 T_QUIET; T_ASSERT_NOTNULL(shmem, "mmap shmem");
60 T_QUIET; T_ASSERT_POSIX_SUCCESS(close(fd), "close temp file");
61 return shmem;
62 }
63
64 static uintptr_t*
65 test_shmem_start(shmem_t shmem)
66 {
67 int idx = atomic_fetch_add(&shmem->ctr, 1);
68 return &shmem->addr_array[idx];
69 }
70
71 static void
72 test_shmem_end(shmem_t shmem)
73 {
74 atomic_fetch_add(&shmem->done, 1);
75 }
76
77 T_HELPER_DECL(wq_stack_aslr_helper,
78 "Confirm that workqueue stacks are ASLRed (Helper)")
79 {
80 shmem_t shmem = test_shmem_open(argv[0], 0);
81 uintptr_t *addr = test_shmem_start(shmem);
82 dispatch_group_t g = dispatch_group_create();
83 dispatch_group_async_f(g, dispatch_get_global_queue(0,0), addr,
84 (dispatch_function_t)thread_routine);
85 dispatch_group_wait(g, DISPATCH_TIME_FOREVER);
86 dispatch_release(g);
87 test_shmem_end(shmem);
88 }
89
90 T_HELPER_DECL(pthread_stack_aslr_helper,
91 "Confirm that stacks are ASLRed (Helper)")
92 {
93 shmem_t shmem = test_shmem_open(argv[0], 0);
94 uintptr_t *addr = test_shmem_start(shmem);
95 pthread_t th;
96 int ret = pthread_create(&th, NULL, thread_routine, addr);
97 assert(ret == 0);
98 ret = pthread_join(th, NULL);
99 assert(ret == 0);
100 test_shmem_end(shmem);
101 }
102
103 static void
104 test_stack_aslr(bool workqueue_thread)
105 {
106 const char *tmpdir = dt_tmpdir();
107 char *tmp;
108 asprintf(&tmp, "%s/pthread_stack_aslr_XXXXX", tmpdir);
109 T_QUIET; T_ASSERT_NOTNULL(mkdtemp(tmp), "mkdtemp");
110
111 char *shmem_name;
112 asprintf(&shmem_name, "%s/shmem", tmp);
113 shmem_t shmem = test_shmem_open(shmem_name, O_CREAT|O_EXCL);
114 uintptr_t *addr_array = shmem->addr_array;
115
116 dt_helper_t helpers[attempts * attempt_rounds];
117 const char* helper = workqueue_thread ? "wq_stack_aslr_helper" :
118 "pthread_stack_aslr_helper";
119 char *helper_args[] = {shmem_name, NULL};
120 size_t helper_idx = 0;
121
122 struct rlimit l;
123 if (!getrlimit(RLIMIT_NOFILE, &l)) {
124 l.rlim_cur += 3 * attempts * attempt_rounds; // 3 fifos per helper
125 T_QUIET; T_ASSERT_POSIX_SUCCESS(setrlimit(RLIMIT_NOFILE, &l),
126 "setrlimit");
127 }
128 signal(SIGCHLD, SIG_IGN);
129
130 int attempt_round = attempt_rounds;
131 again:
132 bzero(shmem, sizeof(struct shmem_s));
133
134 for (int i = 0; i < attempts; i++) {
135 char *t;
136 asprintf(&t, "%s/%d", tmp, i);
137 T_QUIET; T_ASSERT_POSIX_SUCCESS(mkdir(t, 0700), "mkdir");
138 setenv("BATS_TMP_DIR", t, 1); // hack to workaround rdar://33443485
139 free(t);
140 helpers[helper_idx++] = dt_child_helper_args(helper, helper_args);
141 int w = 100;
142 do {
143 if (!w--) {
144 T_QUIET; T_FAIL("Helper should complete in <.1s");
145 goto timeout;
146 }
147 usleep(1000 * 100);
148 } while (shmem->done <= i);
149 }
150 setenv("BATS_TMP_DIR", tmpdir, 1);
151
152 qsort(addr_array, attempts, sizeof(uintptr_t), pointer_compare);
153 T_LOG("Stack address range: %p - %p (+%lx)", (void*)addr_array[0],
154 (void*)addr_array[attempts-1],
155 addr_array[attempts-1] - addr_array[0]);
156
157 int unique_values = 0;
158 T_LOG_VERBOSE("[%p]", (void*)addr_array[0]);
159 for (int i = 1; i < attempts; i++) {
160 T_LOG_VERBOSE("[%p]", (void*)addr_array[i]);
161 if (addr_array[i-1] != addr_array[i]) {
162 unique_values++;
163 }
164 }
165
166 if (--attempt_round) T_MAYFAIL_WITH_REASON("ASLR");
167 T_EXPECT_GE(unique_values, STACK_LOCATIONS,
168 "Should have more than %d unique stack locations", STACK_LOCATIONS);
169 if (attempt_round && unique_values < STACK_LOCATIONS) goto again;
170
171 timeout:
172 T_QUIET; T_EXPECT_POSIX_SUCCESS(unlink(shmem_name), "unlink temp file");
173 free(shmem_name);
174 free(tmp);
175 dt_run_helpers(helpers, helper_idx, 5);
176 }
177
178 T_DECL(pthread_stack_aslr, "Confirm that stacks are ASLRed",
179 T_META_CHECK_LEAKS(NO), T_META_ALL_VALID_ARCHS(YES))
180 {
181 test_stack_aslr(false);
182 }
183
184 T_DECL(wq_stack_aslr, "Confirm that workqueue stacks are ASLRed",
185 T_META_CHECK_LEAKS(NO), T_META_ALL_VALID_ARCHS(YES))
186 {
187 test_stack_aslr(true);
188 }