This is xnu-11215.1.10. See this file in:
#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");
}