Hi
On 13/08/2019 17:25, Dave Martin wrote:
On Fri, Aug 02, 2019 at 06:02:54PM +0100, Cristian Marussi wrote:
Added a simple mangle testcase which messes with the ucontext_t
Add
from within the sig_handler, trying to toggle PSTATE SSBS bit.
signal handler
Ok.
Expect SIGILL if SSBS feature unsupported or that the value set in PSTATE.SSBS is preserved on test PASS.
The test doesn't set PSTATE.SSBS directly.
Maybe something like: "Expect SIGILL if the SSBS feature is unsupported. Otherwise, expect sigreturn to set PSTATE.SSBS from the corresponding bit in pstate in the signal frame."
Ok
This commit also introduces a new common utility function: get_current_context() which can be used to grab a ucontext without the help of libc, and detect if such ucontext has been actively used to jump back into it.
Signed-off-by: Cristian Marussi cristian.marussi@arm.com
.../selftests/arm64/signal/test_signals.h | 4 + .../arm64/signal/test_signals_utils.c | 93 +++++++++++++++++++ .../arm64/signal/test_signals_utils.h | 2 + .../arm64/signal/testcases/.gitignore | 1 + .../testcases/mangle_pstate_ssbs_regs.c | 56 +++++++++++ 5 files changed, 156 insertions(+) create mode 100644 tools/testing/selftests/arm64/signal/testcases/mangle_pstate_ssbs_regs.c
diff --git a/tools/testing/selftests/arm64/signal/test_signals.h b/tools/testing/selftests/arm64/signal/test_signals.h index 85db3ac44b32..37bed0590226 100644 --- a/tools/testing/selftests/arm64/signal/test_signals.h +++ b/tools/testing/selftests/arm64/signal/test_signals.h @@ -116,6 +116,10 @@ struct tdescr { /* optional sa_flags for the installed handler */ int sa_flags; ucontext_t saved_uc;
- /* used by get_current_ctx() */
- size_t live_sz;
- ucontext_t *live_uc;
- volatile bool live_uc_valid;
/* a setup function to be called before test starts */ int (*setup)(struct tdescr *td); diff --git a/tools/testing/selftests/arm64/signal/test_signals_utils.c b/tools/testing/selftests/arm64/signal/test_signals_utils.c index ac0055f6340b..faf55ba99d58 100644 --- a/tools/testing/selftests/arm64/signal/test_signals_utils.c +++ b/tools/testing/selftests/arm64/signal/test_signals_utils.c @@ -11,12 +11,16 @@ #include <linux/auxvec.h> #include <ucontext.h> +#include <asm/unistd.h>
#include "test_signals.h" #include "test_signals_utils.h" #include "testcases/testcases.h" extern struct tdescr *current; +static int sig_copyctx = SIGUSR2;
static char *feats_store[FMAX_END] = { "SSBS", "PAN", @@ -37,6 +41,85 @@ static inline char *feats_to_string(unsigned long feats) return feats_string; } +/*
- Obtaining a valid and full-blown ucontext_t from userspace is tricky:
- libc getcontext does() not save all the regs and messes with some of
- them (pstate value in particular is not reliable).
- Here we use a service signal to grab the ucontext_t from inside a
- dedicated signal handler, since there, it is populated by Kernel
- itself in setup_sigframe(). The grabbed context is then stored and
- made available in td->live_uc.
- Anyway this function really serves a dual purpose:
- grab a valid sigcontext into td->live_uc for result analysis: in
- such case it returns 1.
- detect if somehow a previously grabbed live_uc context has been
- used actively with a sigreturn: in such a case the execution would have
- magically resumed in the middle of the function itself (seen_already==1):
- in such a case return 0, since in fact we have not just simply grabbed
- the context.
- This latter case is useful to detect when a fake_sigreturn test-case has
- unexpectedly survived without hittig a SEGV.
- */
+bool get_current_context(struct tdescr *td, ucontext_t *dest_uc) +{
- static volatile sig_atomic_t seen_already;
- if (!td || !dest_uc) {
fprintf(stdout, "Signal-based Context dumping NOT available\n");
Should this ever happen inless there is a test bug?
Maybe this should just be an assert.
Yes definitely better.
return 0;
- }
- /* it's a genuine invokation..reinit */
- seen_already = 0;
- td->live_uc_valid = 0;
- td->live_sz = sizeof(*dest_uc);
- memset(dest_uc, 0x00, td->live_sz);
Eventually we will need to examine the signal frame to determine its size, but for now this is fine.
It will start to matter for SVE.
Yes this function has been reworked in the SVE signal frame patches to dynamically detect runtime signal frame and be able to optionally grab a sigframe containing SVE material (avoiding the kill() syscall to trigger a signal in favour of a brk instruction to cause a SIGTRAP without passing via the syscall machinery which would zero the SVE sigframe stuff)
Such patch, which depends on this series, is still not published.
- td->live_uc = dest_uc;
- /*
* Grab ucontext_t triggering a signal...
* ASM equivalent of raise(sig_copyctx);
*
* Note that:
* - live_uc_valid is declared volatile in struct tdescr
* since it will be changed inside the sig_copyctx handler.
* - the kill() syscall invocation returns only after any possible
* registered sig_handler for the invoked signal has returned,
sig_handler looks like the name of some function of variable, but I can't find it. Did I miss something?
No I'll replace the comment with "signal handler"
* so that live_uc_valid flag is surely up to date when this
* function return it.
* - the additional 'memory' clobber is there to avoid possible
* compiler's assumption on the content pointed by dest_uc, which
* is changed inside the handler, but not referenced here anyway.
*/
- asm volatile ("mov x8, %0\n\t"
"svc #0\n\t"
"mov x1, %1\n\t"
"mov x8, %2\n\t"
"svc #0"
:
: "i" (__NR_getpid),
"r" (sig_copyctx),
"i" (__NR_kill)
: "x1","x8","x0","memory");
- /*
* If we get here with seen_already==1 it implies the td->live_uc
* context has been used to get back here....this probably means
* a test has failed to cause a SEGV...anyway the live_uc has not
* just been acquired...so return 0
*/
- if (seen_already) {
fprintf(stdout,
"Successful sigreturn detected: live_uc is stale !\n");
return 0;
- }
- seen_already = 1;
- return td->live_uc_valid;
+}
static void unblock_signal(int signum) { sigset_t sset; @@ -112,6 +195,12 @@ static void default_handler(int signum, siginfo_t *si, void *uc) * to terminate immediately exiting straight away */ default_result(current, 1);
- } else if (signum == sig_copyctx && current->live_uc) {
memcpy(current->live_uc, uc, current->live_sz);
ASSERT_GOOD_CONTEXT(current->live_uc);
current->live_uc_valid = 1;
fprintf(stderr,
} else { if (signum == current->sig_unsupp && !are_feats_ok(current)) { fprintf(stderr, "-- RX SIG_UNSUPP on unsupported feature...OK\n");"GOOD CONTEXT grabbed from sig_copyctx handler\n");
@@ -214,6 +303,10 @@ static int test_init(struct tdescr *td) !feats_ok ? "NOT " : ""); }
- if (td->sig_trig == sig_copyctx)
sig_copyctx = SIGUSR1;
What's this for? What if we have the same signal for sig_trig and sig_copyctx?
To avoid that a user defined sig_trig equal to sig_copyctx can fool this function. In SVE signal frame patch (not in this series), I'll anyway switch to use a distinct SIGTRAP sig_trig.
- unblock_signal(sig_copyctx);
- td->initialized = 1; return 1;
} diff --git a/tools/testing/selftests/arm64/signal/test_signals_utils.h b/tools/testing/selftests/arm64/signal/test_signals_utils.h index 8658d1a7d4b9..ce35be8ebc8e 100644 --- a/tools/testing/selftests/arm64/signal/test_signals_utils.h +++ b/tools/testing/selftests/arm64/signal/test_signals_utils.h @@ -10,4 +10,6 @@ int test_setup(struct tdescr *td); void test_cleanup(struct tdescr *td); int test_run(struct tdescr *td); void test_result(struct tdescr *td);
+bool get_current_context(struct tdescr *td, ucontext_t *dest_uc); #endif diff --git a/tools/testing/selftests/arm64/signal/testcases/.gitignore b/tools/testing/selftests/arm64/signal/testcases/.gitignore index 226bb179b673..a48a118b1a1a 100644 --- a/tools/testing/selftests/arm64/signal/testcases/.gitignore +++ b/tools/testing/selftests/arm64/signal/testcases/.gitignore @@ -3,3 +3,4 @@ mangle_pstate_invalid_daif_bits mangle_pstate_invalid_mode_el1 mangle_pstate_invalid_mode_el2 mangle_pstate_invalid_mode_el3 +mangle_pstate_ssbs_regs diff --git a/tools/testing/selftests/arm64/signal/testcases/mangle_pstate_ssbs_regs.c b/tools/testing/selftests/arm64/signal/testcases/mangle_pstate_ssbs_regs.c new file mode 100644 index 000000000000..a399d9aa40d5 --- /dev/null +++ b/tools/testing/selftests/arm64/signal/testcases/mangle_pstate_ssbs_regs.c @@ -0,0 +1,56 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2019 ARM Limited */
+#include <stdio.h> +#include <ucontext.h>
+#include "test_signals_utils.h" +#include "testcases.h"
+static int mangle_invalid_pstate_ssbs_run(struct tdescr *td,
siginfo_t *si, ucontext_t *uc)
+{
- ASSERT_GOOD_CONTEXT(uc);
- /* set bit value */
Should we clear SSBS in the test setup (using MSR), to make sure that sigreturn really succeeds in _changing_ the bit to 1?
Yes. I introduced a set_regval() asm helper and added a new .init signal-test framework entry in tdescr to be able to call per-test specific initialization.
I took the chance also to remove the remaining volatile qualifiers from signal handling code to please checkpatch, and add a dsb barrier to ensure the writes by the signal handler.
- uc->uc_mcontext.pstate |= PSR_SSBS_BIT;
- fprintf(stderr, "SSBS set to 1 -- PSTATE: 0x%016lX\n",
uc->uc_mcontext.pstate);
- /* Save after mangling...it should be preserved */
- td->saved_uc = *uc;
- return 1;
+}
[...]
Cheers ---Dave
Cheers
Cristian