xref: /xnu-8020.140.41/tests/proc_core_name_24152432.c (revision 27b03b360a988dfd3dfdf34262bb0042026747cc)
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 */
24 static const char dump_dir[]         = "/cores";
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 
51 static void
sigalrm_handler(int sig)52 sigalrm_handler(int sig)
53 {
54 	(void)sig;
55 	stop_looking = 1;
56 	return;
57 }
58 
59 static void
list_coredump_files()60 list_coredump_files()
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 
72 static int
fork_and_wait_for_segfault()73 fork_and_wait_for_segfault()
74 {
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 
89 static int
setup_coredump_kevent(struct kevent * kev,int dir)90 setup_coredump_kevent(struct kevent *kev, int dir)
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 
104 static void
look_for_coredump(const char * format,int pid,int kqfd,struct kevent * kev)105 look_for_coredump(const char *format, int pid, int kqfd, struct kevent *kev)
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 
124 		if (ret != -1) {
125 			break;
126 		}
127 
128 		T_LOG("Couldn't find coredump file (try #%d).", i + 1);
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 
140 static void
sysctl_enable_coredumps(void)141 sysctl_enable_coredumps(void)
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 }
206