mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: "Chang S. Bae" <chang.seok.bae@intel.com>
To: linux-kernel@vger.kernel.org, linux-kselftest@vger.kernel.org
Cc: x86@kernel.org, tglx@linutronix.de, mingo@redhat.com,
	bp@alien8.de, dave.hansen@linux.intel.com, shuah@kernel.org,
	chang.seok.bae@intel.com
Subject: [PATCH 5/6] selftests/x86/xstate: Add coredump test
Date: Tue, 10 Nov 2026 03:16:02 +0000	[thread overview]
Message-ID: <20261110031603.698635-6-chang.seok.bae@intel.com> (raw)
In-Reply-To: <20261110031603.698635-1-chang.seok.bae@intel.com>

The existing ptrace test exercises much of the XSTATE ABI handling, but
it does not cover the coredump path entirely. Add a dedicated test to
validate XSTATE handling in core dumps.

Refactor the XSAVE buffer validation code from the ptrace test for reuse.
Also, move most of the coredump-specific preparation and handling into a
header so that the PKRU test outside the x86 selftest can reuse it.

Signed-off-by: Chang S. Bae <chang.seok.bae@intel.com>
---
 tools/testing/selftests/x86/coredump.h | 229 +++++++++++++++++++++++++
 tools/testing/selftests/x86/xstate.c   |  99 ++++++++---
 2 files changed, 308 insertions(+), 20 deletions(-)
 create mode 100644 tools/testing/selftests/x86/coredump.h

