]>
Commit | Line | Data |
---|---|---|
507116e3 A |
1 | #include <darwintest.h> |
2 | #include <sys/types.h> | |
3 | #include <pthread.h> | |
4 | #include <mach/mach_types.h> | |
5 | #include <unistd.h> | |
6 | #include <stdlib.h> | |
7 | #include <dispatch/dispatch.h> | |
8 | ||
e1ee4b85 A |
9 | typedef enum { PTHREAD, WORKQUEUE } thread_type_t; |
10 | ||
11 | typedef enum { | |
12 | NO_CORRUPTION, | |
13 | SIG_CORRUPTION, | |
14 | FULL_CORRUPTION, | |
15 | } corrupt_type_t; | |
16 | ||
507116e3 | 17 | static void * |
e1ee4b85 | 18 | body(void *ctx) |
507116e3 | 19 | { |
e1ee4b85 A |
20 | corrupt_type_t corrupt_type = (corrupt_type_t)ctx; |
21 | pthread_t self = pthread_self(); | |
22 | ||
23 | T_LOG("Helper thread running: %d", corrupt_type); | |
24 | ||
25 | // The pthread_t is stored at the top of the stack and could be | |
26 | // corrupted because of a stack overflow. To make the test more | |
27 | // reliable, we will manually smash the pthread struct directly. | |
28 | switch (corrupt_type) { | |
29 | case NO_CORRUPTION: | |
30 | break; | |
31 | case SIG_CORRUPTION: | |
32 | memset(self, 0x41, 128); | |
33 | break; | |
34 | case FULL_CORRUPTION: /* includes TSD */ | |
507116e3 | 35 | memset(self, 0x41, 4096); |
e1ee4b85 | 36 | break; |
507116e3 | 37 | } |
e1ee4b85 | 38 | |
507116e3 A |
39 | // Expected behavior is that if a thread calls abort, the process should |
40 | // abort promptly. | |
41 | abort(); | |
42 | T_FAIL("Abort didn't?"); | |
43 | } | |
44 | ||
507116e3 | 45 | static void |
e1ee4b85 | 46 | abort_test(thread_type_t type, corrupt_type_t corrupt_type) |
507116e3 A |
47 | { |
48 | pid_t child = fork(); | |
507116e3 A |
49 | |
50 | if (child == 0) { | |
51 | T_LOG("Child running"); | |
52 | switch (type) { | |
53 | case PTHREAD: { | |
54 | pthread_t tid; | |
55 | T_QUIET; | |
56 | T_ASSERT_POSIX_ZERO( | |
e1ee4b85 | 57 | pthread_create(&tid, NULL, body, (void *)corrupt_type), NULL); |
507116e3 A |
58 | break; |
59 | } | |
60 | case WORKQUEUE: { | |
61 | dispatch_async_f(dispatch_get_global_queue( | |
62 | DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), | |
e1ee4b85 | 63 | (void *)corrupt_type, &body); |
507116e3 A |
64 | break; |
65 | } | |
66 | } | |
67 | sleep(5); | |
68 | T_FAIL("Child didn't abort"); | |
69 | exit(-1); | |
e1ee4b85 A |
70 | } |
71 | ||
72 | // Wait and check the exit status of the child | |
73 | int status = 0; | |
74 | pid_t pid = wait(&status); | |
75 | T_QUIET; | |
76 | T_ASSERT_EQ(pid, child, NULL); | |
77 | T_QUIET; | |
78 | T_EXPECT_FALSE(WIFEXITED(status), "WIFEXITED Status: %x", status); | |
79 | T_QUIET; | |
80 | T_EXPECT_TRUE(WIFSIGNALED(status), "WIFSIGNALED Status: %x", status); | |
81 | T_QUIET; | |
82 | T_EXPECT_FALSE(WIFSTOPPED(status), "WIFSTOPPED Status: %x", status); | |
83 | ||
84 | // This test is successful if we trigger a SIGSEGV|SIGBUS or SIGABRT | |
85 | // since both will promptly terminate the program | |
86 | int signal = WTERMSIG(status); | |
87 | ||
88 | #if defined(__i386__) || defined(__x86_64__) | |
89 | // on intel pthread_self() reads a TSD so FULL corruption results | |
90 | // in SIGSEGV/SIGBUS | |
91 | if (corrupt_type == FULL_CORRUPTION) { | |
92 | // any of these signals may happen depending on which libpthread | |
93 | // you're running on. | |
507116e3 | 94 | if (signal == SIGBUS) { |
507116e3 A |
95 | T_LOG("Converting %d to SIGSEGV", signal); |
96 | signal = SIGSEGV; | |
97 | } | |
e1ee4b85 A |
98 | T_EXPECT_EQ(signal, SIGSEGV, NULL); |
99 | T_END; | |
507116e3 | 100 | } |
e1ee4b85 A |
101 | #endif |
102 | ||
103 | /* pthread calls abort_with_reason if only the signature is corrupt */ | |
104 | T_EXPECT_EQ(signal, SIGABRT, NULL); | |
507116e3 A |
105 | } |
106 | ||
107 | static void | |
108 | signal_handler(int signo) | |
109 | { | |
110 | // The user's signal handler should not be called during abort | |
111 | T_FAIL("Unexpected signal: %d\n", signo); | |
112 | } | |
113 | ||
e1ee4b85 | 114 | T_DECL(abort_pthread_corrupt_test_full, "Tests abort") |
507116e3 | 115 | { |
e1ee4b85 | 116 | abort_test(PTHREAD, FULL_CORRUPTION); |
507116e3 A |
117 | } |
118 | ||
e1ee4b85 | 119 | T_DECL(abort_workqueue_corrupt_test_full, "Tests abort") |
507116e3 | 120 | { |
e1ee4b85 | 121 | abort_test(WORKQUEUE, FULL_CORRUPTION); |
507116e3 A |
122 | } |
123 | ||
e1ee4b85 | 124 | T_DECL(abort_pthread_handler_test_full, "Tests abort") |
507116e3 A |
125 | { |
126 | // rdar://52892057 | |
127 | T_SKIP("Abort hangs if the user registers their own SIGSEGV handler"); | |
128 | signal(SIGSEGV, signal_handler); | |
e1ee4b85 | 129 | abort_test(PTHREAD, FULL_CORRUPTION); |
507116e3 A |
130 | } |
131 | ||
e1ee4b85 | 132 | T_DECL(abort_workqueue_handler_test_full, "Tests abort") |
507116e3 A |
133 | { |
134 | // rdar://52892057 | |
135 | T_SKIP("Abort hangs if the user registers their own SIGSEGV handler"); | |
136 | signal(SIGSEGV, signal_handler); | |
e1ee4b85 A |
137 | abort_test(WORKQUEUE, FULL_CORRUPTION); |
138 | } | |
139 | ||
140 | T_DECL(abort_pthread_corrupt_test_sig, "Tests abort") | |
141 | { | |
142 | abort_test(PTHREAD, SIG_CORRUPTION); | |
143 | } | |
144 | ||
145 | T_DECL(abort_workqueue_corrupt_test_sig, "Tests abort") | |
146 | { | |
147 | abort_test(WORKQUEUE, SIG_CORRUPTION); | |
507116e3 A |
148 | } |
149 | ||
150 | T_DECL(abort_pthread_test, "Tests abort") | |
151 | { | |
e1ee4b85 | 152 | abort_test(PTHREAD, NO_CORRUPTION); |
507116e3 A |
153 | } |
154 | ||
155 | T_DECL(abort_workqueue_test, "Tests abort") | |
156 | { | |
e1ee4b85 | 157 | abort_test(WORKQUEUE, NO_CORRUPTION); |
507116e3 | 158 | } |