Skip to content
Permalink
Browse files

Add a test for verifying procinfo note inside coredumps.

Add a first test for triggering a core dump in the debugged process
(via PT_DUMPCORE) and verifying it.  The test finds procinfo note
and checks its contents.

The core dump is processed through libelf.  However, it only provides
for finding all note segments (or sections?).  I had to implement
finding and processing individual notes myself.  I've added
a core_find_note() function that will be reused in future tests.

Reviewed by kamil.
  • Loading branch information...
mgorny committed Jun 30, 2019
1 parent a09a103 commit 4428941850bbc00bd772264bbc0ffdbe1534ae51
Showing with 190 additions and 9 deletions.
  1. +7 −7 tests/lib/libc/sys/Makefile
  2. +183 −2 tests/lib/libc/sys/t_ptrace_wait.c
@@ -1,4 +1,4 @@
# $NetBSD: Makefile,v 1.56 2019/04/26 20:41:10 maya Exp $
# $NetBSD: Makefile,v 1.57 2019/06/30 21:20:04 mgorny Exp $

MKMAN= no

@@ -88,12 +88,12 @@ SRCS.t_mprotect= t_mprotect.c ${SRCS_EXEC_PROT}

LDADD.t_getpid+= -lpthread

LDADD.t_ptrace_wait+= -pthread -lm
LDADD.t_ptrace_wait3+= -pthread -lm
LDADD.t_ptrace_wait4+= -pthread -lm
LDADD.t_ptrace_wait6+= -pthread -lm
LDADD.t_ptrace_waitid+= -pthread -lm
LDADD.t_ptrace_waitpid+= -pthread -lm
LDADD.t_ptrace_wait+= -pthread -lm -lelf
LDADD.t_ptrace_wait3+= -pthread -lm -lelf
LDADD.t_ptrace_wait4+= -pthread -lm -lelf
LDADD.t_ptrace_wait6+= -pthread -lm -lelf
LDADD.t_ptrace_waitid+= -pthread -lm -lelf
LDADD.t_ptrace_waitpid+= -pthread -lm -lelf

.if (${MKRUMP} != "no") && !defined(BSD_MK_COMPAT_FILE)
CPPFLAGS.t_posix_fadvise.c += -D_KERNTYPES
@@ -1,4 +1,4 @@
/* $NetBSD: t_ptrace_wait.c,v 1.129 2019/06/26 12:30:13 mgorny Exp $ */
/* $NetBSD: t_ptrace_wait.c,v 1.130 2019/06/30 21:20:04 mgorny Exp $ */

/*-
* Copyright (c) 2016, 2017, 2018, 2019 The NetBSD Foundation, Inc.
@@ -27,10 +27,11 @@
*/

#include <sys/cdefs.h>
__RCSID("$NetBSD: t_ptrace_wait.c,v 1.129 2019/06/26 12:30:13 mgorny Exp $");
__RCSID("$NetBSD: t_ptrace_wait.c,v 1.130 2019/06/30 21:20:04 mgorny Exp $");

#include <sys/param.h>
#include <sys/types.h>
#include <sys/exec_elf.h>
#include <sys/mman.h>
#include <sys/ptrace.h>
#include <sys/resource.h>
@@ -43,6 +44,7 @@ __RCSID("$NetBSD: t_ptrace_wait.c,v 1.129 2019/06/26 12:30:13 mgorny Exp $");
#include <elf.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <lwp.h>
#include <pthread.h>
#include <sched.h>
@@ -66,6 +68,9 @@ __RCSID("$NetBSD: t_ptrace_wait.c,v 1.129 2019/06/26 12:30:13 mgorny Exp $");
#include <x86/specialreg.h>
#endif

#include <libelf.h>
#include <gelf.h>

#include <atf-c.h>

#include "h_macros.h"
@@ -7639,6 +7644,180 @@ USER_VA0_DISABLE(user_va0_disable_pt_detach, PT_DETACH)

/// ----------------------------------------------------------------------------