diff --git a/tools/testing/selftests/x86/coredump.h b/tools/testing/selftests/x86/coredump.h
new file mode 100644
index 000000000000..8aef96667f87
--- /dev/null
+++ b/tools/testing/selftests/x86/coredump.h
@@ -0,0 +1,229 @@
+// SPDX-License-Identifier: GPL-2.0-only
+#ifndef __SELFTESTS_X86_COREDUMP_H
+#define __SELFTESTS_X86_COREDUMP_H
+
+#include <link.h>
+#include <stdint.h>
+#include <fcntl.h>
+#include <limits.h>
+
+#include <sys/resource.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+
+#include "kselftest.h"
+
+#define CORE_PATTERN_PATH "/proc/sys/kernel/core_pattern"
+#ifndef CORENAME_MAX_SIZE
+#define CORENAME_MAX_SIZE 128
+#endif
+
+static char orig_pattern[CORENAME_MAX_SIZE];
+
+static inline bool get_core_pattern(char *buf, int size)
+{
+	bool ret;
+	FILE *fp;
+
+	if (!buf)
+		return false;
+
+	fp = fopen(CORE_PATTERN_PATH, "r");
+	if (!fp)
+		return false;
+
+	ret = fgets(buf, size, fp) != NULL;
+	if (ret)
+		buf[strcspn(buf, "\n")] = 0;
+
+	fclose(fp);
+
+	return ret;
+}
+
+static inline bool set_core_pattern(const char *pattern)
+{
+	bool ret;
+	FILE *fp;
+
+	if (!pattern)
+		return false;
+
+	fp = fopen(CORE_PATTERN_PATH, "w");
+	if (!fp)
+		return false;
+
+	ret = fprintf(fp, "%s\n", pattern) >= 0;
+
+	fclose(fp);
+
+	return ret;
+}
+
+static inline bool save_core_pattern(void)
+{
+	return get_core_pattern(orig_pattern, CORENAME_MAX_SIZE);
+}
+
+static inline bool set_test_core_pattern(void)
+{
+	const char test_pattern[] = "core.%p";
+
+	if (!strcmp(orig_pattern, test_pattern))
+		return true;
+
+	return set_core_pattern(test_pattern);
+}
+
+static inline bool restore_core_pattern(void)
+{
+	return set_core_pattern(orig_pattern);
+}
+
+static char core_dir[] = "/tmp/x86-coredump-XXXXXX";
+
+static struct corefile {
+	bool dumped;
+	int fd;
+	struct stat st;
+	char *image;
+	char path[PATH_MAX];
+} core_file = { false, -1, { }, NULL, "\0" };
+
+static inline void corefile_reset(void)
+{
+	close(core_file.fd);
+	unlink(core_file.path);
+
+	if (core_file.image != NULL && core_file.image != MAP_FAILED)
+		munmap((void *)core_file.image, core_file.st.st_size);
+
+	core_file = (struct corefile){ .dumped = false, .fd = -1, .st = { },
+				       .image = NULL, .path = "\0" };
+}
+
+static inline void coredump_cleanup(void)
+{
+	if (core_file.dumped)
+		corefile_reset();
+
+	rmdir(core_dir);
+	strcpy(core_dir, "/tmp/x86-coredump-XXXXXX");
+	restore_core_pattern();
+}
+
+/*
+ * Create a temporary directory for the core file and configure core_pattern to
+ * use a predictable file name. Save the original pattern so it can be restored
+ * after the test.
+ */
+static inline bool coredump_prepare(void)
+{
+	struct rlimit lim = { RLIM_INFINITY, RLIM_INFINITY };
+
+	if (setrlimit(RLIMIT_CORE, &lim)) {
+		ksft_test_result_skip("Failed to set rlimit. core Try sudo.\n");
+		return false;
+	}
+
+	if (!save_core_pattern()) {
+		ksft_test_result_skip("Failed to save core pattern.\n");
+		return false;
+	}
+
+	if (!set_test_core_pattern()) {
+		ksft_test_result_skip("Failed to update core pattern. Try sudo.\n");
+		return false;
+	}
+
+	if (!mkdtemp(core_dir)) {
+		coredump_cleanup();
+		ksft_test_result_skip("Failed to create test folder.\n");
+		return false;
+	}
+
+	return true;
+}
+
+/* Always call this for any failed exit after coredump_prepare() */
+static inline void coredump_exit_fail_msg(const char *msg)
+{
+	coredump_cleanup();
+	ksft_exit_fail_msg("%s\n", msg);
+}
+
+static inline void coredump_trigger(void)
+{
+	volatile unsigned int *ptr = NULL;
+
+	*ptr = 0xDEAD;
+}
+
+static inline void corefile_map(pid_t child)
+{
+	snprintf(core_file.path, sizeof(core_file.path), "%s/core.%d", core_dir, child);
+
+	core_file.fd = open(core_file.path, O_RDONLY);
+	if (core_file.fd < 0)
+		coredump_exit_fail_msg("Cannot open core file.");
+
+	core_file.dumped = true;
+
+	if (fstat(core_file.fd, &core_file.st))
+		coredump_exit_fail_msg("Invalid core file.");
+
+	core_file.image = mmap(NULL, core_file.st.st_size, PROT_READ, MAP_PRIVATE, core_file.fd, 0);
+	if (core_file.image == MAP_FAILED)
+		coredump_exit_fail_msg("Cannot map core file.");
+}
+
+#ifndef NT_X86_XSTATE
+#define NT_X86_XSTATE	0x202
+#endif
+
+#define next_pos(pos, size)		((pos) + (((size) + 3) & ~3))
+
+/* Program Header */
+#define phdr_ptr(core, offset)		(ElfW(Phdr) *)(core + offset)
+
+/* Note Header */
+#define nhdr_ptr(core, offset)		(ElfW(Nhdr) *)(core + offset)
+
+/*
+ * Locate the NT_X86_XSTATE note in the ELF core image and return a pointer to
+ * its XSTATE data.
+ */
+static inline char *retrieve_corefile_xbuf(void)
+{
+	ElfW(Ehdr) *ehdr;
+	ElfW(Phdr) *phdr;
+	ElfW(Nhdr) *nhdr;
+	size_t pos, end;
+	int i;
+
+	ehdr = (ElfW(Ehdr) *)core_file.image;
+	phdr = phdr_ptr(core_file.image, ehdr->e_phoff);
+
+	for (i = 0; i < ehdr->e_phnum; i++) {
+		if (phdr[i].p_type != PT_NOTE)
+			continue;
+
+		end = phdr[i].p_offset + phdr[i].p_filesz;
+		pos = phdr[i].p_offset;
+
+		while (pos + sizeof(*nhdr) <= end) {
+			nhdr  = nhdr_ptr(core_file.image, pos);
+			pos   = next_pos(pos + sizeof(*nhdr), nhdr->n_namesz);
+
+			if (nhdr->n_type == NT_X86_XSTATE)
+				return core_file.image + pos;
+
+			pos = next_pos(pos, nhdr->n_descsz);
+		}
+	}
+
+	coredump_exit_fail_msg("No NT_X86_XSTATE was found.");
+	_exit(1);
+}
+
+#endif /* __SELFTESTS_X86_COREDUMP_H */
diff --git a/tools/testing/selftests/x86/xstate.c b/tools/testing/selftests/x86/xstate.c
index a2f25ca848fb..fd04fd6fa631 100644
--- a/tools/testing/selftests/x86/xstate.c
+++ b/tools/testing/selftests/x86/xstate.c
@@ -14,6 +14,7 @@
 
 #include "helpers.h"
 #include "xstate.h"
