[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <ba63fb0dd083c679249c41cb7d931437ca3598a8.1685443199.git.falcon@tinylab.org>
Date: Tue, 30 May 2023 19:05:29 +0800
From: Zhangjin Wu <falcon@...ylab.org>
To: w@....eu
Cc: falcon@...ylab.org, linux-kernel@...r.kernel.org,
linux-kselftest@...r.kernel.org, linux-riscv@...ts.infradead.org,
thomas@...ch.de
Subject: [PATCH 3/4] selftests/nolibc: add user space efault handler
Some hooks are added to record the test case and the test context, while
traps on invalid data pointer access, try to continue next test if
possible.
Signed-off-by: Zhangjin Wu <falcon@...ylab.org>
---
tools/testing/selftests/nolibc/nolibc-test.c | 151 ++++++++++++++++++-
1 file changed, 149 insertions(+), 2 deletions(-)
diff --git a/tools/testing/selftests/nolibc/nolibc-test.c b/tools/testing/selftests/nolibc/nolibc-test.c
index b8fd7fcf56a6..9f9a09529a4f 100644
--- a/tools/testing/selftests/nolibc/nolibc-test.c
+++ b/tools/testing/selftests/nolibc/nolibc-test.c
@@ -114,6 +114,149 @@ const char *errorname(int err)
}
}
+/* emulate EFAULT return in user space with isigaction/sigsetjmp/siglongjmp */
+#ifndef NOLIBC
+#ifndef NO_USER_SPACE_EFAULT
+#define USER_SPACE_EFAULT
+#endif
+#endif
+
+#ifdef USER_SPACE_EFAULT
+#include <setjmp.h>
+
+static int next_test = 0;
+static int test_llen = 0;
+static int test_sig = 0;
+static int expect_sig = 0;
+static int test_idx = 0;
+static int test_ret = 0;
+static int test_iteration = 0;
+static int test_iterations = 0;
+static sigjmp_buf mark;
+
+static int pad_spc(int llen, int cnt, const char *fmt, ...);
+static struct test test_names[];
+typedef int (*func_t)(int min, int max);
+static func_t test_func = NULL;
+
+#define CASE_SIG(sig) \
+ case sig: return #sig
+
+/* returns the signal name or the decimal value for less common ones. */
+const char *signame(int sig)
+{
+ switch (sig) {
+ CASE_SIG(SIGSEGV);
+ default:
+ return itoa(sig);
+ }
+}
+
+static void record_test_context(int idx, int iteration, int iterations)
+{
+ test_idx = idx;
+ test_iteration = iteration;
+ test_iterations = iterations;
+}
+
+static void record_test_case(int test, int llen, int ret, char *name)
+{
+ test_llen = llen - 1;
+ test_ret = ret;
+ next_test = test + 1;
+}
+
+static void restore_from_trap(void)
+{
+ int idx;
+ int err;
+ int i;
+ int min = 0;
+ int max = INT_MAX;
+
+ test_llen += printf(" ! %d %s ", test_sig, signame(test_sig));
+ if (test_sig == expect_sig)
+ pad_spc(test_llen, 64, "[OK]\n");
+ else {
+ test_ret++;
+ pad_spc(test_llen, 64, "[FAIL]\n");
+ }
+
+ if (next_test <= test_names[test_idx].max) {
+ test_func = test_names[test_idx].func;
+ err = test_func(next_test, test_names[test_idx].max);
+ test_ret += err;
+ printf("Errors during this test: %d\n\n", err);
+ }
+
+ for (i = test_iteration; i < test_iterations; i++) {
+ /* for current iterations */
+ if (i == test_iteration) {
+ idx = test_idx + 1;
+ } else {
+ printf("Current iteration: %d\n\n", i + 1);
+
+ /* for left iterations */
+ idx = 0;
+ test_ret = 0;
+ }
+
+ for (; test_names[idx].name; idx++) {
+ if (test_names[idx].run != 0) {
+ printf("Running test '%s'\n", test_names[idx].name);
+ record_test_context(idx, i, test_iterations);
+ err = test_names[idx].func(test_names[idx].min, test_names[idx].max);
+ test_ret += err;
+ printf("Errors during this test: %d\n\n", err);
+ }
+ }
+ printf("Total number of errors in the %d iteration(s): %d\n\n", i + 1, test_ret);
+ }
+}
+
+static void trap_handler(int sig, siginfo_t *si, void *p)
+{
+ test_sig = sig;
+ if (sig != SIGKILL)
+ siglongjmp(mark, -1);
+}
+
+static void register_expect_trap(int experr1, int experr2)
+{
+ if (experr1 == EFAULT || experr2 == EFAULT)
+ expect_sig = SIGSEGV;
+ else
+ expect_sig = 0;
+}
+
+static void register_trap_handler(void)
+{
+ int ret = 0;
+
+ struct sigaction sa = {0};
+ sa.sa_sigaction = trap_handler;
+ sa.sa_flags = SA_SIGINFO;
+ ret = sigaction(SIGSEGV, &sa, NULL);
+ if (ret == -1) {
+ perror("sigaction");
+ exit(1);
+ }
+
+ if (sigsetjmp(mark, 1) != 0) {
+ restore_from_trap();
+ exit(0);
+ }
+}
+
+#define has_user_space_efault() (1)
+#else
+#define record_test_context(idx, iteration, iterations) do { } while (0)
+#define record_test_case(test, llen, name, ret) do { } while (0)
+#define register_expect_trap(experr1, experr2) do { } while (0)
+#define register_trap_handler() do { } while (0)
+#define has_user_space_efault() (0)
+#endif
+
static void putcharn(char c, size_t n)
{
char buf[64];
@@ -304,7 +447,7 @@ static int expect_sysne(int expr, int llen, int val)
#define EXPECT_SYSER2(cond, expr, expret, experr1, experr2) \
- do { if (!cond) pad_spc(llen, 64, "[SKIPPED]\n"); else ret += expect_syserr2(expr, expret, experr1, experr2, llen); } while (0)
+ do { if (!cond) pad_spc(llen, 64, "[SKIPPED]\n"); else { register_expect_trap(experr1, experr2); ret += expect_syserr2(expr, expret, experr1, experr2, llen); } } while (0)
#define EXPECT_SYSER(cond, expr, expret, experr) \
EXPECT_SYSER2(cond, expr, expret, experr, 0)
@@ -439,7 +582,7 @@ static int expect_strne(const char *expr, int llen, const char *cmp)
/* declare tests based on line numbers. There must be exactly one test per line. */
#define CASE_TEST(name) \
- case __LINE__: llen += printf("%d %s", test, #name);
+ case __LINE__: llen += printf("%d %s", test, #name); record_test_case(test, llen, ret, #name);
/* used by some syscall tests below */
@@ -974,6 +1117,9 @@ int main(int argc, char **argv, char **envp)
if (getpid() == 1)
prepare();
+ /* register exception restore support if enabled */
+ register_trap_handler();
+
/* the definition of a series of tests comes from either argv[1] or the
* "NOLIBC_TEST" environment variable. It's made of a comma-delimited
* series of test names and optional ranges:
@@ -1071,6 +1217,7 @@ int main(int argc, char **argv, char **envp)
for (idx = 0; test_names[idx].name; idx++) {
if (test_names[idx].run != 0) {
printf("Running test '%s', from %d to %d\n", test_names[idx].name, test_names[idx].min, test_names[idx].max);
+ record_test_context(idx, i, run);
err = test_names[idx].func(test_names[idx].min, test_names[idx].max);
ret += err;
printf("Errors during this test: %d\n\n", err);
--
2.25.1
Powered by blists - more mailing lists