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