+#include "coredump.h"
 
 /*
  * The userspace xstate test suite is designed to be generic and operates
@@ -243,29 +244,11 @@ static struct xsave_buffer *stashed_xbuf;
  * Ptrace test for the ABI format as described in arch/x86/include/asm/user.h
  */
 
-/*
- * Make sure the ptracee has the expanded kernel buffer on the first use.
- * Then, initialize the state before performing the state injection from
- * the ptracer. For non-dynamic states, this is benign.
- */
-static inline void ptracee_touch_xstate(void)
-{
-	load_rand_xstate(&xstate, stashed_xbuf);
-	load_init_xstate(&xstate, stashed_xbuf);
-}
-
-static bool validate_ptracer_xstate(pid_t target, struct iovec iov,
-				    struct xsave_buffer *xbuf,
-				    struct xsave_buffer *expected)
+static bool validate_abi_xstate(struct xsave_buffer *xbuf,
+				struct xsave_buffer *expected)
 {
 	unsigned int failed = 0;
 
-	if (ptrace(PTRACE_SETREGSET, target, (uint32_t)NT_X86_XSTATE, &iov))
-		ksft_exit_fail_msg("PTRACE_SETREGSET failed\n");
-
-	if (ptrace(PTRACE_GETREGSET, target, (uint32_t)NT_X86_XSTATE, &iov))
-		ksft_exit_fail_msg("PTRACE_GETREGSET failed\n");
-
 	/*
 	 * To track this whole result only, use printf() for skipping sub-test
 	 * counting.
@@ -295,6 +278,30 @@ static bool validate_ptracer_xstate(pid_t target, struct iovec iov,
 	return failed == 0;
 }
 
+/*
+ * Make sure the ptracee has the expanded kernel buffer on the first use.
+ * Then, initialize the state before performing the state injection from
+ * the ptracer. For non-dynamic states, this is benign.
+ */
+static inline void ptracee_touch_xstate(void)
+{
+	load_rand_xstate(&xstate, stashed_xbuf);
+	load_init_xstate(&xstate, stashed_xbuf);
+}
+
+static bool validate_ptracer_xstate(pid_t target, struct iovec iov,
+				    struct xsave_buffer *xbuf,
+				    struct xsave_buffer *expected)
+{
+	if (ptrace(PTRACE_SETREGSET, target, (uint32_t)NT_X86_XSTATE, &iov))
+		ksft_exit_fail_msg("PTRACE_SETREGSET failed\n");
+
+	if (ptrace(PTRACE_GETREGSET, target, (uint32_t)NT_X86_XSTATE, &iov))
+		ksft_exit_fail_msg("PTRACE_GETREGSET failed\n");
+
+	return validate_abi_xstate(xbuf, expected);
+}
+
 /*
  * Ptracer injects the randomized xstate data. It also reads before and
  * after that, which will execute the kernel's state copy functions.
@@ -388,6 +395,57 @@ static void test_ptrace(void)
 	free(stashed_xbuf);
 }
 
+static void test_coredump(void)
+{
+	struct xsave_buffer *xbuf, *corefile_xbuf;
+	pid_t child;
+	int status;
+
+	if (!coredump_prepare())
+		return;
+
+	ksft_print_msg("[RUN]\t%s: validate coredump.\n", xstate.name);
+
+	/*
+	 * Use a shared buffer to retain the expected XSTATE across fork() and
+	 * compare it against the XSTATE saved in the core dump.
+	 */
+	xbuf = mmap(NULL, get_xbuf_size(),
+		    PROT_READ | PROT_WRITE,
+		    MAP_SHARED | MAP_ANONYMOUS, -1, 0);
+	if (xbuf == MAP_FAILED)
+		coredump_exit_fail_msg("mmap() for the shared xsave buffer failed.");
+
+	child = fork();
+	if (child < 0)
+		coredump_exit_fail_msg("fork() failed.");
+
+	if (!child) {
+		if (chdir(core_dir)) {
+			printf("\tchdir() failed.\n");
+			_exit(1);
+		}
+		load_rand_xstate(&xstate, xbuf);
+		coredump_trigger();
+		_exit(1);
+	}
+
+	if (waitpid(child, &status, 0) != child ||
+	    !WIFSIGNALED(status) || WTERMSIG(status) != SIGSEGV)
+		coredump_exit_fail_msg("Child didn't SIGSEGV successfully.");
+
+	corefile_map(child);
+	corefile_xbuf = (struct xsave_buffer *)retrieve_corefile_xbuf();
+
+	if (validate_abi_xstate(corefile_xbuf, xbuf))
+		ksft_test_result_pass("%s for %s\n", __func__, xstate.name);
+	else
+		ksft_test_result_fail("%s for %s\n", __func__, xstate.name);
+
+	munmap(xbuf, get_xbuf_size());
+	coredump_cleanup();
+}
+
 /*
  * Test signal delivery for the ABI compatibility.
  * See the ABI format: arch/x86/include/uapi/asm/sigcontext.h
@@ -511,6 +569,7 @@ static struct test_info {
 	{ .nr = 1, .fn = test_context_switch, },
 	{ .nr = 2, .fn = test_ptrace, },
 	{ .nr = 2, .fn = test_signal, },
+	{ .nr = 1, .fn = test_coredump, },
 };
 
 int test_xstate(uint32_t feature_num)
-- 
2.53.0


  parent reply	other threads:[~2026-11-10  3:43 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-11-10  3:15 [PATCH 0/6] selftests/x86/xstate: more test cases for coredump and ptrace Chang S. Bae
2026-11-10  3:15 ` [PATCH 1/6] selftests/x86/xstate: Track testing status by kselftest API Chang S. Bae
2026-11-10  3:15 ` [PATCH 2/6] selftests/x86/xstate: Pre-allocate ptracee XSAVE buffer Chang S. Bae
2026-11-10  3:16 ` [PATCH 3/6] selftests/x86/xstate: Validate XSTATE_BV in ptrace test Chang S. Bae
2026-11-10  3:16 ` [PATCH 4/6] selftests/x86/xstate: Validate state reset " Chang S. Bae
2026-11-10  3:16 ` Chang S. Bae [this message]
2026-11-10  3:16 ` [PATCH 6/6] selftests/mm: Add coredump test for x86 Chang S. Bae

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20261110031603.698635-6-chang.seok.bae@intel.com \
    --to=chang.seok.bae@intel.com \
    --cc=bp@alien8.de \
    --cc=dave.hansen@linux.intel.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-kselftest@vger.kernel.org \
    --cc=mingo@redhat.com \
    --cc=shuah@kernel.org \
    --cc=tglx@linutronix.de \
    --cc=x86@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®