* [PATCH v1 3/4] selftests/x86: Consolidate signal handler helpers
2023-04-03 4:43 [PATCH v1 0/4] selftests/x86: Improve signal test code Chang S. Bae
2023-04-03 4:43 ` [PATCH v1 1/4] selftests/x86: Fix the altstack free Chang S. Bae
2023-04-03 4:43 ` [PATCH v1 2/4] selftests/x86/mov_ss_trap: Include processor-flags.h Chang S. Bae
@ 2023-04-03 4:43 ` Chang S. Bae
2023-04-03 4:43 ` [PATCH v1 4/4] selftests/x86: Refactor altstack setup code Chang S. Bae
3 siblings, 0 replies; 5+ messages in thread
From: Chang S. Bae @ 2023-04-03 4:43 UTC (permalink / raw)
To: linux-kselftest, linux-kernel
Cc: shuah, luto, dave.hansen, tglx, bp, jun.miao, chang.seok.bae,
Borislav Petkov
Many test cases have the same signal handler (un)register helpers
which have been copied exactly over there.
Clean up this by moving this helper into a common place. Also, update
the Makefile to support this shared code.
Signed-off-by: Chang S. Bae <chang.seok.bae@intel.com>
Cc: Shuah Khan <shuah@kernel.org>
Cc: Andy Lutomirski <luto@kernel.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Borislav Petkov <bp@alien8.de>
Cc: linux-kernel@vger.kernel.org
Cc: linux-kselftest@vger.kernel.org
---
tools/testing/selftests/x86/Makefile | 16 ++++++++--
tools/testing/selftests/x86/amx.c | 25 +---------------
| 15 +---------
tools/testing/selftests/x86/entry_from_vm86.c | 25 ++--------------
tools/testing/selftests/x86/fsgsbase.c | 25 ++--------------
tools/testing/selftests/x86/helpers.c | 30 +++++++++++++++++++
tools/testing/selftests/x86/helpers.h | 6 ++++
tools/testing/selftests/x86/ioperm.c | 26 ++--------------
tools/testing/selftests/x86/iopl.c | 26 ++--------------
tools/testing/selftests/x86/ldt_gdt.c | 19 +++---------
tools/testing/selftests/x86/mov_ss_trap.c | 15 ++--------
tools/testing/selftests/x86/ptrace_syscall.c | 24 ++-------------
tools/testing/selftests/x86/sigaltstack.c | 27 ++---------------
tools/testing/selftests/x86/sigreturn.c | 25 ++--------------
.../selftests/x86/single_step_syscall.c | 23 --------------
.../testing/selftests/x86/syscall_arg_fault.c | 13 --------
tools/testing/selftests/x86/syscall_nt.c | 13 --------
tools/testing/selftests/x86/sysret_rip.c | 24 +--------------
tools/testing/selftests/x86/test_vsyscall.c | 13 --------
tools/testing/selftests/x86/unwind_vdso.c | 13 --------
20 files changed, 72 insertions(+), 331 deletions(-)
create mode 100644 tools/testing/selftests/x86/helpers.c
diff --git a/tools/testing/selftests/x86/Makefile b/tools/testing/selftests/x86/Makefile
index ca9374b56ead..382585a314c0 100644
--- a/tools/testing/selftests/x86/Makefile
+++ b/tools/testing/selftests/x86/Makefile
@@ -36,6 +36,10 @@ BINARIES_64 := $(patsubst %,$(OUTPUT)/%,$(BINARIES_64))
CFLAGS := -O2 -g -std=gnu99 -pthread -Wall $(KHDR_INCLUDES)
+LIB := helpers
+LIB_OBJ_32 := $(patsubst %,$(OUTPUT)/%.o,$(LIB:%=%_32))
+LIB_OBJ_64 := $(patsubst %,$(OUTPUT)/%.o,$(LIB:%=%_64))
+
# call32_from_64 in thunks.S uses absolute addresses.
ifeq ($(CAN_BUILD_WITH_NOPIE),1)
CFLAGS += -no-pie
@@ -69,12 +73,18 @@ all_32: $(BINARIES_32)
all_64: $(BINARIES_64)
-EXTRA_CLEAN := $(BINARIES_32) $(BINARIES_64)
+EXTRA_CLEAN := $(BINARIES_32) $(BINARIES_64) $(LIB_OBJ_32) $(LIB_OBJ_64)
+
+$(LIB_OBJ_32): $(OUTPUT)/%_32.o: %.c
+ $(CC) -O -m32 -o $@ -c $^ $(CFLAGS) $(EXTRA_CFLAGS)
+
+$(LIB_OBJ_64): $(OUTPUT)/%_64.o: %.c
+ $(CC) -O -m64 -o $@ -c $^ $(CFLAGS) $(EXTRA_CFLAGS)
-$(BINARIES_32): $(OUTPUT)/%_32: %.c helpers.h
+$(BINARIES_32): $(OUTPUT)/%_32: %.c helpers.h $(LIB_OBJ_32)
$(CC) -m32 -o $@ $(CFLAGS) $(EXTRA_CFLAGS) $^ -lrt -ldl -lm
-$(BINARIES_64): $(OUTPUT)/%_64: %.c helpers.h
+$(BINARIES_64): $(OUTPUT)/%_64: %.c helpers.h $(LIB_OBJ_64)
$(CC) -m64 -o $@ $(CFLAGS) $(EXTRA_CFLAGS) $^ -lrt -ldl
# x86_64 users should be encouraged to install 32-bit libraries
diff --git a/tools/testing/selftests/x86/amx.c b/tools/testing/selftests/x86/amx.c
index 854f7d61be89..1e8e60b1f7b9 100644
--- a/tools/testing/selftests/x86/amx.c
+++ b/tools/testing/selftests/x86/amx.c
@@ -20,6 +20,7 @@
#include <sys/uio.h>
#include "../kselftest.h" /* For __cpuid_count() */
+#include "helpers.h"
#ifndef __x86_64__
# error This test is 64-bit only
@@ -61,30 +62,6 @@ static inline void xrstor(struct xsave_buffer *xbuf, uint64_t rfbm)
/* err() exits and will not return */
#define fatal_error(msg, ...) err(1, "[FAIL]\t" msg, ##__VA_ARGS__)
-static void sethandler(int sig, void (*handler)(int, siginfo_t *, void *),
- int flags)
-{
- struct sigaction sa;
-
- memset(&sa, 0, sizeof(sa));
- sa.sa_sigaction = handler;
- sa.sa_flags = SA_SIGINFO | flags;
- sigemptyset(&sa.sa_mask);
- if (sigaction(sig, &sa, 0))
- fatal_error("sigaction");
-}
-
-static void clearhandler(int sig)
-{
- struct sigaction sa;
-
- memset(&sa, 0, sizeof(sa));
- sa.sa_handler = SIG_DFL;
- sigemptyset(&sa.sa_mask);
- if (sigaction(sig, &sa, 0))
- fatal_error("sigaction");
-}
-
#define XFEATURE_XTILECFG 17
#define XFEATURE_XTILEDATA 18
#define XFEATURE_MASK_XTILECFG (1 << XFEATURE_XTILECFG)
--git a/tools/testing/selftests/x86/corrupt_xstate_header.c b/tools/testing/selftests/x86/corrupt_xstate_header.c
index cf9ce8fbb656..111ec58cd1d0 100644
--- a/tools/testing/selftests/x86/corrupt_xstate_header.c
+++ b/tools/testing/selftests/x86/corrupt_xstate_header.c
@@ -11,13 +11,13 @@
#include <stdio.h>
#include <string.h>
#include <sched.h>
-#include <signal.h>
#include <err.h>
#include <unistd.h>
#include <stdint.h>
#include <sys/wait.h>
#include "../kselftest.h" /* For __cpuid_count() */
+#include "helpers.h"
static inline int xsave_enabled(void)
{
@@ -29,19 +29,6 @@ static inline int xsave_enabled(void)
return ecx & (1U << 27);
}
-static void sethandler(int sig, void (*handler)(int, siginfo_t *, void *),
- int flags)
-{
- struct sigaction sa;
-
- memset(&sa, 0, sizeof(sa));
- sa.sa_sigaction = handler;
- sa.sa_flags = SA_SIGINFO | flags;
- sigemptyset(&sa.sa_mask);
- if (sigaction(sig, &sa, 0))
- err(1, "sigaction");
-}
-
static void sigusr1(int sig, siginfo_t *info, void *uc_void)
{
ucontext_t *uc = uc_void;
diff --git a/tools/testing/selftests/x86/entry_from_vm86.c b/tools/testing/selftests/x86/entry_from_vm86.c
index d1e919b0c1dc..535596f9ce24 100644
--- a/tools/testing/selftests/x86/entry_from_vm86.c
+++ b/tools/testing/selftests/x86/entry_from_vm86.c
@@ -11,7 +11,6 @@
#include <assert.h>
#include <stdlib.h>
#include <sys/syscall.h>
-#include <sys/signal.h>
#include <sys/ucontext.h>
#include <unistd.h>
#include <stdio.h>
@@ -24,31 +23,11 @@
#include <errno.h>
#include <sys/vm86.h>
+#include "helpers.h"
+
static unsigned long load_addr = 0x10000;
static int nerrs = 0;
-static void sethandler(int sig, void (*handler)(int, siginfo_t *, void *),
- int flags)
-{
- struct sigaction sa;
- memset(&sa, 0, sizeof(sa));
- sa.sa_sigaction = handler;
- sa.sa_flags = SA_SIGINFO | flags;
- sigemptyset(&sa.sa_mask);
- if (sigaction(sig, &sa, 0))
- err(1, "sigaction");
-}
-
-static void clearhandler(int sig)
-{
- struct sigaction sa;
- memset(&sa, 0, sizeof(sa));
- sa.sa_handler = SIG_DFL;
- sigemptyset(&sa.sa_mask);
- if (sigaction(sig, &sa, 0))
- err(1, "sigaction");
-}
-
static sig_atomic_t got_signal;
static void sighandler(int sig, siginfo_t *info, void *ctx_void)
diff --git a/tools/testing/selftests/x86/fsgsbase.c b/tools/testing/selftests/x86/fsgsbase.c
index 8c780cce941d..94642c57e483 100644
--- a/tools/testing/selftests/x86/fsgsbase.c
+++ b/tools/testing/selftests/x86/fsgsbase.c
@@ -15,7 +15,6 @@
#include <sys/user.h>
#include <asm/prctl.h>
#include <sys/prctl.h>
-#include <signal.h>
#include <limits.h>
#include <sys/ucontext.h>
#include <sched.h>
@@ -28,6 +27,8 @@
#include <sys/wait.h>
#include <setjmp.h>
+#include "helpers.h"
+
#ifndef __x86_64__
# error This test is 64-bit only
#endif
@@ -39,28 +40,6 @@ static unsigned short *shared_scratch;
static int nerrs;
-static void sethandler(int sig, void (*handler)(int, siginfo_t *, void *),
- int flags)
-{
- struct sigaction sa;
- memset(&sa, 0, sizeof(sa));
- sa.sa_sigaction = handler;
- sa.sa_flags = SA_SIGINFO | flags;
- sigemptyset(&sa.sa_mask);
- if (sigaction(sig, &sa, 0))
- err(1, "sigaction");
-}
-
-static void clearhandler(int sig)
-{
- struct sigaction sa;
- memset(&sa, 0, sizeof(sa));
- sa.sa_handler = SIG_DFL;
- sigemptyset(&sa.sa_mask);
- if (sigaction(sig, &sa, 0))
- err(1, "sigaction");
-}
-
static void sigsegv(int sig, siginfo_t *si, void *ctx_void)
{
ucontext_t *ctx = (ucontext_t*)ctx_void;
diff --git a/tools/testing/selftests/x86/helpers.c b/tools/testing/selftests/x86/helpers.c
new file mode 100644
index 000000000000..a6ecc42d359f
--- /dev/null
+++ b/tools/testing/selftests/x86/helpers.c
@@ -0,0 +1,30 @@
+// SPDX-License-Identifier: GPL-2.0-only
+#define _GNU_SOURCE
+#include <string.h>
+#include <err.h>
+
+#include "helpers.h"
+
+void sethandler(int sig, void (*handler)(int, siginfo_t *, void *),
+ int flags)
+{
+ struct sigaction sa;
+
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_sigaction = handler;
+ sa.sa_flags = SA_SIGINFO | flags;
+ sigemptyset(&sa.sa_mask);
+ if (sigaction(sig, &sa, 0))
+ err(1, "sigaction");
+}
+
+void clearhandler(int sig)
+{
+ struct sigaction sa;
+
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_handler = SIG_DFL;
+ sigemptyset(&sa.sa_mask);
+ if (sigaction(sig, &sa, 0))
+ err(1, "sigaction");
+}
diff --git a/tools/testing/selftests/x86/helpers.h b/tools/testing/selftests/x86/helpers.h
index 4ef42c4559a9..35ff4df35397 100644
--- a/tools/testing/selftests/x86/helpers.h
+++ b/tools/testing/selftests/x86/helpers.h
@@ -2,8 +2,14 @@
#ifndef __SELFTESTS_X86_HELPERS_H
#define __SELFTESTS_X86_HELPERS_H
+#include <signal.h>
#include <asm/processor-flags.h>
+void sethandler(int sig, void (*handler)(int, siginfo_t *, void *),
+ int flags);
+
+void clearhandler(int sig);
+
static inline unsigned long get_eflags(void)
{
#ifdef __x86_64__
diff --git a/tools/testing/selftests/x86/ioperm.c b/tools/testing/selftests/x86/ioperm.c
index 57ec5e99edb9..103573c3d855 100644
--- a/tools/testing/selftests/x86/ioperm.c
+++ b/tools/testing/selftests/x86/ioperm.c
@@ -8,7 +8,6 @@
#include <err.h>
#include <stdio.h>
#include <stdint.h>
-#include <signal.h>
#include <setjmp.h>
#include <stdlib.h>
#include <string.h>
@@ -20,30 +19,9 @@
#include <sched.h>
#include <sys/io.h>
-static int nerrs = 0;
-
-static void sethandler(int sig, void (*handler)(int, siginfo_t *, void *),
- int flags)
-{
- struct sigaction sa;
- memset(&sa, 0, sizeof(sa));
- sa.sa_sigaction = handler;
- sa.sa_flags = SA_SIGINFO | flags;
- sigemptyset(&sa.sa_mask);
- if (sigaction(sig, &sa, 0))
- err(1, "sigaction");
-
-}
+#include "helpers.h"
-static void clearhandler(int sig)
-{
- struct sigaction sa;
- memset(&sa, 0, sizeof(sa));
- sa.sa_handler = SIG_DFL;
- sigemptyset(&sa.sa_mask);
- if (sigaction(sig, &sa, 0))
- err(1, "sigaction");
-}
+static int nerrs = 0;
static jmp_buf jmpbuf;
diff --git a/tools/testing/selftests/x86/iopl.c b/tools/testing/selftests/x86/iopl.c
index 7e3e09c1abac..09544e598def 100644
--- a/tools/testing/selftests/x86/iopl.c
+++ b/tools/testing/selftests/x86/iopl.c
@@ -8,7 +8,6 @@
#include <err.h>
#include <stdio.h>
#include <stdint.h>
-#include <signal.h>
#include <setjmp.h>
#include <stdlib.h>
#include <string.h>
@@ -20,30 +19,9 @@
#include <sched.h>
#include <sys/io.h>
-static int nerrs = 0;
-
-static void sethandler(int sig, void (*handler)(int, siginfo_t *, void *),
- int flags)
-{
- struct sigaction sa;
- memset(&sa, 0, sizeof(sa));
- sa.sa_sigaction = handler;
- sa.sa_flags = SA_SIGINFO | flags;
- sigemptyset(&sa.sa_mask);
- if (sigaction(sig, &sa, 0))
- err(1, "sigaction");
-
-}
+#include "helpers.h"
-static void clearhandler(int sig)
-{
- struct sigaction sa;
- memset(&sa, 0, sizeof(sa));
- sa.sa_handler = SIG_DFL;
- sigemptyset(&sa.sa_mask);
- if (sigaction(sig, &sa, 0))
- err(1, "sigaction");
-}
+static int nerrs = 0;
static jmp_buf jmpbuf;
diff --git a/tools/testing/selftests/x86/ldt_gdt.c b/tools/testing/selftests/x86/ldt_gdt.c
index 3a29346e1452..80acade5d7df 100644
--- a/tools/testing/selftests/x86/ldt_gdt.c
+++ b/tools/testing/selftests/x86/ldt_gdt.c
@@ -8,7 +8,6 @@
#include <err.h>
#include <stdio.h>
#include <stdint.h>
-#include <signal.h>
#include <setjmp.h>
#include <stdlib.h>
#include <string.h>
@@ -26,6 +25,8 @@
#include <asm/prctl.h>
#include <sys/prctl.h>
+#include "helpers.h"
+
#define AR_ACCESSED (1<<8)
#define AR_TYPE_RODATA (0 * (1<<9))
@@ -506,20 +507,6 @@ static void fix_sa_restorer(int sig)
}
#endif
-static void sethandler(int sig, void (*handler)(int, siginfo_t *, void *),
- int flags)
-{
- struct sigaction sa;
- memset(&sa, 0, sizeof(sa));
- sa.sa_sigaction = handler;
- sa.sa_flags = SA_SIGINFO | flags;
- sigemptyset(&sa.sa_mask);
- if (sigaction(sig, &sa, 0))
- err(1, "sigaction");
-
- fix_sa_restorer(sig);
-}
-
static jmp_buf jmpbuf;
static void sigsegv(int sig, siginfo_t *info, void *ctx_void)
@@ -549,9 +536,11 @@ static void do_multicpu_tests(void)
}
sethandler(SIGSEGV, sigsegv, 0);
+ fix_sa_restorer(SIGSEGV);
#ifdef __i386__
/* True 32-bit kernels send SIGILL instead of SIGSEGV on IRET faults. */
sethandler(SIGILL, sigsegv, 0);
+ fix_sa_restorer(SIGILL);
#endif
printf("[RUN]\tCross-CPU LDT invalidation\n");
diff --git a/tools/testing/selftests/x86/mov_ss_trap.c b/tools/testing/selftests/x86/mov_ss_trap.c
index f8633aafc90c..cfec7a3e30b7 100644
--- a/tools/testing/selftests/x86/mov_ss_trap.c
+++ b/tools/testing/selftests/x86/mov_ss_trap.c
@@ -35,7 +35,8 @@
#include <string.h>
#include <setjmp.h>
#include <sys/prctl.h>
-#include <asm/processor-flags.h>
+
+#include "helpers.h"
#if __x86_64__
# define REG_IP REG_RIP
@@ -93,18 +94,6 @@ static void enable_watchpoint(void)
}
}
-static void sethandler(int sig, void (*handler)(int, siginfo_t *, void *),
- int flags)
-{
- struct sigaction sa;
- memset(&sa, 0, sizeof(sa));
- sa.sa_sigaction = handler;
- sa.sa_flags = SA_SIGINFO | flags;
- sigemptyset(&sa.sa_mask);
- if (sigaction(sig, &sa, 0))
- err(1, "sigaction");
-}
-
static char const * const signames[] = {
[SIGSEGV] = "SIGSEGV",
[SIGBUS] = "SIBGUS",
diff --git a/tools/testing/selftests/x86/ptrace_syscall.c b/tools/testing/selftests/x86/ptrace_syscall.c
index 12aaa063196e..360ec88d5432 100644
--- a/tools/testing/selftests/x86/ptrace_syscall.c
+++ b/tools/testing/selftests/x86/ptrace_syscall.c
@@ -15,6 +15,8 @@
#include <asm/ptrace-abi.h>
#include <sys/auxv.h>
+#include "helpers.h"
+
/* Bitness-agnostic defines for user_regs_struct fields. */
#ifdef __x86_64__
# define user_syscall_nr orig_rax
@@ -93,18 +95,6 @@ static siginfo_t wait_trap(pid_t chld)
return si;
}
-static void sethandler(int sig, void (*handler)(int, siginfo_t *, void *),
- int flags)
-{
- struct sigaction sa;
- memset(&sa, 0, sizeof(sa));
- sa.sa_sigaction = handler;
- sa.sa_flags = SA_SIGINFO | flags;
- sigemptyset(&sa.sa_mask);
- if (sigaction(sig, &sa, 0))
- err(1, "sigaction");
-}
-
static void setsigign(int sig, int flags)
{
struct sigaction sa;
@@ -116,16 +106,6 @@ static void setsigign(int sig, int flags)
err(1, "sigaction");
}
-static void clearhandler(int sig)
-{
- struct sigaction sa;
- memset(&sa, 0, sizeof(sa));
- sa.sa_handler = SIG_DFL;
- sigemptyset(&sa.sa_mask);
- if (sigaction(sig, &sa, 0))
- err(1, "sigaction");
-}
-
#ifdef __x86_64__
# define REG_BP REG_RBP
#else
diff --git a/tools/testing/selftests/x86/sigaltstack.c b/tools/testing/selftests/x86/sigaltstack.c
index 22a88b764a8e..7c2bd27908d5 100644
--- a/tools/testing/selftests/x86/sigaltstack.c
+++ b/tools/testing/selftests/x86/sigaltstack.c
@@ -1,7 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
#define _GNU_SOURCE
-#include <signal.h>
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
@@ -14,6 +13,8 @@
#include <sys/resource.h>
#include <setjmp.h>
+#include "helpers.h"
+
/* sigaltstack()-enforced minimum stack */
#define ENFORCED_MINSIGSTKSZ 2048
@@ -27,30 +28,6 @@ static bool sigalrm_expected;
static unsigned long at_minstack_size;
-static void sethandler(int sig, void (*handler)(int, siginfo_t *, void *),
- int flags)
-{
- struct sigaction sa;
-
- memset(&sa, 0, sizeof(sa));
- sa.sa_sigaction = handler;
- sa.sa_flags = SA_SIGINFO | flags;
- sigemptyset(&sa.sa_mask);
- if (sigaction(sig, &sa, 0))
- err(1, "sigaction");
-}
-
-static void clearhandler(int sig)
-{
- struct sigaction sa;
-
- memset(&sa, 0, sizeof(sa));
- sa.sa_handler = SIG_DFL;
- sigemptyset(&sa.sa_mask);
- if (sigaction(sig, &sa, 0))
- err(1, "sigaction");
-}
-
static int setup_altstack(void *start, unsigned long size)
{
stack_t ss;
diff --git a/tools/testing/selftests/x86/sigreturn.c b/tools/testing/selftests/x86/sigreturn.c
index 5d7961a5f7f6..b2282ca57e23 100644
--- a/tools/testing/selftests/x86/sigreturn.c
+++ b/tools/testing/selftests/x86/sigreturn.c
@@ -36,7 +36,6 @@
#include <string.h>
#include <inttypes.h>
#include <sys/mman.h>
-#include <sys/signal.h>
#include <sys/ucontext.h>
#include <asm/ldt.h>
#include <err.h>
@@ -46,6 +45,8 @@
#include <sys/ptrace.h>
#include <sys/user.h>
+#include "helpers.h"
+
/* Pull in AR_xyz defines. */
typedef unsigned int u32;
typedef unsigned short u16;
@@ -138,28 +139,6 @@ static unsigned short LDT3(int idx)
return (idx << 3) | 7;
}
-static void sethandler(int sig, void (*handler)(int, siginfo_t *, void *),
- int flags)
-{
- struct sigaction sa;
- memset(&sa, 0, sizeof(sa));
- sa.sa_sigaction = handler;
- sa.sa_flags = SA_SIGINFO | flags;
- sigemptyset(&sa.sa_mask);
- if (sigaction(sig, &sa, 0))
- err(1, "sigaction");
-}
-
-static void clearhandler(int sig)
-{
- struct sigaction sa;
- memset(&sa, 0, sizeof(sa));
- sa.sa_handler = SIG_DFL;
- sigemptyset(&sa.sa_mask);
- if (sigaction(sig, &sa, 0))
- err(1, "sigaction");
-}
-
static void add_ldt(const struct user_desc *desc, unsigned short *var,
const char *name)
{
diff --git a/tools/testing/selftests/x86/single_step_syscall.c b/tools/testing/selftests/x86/single_step_syscall.c
index 2d8e0edca23f..4c9b8fd7a41a 100644
--- a/tools/testing/selftests/x86/single_step_syscall.c
+++ b/tools/testing/selftests/x86/single_step_syscall.c
@@ -21,7 +21,6 @@
#include <string.h>
#include <inttypes.h>
#include <sys/mman.h>
-#include <sys/signal.h>
#include <sys/ucontext.h>
#include <asm/ldt.h>
#include <err.h>
@@ -33,28 +32,6 @@
#include "helpers.h"
-static void sethandler(int sig, void (*handler)(int, siginfo_t *, void *),
- int flags)
-{
- struct sigaction sa;
- memset(&sa, 0, sizeof(sa));
- sa.sa_sigaction = handler;
- sa.sa_flags = SA_SIGINFO | flags;
- sigemptyset(&sa.sa_mask);
- if (sigaction(sig, &sa, 0))
- err(1, "sigaction");
-}
-
-static void clearhandler(int sig)
-{
- struct sigaction sa;
- memset(&sa, 0, sizeof(sa));
- sa.sa_handler = SIG_DFL;
- sigemptyset(&sa.sa_mask);
- if (sigaction(sig, &sa, 0))
- err(1, "sigaction");
-}
-
static volatile sig_atomic_t sig_traps, sig_eflags;
sigjmp_buf jmpbuf;
diff --git a/tools/testing/selftests/x86/syscall_arg_fault.c b/tools/testing/selftests/x86/syscall_arg_fault.c
index 461fa41a4d02..1149ac24921a 100644
--- a/tools/testing/selftests/x86/syscall_arg_fault.c
+++ b/tools/testing/selftests/x86/syscall_arg_fault.c
@@ -9,7 +9,6 @@
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
-#include <sys/signal.h>
#include <sys/ucontext.h>
#include <err.h>
#include <setjmp.h>
@@ -17,18 +16,6 @@
#include "helpers.h"
-static void sethandler(int sig, void (*handler)(int, siginfo_t *, void *),
- int flags)
-{
- struct sigaction sa;
- memset(&sa, 0, sizeof(sa));
- sa.sa_sigaction = handler;
- sa.sa_flags = SA_SIGINFO | flags;
- sigemptyset(&sa.sa_mask);
- if (sigaction(sig, &sa, 0))
- err(1, "sigaction");
-}
-
static volatile sig_atomic_t sig_traps;
static sigjmp_buf jmpbuf;
diff --git a/tools/testing/selftests/x86/syscall_nt.c b/tools/testing/selftests/x86/syscall_nt.c
index a108b80dd082..1a019bc43b9d 100644
--- a/tools/testing/selftests/x86/syscall_nt.c
+++ b/tools/testing/selftests/x86/syscall_nt.c
@@ -10,7 +10,6 @@
#include <stdio.h>
#include <unistd.h>
#include <string.h>
-#include <signal.h>
#include <err.h>
#include <sys/syscall.h>
@@ -18,18 +17,6 @@
static unsigned int nerrs;
-static void sethandler(int sig, void (*handler)(int, siginfo_t *, void *),
- int flags)
-{
- struct sigaction sa;
- memset(&sa, 0, sizeof(sa));
- sa.sa_sigaction = handler;
- sa.sa_flags = SA_SIGINFO | flags;
- sigemptyset(&sa.sa_mask);
- if (sigaction(sig, &sa, 0))
- err(1, "sigaction");
-}
-
static void sigtrap(int sig, siginfo_t *si, void *ctx_void)
{
}
diff --git a/tools/testing/selftests/x86/sysret_rip.c b/tools/testing/selftests/x86/sysret_rip.c
index 84d74be1d902..da5164fde79a 100644
--- a/tools/testing/selftests/x86/sysret_rip.c
+++ b/tools/testing/selftests/x86/sysret_rip.c
@@ -11,7 +11,6 @@
#include <stdio.h>
#include <string.h>
#include <inttypes.h>
-#include <sys/signal.h>
#include <sys/ucontext.h>
#include <sys/syscall.h>
#include <err.h>
@@ -22,6 +21,7 @@
#include <sys/mman.h>
#include <assert.h>
+#include "helpers.h"
asm (
".pushsection \".text\", \"ax\"\n\t"
@@ -39,28 +39,6 @@ asm (
extern const char test_page[];
static void const *current_test_page_addr = test_page;
-static void sethandler(int sig, void (*handler)(int, siginfo_t *, void *),
- int flags)
-{
- struct sigaction sa;
- memset(&sa, 0, sizeof(sa));
- sa.sa_sigaction = handler;
- sa.sa_flags = SA_SIGINFO | flags;
- sigemptyset(&sa.sa_mask);
- if (sigaction(sig, &sa, 0))
- err(1, "sigaction");
-}
-
-static void clearhandler(int sig)
-{
- struct sigaction sa;
- memset(&sa, 0, sizeof(sa));
- sa.sa_handler = SIG_DFL;
- sigemptyset(&sa.sa_mask);
- if (sigaction(sig, &sa, 0))
- err(1, "sigaction");
-}
-
/* State used by our signal handlers. */
static gregset_t initial_regs;
diff --git a/tools/testing/selftests/x86/test_vsyscall.c b/tools/testing/selftests/x86/test_vsyscall.c
index 47cab972807c..d54f5e767abe 100644
--- a/tools/testing/selftests/x86/test_vsyscall.c
+++ b/tools/testing/selftests/x86/test_vsyscall.c
@@ -11,7 +11,6 @@
#include <dlfcn.h>
#include <string.h>
#include <inttypes.h>
-#include <signal.h>
#include <sys/ucontext.h>
#include <errno.h>
#include <err.h>
@@ -39,18 +38,6 @@
/* max length of lines in /proc/self/maps - anything longer is skipped here */
#define MAPS_LINE_LEN 128
-static void sethandler(int sig, void (*handler)(int, siginfo_t *, void *),
- int flags)
-{
- struct sigaction sa;
- memset(&sa, 0, sizeof(sa));
- sa.sa_sigaction = handler;
- sa.sa_flags = SA_SIGINFO | flags;
- sigemptyset(&sa.sa_mask);
- if (sigaction(sig, &sa, 0))
- err(1, "sigaction");
-}
-
/* vsyscalls and vDSO */
bool vsyscall_map_r = false, vsyscall_map_x = false;
diff --git a/tools/testing/selftests/x86/unwind_vdso.c b/tools/testing/selftests/x86/unwind_vdso.c
index 4c311e1af4c7..51ba53904130 100644
--- a/tools/testing/selftests/x86/unwind_vdso.c
+++ b/tools/testing/selftests/x86/unwind_vdso.c
@@ -31,7 +31,6 @@ int main()
#include <string.h>
#include <inttypes.h>
#include <sys/mman.h>
-#include <signal.h>
#include <sys/ucontext.h>
#include <err.h>
#include <stddef.h>
@@ -43,18 +42,6 @@ int main()
#include <dlfcn.h>
#include <unwind.h>
-static void sethandler(int sig, void (*handler)(int, siginfo_t *, void *),
- int flags)
-{
- struct sigaction sa;
- memset(&sa, 0, sizeof(sa));
- sa.sa_sigaction = handler;
- sa.sa_flags = SA_SIGINFO | flags;
- sigemptyset(&sa.sa_mask);
- if (sigaction(sig, &sa, 0))
- err(1, "sigaction");
-}
-
static volatile sig_atomic_t nerrs;
static unsigned long sysinfo;
static bool got_sysinfo = false;
--
2.17.1
^ permalink raw reply related [flat|nested] 5+ messages in thread* [PATCH v1 4/4] selftests/x86: Refactor altstack setup code
2023-04-03 4:43 [PATCH v1 0/4] selftests/x86: Improve signal test code Chang S. Bae
` (2 preceding siblings ...)
2023-04-03 4:43 ` [PATCH v1 3/4] selftests/x86: Consolidate signal handler helpers Chang S. Bae
@ 2023-04-03 4:43 ` Chang S. Bae
3 siblings, 0 replies; 5+ messages in thread
From: Chang S. Bae @ 2023-04-03 4:43 UTC (permalink / raw)
To: linux-kselftest, linux-kernel
Cc: shuah, luto, dave.hansen, tglx, bp, jun.miao, chang.seok.bae,
Borislav Petkov
The sigaltstack setup code is almost the same across x86 tests. Most
of the test probably just needs a ready-to-use altstack instead of all
the setup code.
Refactor them to these helpers to simplify the test code:
- setup_sigaltstack()
Allocate an altstack memory with a size more than the
kernel-provided minimum. Then register the altstack via
sigaltstack(2).
- cleanup_sigaltstack()
Unregister the altstack and free up the memory.
Then, these two function calls are supposed to be enough for most
sigaltstack-needed test cases. But, when explicit sigaltstack() is
needed, another helper is available:
- init_sigaltstack()
Do the same as setup_sigaltstack(), except for not invoking the
sigaltstack syscall.
Finally, while here, ensure an altstack cleanup everywhere an altstack
is used.
Signed-off-by: Chang S. Bae <chang.seok.bae@intel.com>
Cc: Shuah Khan <shuah@kernel.org>
Cc: Borislav Petkov <bp@alien8.de>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Andy Lutomirski <luto@kernel.org>
Cc: linux-kselftest@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
---
The patch is based on another series that fixes the sigaltstack test:
https://lore.kernel.org/all/20230330233520.21937-2-chang.seok.bae@intel.com/
Considerably that fix should go first before this cleanup rework.
---
tools/testing/selftests/x86/amx.c | 42 ++++------
tools/testing/selftests/x86/helpers.c | 80 +++++++++++++++++++
tools/testing/selftests/x86/helpers.h | 4 +
tools/testing/selftests/x86/mov_ss_trap.c | 11 +--
tools/testing/selftests/x86/sigaltstack.c | 40 ++++------
tools/testing/selftests/x86/sigreturn.c | 10 +--
.../selftests/x86/single_step_syscall.c | 12 +--
.../testing/selftests/x86/syscall_arg_fault.c | 11 +--
8 files changed, 130 insertions(+), 80 deletions(-)
diff --git a/tools/testing/selftests/x86/amx.c b/tools/testing/selftests/x86/amx.c
index 1e8e60b1f7b9..70e1d64e9c57 100644
--- a/tools/testing/selftests/x86/amx.c
+++ b/tools/testing/selftests/x86/amx.c
@@ -362,29 +362,11 @@ static void validate_xcomp_perm(enum expected_result exp)
# define AT_MINSIGSTKSZ 51
#endif
-static void *alloc_altstack(unsigned int size)
+static void setup_altstack(stack_t *stack, enum expected_result exp)
{
- void *altstack;
-
- altstack = mmap(NULL, size, PROT_READ | PROT_WRITE,
- MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, -1, 0);
-
- if (altstack == MAP_FAILED)
- fatal_error("mmap() for altstack");
-
- return altstack;
-}
-
-static void setup_altstack(void *addr, unsigned long size, enum expected_result exp)
-{
- stack_t ss;
int rc;
- memset(&ss, 0, sizeof(ss));
- ss.ss_size = size;
- ss.ss_sp = addr;
-
- rc = sigaltstack(&ss, NULL);
+ rc = sigaltstack(stack, NULL);
if (exp == FAIL_EXPECTED) {
if (rc) {
@@ -401,7 +383,7 @@ static void test_dynamic_sigaltstack(void)
{
unsigned int small_size, enough_size;
unsigned long minsigstksz;
- void *altstack;
+ stack_t stack = { };
minsigstksz = getauxval(AT_MINSIGSTKSZ);
printf("\tAT_MINSIGSTKSZ = %lu\n", minsigstksz);
@@ -416,9 +398,9 @@ static void test_dynamic_sigaltstack(void)
return;
}
- enough_size = minsigstksz * 2;
-
- altstack = alloc_altstack(enough_size);
+ if (init_sigaltstack(&stack) != 0)
+ fatal_error("sigaltstack allocation failed.");
+ enough_size = stack.ss_size;
printf("\tAllocate memory for altstack (%u bytes).\n", enough_size);
/*
@@ -427,7 +409,8 @@ static void test_dynamic_sigaltstack(void)
*/
small_size = minsigstksz - xtiledata.size;
printf("\tAfter sigaltstack() with small size (%u bytes).\n", small_size);
- setup_altstack(altstack, small_size, SUCCESS_EXPECTED);
+ stack.ss_size = small_size;
+ setup_altstack(&stack, SUCCESS_EXPECTED);
validate_req_xcomp_perm(FAIL_EXPECTED);
/*
@@ -436,7 +419,8 @@ static void test_dynamic_sigaltstack(void)
* and thus ARCH_REQ_XCOMP_PERM should succeed.
*/
printf("\tAfter sigaltstack() with enough size (%u bytes).\n", enough_size);
- setup_altstack(altstack, enough_size, SUCCESS_EXPECTED);
+ stack.ss_size = enough_size;
+ setup_altstack(&stack, SUCCESS_EXPECTED);
validate_req_xcomp_perm(SUCCESS_EXPECTED);
/*
@@ -446,7 +430,11 @@ static void test_dynamic_sigaltstack(void)
* once XTILEDATA permission is established.
*/
printf("\tThen, sigaltstack() with small size (%u bytes).\n", small_size);
- setup_altstack(altstack, small_size, FAIL_EXPECTED);
+ stack.ss_size = small_size;
+ setup_altstack(&stack, FAIL_EXPECTED);
+
+ stack.ss_size = enough_size;
+ cleanup_sigaltstack(&stack);
}
static void test_dynamic_state(void)
diff --git a/tools/testing/selftests/x86/helpers.c b/tools/testing/selftests/x86/helpers.c
index a6ecc42d359f..1f5b1e5a3d66 100644
--- a/tools/testing/selftests/x86/helpers.c
+++ b/tools/testing/selftests/x86/helpers.c
@@ -3,6 +3,15 @@
#include <string.h>
#include <err.h>
+#include <sys/auxv.h>
+#include <sys/mman.h>
+
+#include "helpers.h"
+
+#ifndef AT_MINSIGSTKSZ
+# define AT_MINSIGSTKSZ 51
+#endif
+
#include "helpers.h"
void sethandler(int sig, void (*handler)(int, siginfo_t *, void *),
@@ -28,3 +37,74 @@ void clearhandler(int sig)
if (sigaction(sig, &sa, 0))
err(1, "sigaction");
}
+
+#define ALTSTKSZ 8096
+
+static unsigned long get_sigaltstacksz(void)
+{
+ return getauxval(AT_MINSIGSTKSZ) + ALTSTKSZ;
+}
+
+/**
+ * init_sigalstack -- allocate an altstack without registration
+ * @stack: stack_t pointer
+ * Returns: 0 if successful; otherwise, nonzero
+ *
+ * Unless testing with different sizes, setup_sigaltstack() should be
+ * enough to provide a ready-to-use stack
+ */
+int init_sigaltstack(stack_t *stack)
+{
+ if (!stack)
+ return -1;
+
+ if (stack->ss_size > 0 && stack->ss_sp > 0)
+ return 0;
+
+ stack->ss_size = get_sigaltstacksz();
+
+ stack->ss_sp = mmap(NULL, stack->ss_size, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, -1, 0);
+ if (stack->ss_sp == MAP_FAILED)
+ return -1;
+
+ return 0;
+}
+
+/**
+ * setup_sigaltstack -- allocate and register an altstack
+ * @stack: stack_t pointer
+ * Returns: 0 if successful; otherwise, nonzero
+ */
+int setup_sigaltstack(stack_t *stack)
+{
+ int rc;
+
+ rc = init_sigaltstack(stack);
+ if (rc)
+ return -1;
+
+ return sigaltstack(stack, NULL);
+}
+
+/**
+ * cleanup_sigaltstack -- unregister and free an altstack
+ * @stack: stack_t pointer
+ * Returns: None
+ */
+void cleanup_sigaltstack(stack_t *stack)
+{
+ size_t size;
+ void *sp;
+
+ if (!stack)
+ return;
+
+ size = stack->ss_size;
+ sp = stack->ss_sp;
+
+ stack->ss_flags = SS_DISABLE;
+ sigaltstack(stack, NULL);
+
+ munmap(sp, size);
+}
diff --git a/tools/testing/selftests/x86/helpers.h b/tools/testing/selftests/x86/helpers.h
index 35ff4df35397..a47ba1a2d929 100644
--- a/tools/testing/selftests/x86/helpers.h
+++ b/tools/testing/selftests/x86/helpers.h
@@ -5,6 +5,10 @@
#include <signal.h>
#include <asm/processor-flags.h>
+int init_sigaltstack(stack_t *stack);
+int setup_sigaltstack(stack_t *stack);
+void cleanup_sigaltstack(stack_t *stack);
+
void sethandler(int sig, void (*handler)(int, siginfo_t *, void *),
int flags);
diff --git a/tools/testing/selftests/x86/mov_ss_trap.c b/tools/testing/selftests/x86/mov_ss_trap.c
index cfec7a3e30b7..c9ffc8b3a214 100644
--- a/tools/testing/selftests/x86/mov_ss_trap.c
+++ b/tools/testing/selftests/x86/mov_ss_trap.c
@@ -133,13 +133,8 @@ int main()
stack_t stack = { };
unsigned long nr;
- stack.ss_size = SIGSTKSZ;
- stack.ss_sp = malloc(sizeof(char) * SIGSTKSZ);
- if (!stack.ss_sp)
- err(1, "malloc()");
-
- if (sigaltstack(&stack, NULL) != 0)
- err(1, "sigaltstack()");
+ if (setup_sigaltstack(&stack) != 0)
+ err(1, "sigaltstack");
asm volatile ("mov %%ss, %[ss]" : [ss] "=m" (ss));
printf("\tSS = 0x%hx, &SS = 0x%p\n", ss, &ss);
@@ -271,7 +266,7 @@ int main()
);
}
- free(stack.ss_sp);
+ cleanup_sigaltstack(&stack);
printf("[OK]\tI aten't dead\n");
return 0;
}
diff --git a/tools/testing/selftests/x86/sigaltstack.c b/tools/testing/selftests/x86/sigaltstack.c
index 7c2bd27908d5..6f753406ca91 100644
--- a/tools/testing/selftests/x86/sigaltstack.c
+++ b/tools/testing/selftests/x86/sigaltstack.c
@@ -28,17 +28,6 @@ static bool sigalrm_expected;
static unsigned long at_minstack_size;
-static int setup_altstack(void *start, unsigned long size)
-{
- stack_t ss;
-
- memset(&ss, 0, sizeof(ss));
- ss.ss_size = size;
- ss.ss_sp = start;
-
- return sigaltstack(&ss, NULL);
-}
-
static jmp_buf jmpbuf;
static void sigsegv(int sig, siginfo_t *info, void *ctx_void)
@@ -63,14 +52,14 @@ static void sigalrm(int sig, siginfo_t *info, void *ctx_void)
}
}
-static void test_sigaltstack(void *altstack, unsigned long size)
+static void test_sigaltstack(stack_t *stack)
{
- if (setup_altstack(altstack, size)) {
+ if (sigaltstack(stack, NULL)) {
/*
* The kernel may return ENOMEM when the altstack size
* is insufficient. Skip the test in this case.
*/
- if (errno == ENOMEM && size < at_minstack_size) {
+ if (errno == ENOMEM && stack->ss_size < at_minstack_size) {
printf("[SKIP]\tThe running kernel disallows an insufficient size.\n");
return;
}
@@ -78,7 +67,7 @@ static void test_sigaltstack(void *altstack, unsigned long size)
err(1, "sigaltstack()");
}
- sigalrm_expected = (size > at_minstack_size) ? true : false;
+ sigalrm_expected = (stack->ss_size > at_minstack_size) ? true : false;
sethandler(SIGSEGV, sigsegv, 0);
sethandler(SIGALRM, sigalrm, SA_ONSTACK);
@@ -97,19 +86,24 @@ static void test_sigaltstack(void *altstack, unsigned long size)
int main(void)
{
- void *altstack;
+ unsigned long enough_size;
+ stack_t stack = { };
at_minstack_size = getauxval(AT_MINSIGSTKSZ);
- altstack = mmap(NULL, at_minstack_size + SIGSTKSZ, PROT_READ | PROT_WRITE,
- MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, -1, 0);
- if (altstack == MAP_FAILED)
- err(1, "mmap()");
+ if (init_sigaltstack(&stack) != 0)
+ err(1, "sigaltstack allocation failed.");
+ enough_size = stack.ss_size;
+
+ if ((ENFORCED_MINSIGSTKSZ + 1) < at_minstack_size) {
+ stack.ss_size = ENFORCED_MINSIGSTKSZ + 1;
+ test_sigaltstack(&stack);
+ }
- if ((ENFORCED_MINSIGSTKSZ + 1) < at_minstack_size)
- test_sigaltstack(altstack, ENFORCED_MINSIGSTKSZ + 1);
+ stack.ss_size = enough_size;
+ test_sigaltstack(&stack);
- test_sigaltstack(altstack, at_minstack_size + SIGSTKSZ);
+ cleanup_sigaltstack(&stack);
return nerrs == 0 ? 0 : 1;
}
diff --git a/tools/testing/selftests/x86/sigreturn.c b/tools/testing/selftests/x86/sigreturn.c
index b2282ca57e23..f9e06a13ad82 100644
--- a/tools/testing/selftests/x86/sigreturn.c
+++ b/tools/testing/selftests/x86/sigreturn.c
@@ -741,17 +741,13 @@ int main()
{
int total_nerrs = 0;
unsigned short my_cs, my_ss;
+ stack_t stack = { };
asm volatile ("mov %%cs,%0" : "=r" (my_cs));
asm volatile ("mov %%ss,%0" : "=r" (my_ss));
setup_ldt();
- stack_t stack = {
- /* Our sigaltstack scratch space. */
- .ss_sp = malloc(sizeof(char) * SIGSTKSZ),
- .ss_size = SIGSTKSZ,
- };
- if (sigaltstack(&stack, NULL) != 0)
+ if (setup_sigaltstack(&stack) != 0)
err(1, "sigaltstack");
sethandler(SIGUSR1, sigusr1, 0);
@@ -849,6 +845,6 @@ int main()
total_nerrs += test_nonstrict_ss();
#endif
- free(stack.ss_sp);
+ cleanup_sigaltstack(&stack);
return total_nerrs ? 1 : 0;
}
diff --git a/tools/testing/selftests/x86/single_step_syscall.c b/tools/testing/selftests/x86/single_step_syscall.c
index 4c9b8fd7a41a..4e3224e52e30 100644
--- a/tools/testing/selftests/x86/single_step_syscall.c
+++ b/tools/testing/selftests/x86/single_step_syscall.c
@@ -126,13 +126,8 @@ int main()
int tmp;
#endif
- stack.ss_size = SIGSTKSZ;
- stack.ss_sp = malloc(sizeof(char) * SIGSTKSZ);
- if (!stack.ss_sp)
- err(1, "malloc()");
-
- if (sigaltstack(&stack, NULL) != 0)
- err(1, "sigaltstack()");
+ if (setup_sigaltstack(&stack) != 0)
+ err(1, "sigaltstack");
sethandler(SIGTRAP, sigtrap, 0);
@@ -193,6 +188,7 @@ int main()
*/
if (sigsetjmp(jmpbuf, 1) == 0) {
unsigned long nr = SYS_getpid;
+
printf("[RUN]\tSet TF and check SYSENTER\n");
sethandler(SIGSEGV, print_and_longjmp,
SA_RESETHAND | SA_ONSTACK);
@@ -217,6 +213,6 @@ int main()
/* Now make sure that another fast syscall doesn't set TF again. */
fast_syscall_no_tf();
- free(stack.ss_sp);
+ cleanup_sigaltstack(&stack);
return 0;
}
diff --git a/tools/testing/selftests/x86/syscall_arg_fault.c b/tools/testing/selftests/x86/syscall_arg_fault.c
index 1149ac24921a..1b22ef548582 100644
--- a/tools/testing/selftests/x86/syscall_arg_fault.c
+++ b/tools/testing/selftests/x86/syscall_arg_fault.c
@@ -87,12 +87,9 @@ static void sigill(int sig, siginfo_t *info, void *ctx_void)
int main()
{
- stack_t stack = {
- /* Our sigaltstack scratch space. */
- .ss_sp = malloc(sizeof(char) * SIGSTKSZ),
- .ss_size = SIGSTKSZ,
- };
- if (sigaltstack(&stack, NULL) != 0)
+ stack_t stack = { };
+
+ if (setup_sigaltstack(&stack) != 0)
err(1, "sigaltstack");
sethandler(SIGSEGV, sigsegv_or_sigbus, SA_ONSTACK);
@@ -218,6 +215,6 @@ int main()
set_eflags(get_eflags() & ~X86_EFLAGS_TF);
#endif
- free(stack.ss_sp);
+ cleanup_sigaltstack(&stack);
return 0;
}
--
2.17.1
^ permalink raw reply related [flat|nested] 5+ messages in thread