/*
* Parse the core file and find the requested note. If the reading or parsing
* fails, the test is failed. If the note is found, it is read onto buf, up to
* buf_len. The actual length of the note is returned (which can be greater
* than buf_len, indicating that it has been truncated). If the note is not
* found, -1 is returned.
*/
static ssize_t core_find_note(const char *core_path,
const char *note_name, uint64_t note_type, void *buf, size_t buf_len)
{
int core_fd;
Elf *core_elf;
size_t core_numhdr, i;
ssize_t ret = -1;
/* note: we assume note name will be null-terminated */
size_t name_len = strlen(note_name) + 1;

SYSCALL_REQUIRE((core_fd = open(core_path, O_RDONLY)) != -1);
SYSCALL_REQUIRE(elf_version(EV_CURRENT) != EV_NONE);
SYSCALL_REQUIRE((core_elf = elf_begin(core_fd, ELF_C_READ, NULL)));

SYSCALL_REQUIRE(elf_getphnum(core_elf, &core_numhdr) != 0);
for (i = 0; i < core_numhdr && ret == -1; i++) {
GElf_Phdr core_hdr;
size_t offset;
SYSCALL_REQUIRE(gelf_getphdr(core_elf, i, &core_hdr));
if (core_hdr.p_type != PT_NOTE)
continue;

for (offset = core_hdr.p_offset;
offset < core_hdr.p_offset + core_hdr.p_filesz;) {
Elf64_Nhdr note_hdr;
char name_buf[64];

switch (gelf_getclass(core_elf)) {
case ELFCLASS64:
SYSCALL_REQUIRE(pread(core_fd, &note_hdr,
sizeof(note_hdr), offset)
== sizeof(note_hdr));
offset += sizeof(note_hdr);
break;
case ELFCLASS32:
{
Elf32_Nhdr tmp_hdr;
SYSCALL_REQUIRE(pread(core_fd, &tmp_hdr,
sizeof(tmp_hdr), offset)
== sizeof(tmp_hdr));
offset += sizeof(tmp_hdr);
note_hdr.n_namesz = tmp_hdr.n_namesz;
note_hdr.n_descsz = tmp_hdr.n_descsz;
note_hdr.n_type = tmp_hdr.n_type;
}
break;
}

/* indicates end of notes */
if (note_hdr.n_namesz == 0 || note_hdr.n_descsz == 0)
break;
if (note_hdr.n_namesz == name_len &&
note_hdr.n_namesz <= sizeof(name_buf)) {
SYSCALL_REQUIRE(pread(core_fd, name_buf,
note_hdr.n_namesz, offset)
== note_hdr.n_namesz);

if (!strncmp(note_name, name_buf, name_len) &&
note_hdr.n_type == note_type)
ret = note_hdr.n_descsz;
}

offset += note_hdr.n_namesz;
/* fix to alignment */
offset = ((offset + core_hdr.p_align - 1)
/ core_hdr.p_align) * core_hdr.p_align;

/* if name & type matched above */
if (ret != -1) {
ssize_t read_len = MIN(buf_len,
note_hdr.n_descsz);
SYSCALL_REQUIRE(pread(core_fd, buf,
read_len, offset) == read_len);
break;
}

offset += note_hdr.n_descsz;
}
}

elf_end(core_elf);
close(core_fd);

return ret;
}

ATF_TC(core_dump_procinfo);
ATF_TC_HEAD(core_dump_procinfo, tc)
{
atf_tc_set_md_var(tc, "descr",
"Trigger a core dump and verify its contents.");
}

ATF_TC_BODY(core_dump_procinfo, tc)
{
const int exitval = 5;
pid_t child, wpid;
#if defined(TWAIT_HAVE_STATUS)
const int sigval = SIGTRAP;
int status;
#endif
char core_path[] = "/tmp/core.XXXXXX";
int core_fd;
struct netbsd_elfcore_procinfo procinfo;

DPRINTF("Before forking process PID=%d\n", getpid());
SYSCALL_REQUIRE((child = fork()) != -1);
if (child == 0) {
DPRINTF("Before calling PT_TRACE_ME from child %d\n", getpid());
FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);

DPRINTF("Before triggering SIGTRAP\n");
trigger_trap();

DPRINTF("Before exiting of the child process\n");
_exit(exitval);
}
DPRINTF("Parent process PID=%d, child's PID=%d\n", getpid(), child);

DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME);
TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);

validate_status_stopped(status, sigval);

SYSCALL_REQUIRE((core_fd = mkstemp(core_path)) != -1);
close(core_fd);

DPRINTF("Call DUMPCORE for the child process\n");
SYSCALL_REQUIRE(ptrace(PT_DUMPCORE, child, core_path, strlen(core_path))
!= -1);

DPRINTF("Read core file\n");
ATF_REQUIRE_EQ(core_find_note(core_path, "NetBSD-CORE",
ELF_NOTE_NETBSD_CORE_PROCINFO, &procinfo, sizeof(procinfo)),
sizeof(procinfo));

ATF_CHECK_EQ(procinfo.cpi_version, 1);
ATF_CHECK_EQ(procinfo.cpi_cpisize, sizeof(procinfo));
ATF_CHECK_EQ(procinfo.cpi_signo, SIGTRAP);
ATF_CHECK_EQ(procinfo.cpi_pid, child);
ATF_CHECK_EQ(procinfo.cpi_ppid, getpid());
ATF_CHECK_EQ(procinfo.cpi_pgrp, getpgid(child));
ATF_CHECK_EQ(procinfo.cpi_sid, getsid(child));
ATF_CHECK_EQ(procinfo.cpi_ruid, getuid());
ATF_CHECK_EQ(procinfo.cpi_euid, geteuid());
ATF_CHECK_EQ(procinfo.cpi_rgid, getgid());
ATF_CHECK_EQ(procinfo.cpi_egid, getegid());
ATF_CHECK_EQ(procinfo.cpi_nlwps, 1);
ATF_CHECK_EQ(procinfo.cpi_siglwp, 1);

unlink(core_path);

DPRINTF("Before resuming the child process where it left off and "
"without signal to be sent\n");
SYSCALL_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);

DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME);
TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);

validate_status_exited(status, exitval);

DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME);
TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0));
}

/// ----------------------------------------------------------------------------

#include "t_ptrace_amd64_wait.h"
#include "t_ptrace_i386_wait.h"
#include "t_ptrace_x86_wait.h"
@@ -8130,6 +8309,8 @@ ATF_TP_ADD_TCS(tp)
ATF_TP_ADD_TC(tp, user_va0_disable_pt_syscall);
ATF_TP_ADD_TC(tp, user_va0_disable_pt_detach);

ATF_TP_ADD_TC(tp, core_dump_procinfo);

ATF_TP_ADD_TCS_PTRACE_WAIT_AMD64();
ATF_TP_ADD_TCS_PTRACE_WAIT_I386();
ATF_TP_ADD_TCS_PTRACE_WAIT_X86();

0 comments on commit 4428941

Please sign in to comment.
You can’t perform that action at this time.