From: Andrea Cervesato <andrea.cervesato@suse.de>
To: Linux Test Project <ltp@lists.linux.it>
Subject: [LTP] [PATCH v9 2/3] coredump01: New core_pattern specifiers test
Date: Wed, 09 Sep 2026 12:04:49 +0200 [thread overview]
Message-ID: <20260909-coredump-v9-2-6b0ee752f57e@suse.com> (raw)
In-Reply-To: <20260909-coredump-v9-0-6b0ee752f57e@suse.com>
From: Andrea Cervesato <andrea.cervesato@suse.com>
LTP has no coverage for the core_pattern specifiers expansion, neither
for the pipe flavor of core_pattern, where the kernel spawns a user
space helper and writes the core dump into its standard input.
The new test verifies that %e, %p and %s are expanded both in the core
file name and in the arguments of a piped helper, checking also that
the helper really receives an ELF core dump.
Root is required to rewrite the system wide core_pattern, which is
restored by the test library on all exit paths.
Reviewed-by: Li Wang <li.wang@linux.dev>
Reviewed-by: Cyril Hrubis <chrubis@suse.cz>
Signed-off-by: Andrea Cervesato <andrea.cervesato@suse.com>
---
runtest/kernel_misc | 1 +
testcases/kernel/Makefile | 1 +
testcases/kernel/coredump/.gitignore | 2 +
testcases/kernel/coredump/Makefile | 7 ++
testcases/kernel/coredump/coredump01.c | 162 ++++++++++++++++++++++++++
testcases/kernel/coredump/coredump01_helper.c | 73 ++++++++++++
testcases/kernel/coredump/coredump_common.h | 52 +++++++++
7 files changed, 298 insertions(+)
diff --git a/runtest/kernel_misc b/runtest/kernel_misc
index cc3562cb7..ecf9ee2a2 100644
--- a/runtest/kernel_misc
+++ b/runtest/kernel_misc
@@ -17,3 +17,4 @@ zram02 zram02.sh
zram03 zram03
umip_basic_test umip_basic_test
aslr01 aslr01
+coredump01 coredump01
diff --git a/testcases/kernel/Makefile b/testcases/kernel/Makefile
index ac816e4e8..6b303c14b 100644
--- a/testcases/kernel/Makefile
+++ b/testcases/kernel/Makefile
@@ -19,6 +19,7 @@ SUBDIRS += lib
SUBDIRS += connectors \
containers \
controllers \
+ coredump \
crypto \
device-drivers \
firmware \
diff --git a/testcases/kernel/coredump/.gitignore b/testcases/kernel/coredump/.gitignore
new file mode 100644
index 000000000..cd0b51700
--- /dev/null
+++ b/testcases/kernel/coredump/.gitignore
@@ -0,0 +1,2 @@
+/coredump01
+/coredump01_helper
diff --git a/testcases/kernel/coredump/Makefile b/testcases/kernel/coredump/Makefile
new file mode 100644
index 000000000..951a3e976
--- /dev/null
+++ b/testcases/kernel/coredump/Makefile
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2026 Linux Test Project
+
+top_srcdir ?= ../../..
+
+include $(top_srcdir)/include/mk/testcases.mk
+include $(top_srcdir)/include/mk/generic_leaf_target.mk
diff --git a/testcases/kernel/coredump/coredump01.c b/testcases/kernel/coredump/coredump01.c
new file mode 100644
index 000000000..c4fb92e4d
--- /dev/null
+++ b/testcases/kernel/coredump/coredump01.c
@@ -0,0 +1,162 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2026 Linux Test Project
+ */
+
+/*\
+ * Verify that the kernel expands the :manpage:`core(5)` specifiers %e
+ * (executable name), %p (PID) and %s (signal number) when it creates a
+ * core dump.
+ *
+ * Both flavors of ``/proc/sys/kernel/core_pattern`` are tested:
+ *
+ * - a plain file name, where the specifiers become part of the core file
+ * name
+ * - a pipe to a user space helper, where the specifiers are expanded in
+ * the helper arguments and the core dump is written to the helper
+ * standard input
+ *
+ * The test needs root because it rewrites the system wide core_pattern.
+ * The original value is saved and restored by the test library on all
+ * exit paths.
+ *
+ * [Algorithm]
+ *
+ * - Point core_pattern into the test temporary directory
+ * - Fork a child which aborts itself to produce a core dump
+ * - For the file pattern check that ``core.<exe>.<pid>.<signal>`` was created
+ * - For the pipe pattern check the arguments the helper was called with
+ * and that it received an ELF core dump on its standard input
+ */
+
+#include "coredump_common.h"
+#include "tst_kconfig.h"
+
+#define HELPER "coredump01_helper"
+#define HELPER_TIMEOUT 10
+
+static char helper_path[PATH_MAX];
+static int static_usermodehelper;
+
+/*
+ * e_ident[] and e_type live at the same file offset in ELF32 and ELF64,
+ * so we can validate both without picking a class.
+ */
+static void verify_elf_core(const char *path)
+{
+ unsigned char hdr[EI_NIDENT + sizeof(Elf32_Half)];
+ Elf32_Half e_type;
+ int fd;
+
+ fd = SAFE_OPEN(path, O_RDONLY);
+ SAFE_READ(1, fd, hdr, sizeof(hdr));
+ SAFE_CLOSE(fd);
+
+ memcpy(&e_type, hdr + EI_NIDENT, sizeof(e_type));
+
+ TST_EXP_EXPR(!memcmp(hdr, ELFMAG, SELFMAG), "%s starts with ELF magic", path);
+ TST_EXP_EQ_LI(e_type, ET_CORE);
+}
+
+static void verify_file_pattern(void)
+{
+ char dump[PATH_MAX + 32];
+ pid_t pid;
+
+ set_pattern("%s/core.%%e.%%p.%%s", cwd);
+
+ pid = crash_child();
+
+ snprintf(dump, sizeof(dump), "%s/core.coredump01.%d.%d", cwd, pid, SIGABRT);
+
+ TST_EXP_PASS(access(dump, F_OK), "core.%%e.%%p.%%s expanded to core.coredump01.%d.%d",
+ pid, SIGABRT);
+ if (!TST_PASS)
+ return;
+
+ verify_elf_core(dump);
+}
+
+static void verify_pipe_pattern(void)
+{
+ char res[PATH_MAX + 32], exe[PATH_MAX];
+ int pid_seen, sig_seen, elf, et_core;
+ long long bytes;
+ pid_t pid;
+
+ if (static_usermodehelper) {
+ tst_res(TCONF, "CONFIG_STATIC_USERMODEHELPER is enabled, skipping pipe core_pattern");
+ return;
+ }
+
+ set_pattern("|%s %%e %%p %%s %s/res.%%p", helper_path, cwd);
+
+ pid = crash_child();
+
+ snprintf(res, sizeof(res), "%s/res.%d", cwd, pid);
+
+ /* the kernel spawns the helper asynchronously */
+ if (TST_RETRY_FN_EXP_BACKOFF(access(res, F_OK), TST_RETVAL_EQ0, HELPER_TIMEOUT)) {
+ tst_res(TFAIL, "%s did not report any core dump", HELPER);
+ return;
+ }
+
+ SAFE_FILE_SCANF(res, "exe=%15s pid=%d sig=%d bytes=%lld elf=%d et_core=%d",
+ exe, &pid_seen, &sig_seen, &bytes, &elf, &et_core);
+
+ TST_EXP_EQ_STR(exe, "coredump01");
+ TST_EXP_EQ_LI(pid_seen, pid);
+ TST_EXP_EQ_LI(sig_seen, SIGABRT);
+ TST_EXP_EXPR(elf && bytes > 0, "%s read %lli bytes of ELF core dump", HELPER, bytes);
+ TST_EXP_EQ_LI(et_core, 1);
+}
+
+static struct tcase {
+ void (*verify_fn)(void);
+ const char *desc;
+} tcases[] = {
+ {verify_file_pattern, "file core_pattern"},
+ {verify_pipe_pattern, "pipe core_pattern"},
+};
+
+static void run(unsigned int n)
+{
+ struct tcase *tc = &tcases[n];
+
+ tst_res(TINFO, "Testing %s", tc->desc);
+ tc->verify_fn();
+}
+
+static void setup(void)
+{
+ char path[PATH_MAX];
+ struct tst_kconfig_var kconfig = TST_KCONFIG_INIT("CONFIG_STATIC_USERMODEHELPER");
+
+ coredump_setup();
+
+ tst_kconfig_read(&kconfig, 1);
+ static_usermodehelper = (kconfig.choice == 'y');
+
+ if (tst_get_path(HELPER, path, sizeof(path)))
+ tst_brk(TCONF, "'%s' not found in $PATH", HELPER);
+
+ if (!realpath(path, helper_path))
+ tst_brk(TBROK | TERRNO, "realpath(%s) failed", path);
+}
+
+static struct tst_test test = {
+ .test = run,
+ .tcnt = ARRAY_SIZE(tcases),
+ .setup = setup,
+ .needs_root = 1,
+ .needs_tmpdir = 1,
+ .forks_child = 1,
+ .needs_kconfigs = (const char* []) {
+ "CONFIG_COREDUMP=y",
+ NULL,
+ },
+ .save_restore = (const struct tst_path_val[]) {
+ {PATH_KERN_CORE_PATTERN, NULL, TST_SR_TCONF},
+ {}
+ },
+};
diff --git a/testcases/kernel/coredump/coredump01_helper.c b/testcases/kernel/coredump/coredump01_helper.c
new file mode 100644
index 000000000..b454de476
--- /dev/null
+++ b/testcases/kernel/coredump/coredump01_helper.c
@@ -0,0 +1,73 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2026 Linux Test Project
+ */
+
+/*
+ * Core dump collector for the piped core_pattern tested by coredump01.
+ *
+ * Avoiding the LTP API here is correct, since the kernel spawns the helper
+ * through ``call_usermodehelper()`` without the LTP IPC environment.
+ *
+ * The helper is called as:
+ *
+ * coredump01_helper <exe> <pid> <signal> <result file>
+ *
+ * and stores what it received in the result file, which is published with
+ * :manpage:`rename()` so that the polling test never reads a partial line.
+ */
+
+#include <elf.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+int main(int argc, char *argv[])
+{
+ unsigned char buf[4096], hdr[EI_NIDENT + sizeof(Elf32_Half)];
+ char tmp[PATH_MAX];
+ unsigned int hdr_len = 0;
+ long long bytes = 0;
+ ssize_t rval, i;
+ int fd, elf = 0, et_core = 0;
+ Elf32_Half e_type;
+
+ if (argc < 5)
+ return 1;
+
+ /*
+ * The ELF header is only meaningful at the very beginning of the
+ * stream. Collect enough bytes to read e_ident and e_type.
+ */
+ while ((rval = read(STDIN_FILENO, buf, sizeof(buf))) > 0) {
+ for (i = 0; i < rval && hdr_len < sizeof(hdr); i++)
+ hdr[hdr_len++] = buf[i];
+
+ bytes += rval;
+ }
+
+ if (rval < 0)
+ return 1;
+
+ if (hdr_len >= sizeof(hdr)) {
+ if (!memcmp(hdr, ELFMAG, SELFMAG))
+ elf = 1;
+ memcpy(&e_type, hdr + EI_NIDENT, sizeof(e_type));
+ et_core = (e_type == ET_CORE);
+ }
+
+ snprintf(tmp, sizeof(tmp), "%s.tmp", argv[4]);
+
+ fd = open(tmp, O_WRONLY | O_CREAT | O_TRUNC, 0644);
+ if (fd < 0)
+ return 1;
+
+ dprintf(fd, "exe=%s pid=%s sig=%s bytes=%lld elf=%d et_core=%d\n",
+ argv[1], argv[2], argv[3], bytes, elf, et_core);
+
+ close(fd);
+
+ return rename(tmp, argv[4]) ? 1 : 0;
+}
diff --git a/testcases/kernel/coredump/coredump_common.h b/testcases/kernel/coredump/coredump_common.h
new file mode 100644
index 000000000..bccb4bcaa
--- /dev/null
+++ b/testcases/kernel/coredump/coredump_common.h
@@ -0,0 +1,52 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2026 Linux Test Project
+ */
+
+#ifndef COREDUMP_COMMON_H
+#define COREDUMP_COMMON_H
+
+#include <elf.h>
+#include <sys/wait.h>
+
+#include "tst_test.h"
+#include "lapi/prctl.h"
+
+static char cwd[PATH_MAX];
+
+static void set_pattern(const char *fmt, ...)
+{
+ va_list va;
+
+ va_start(va, fmt);
+ SAFE_FILE_VPRINTF(PATH_KERN_CORE_PATTERN, fmt, va);
+ va_end(va);
+}
+
+static pid_t crash_child(void)
+{
+ int status;
+ pid_t pid;
+
+ pid = SAFE_FORK();
+ if (!pid)
+ abort();
+
+ SAFE_WAITPID(pid, &status, 0);
+
+ if (!WIFSIGNALED(status) || !WCOREDUMP(status))
+ tst_brk(TFAIL, "Child did not dump core");
+
+ return pid;
+}
+
+static void coredump_setup(void)
+{
+ struct rlimit rl = {RLIM_INFINITY, RLIM_INFINITY};
+
+ SAFE_SETRLIMIT(RLIMIT_CORE, &rl);
+ SAFE_PRCTL(PR_SET_DUMPABLE, 1, 0, 0, 0);
+ SAFE_GETCWD(cwd, sizeof(cwd));
+}
+
+#endif /* COREDUMP_COMMON_H */
--
2.51.0
--
Mailing list info: https://lists.linux.it/listinfo/ltp
next prev parent reply other threads:[~2026-09-09 10:05 UTC|newest]
Thread overview: 6+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-09 10:04 [LTP] [PATCH v9 0/3] coredump testing suite Andrea Cervesato
2026-09-09 10:04 ` [LTP] [PATCH v9 1/3] lib: Add SAFE_FILE_VPRINTF() Andrea Cervesato
2026-09-09 10:35 ` [LTP] " linuxtestproject.agent
2026-09-09 12:05 ` Andrea Cervesato via ltp
2026-09-09 10:04 ` Andrea Cervesato [this message]
2026-09-09 10:04 ` [LTP] [PATCH v9 3/3] coredump02: Verify ELF structure and notes Andrea Cervesato
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=20260909-coredump-v9-2-6b0ee752f57e@suse.com \
--to=andrea.cervesato@suse.de \
--cc=ltp@lists.linux.it \
/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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.