]>
Commit | Line | Data |
---|---|---|
d9a64523 A |
1 | #include <darwintest.h> |
2 | #include <errno.h> | |
3 | #include <fcntl.h> | |
4 | #include <sys/types.h> | |
5 | #include <sys/event.h> | |
6 | #include <sys/time.h> | |
7 | #include <sys/sysctl.h> | |
8 | #include <sys/resource.h> | |
9 | #include <signal.h> | |
10 | #include <stdlib.h> | |
11 | #include <string.h> | |
12 | #include <stdio.h> | |
13 | #include <TargetConditionals.h> | |
14 | #include <unistd.h> | |
15 | #include <dirent.h> | |
16 | ||
17 | #define BUFFLEN 2048 | |
18 | #define EVILLEN 19 | |
19 | #define TIMEOUT 420 /* Timeout in seconds to wait for coredumps to appear */ | |
20 | ||
21 | static const char corefile_ctl[] = "kern.corefile"; | |
22 | static const char coredump_ctl[] = "kern.coredump"; | |
23 | /* The directory where coredumps will be */ | |
0a7de745 | 24 | static const char dump_dir[] = "/cores"; |
d9a64523 A |
25 | /* The default coredump location if the kern.coredump ctl is invalid */ |
26 | static const char default_dump_fmt[] = "/cores/core.%d"; | |
27 | /* The coredump location when we set kern.coredump ctl to something valid */ | |
28 | static const char valid_dump_fmt[] = "/cores/test-core.%d"; | |
29 | static const char ls_path[] = "/bin/ls"; | |
30 | ||
31 | /* /cores/core.%(null), then BORK immediately after. */ | |
32 | static char evil[] = "/cores/core.%\0BORK"; | |
33 | /* A valid coredump location to test. */ | |
34 | static char valid_dump_loc[] = "/cores/test-core.%P"; | |
35 | ||
36 | static const struct rlimit lim_infty = { | |
37 | RLIM_INFINITY, | |
38 | RLIM_INFINITY | |
39 | }; | |
40 | ||
41 | static volatile int stop_looking = 0; | |
42 | ||
43 | static const struct timespec timeout = { | |
44 | TIMEOUT, | |
45 | 0 | |
46 | }; | |
47 | ||
48 | #if TARGET_OS_OSX | |
49 | static int fork_and_wait_for_segfault(void); | |
50 | ||
0a7de745 A |
51 | static void |
52 | sigalrm_handler(int sig) | |
d9a64523 A |
53 | { |
54 | (void)sig; | |
55 | stop_looking = 1; | |
56 | return; | |
57 | } | |
58 | ||
0a7de745 A |
59 | static void |
60 | list_coredump_files() | |
d9a64523 A |
61 | { |
62 | int ret; | |
63 | char buf[BUFFLEN] = { 0 }; | |
64 | ||
65 | T_LOG("Contents of %s:", dump_dir); | |
66 | snprintf(buf, BUFFLEN, "%s %s", ls_path, dump_dir); | |
67 | ret = system(buf); | |
68 | T_ASSERT_POSIX_SUCCESS(ret, "Listing contents of cores directory"); | |
69 | return; | |
70 | } | |
71 | ||
0a7de745 A |
72 | static int |
73 | fork_and_wait_for_segfault() | |
74 | { | |
d9a64523 A |
75 | int pid, ret; |
76 | pid = fork(); | |
77 | if (pid == 0) { | |
78 | unsigned int *ptr = NULL; /* Cause a segfault so that we get a coredump */ | |
79 | *ptr = 0xdeadd00d; | |
80 | T_FAIL("Expected segmentation fault on write to NULL pointer"); | |
81 | } | |
82 | T_ASSERT_TRUE(pid != -1, "Checking fork success in parent"); | |
83 | ||
84 | ret = wait(NULL); | |
85 | T_ASSERT_TRUE(ret != -1, "Waited for child to segfault and dump core"); | |
86 | return pid; | |
87 | } | |
88 | ||
0a7de745 A |
89 | static int |
90 | setup_coredump_kevent(struct kevent *kev, int dir) | |
d9a64523 A |
91 | { |
92 | int ret; | |
93 | int kqfd; | |
94 | ||
95 | EV_SET(kev, dir, EVFILT_VNODE, EV_ADD, NOTE_WRITE, 0, NULL); | |
96 | kqfd = kqueue(); | |
97 | T_ASSERT_POSIX_SUCCESS(kqfd, "kqueue: get kqueue for coredump monitoring"); | |
98 | ||
99 | ret = kevent(kqfd, kev, 1, NULL, 0, NULL); | |
100 | T_ASSERT_POSIX_SUCCESS(ret, "kevent: setup directory monitoring for coredump"); | |
101 | return kqfd; | |
102 | } | |
103 | ||
0a7de745 A |
104 | static void |
105 | look_for_coredump(const char *format, int pid, int kqfd, struct kevent *kev) | |
d9a64523 A |
106 | { |
107 | int ret = 0; | |
108 | int i = 0; | |
109 | char buf[BUFFLEN]; | |
110 | memset(buf, 0, BUFFLEN); | |
111 | /* | |
112 | * Something else might touch this directory. If we get notified and don't see | |
113 | * anything, try a few more times before failing. | |
114 | */ | |
115 | alarm(TIMEOUT); | |
116 | while (!stop_looking) { | |
117 | /* Wait for kevent to tell us the coredump folder was modified */ | |
118 | ret = kevent(kqfd, NULL, 0, kev, 1, &timeout); | |
119 | T_ASSERT_POSIX_SUCCESS(ret, "kevent: Waiting for coredump to appear"); | |
120 | ||
121 | snprintf(buf, BUFFLEN, format, pid); | |
122 | ret = remove(buf); | |
123 | ||
0a7de745 | 124 | if (ret != -1) { |
d9a64523 | 125 | break; |
0a7de745 | 126 | } |
d9a64523 | 127 | |
0a7de745 | 128 | T_LOG("Couldn't find coredump file (try #%d).", i + 1); |
d9a64523 A |
129 | i++; |
130 | } | |
131 | alarm(0); | |
132 | ||
133 | if (ret == -1) { | |
134 | /* Couldn't find the coredump -- list contents of /cores */ | |
135 | list_coredump_files(); | |
136 | } | |
137 | T_ASSERT_POSIX_SUCCESS(ret, "Removing coredump file (should be at %s)", buf); | |
138 | } | |
139 | ||
0a7de745 A |
140 | static void |
141 | sysctl_enable_coredumps(void) | |
d9a64523 A |
142 | { |
143 | int ret; | |
144 | int enable_core_dump = 1; | |
145 | size_t oldlen = BUFFLEN; | |
146 | char buf[BUFFLEN]; | |
147 | memset(buf, 0, BUFFLEN); | |
148 | ||
149 | ret = sysctlbyname(coredump_ctl, buf, &oldlen, &enable_core_dump, sizeof(int)); | |
150 | T_ASSERT_POSIX_SUCCESS(ret, "sysctl: enable core dumps"); | |
151 | ||
152 | ret = setrlimit(RLIMIT_CORE, &lim_infty); | |
153 | T_ASSERT_POSIX_SUCCESS(ret, "setrlimit: remove limit on maximum coredump size"); | |
154 | } | |
155 | #endif | |
156 | ||
157 | T_DECL( | |
158 | proc_core_name_24152432, | |
159 | "Tests behavior of core dump when kern.corefile ends in %, e.g., /cores/core.%", | |
160 | T_META_ASROOT(true), | |
161 | T_META_IGNORECRASHES("proc_core_name_24152432.*")) | |
162 | { | |
163 | #if TARGET_OS_OSX | |
164 | DIR *dirp; | |
165 | int ret, pid, dir; | |
166 | char buf[BUFFLEN]; | |
167 | memset(buf, 0, BUFFLEN); | |
168 | size_t oldlen = BUFFLEN; | |
169 | struct kevent kev; | |
170 | sig_t sig; | |
171 | int kqfd; | |
172 | ||
173 | sig = signal(SIGALRM, sigalrm_handler); | |
174 | T_WITH_ERRNO; T_EXPECT_NE(sig, SIG_ERR, "signal: set sigalrm handler"); | |
175 | ||
176 | dirp = opendir(dump_dir); | |
177 | T_ASSERT_NOTNULL(dirp, "opendir: opening coredump directory"); | |
178 | dir = dirfd(dirp); | |
179 | T_ASSERT_POSIX_SUCCESS(dir, "dirfd: getting file descriptor for coredump directory"); | |
180 | kqfd = setup_coredump_kevent(&kev, dir); | |
181 | ||
182 | sysctl_enable_coredumps(); | |
183 | ||
184 | ret = sysctlbyname(corefile_ctl, buf, &oldlen, evil, EVILLEN); | |
185 | T_ASSERT_POSIX_SUCCESS(ret, "sysctl: set bad core dump location, old value was %s", buf); | |
186 | memset(buf, 0, BUFFLEN); | |
187 | oldlen = BUFFLEN; | |
188 | ||
189 | pid = fork_and_wait_for_segfault(); | |
190 | look_for_coredump(default_dump_fmt, pid, kqfd, &kev); | |
191 | ||
192 | ret = sysctlbyname(corefile_ctl, buf, &oldlen, valid_dump_loc, strlen(valid_dump_loc)); | |
193 | T_ASSERT_POSIX_SUCCESS(ret, "sysctl: set valid core dump location, old value was %s", buf); | |
194 | memset(buf, 0, BUFFLEN); | |
195 | ||
196 | pid = fork_and_wait_for_segfault(); | |
197 | look_for_coredump(valid_dump_fmt, pid, kqfd, &kev); | |
198 | ||
199 | closedir(dirp); | |
200 | close(kqfd); | |
201 | #else | |
202 | T_LOG("proc_core_name appears in OS X only, skipping test."); | |
203 | #endif | |
204 | T_PASS("proc_core_name_24152432 PASSED"); | |
205 | } |