lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
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

Powered by Openwall GNU/*/Linux Powered by OpenVZ