Hi
this patch aims to add the initial arch-specific arm64 support to kselftest starting with signals-related test-cases. A common internal test-case layout is proposed which then it is anyway wired-up to the toplevel kselftest Makefile, so that it should be possible at the end to run it on an arm64 target in the usual way with KSFT.
~/linux# make TARGETS=arm64 kselftest
Notes: ----- - further details in the included READMEs
- more tests still to be written (current strategy is going through the related Kernel signal-handling code and write a test for each possible and sensible code-path)
- a bit of overlap around KSFT arm64/ Makefiles is expected with this recently proposed patch-series: http://lists.infradead.org/pipermail/linux-arm-kernel/2019-June/659432.html
Cristian Marussi (13): kselftest: arm64: introduce new boilerplate code kselftest: arm64: adds arm64/signal support code kselftest: arm64: mangle_sp_misaligned kselftest: arm64: mangle_pc_invalid kselftest: arm64: mangle_pstate_invalid_daif_bits kselftest: arm64: mangle_pstate_invalid_state_toggle kselftest: arm64: mangle_pstate_invalid_mode_el? kselftest: arm64: mangle_pstate_ssbs_regs kselftest: arm64: fake_sigreturn_misaligned kselftest: arm64: fake_sigreturn_bad_magic kselftest: arm64: fake_sigreturn_bad_size kselftest: arm64: fake_sigreturn_bad_size_for_magic0 kselftest: arm64: fake_sigreturn_overflow_reserved
tools/testing/selftests/Makefile | 1 + tools/testing/selftests/arm64/Makefile | 51 ++++ tools/testing/selftests/arm64/README | 44 +++ .../testing/selftests/arm64/signal/.gitignore | 5 + tools/testing/selftests/arm64/signal/Makefile | 86 ++++++ tools/testing/selftests/arm64/signal/README | 56 ++++ .../testing/selftests/arm64/signal/signals.S | 64 +++++ .../arm64/signal/test_arm64_signals.sh | 44 +++ .../selftests/arm64/signal/test_signals.c | 30 ++ .../selftests/arm64/signal/test_signals.h | 136 ++++++++++ .../arm64/signal/test_signals_utils.c | 256 ++++++++++++++++++ .../arm64/signal/test_signals_utils.h | 110 ++++++++ .../arm64/signal/testcases/.gitignore | 13 + .../testcases/fake_sigreturn_bad_magic.c | 42 +++ .../testcases/fake_sigreturn_bad_size.c | 40 +++ .../fake_sigreturn_bad_size_for_magic0.c | 44 +++ .../testcases/fake_sigreturn_misaligned.c | 30 ++ .../fake_sigreturn_overflow_reserved.c | 48 ++++ .../signal/testcases/mangle_pc_invalid.c | 24 ++ .../mangle_pstate_invalid_daif_bits.c | 25 ++ .../mangle_pstate_invalid_mode_el1.c | 25 ++ .../mangle_pstate_invalid_mode_el2.c | 25 ++ .../mangle_pstate_invalid_mode_el3.c | 25 ++ .../mangle_pstate_invalid_state_toggle.c | 25 ++ .../testcases/mangle_pstate_ssbs_regs.c | 41 +++ .../signal/testcases/mangle_sp_misaligned.c | 24 ++ .../arm64/signal/testcases/testcases.c | 123 +++++++++ .../arm64/signal/testcases/testcases.h | 85 ++++++ 28 files changed, 1522 insertions(+) create mode 100644 tools/testing/selftests/arm64/Makefile create mode 100644 tools/testing/selftests/arm64/README create mode 100644 tools/testing/selftests/arm64/signal/.gitignore create mode 100644 tools/testing/selftests/arm64/signal/Makefile create mode 100644 tools/testing/selftests/arm64/signal/README create mode 100644 tools/testing/selftests/arm64/signal/signals.S create mode 100755 tools/testing/selftests/arm64/signal/test_arm64_signals.sh create mode 100644 tools/testing/selftests/arm64/signal/test_signals.c create mode 100644 tools/testing/selftests/arm64/signal/test_signals.h create mode 100644 tools/testing/selftests/arm64/signal/test_signals_utils.c create mode 100644 tools/testing/selftests/arm64/signal/test_signals_utils.h create mode 100644 tools/testing/selftests/arm64/signal/testcases/.gitignore create mode 100644 tools/testing/selftests/arm64/signal/testcases/fake_sigreturn_bad_magic.c create mode 100644 tools/testing/selftests/arm64/signal/testcases/fake_sigreturn_bad_size.c create mode 100644 tools/testing/selftests/arm64/signal/testcases/fake_sigreturn_bad_size_for_magic0.c create mode 100644 tools/testing/selftests/arm64/signal/testcases/fake_sigreturn_misaligned.c create mode 100644 tools/testing/selftests/arm64/signal/testcases/fake_sigreturn_overflow_reserved.c create mode 100644 tools/testing/selftests/arm64/signal/testcases/mangle_pc_invalid.c create mode 100644 tools/testing/selftests/arm64/signal/testcases/mangle_pstate_invalid_daif_bits.c create mode 100644 tools/testing/selftests/arm64/signal/testcases/mangle_pstate_invalid_mode_el1.c create mode 100644 tools/testing/selftests/arm64/signal/testcases/mangle_pstate_invalid_mode_el2.c create mode 100644 tools/testing/selftests/arm64/signal/testcases/mangle_pstate_invalid_mode_el3.c create mode 100644 tools/testing/selftests/arm64/signal/testcases/mangle_pstate_invalid_state_toggle.c create mode 100644 tools/testing/selftests/arm64/signal/testcases/mangle_pstate_ssbs_regs.c create mode 100644 tools/testing/selftests/arm64/signal/testcases/mangle_sp_misaligned.c create mode 100644 tools/testing/selftests/arm64/signal/testcases/testcases.c create mode 100644 tools/testing/selftests/arm64/signal/testcases/testcases.h
Added a new arm64-specific empty subsystem amongst TARGETS of KSFT build framework; once populated with testcases, it will be possible to build and invoke the new KSFT TARGETS=arm64 related tests from the toplevel Makefile in the usual ways.
Signed-off-by: Cristian Marussi cristian.marussi@arm.com --- tools/testing/selftests/Makefile | 1 + tools/testing/selftests/arm64/Makefile | 51 ++++++++++++++++++++++++++ tools/testing/selftests/arm64/README | 44 ++++++++++++++++++++++ 3 files changed, 96 insertions(+) create mode 100644 tools/testing/selftests/arm64/Makefile create mode 100644 tools/testing/selftests/arm64/README
diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile index 9781ca79794a..4ff0b41ead8a 100644 --- a/tools/testing/selftests/Makefile +++ b/tools/testing/selftests/Makefile @@ -1,5 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 TARGETS = android +TARGETS += arm64 TARGETS += bpf TARGETS += breakpoints TARGETS += capabilities diff --git a/tools/testing/selftests/arm64/Makefile b/tools/testing/selftests/arm64/Makefile new file mode 100644 index 000000000000..03a0d4f71218 --- /dev/null +++ b/tools/testing/selftests/arm64/Makefile @@ -0,0 +1,51 @@ +# SPDX-License-Identifier: GPL-2.0 +# Copyright (C) 2019 ARM Limited + +# When ARCH not overridden for crosscompiling, lookup machine +ARCH ?= $(shell uname -m) +ARCH := $(shell echo $(ARCH) | sed -e s/aarch64/arm64/) + +ifeq ("x$(ARCH)", "xarm64") +SUBDIRS := +else +SUBDIRS := +endif + +CFLAGS := -Wall -O2 -g + +export CC +export CFLAGS + +all: + @for DIR in $(SUBDIRS); do \ + BUILD_TARGET=$(OUTPUT)/$$DIR; \ + mkdir -p $$BUILD_TARGET; \ + make OUTPUT=$$BUILD_TARGET -C $$DIR $@; \ + done + +install: all + @for DIR in $(SUBDIRS); do \ + BUILD_TARGET=$(OUTPUT)/$$DIR; \ + make OUTPUT=$$BUILD_TARGET -C $$DIR $@; \ + done + +run_tests: all + @for DIR in $(SUBDIRS); do \ + BUILD_TARGET=$(OUTPUT)/$$DIR; \ + make OUTPUT=$$BUILD_TARGET -C $$DIR $@; \ + done + +# Avoid any output on non arm64 on emit_tests +emit_tests: all + @for DIR in $(SUBDIRS); do \ + BUILD_TARGET=$(OUTPUT)/$$DIR; \ + make OUTPUT=$$BUILD_TARGET -C $$DIR $@; \ + done + +clean: + @for DIR in $(SUBDIRS); do \ + BUILD_TARGET=$(OUTPUT)/$$DIR; \ + make OUTPUT=$$BUILD_TARGET -C $$DIR $@; \ + done + +.PHONY: all clean install run_tests emit_tests diff --git a/tools/testing/selftests/arm64/README b/tools/testing/selftests/arm64/README new file mode 100644 index 000000000000..d5aa7a2eda78 --- /dev/null +++ b/tools/testing/selftests/arm64/README @@ -0,0 +1,44 @@ +KSelfTest ARM64 +=============== + +- These tests are arm64 specific and so not built or run but just skipped + completely when env-variable ARCH is found to be different than 'arm64' + and `uname -m` reports other than 'aarch64'. + +- Holding true the above, ARM64 KSFT tests can be run: + + + as standalone (example for signal tests) + + $ make -C tools/testing/selftest/arm64/signal clean + $ make -C tools/testing/selftest/arm64/signal \ + INSTALL_PATH=<your-installation-path> install + + and then launching on the target device inside the installed path: + + device# cd <your-installed-path> && ./test_arm64_signals.sh [-k | -v] + + + within the KSelfTest framework using standard Linux top-level-makefile + targets: + + $ make TARGETS=arm64 kselftest-clean + $ make TARGETS=arm64 kselftest + + Further details on building and running KFST can be found in: + Documentation/dev-tools/kselftest.rst + +- Tests can depend on some arch-specific definitions which can be found in a + standard Kernel Headers installation in $(top_srcdir)/usr/include. + Such Kernel Headers are automatically installed (via make headers_install) + by KSFT framework itself in a dedicated directory when tests are launched + via KSFT itself; when running standalone, instead, a Warning is issued + if such headers cannot be found somewhere (we try to guess a few standard + locations anyway) + +- Some of these tests may be related to possibly not implemented ARMv8 + features: depending on their implementation status on the effective HW + we'll expect different results. The tests' harness will take care to check + at run-time if the required features are supported and will act accordingly. + Moreover, in order to avoid any kind of compile-time dependency on the + toolchain (possibly due to the above mentioned not-implemented features), + we make strictly use of direct 'S3_ sysreg' raw-encoding while checking for + those features and/or lookin up sysregs.
On Thu, Jun 13, 2019 at 12:13:23PM +0100, Cristian Marussi wrote:
Added a new arm64-specific empty subsystem amongst TARGETS of KSFT build framework; once populated with testcases, it will be possible to build and invoke the new KSFT TARGETS=arm64 related tests from the toplevel Makefile in the usual ways.
Signed-off-by: Cristian Marussi cristian.marussi@arm.com
tools/testing/selftests/Makefile | 1 + tools/testing/selftests/arm64/Makefile | 51 ++++++++++++++++++++++++++ tools/testing/selftests/arm64/README | 44 ++++++++++++++++++++++ 3 files changed, 96 insertions(+) create mode 100644 tools/testing/selftests/arm64/Makefile create mode 100644 tools/testing/selftests/arm64/README
diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile index 9781ca79794a..4ff0b41ead8a 100644 --- a/tools/testing/selftests/Makefile +++ b/tools/testing/selftests/Makefile @@ -1,5 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 TARGETS = android +TARGETS += arm64 TARGETS += bpf TARGETS += breakpoints TARGETS += capabilities diff --git a/tools/testing/selftests/arm64/Makefile b/tools/testing/selftests/arm64/Makefile new file mode 100644 index 000000000000..03a0d4f71218 --- /dev/null +++ b/tools/testing/selftests/arm64/Makefile @@ -0,0 +1,51 @@ +# SPDX-License-Identifier: GPL-2.0 +# Copyright (C) 2019 ARM Limited
+# When ARCH not overridden for crosscompiling, lookup machine +ARCH ?= $(shell uname -m) +ARCH := $(shell echo $(ARCH) | sed -e s/aarch64/arm64/)
+ifeq ("x$(ARCH)", "xarm64") +SUBDIRS := +else +SUBDIRS := +endif
+CFLAGS := -Wall -O2 -g
+export CC +export CFLAGS
+all:
- @for DIR in $(SUBDIRS); do \
BUILD_TARGET=$(OUTPUT)/$$DIR; \
mkdir -p $$BUILD_TARGET; \
make OUTPUT=$$BUILD_TARGET -C $$DIR $@; \
- done
+install: all
- @for DIR in $(SUBDIRS); do \
BUILD_TARGET=$(OUTPUT)/$$DIR; \
make OUTPUT=$$BUILD_TARGET -C $$DIR $@; \
- done
+run_tests: all
- @for DIR in $(SUBDIRS); do \
BUILD_TARGET=$(OUTPUT)/$$DIR; \
make OUTPUT=$$BUILD_TARGET -C $$DIR $@; \
- done
+# Avoid any output on non arm64 on emit_tests +emit_tests: all
- @for DIR in $(SUBDIRS); do \
BUILD_TARGET=$(OUTPUT)/$$DIR; \
make OUTPUT=$$BUILD_TARGET -C $$DIR $@; \
- done
+clean:
- @for DIR in $(SUBDIRS); do \
BUILD_TARGET=$(OUTPUT)/$$DIR; \
make OUTPUT=$$BUILD_TARGET -C $$DIR $@; \
- done
+.PHONY: all clean install run_tests emit_tests diff --git a/tools/testing/selftests/arm64/README b/tools/testing/selftests/arm64/README new file mode 100644 index 000000000000..d5aa7a2eda78 --- /dev/null +++ b/tools/testing/selftests/arm64/README @@ -0,0 +1,44 @@ +KSelfTest ARM64 +===============
+- These tests are arm64 specific and so not built or run but just skipped
- completely when env-variable ARCH is found to be different than 'arm64'
- and `uname -m` reports other than 'aarch64'.
+- Holding true the above, ARM64 KSFT tests can be run:
- as standalone (example for signal tests)
$ make -C tools/testing/selftest/arm64/signal clean
$ make -C tools/testing/selftest/arm64/signal \
INSTALL_PATH=<your-installation-path> install
and then launching on the target device inside the installed path:
device# cd <your-installed-path> && ./test_arm64_signals.sh [-k | -v]
(Similar nits to those on the arm64-specific documentation in the next patch.)
Otherwise, this looks sensible.
[...]
Cheers ---Dave
Added some arm64/signal specific boilerplate and utility code to help further testcase development. Still no test case code included though.
Signed-off-by: Cristian Marussi cristian.marussi@arm.com --- tools/testing/selftests/arm64/Makefile | 2 +- .../testing/selftests/arm64/signal/.gitignore | 5 + tools/testing/selftests/arm64/signal/Makefile | 86 ++++++ tools/testing/selftests/arm64/signal/README | 56 ++++ .../testing/selftests/arm64/signal/signals.S | 64 +++++ .../arm64/signal/test_arm64_signals.sh | 44 +++ .../selftests/arm64/signal/test_signals.c | 30 ++ .../selftests/arm64/signal/test_signals.h | 136 ++++++++++ .../arm64/signal/test_signals_utils.c | 256 ++++++++++++++++++ .../arm64/signal/test_signals_utils.h | 110 ++++++++ .../arm64/signal/testcases/testcases.c | 123 +++++++++ .../arm64/signal/testcases/testcases.h | 85 ++++++ 12 files changed, 996 insertions(+), 1 deletion(-) create mode 100644 tools/testing/selftests/arm64/signal/.gitignore create mode 100644 tools/testing/selftests/arm64/signal/Makefile create mode 100644 tools/testing/selftests/arm64/signal/README create mode 100644 tools/testing/selftests/arm64/signal/signals.S create mode 100755 tools/testing/selftests/arm64/signal/test_arm64_signals.sh create mode 100644 tools/testing/selftests/arm64/signal/test_signals.c create mode 100644 tools/testing/selftests/arm64/signal/test_signals.h create mode 100644 tools/testing/selftests/arm64/signal/test_signals_utils.c create mode 100644 tools/testing/selftests/arm64/signal/test_signals_utils.h create mode 100644 tools/testing/selftests/arm64/signal/testcases/testcases.c create mode 100644 tools/testing/selftests/arm64/signal/testcases/testcases.h
diff --git a/tools/testing/selftests/arm64/Makefile b/tools/testing/selftests/arm64/Makefile index 03a0d4f71218..af59dc74e0dc 100644 --- a/tools/testing/selftests/arm64/Makefile +++ b/tools/testing/selftests/arm64/Makefile @@ -6,7 +6,7 @@ ARCH ?= $(shell uname -m) ARCH := $(shell echo $(ARCH) | sed -e s/aarch64/arm64/)
ifeq ("x$(ARCH)", "xarm64") -SUBDIRS := +SUBDIRS := signal else SUBDIRS := endif diff --git a/tools/testing/selftests/arm64/signal/.gitignore b/tools/testing/selftests/arm64/signal/.gitignore new file mode 100644 index 000000000000..7234ebd99363 --- /dev/null +++ b/tools/testing/selftests/arm64/signal/.gitignore @@ -0,0 +1,5 @@ +# Helper script's internal testcases list (TPROGS) is regenerated +# each time by Makefile on standalone (non KSFT driven) runs. +# Committing such list creates a dependency between testcases +# patches such that they are no more easily revertable. Just ignore. +test_arm64_signals.sh diff --git a/tools/testing/selftests/arm64/signal/Makefile b/tools/testing/selftests/arm64/signal/Makefile new file mode 100644 index 000000000000..9518841124a3 --- /dev/null +++ b/tools/testing/selftests/arm64/signal/Makefile @@ -0,0 +1,86 @@ +# SPDX-License-Identifier: GPL-2.0 +# Copyright (C) 2019 ARM Limited + +# Supports also standalone invokation out of KSFT-tree. +# +# Build standalone with (issued from within the dir containing this makefile): +# +# host:~$ make clean && make INSTALL_PATH=<your-device-instdir> install +# +# Run standalone on device with: +# device:~# <your-device-instdir>/test_arm64_signals.sh [-k|-v] +# + +# A proper top_srcdir is needed both by KSFT(lib.mk) +# and standalone builds +top_srcdir = ../../../../.. + +CFLAGS += -std=gnu99 -I. -I$(top_srcdir)/tools/testing/selftests/ +SRCS := $(filter-out testcases/testcases.c,$(wildcard testcases/*.c)) +PROGS := $(patsubst %.c,%,$(SRCS)) + +# Guessing as best as we can where the Kernel headers +# could have been installed depending on ENV config and +# type of invocation. +ifeq ($(KBUILD_OUTPUT),) +khdr_dir = $(top_srcdir)/usr/include +else +ifeq (0,$(MAKELEVEL)) +khdr_dir = $(KBUILD_OUTPUT)/usr/include +else +# the KSFT preferred location when KBUILD_OUTPUT is set +khdr_dir = $(KBUILD_OUTPUT)/kselftest/usr/include +endif +endif + +CFLAGS += -I$(khdr_dir) + +# Standalone run +ifeq (0,$(MAKELEVEL)) +CC := $(CROSS_COMPILE)gcc +RUNNER = test_arm64_signals.sh +INSTALL_PATH ?= install/ + +all: $(RUNNER) + +$(RUNNER): $(PROGS) + sed -i -e 's#PROGS=.*#PROGS="$(PROGS)"#' $@ + +install: all + mkdir -p $(INSTALL_PATH)/testcases + cp $(PROGS) $(INSTALL_PATH)/testcases + cp $(RUNNER) $(INSTALL_PATH)/ + +.PHONY clean: + rm -f $(PROGS) +# KSFT run +else +# Generated binaries to be installed by top KSFT script +TEST_GEN_PROGS := $(notdir $(PROGS)) + +# Get Kernel headers installed and use them. +KSFT_KHDR_INSTALL := 1 + +# This include mk will also mangle the TEST_GEN_PROGS list +# to account for any OUTPUT target-dirs optionally provided +# by the toplevel makefile +include ../../lib.mk + +$(TEST_GEN_PROGS): $(PROGS) + -mkdir -p $(OUTPUT) + -cp $(PROGS) $(OUTPUT)/ + +clean: + $(CLEAN) + rm -f $(PROGS) +endif + +# Common test-unit targets to build common-layout test-cases executables +# Needs secondary expansion to properly include the testcase c-file in pre-reqs +.SECONDEXPANSION: +$(PROGS): test_signals.c test_signals_utils.c signals.S testcases/testcases.c $$@.c test_signals.h test_signals_utils.h testcases/testcases.h + @if [ ! -d $(khdr_dir) ]; then \ + echo -n "\n!!! WARNING: $(khdr_dir) NOT FOUND."; \ + echo "===> Are you sure Kernel Headers have been installed properly ?\n"; \ + fi + $(CC) $(CFLAGS) $^ -o $@ diff --git a/tools/testing/selftests/arm64/signal/README b/tools/testing/selftests/arm64/signal/README new file mode 100644 index 000000000000..315d77558e14 --- /dev/null +++ b/tools/testing/selftests/arm64/signal/README @@ -0,0 +1,56 @@ +KSelfTest arm64/signal/ +======================= + +Signals Tests ++++++++++++++ + +- Tests are built around a common main compilation unit: such shared main + enforces a standard sequence of operations needed to perform a single + signal-test (setup/trigger/run/result/cleanup) + +- The above mentioned ops are configurable on a test-by-test basis: each test + is described (and configured) using the descriptor signals.h::struct tdescr + +- Each signal testcase is compiled into its own executable: a separate + executable is used for each test since many tests complete successfully + by receiving some kind of 'Term' signal from the Kernel, so it's safer + to run each test unit in its own standalone process, so as to start each + test from a clean-slate. + +- New tests can be simply defined in testcases/ dir providing a proper struct + tdescr overriding all the defaults we wish to change (as of now providing a + custom run method is mandatory though) + +- Signals' test-cases hereafter defined belong currently to two + principal families: + + - 'mangle_' tests: a real signal (SIGUSR1) is raised and used as a trigger + and then the test case code messes-up with the sigframe ucontext_t from + inside the sighandler itself. + + - 'fake_sigreturn_' tests: a brand new custom artificial sigframe structure + is placed on the stack and a sigreturn syscall is called to simulate a + real signal return. This kind of tests does not use a trigger usually and + they are just fired using some simple included assembly trampoline code. + + - Most of these tests are successfully passing if the process gets killed by + some 'Term' signal: usually SIGSEGV or SIGBUS. Since, while writing this + kind of tests, it is extremely easy in fact to end-up injecting other + unrelated SEGV bugs in the testcases, it becomes extremely tricky to + be really sure that the tests are really addressing what they are meant + to address and they are not instead falling apart due to unplanned bugs. + In order to alleviate the misery of the life of such test-developer, a few + helpers are provided: + + - a couple of ASSERT_BAD/GOOD_CONTEXT() macros to easily parse a ucontext_t + and verify if it is indeed GOOD or BAD (depending on what we were + expecting), using the same logic/perspective as in the arm64 Kernel signals + routines. + + - a sanity mechanism to be used in 'fake_sigreturn_'-alike tests: enabled by + default it takes care to verify that the test-execution had at least + successfully progressed upto the stage of triggering the fake sigreturn + call. + + In both cases test results are expected in terms of some 'Term' signal sent + by the Kernel to the test process, or analyzing some final regs state. diff --git a/tools/testing/selftests/arm64/signal/signals.S b/tools/testing/selftests/arm64/signal/signals.S new file mode 100644 index 000000000000..4f510b3a3b4b --- /dev/null +++ b/tools/testing/selftests/arm64/signal/signals.S @@ -0,0 +1,64 @@ +/* + * SPDX-License-Identifier: GPL-2.0 + * Copyright (C) 2019 ARM Limited + */ + +#include <asm/unistd.h> + +.section .rodata +call_fmt: + .asciz "Calling sigreturn with fake sigframe sized:%zd at calculated SP @%08lX\n" + +.text + +.extern current + +.globl fake_sigreturn + +/* fake_sigreturn x0:&sigframe x1:sigframe_size x2:alignment_SP */ +fake_sigreturn: + stp x20, x21, [sp, #-16]! + stp x22, x23, [sp, #-16]! + + mov x20, x0 + mov x21, x1 + mov x22, x2 + mov x23, sp + + /* create space on the stack for fake sigframe..."x22"-aligned */ + mov x0, #0 + add x0, x21, x22 + sub x22, x22, #1 + bic x0, x0, x22 + sub x23, x23, x0 + + ldr x0, =call_fmt + mov x1, x21 + mov x2, x23 + bl printf + + mov sp, x23 + + /* now fill it with the provided content... */ + mov x0, sp + mov x1, x20 + mov x2, x21 + bl memcpy + + /* + * Here saving a last minute SP to current->token acts as a marker: + * if we got here, we are successfully faking a sigreturn; in other + * words we are reasonably sure no bad Term signal has been raised + * till now for unrelated reasons, so we should consider the possibly + * observed SEGV coming from Kernel restore_sigframe() and triggered + * as expected from our test-case. + * For simplicity field 'token' is laid out as first in struct tdescr + */ + ldr x0, current + str x23, [x0, #0] + /* SP is already pointing back to the just built fake sigframe here */ + mov x8, #__NR_rt_sigreturn + svc #0 + + /* This should not return here...looping lead to a timeout */ + b . diff --git a/tools/testing/selftests/arm64/signal/test_arm64_signals.sh b/tools/testing/selftests/arm64/signal/test_arm64_signals.sh new file mode 100755 index 000000000000..ecaa5a67d3ca --- /dev/null +++ b/tools/testing/selftests/arm64/signal/test_arm64_signals.sh @@ -0,0 +1,44 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0 +# Copyright (C) 2019 ARM Limited + +ret=0 +keep_on_fail=0 +err_out="2> /dev/null" + +# avoiding getopt +while [ $# -gt 0 ] +do + case $1 in + "-k") + keep_on_fail=1 + shift + ;; + "-v") + err_out="" + shift + ;; + *) + shift + ;; + esac +done + +TPROGS= + +i=0 +tot=$(echo $TPROGS | wc -w) + +for test in $TPROGS +do + eval ./$test $err_out + if [ $? != 0 ]; then + [ $keep_on_fail = 0 ] && echo "===>>> FAILED:: $test <<<===" && ret=1 && break + else + i=$((i + 1)) + fi +done + +echo "==>> PASSED: $i/$tot" + +exit $ret diff --git a/tools/testing/selftests/arm64/signal/test_signals.c b/tools/testing/selftests/arm64/signal/test_signals.c new file mode 100644 index 000000000000..afadb8ae33e4 --- /dev/null +++ b/tools/testing/selftests/arm64/signal/test_signals.c @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2019 ARM Limited */ + +#include <stdio.h> +#include <unistd.h> +#include <stddef.h> + +#include <kselftest.h> + +#include "test_signals.h" +#include "test_signals_utils.h" + +struct tdescr *current; +extern struct tdescr tde; + +int main(int argc, char *argv[]) +{ + current = &tde; + + ksft_print_msg("%s :: %s - SIG_TRIG:%d SIG_OK:%d -- current:%p\n", + current->name, current->descr, current->sig_trig, + current->sig_ok, current); + if (test_setup(current)) { + if (test_run(current)) + test_result(current); + test_cleanup(current); + } + + return !current->pass; +} diff --git a/tools/testing/selftests/arm64/signal/test_signals.h b/tools/testing/selftests/arm64/signal/test_signals.h new file mode 100644 index 000000000000..49536326db04 --- /dev/null +++ b/tools/testing/selftests/arm64/signal/test_signals.h @@ -0,0 +1,136 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2019 ARM Limited */ + +#ifndef __TEST_SIGNALS_H__ +#define __TEST_SIGNALS_H__ + +#include <assert.h> +#include <stdbool.h> +#include <signal.h> +#include <ucontext.h> +#include <stdint.h> + +/* + * Using ARCH specific and sanitized Kernel headers installed by KSFT + * framework since we asked for it by setting flag KSFT_KHDR_INSTALL + * in our Makefile. + */ +#include <asm/ptrace.h> +#include <asm/hwcap.h> + +/* pasted from include/linux/stringify.h */ +#define __stringify_1(x...) #x +#define __stringify(x...) __stringify_1(x) + +#define FEAT_SSBS (1 << 0) +#define FEAT_PAN (1 << 1) +#define FEAT_UAO (1 << 2) + + /* + * Reads a sysreg using the, possibly provided, S3_ encoding in order to + * avoid inject any dependency on the used toolchain regarding possibly + * still unsupported ARMv8 extensions. + * + * Using a standard mnemonic here to indicate the specific sysreg (like SSBS) + * would introduce a compile-time dependency on possibly unsupported ARMv8 + * Extensions: you could end-up failing to build the test depending on the + * available toolchain. + * This is undesirable since some tests, even if specifically targeted at some + * ARMv8 Extensions, can be plausibly run even on hardware lacking the above + * optional ARM features. (SSBS bit preservation is an example: Kernel handles + * it transparently not caring at all about the effective set of supported + * features). + * On the other side we will expect to observe different behaviours if the + * feature is supported or not: usually getting a SIGILL when trying to use + * unsupported features. For this reason we have anyway in place some + * preliminary run-time checks about the cpu effectively supported features. + * + * This helper macro is meant to be used for regs readable at EL0, BUT some + * EL1 sysregs are indeed readable too through MRS emulation Kernel-mechanism + * if the required reg is included in the supported encoding space: + * + * Documentation/arm64/cpu-feature-regsiters.txt + * + * "The infrastructure emulates only the following system register space: + * Op0=3, Op1=0, CRn=0, CRm=0,4,5,6,7 + */ +#define get_regval(regname, out) \ + asm volatile("mrs %0, " __stringify(regname) : "=r" (out) :: "memory") + +/* Regs encoding and masks naming copied in from sysreg.h */ +#define SYS_ID_AA64MMFR1_EL1 S3_0_C0_C7_1 /* MRS Emulated */ +#define SYS_ID_AA64MMFR2_EL1 S3_0_C0_C7_2 /* MRS Emulated */ +#define ID_AA64MMFR1_PAN_SHIFT 20 +#define ID_AA64MMFR2_UAO_SHIFT 4 + +/* Local Helpers */ +#define IS_PAN_SUPPORTED(val) \ + (!!((val) & (0xf << ID_AA64MMFR1_PAN_SHIFT))) +#define IS_UAO_SUPPORTED(val) \ + (!!((val) & (0xf << ID_AA64MMFR2_UAO_SHIFT))) + +#define MRS_SSBS_SYSREG S3_3_C4_C2_6 /* EL0 supported */ +#define MRS_SSBS_BIT (1 << 12) + +/* + * A descriptor used to describe and configure a test case. + * Fields with a non-trivial meaning are described inline in the following. + */ +struct tdescr { + /* KEEP THIS FIELD FIRST for easier lookup from assembly */ + void *token; + /* when disabled token based sanity checking is skipped in handler */ + bool sanity_disabled; + /* just a name for the test-case; manadatory field */ + char *name; + char *descr; + unsigned long feats_required; + /* bitmask of effectively supported feats: populated at run-time */ + unsigned long feats_supported; + bool feats_ok; + bool initialized; + unsigned int minsigstksz; + /* signum used as a test trigger. Zero if no trigger-signal is used */ + int sig_trig; + /* + * signum considered as a successfull test completion. + * Zero when no signal is expected on success + */ + int sig_ok; + /* signum expected on unsupported CPU features. */ + int sig_unsupp; + /* + * used to grab a sigcontext from a signal handler + * automatically set to SIGUSR2 if not configured + */ + int sig_copyctx; + /* a timeout in second for test completion */ + unsigned int timeout; + bool triggered; + unsigned int handled; + bool pass; + /* optional sa_flags for the installed handler */ + int sa_flags; + ucontext_t saved_uc; + /* used by copy_ctx */ + ucontext_t *live_uc; + size_t live_sz; + + /* a setup function to be called before test starts */ + int (*setup)(struct tdescr *td); + int (*cleanup)(struct tdescr *td); + + /* an optional function to be used as a trigger for test starting */ + int (*trigger)(struct tdescr *td); + /* + * the actual test-core: invoked differently depending on the + * presence of the trigger function above; this is mandatory + */ + int (*run)(struct tdescr *td, siginfo_t *si, ucontext_t *uc); + + /* an optional function for custom results' processing */ + int (*check_result)(struct tdescr *td); + + void *priv; +}; +#endif diff --git a/tools/testing/selftests/arm64/signal/test_signals_utils.c b/tools/testing/selftests/arm64/signal/test_signals_utils.c new file mode 100644 index 000000000000..c00ba355dc1b --- /dev/null +++ b/tools/testing/selftests/arm64/signal/test_signals_utils.c @@ -0,0 +1,256 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2019 ARM Limited */ + +#include <stdio.h> +#include <stdlib.h> +#include <signal.h> +#include <unistd.h> +#include <assert.h> +#include <sys/auxv.h> +#include <linux/auxvec.h> + +#include "test_signals.h" +#include "test_signals_utils.h" +#include "testcases/testcases.h" + +extern struct tdescr *current; + +void dump_uc(const char *prefix, ucontext_t *uc, int filter) +{ + int i; + + if (prefix) + fprintf(stderr, "%s", prefix); + if (filter & DUMP_REGS) + for (i = 0; i < 29; i++) + fprintf(stderr, "x%02d:%016llX\n", + i, uc->uc_mcontext.regs[i]); + if (filter & DUMP_FP) + fprintf(stderr, "x%02d(fp):%016llX\n", + i, uc->uc_mcontext.regs[i]); + i++; + if (filter & DUMP_LR) + fprintf(stderr, "x%02d(lr):%016llX\n", + i, uc->uc_mcontext.regs[i]); + if (filter & DUMP_SP) + fprintf(stderr, "sp:%016llX\n", uc->uc_mcontext.sp); + if (filter & DUMP_PC) + fprintf(stderr, "pc:%016llX\n", uc->uc_mcontext.pc); + if (filter & DUMP_PSTATE) + fprintf(stderr, "pstate:%016llX\n", uc->uc_mcontext.pstate); + if (filter & DUMP_FAULT) + fprintf(stderr, "fault_address:%016llX\n", + uc->uc_mcontext.fault_address); +} + +static void unblock_signal(int signum) +{ + sigset_t sset; + + sigemptyset(&sset); + sigaddset(&sset, signum); + sigprocmask(SIG_UNBLOCK, &sset, NULL); +} + +static int default_result(struct tdescr *td, int force_exit); + +static void default_handler(int signum, siginfo_t *si, void *uc) +{ + if (signum == current->sig_trig) { + SAFE_WRITE(2, "Handling SIG_TRIG\n"); + current->triggered = 1; + /* ->run was asserted NON-NULL in test_setup() already */ + current->run(current, si, uc); + } else if (signum == SIGILL && !current->initialized) { + /* + * A SIGILL here while still not initialized means we fail + * to even asses the existence of feature + */ + SAFE_WRITE(1, "Marking UNSUPPORTED features.\n"); + } else if (current->sig_ok && signum == current->sig_ok) { + /* it's a bug in the test code when this assert fail */ + assert(!current->sig_trig || current->triggered); + if (!current->sanity_disabled) { + fprintf(stderr, + "SIG_OK -- si_addr@:0x%p token@:0x%p\n", + si->si_addr, current->token); + assert(current->token); + } + SAFE_WRITE(2, "Handling SIG_OK\n"); + current->pass = 1; + current->handled++; + /* + * Some tests can lead to SEGV loops: in such a case we want + * to terminate immediately exiting straight away + */ + default_result(current, 1); + } else if (current->sig_copyctx && signum == current->sig_copyctx) { + memcpy(current->live_uc, uc, current->live_sz); + ASSERT_GOOD_CONTEXT(current->live_uc); + SAFE_WRITE(2, + "GOOD CONTEXT grabbed from sig_copyctx handler\n"); + } else { + if (signum == current->sig_unsupp && !current->feats_ok) { + SAFE_WRITE(2, "-- RX SIG_UNSUPP on unsupported feature...OK\n"); + current->pass = 1; + } else if (signum == SIGALRM && current->timeout) { + SAFE_WRITE(2, "-- Timeout !\n"); + } else { + SAFE_WRITE(2, "-- UNEXPECTED SIGNAL\n"); + } + default_result(current, 1); + } +} + +static int default_setup(struct tdescr *td) +{ + struct sigaction sa; + + sa.sa_sigaction = default_handler; + sa.sa_flags = SA_SIGINFO; + if (td->sa_flags) + sa.sa_flags |= td->sa_flags; + sigemptyset(&sa.sa_mask); + /* uncatchable signals naturally skipped ... */ + for (int sig = 1; sig < 32; sig++) + sigaction(sig, &sa, NULL); + /* + * RT Signals default disposition is Term but they cannot be + * generated by the Kernel in response to our tests; so just catch + * them all and report them as UNEXPECTED signals. + */ + for (int sig = SIGRTMIN; sig <= SIGRTMAX; sig++) + sigaction(sig, &sa, NULL); + + /* just in case...unblock explicitly all we need */ + if (td->sig_trig) + unblock_signal(td->sig_trig); + if (td->sig_ok) + unblock_signal(td->sig_ok); + if (td->sig_unsupp) + unblock_signal(td->sig_unsupp); + + if (td->timeout) { + unblock_signal(SIGALRM); + alarm(td->timeout); + } + fprintf(stderr, "Registered handlers for all signals.\n"); + + return 1; +} + +static inline int default_trigger(struct tdescr *td) +{ + return !raise(td->sig_trig); +} + +static int default_result(struct tdescr *td, int force_exit) +{ + if (td->pass) + SAFE_WRITE(2, "==>> completed. PASS(1)\n"); + else + SAFE_WRITE(1, "==>> completed. FAIL(0)\n"); + if (!force_exit) + return td->pass; + else + exit(td->pass ? EXIT_SUCCESS : EXIT_FAILURE); +} + +static int test_init(struct tdescr *td) +{ + td->minsigstksz = getauxval(AT_MINSIGSTKSZ); + if (!td->minsigstksz) + td->minsigstksz = MINSIGSTKSZ; + fprintf(stderr, "Detected MINSTKSIGSZ:%d\n", td->minsigstksz); + + if (td->feats_required) { + td->feats_supported = 0; + /* + * Checking for CPU required features using both the + * auxval and the arm64 MRS Emulation to read sysregs. + */ + if (getauxval(AT_HWCAP) & HWCAP_CPUID) { + uint64_t val = 0; + + if (td->feats_required & FEAT_SSBS) { + if (getauxval(AT_HWCAP) & HWCAP_SSBS) + td->feats_supported |= FEAT_SSBS; + } + if (td->feats_required & FEAT_PAN) { + get_regval(SYS_ID_AA64MMFR1_EL1, val); + if (IS_PAN_SUPPORTED(val)) + td->feats_supported |= FEAT_PAN; + } + if (td->feats_required & FEAT_UAO) { + get_regval(SYS_ID_AA64MMFR2_EL1 , val); + if (IS_UAO_SUPPORTED(val)) + td->feats_supported |= FEAT_UAO; + } + } else { + fprintf(stderr, + "CPUID regs NOT available. Marking features unsupported\n"); + } + td->feats_ok = td->feats_required == td->feats_supported; + fprintf(stderr, "Required Features %08lX are %ssupported\n", + td->feats_required, !td->feats_ok ? "NOT " : ""); + } + + if (!td->sig_copyctx) { + if (td->sig_trig != SIGUSR2) + td->sig_copyctx = SIGUSR2; + else if (td->sig_trig != SIGUSR1) + td->sig_copyctx = SIGUSR1; + else + td->sig_copyctx = 0; + } + + if (td->sig_copyctx) + unblock_signal(td->sig_copyctx); + + td->initialized = 1; + return 1; +} + +int test_setup(struct tdescr *td) +{ + /* assert core invariants symptom of a rotten testcase */ + assert(current); + assert(td); + assert(td->name); + assert(td->run); + + if (!test_init(td)) + return 0; + + if (td->setup) + return td->setup(td); + else + return default_setup(td); +} + +int test_run(struct tdescr *td) +{ + if (td->sig_trig) { + if (td->trigger) + return td->trigger(td); + else + return default_trigger(td); + } else { + return td->run(td, NULL, NULL); + } +} + +int test_result(struct tdescr *td) +{ + if (td->check_result) + td->check_result(td); + return default_result(td, 0); +} + +int test_cleanup(struct tdescr *td) +{ + if (td->cleanup) + return td->cleanup(td); + else + return 1; +} diff --git a/tools/testing/selftests/arm64/signal/test_signals_utils.h b/tools/testing/selftests/arm64/signal/test_signals_utils.h new file mode 100644 index 000000000000..e7ebae8e8077 --- /dev/null +++ b/tools/testing/selftests/arm64/signal/test_signals_utils.h @@ -0,0 +1,110 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2019 ARM Limited */ + +#ifndef __TEST_SIGNALS_UTILS_H__ +#define __TEST_SIGNALS_UTILS_H__ +#include <stdio.h> +#include <signal.h> +#include <unistd.h> +#include <ucontext.h> +#include <string.h> + +#include <asm/unistd.h> + +#include "test_signals.h" + +#define DUMP_REGS (1 << 0) +#define DUMP_FP (1 << 1) +#define DUMP_LR (1 << 2) +#define DUMP_SP (1 << 3) +#define DUMP_PC (1 << 4) +#define DUMP_PSTATE (1 << 5) +#define DUMP_FAULT (1 << 6) +#define DUMP_ALL 0xffffffff + +/* Using a signal-safe function to write something out */ +#define SAFE_WRITE(fd, str) \ + do { \ + int bytes = 0; \ + bytes = write((fd), (str) + bytes, sizeof(str) - bytes); \ + if (bytes < 0 || bytes == sizeof(str)) \ + break; \ + } while(1) + +/* Be careful this helper is NOT signal-safe */ +void dump_uc(const char *prefix, ucontext_t *uc, int filter); + +int test_setup(struct tdescr *td); +int test_cleanup(struct tdescr *td); +int test_run(struct tdescr *td); +int test_result(struct tdescr *td); +int fake_sigreturn(void *sigframe, size_t sz, int alignment); + +/* + * 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: + * + * 1. grab a valid sigcontext into td->live_uc for result analysis: in + * such case it returns 1. + * + * 2. 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. + */ +static inline __attribute__((always_inline)) +int get_current_context(struct tdescr *td, ucontext_t *uc) +{ + static volatile int seen_already; + + if (!td || !td->sig_copyctx || !uc) { + SAFE_WRITE(1, "Signal-based Context dumping NOT available\n"); + return 0; + } + + /* it's a genuine invokation..reinit */ + seen_already = 0; + td->live_uc = uc; + td->live_sz = sizeof(*uc); + memset(td->live_uc, 0x00, td->live_sz); + /* + * Grab ucontext_t triggering a signal... + * ASM equivalent of raise(td->sig_copyctx); + */ + asm volatile ("mov x8, %0\n\t" + "svc #0\n\t" + "mov x1, %1\n\t" + "mov x8, %2\n\t" + "svc #0" : + : "r" (__NR_getpid), + "r" (td->sig_copyctx), + "r" (__NR_kill) + : "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) { + SAFE_WRITE(1, "....and we're back....seen_already !\n"); + return 0; + } + seen_already = 1; + + return 1; +} + +#endif diff --git a/tools/testing/selftests/arm64/signal/testcases/testcases.c b/tools/testing/selftests/arm64/signal/testcases/testcases.c new file mode 100644 index 000000000000..9f83f3517325 --- /dev/null +++ b/tools/testing/selftests/arm64/signal/testcases/testcases.c @@ -0,0 +1,123 @@ +#include "testcases.h" + +struct _aarch64_ctx *get_header(struct _aarch64_ctx *head, uint32_t magic, + size_t resv_sz, size_t *offset) +{ + size_t offs = 0; + + while (head && head->magic != magic && offs < resv_sz - HDR_SZ) { + offs += head->size; + head = GET_RESV_NEXT_HEAD(head); + } + if (!head || head->magic != magic) + return NULL; + else if (offset) + *offset = offs; + + return head; +} + +bool validate_extra_context(struct extra_context *extra, char **err) +{ + struct _aarch64_ctx *term; + + if (!extra || !err) + return false; + + fprintf(stderr, "Validating EXTRA...\n"); + term = GET_RESV_NEXT_HEAD(extra); + if (!term || term->magic || term->size) { + SET_CTX_ERR("UN-Terminated EXTRA context"); + return false; + } + if (extra->datap & ~0x10UL) + SET_CTX_ERR("Extra DATAP misaligned"); + else if (extra->size & ~0x10UL) + SET_CTX_ERR("Extra SIZE misaligned"); + else if (extra->datap != (uint64_t)term + sizeof(*term)) + SET_CTX_ERR("Extra DATAP broken"); + if (*err) + return false; + + fprintf(stderr, "GOOD EXTRA CONTEXT FOUND !\n"); + + return true; +} + +bool validate_reserved(ucontext_t *uc, size_t resv_sz, char **err) +{ + bool terminated = false; + size_t offs = 0; + int flags = 0; + struct extra_context *extra = NULL; + struct _aarch64_ctx *head = + (struct _aarch64_ctx *)uc->uc_mcontext.__reserved; + + if (!err) + return false; + /* Walk till the end terminator verifying __reserved contents */ + while (head && !terminated && offs < resv_sz) { + if ((uint64_t)head & 0x0fUL) { + SET_CTX_ERR("Misaligned HEAD"); + return false; + } + + switch (head->magic) { + case 0: + if (head->size) + SET_CTX_ERR("Bad size for MAGIC0"); + else + terminated = true; + break; + case FPSIMD_MAGIC: + if (flags & FPSIMD_CTX) + SET_CTX_ERR("Multiple FPSIMD"); + else if (head->size != + sizeof(struct fpsimd_context)) + SET_CTX_ERR("Bad size for FPSIMD context"); + flags |= FPSIMD_CTX; + break; + case ESR_MAGIC: + if (head->size != sizeof(struct esr_context)) + SET_CTX_ERR("Bad size for ESR context"); + break; + case SVE_MAGIC: + if (flags & SVE_CTX) + SET_CTX_ERR("Multiple SVE"); + else if (head->size != + sizeof(struct sve_context)) + SET_CTX_ERR("Bad size for SVE context"); + flags |= SVE_CTX; + break; + case EXTRA_MAGIC: + if (flags & EXTRA_CTX) + SET_CTX_ERR("Multiple EXTRA"); + else if (head->size != + sizeof(struct extra_context)) + SET_CTX_ERR("Bad size for EXTRA context"); + flags |= EXTRA_CTX; + extra = (struct extra_context *)head; + break; + default: + SET_CTX_ERR("Unknown MAGIC !"); + break; + } + + if (*err) + return false; + + offs += head->size; + if (resv_sz - offs < sizeof(*head)) { + SET_CTX_ERR("Broken HEAD"); + return false; + } + + if (flags & EXTRA_CTX) + if (!validate_extra_context(extra, err)) + return false; + + head = GET_RESV_NEXT_HEAD(head); + } + + return true; +} diff --git a/tools/testing/selftests/arm64/signal/testcases/testcases.h b/tools/testing/selftests/arm64/signal/testcases/testcases.h new file mode 100644 index 000000000000..4f704c1501aa --- /dev/null +++ b/tools/testing/selftests/arm64/signal/testcases/testcases.h @@ -0,0 +1,85 @@ +#ifndef __TESTCASES_H__ +#define __TESTCASES_H__ + +#include <stdio.h> +#include <stdint.h> +#include <stdbool.h> +#include <unistd.h> +#include <ucontext.h> + +#include <asm/sigcontext.h> + +#define FPSIMD_CTX (1 << 0) +#define SVE_CTX (1 << 1) +#define EXTRA_CTX (1 << 2) + +#define HDR_SZ \ + sizeof(struct _aarch64_ctx) + +#define GET_SF_RESV_HEAD(sf) \ + (struct _aarch64_ctx *)(&sf.uc.uc_mcontext.__reserved) + +#define GET_SF_RESV_SIZE(sf) \ + sizeof(sf.uc.uc_mcontext.__reserved) + +#define GET_UCP_RESV_SIZE(ucp) \ + sizeof(((ucontext_t *)ucp)->uc_mcontext.__reserved) + +#define ASSERT_BAD_CONTEXT(uc) \ + do { \ + char *err = NULL; \ + assert(!validate_reserved((uc), GET_UCP_RESV_SIZE((uc)), &err)); \ + if (err) \ + fprintf(stderr, "Using badly built context - ERR: %s\n", err);\ + } while(0) + +#define ASSERT_GOOD_CONTEXT(uc) \ + do { \ + char *err = NULL; \ + if (!validate_reserved((uc), GET_UCP_RESV_SIZE((uc)), &err)) { \ + if (err) \ + fprintf(stderr, "Detected BAD context - ERR: %s\n", err);\ + assert(0); \ + } else { \ + fprintf(stderr, "uc context validated.\n"); \ + } \ + } while(0) + +/* head->size accounts both for payload and header _aarch64_ctx size ! */ +#define GET_RESV_NEXT_HEAD(h) \ + (struct _aarch64_ctx *)((uint8_t *)(h) + (h)->size) + +#define SET_CTX_ERR(str) \ + do { \ + if (err) \ + *err = str; \ + } while(0) + +struct a_sigframe { + siginfo_t info; + ucontext_t uc; +}; + + +bool validate_reserved(ucontext_t *uc, size_t resv_sz, char **err); + +bool validate_extra_context(struct extra_context *extra, char **err); + +struct _aarch64_ctx *get_header(struct _aarch64_ctx *head, uint32_t magic, + size_t resv_sz, size_t *offset); + +static inline struct _aarch64_ctx *get_terminator(struct _aarch64_ctx *head, + size_t resv_sz, + size_t *offset) +{ + return get_header(head, 0, resv_sz, offset); +} + +static inline void write_terminator(struct _aarch64_ctx *tail) +{ + if (tail) { + tail->magic = 0; + tail->size = 0; + } +} +#endif
On Thu, Jun 13, 2019 at 12:13:24PM +0100, Cristian Marussi wrote:
Added some arm64/signal specific boilerplate and utility code to help further testcase development. Still no test case code included though.
There is a lot of code here, doing a variety of different things.
It may be more digestible if the common code is added in pieces, starting with whatever is needed for the simplest testcase, then adding that testcase, then extending the common code for the next case, and so on.
This would make it easier to see why each bit of common code is being added, and how it gets used.
I'll try to review as-is for now, but it might be worth splitting this patch up a bit when reposting.
Signed-off-by: Cristian Marussi cristian.marussi@arm.com
tools/testing/selftests/arm64/Makefile | 2 +- .../testing/selftests/arm64/signal/.gitignore | 5 + tools/testing/selftests/arm64/signal/Makefile | 86 ++++++ tools/testing/selftests/arm64/signal/README | 56 ++++ .../testing/selftests/arm64/signal/signals.S | 64 +++++ .../arm64/signal/test_arm64_signals.sh | 44 +++ .../selftests/arm64/signal/test_signals.c | 30 ++ .../selftests/arm64/signal/test_signals.h | 136 ++++++++++ .../arm64/signal/test_signals_utils.c | 256 ++++++++++++++++++ .../arm64/signal/test_signals_utils.h | 110 ++++++++ .../arm64/signal/testcases/testcases.c | 123 +++++++++ .../arm64/signal/testcases/testcases.h | 85 ++++++ 12 files changed, 996 insertions(+), 1 deletion(-) create mode 100644 tools/testing/selftests/arm64/signal/.gitignore create mode 100644 tools/testing/selftests/arm64/signal/Makefile create mode 100644 tools/testing/selftests/arm64/signal/README create mode 100644 tools/testing/selftests/arm64/signal/signals.S create mode 100755 tools/testing/selftests/arm64/signal/test_arm64_signals.sh create mode 100644 tools/testing/selftests/arm64/signal/test_signals.c create mode 100644 tools/testing/selftests/arm64/signal/test_signals.h create mode 100644 tools/testing/selftests/arm64/signal/test_signals_utils.c create mode 100644 tools/testing/selftests/arm64/signal/test_signals_utils.h create mode 100644 tools/testing/selftests/arm64/signal/testcases/testcases.c create mode 100644 tools/testing/selftests/arm64/signal/testcases/testcases.h
diff --git a/tools/testing/selftests/arm64/Makefile b/tools/testing/selftests/arm64/Makefile index 03a0d4f71218..af59dc74e0dc 100644 --- a/tools/testing/selftests/arm64/Makefile +++ b/tools/testing/selftests/arm64/Makefile @@ -6,7 +6,7 @@ ARCH ?= $(shell uname -m) ARCH := $(shell echo $(ARCH) | sed -e s/aarch64/arm64/) ifeq ("x$(ARCH)", "xarm64") -SUBDIRS := +SUBDIRS := signal else SUBDIRS := endif diff --git a/tools/testing/selftests/arm64/signal/.gitignore b/tools/testing/selftests/arm64/signal/.gitignore new file mode 100644 index 000000000000..7234ebd99363 --- /dev/null +++ b/tools/testing/selftests/arm64/signal/.gitignore @@ -0,0 +1,5 @@ +# Helper script's internal testcases list (TPROGS) is regenerated +# each time by Makefile on standalone (non KSFT driven) runs. +# Committing such list creates a dependency between testcases +# patches such that they are no more easily revertable. Just ignore. +test_arm64_signals.sh diff --git a/tools/testing/selftests/arm64/signal/Makefile b/tools/testing/selftests/arm64/signal/Makefile new file mode 100644 index 000000000000..9518841124a3 --- /dev/null +++ b/tools/testing/selftests/arm64/signal/Makefile @@ -0,0 +1,86 @@ +# SPDX-License-Identifier: GPL-2.0 +# Copyright (C) 2019 ARM Limited
+# Supports also standalone invokation out of KSFT-tree. +# +# Build standalone with (issued from within the dir containing this makefile): +# +# host:~$ make clean && make INSTALL_PATH=<your-device-instdir> install
Minor nit: why clean? Minor nit: this won't work if run from ~. Maybe just say "$" for the prompt.
+# +# Run standalone on device with: +# device:~# <your-device-instdir>/test_arm64_signals.sh [-k|-v]
Minor nit: do these tests actually need to run as root? If not (and for the above reasons) you could again just say "$".
+#
+# A proper top_srcdir is needed both by KSFT(lib.mk) +# and standalone builds +top_srcdir = ../../../../..
+CFLAGS += -std=gnu99 -I. -I$(top_srcdir)/tools/testing/selftests/ +SRCS := $(filter-out testcases/testcases.c,$(wildcard testcases/*.c)) +PROGS := $(patsubst %.c,%,$(SRCS))
+# Guessing as best as we can where the Kernel headers +# could have been installed depending on ENV config and +# type of invocation. +ifeq ($(KBUILD_OUTPUT),) +khdr_dir = $(top_srcdir)/usr/include +else +ifeq (0,$(MAKELEVEL)) +khdr_dir = $(KBUILD_OUTPUT)/usr/include +else +# the KSFT preferred location when KBUILD_OUTPUT is set +khdr_dir = $(KBUILD_OUTPUT)/kselftest/usr/include +endif +endif
+CFLAGS += -I$(khdr_dir)
+# Standalone run +ifeq (0,$(MAKELEVEL)) +CC := $(CROSS_COMPILE)gcc +RUNNER = test_arm64_signals.sh +INSTALL_PATH ?= install/
+all: $(RUNNER)
+$(RUNNER): $(PROGS)
- sed -i -e 's#PROGS=.*#PROGS="$(PROGS)"#' $@
+install: all
- mkdir -p $(INSTALL_PATH)/testcases
- cp $(PROGS) $(INSTALL_PATH)/testcases
- cp $(RUNNER) $(INSTALL_PATH)/
+.PHONY clean:
- rm -f $(PROGS)
+# KSFT run +else +# Generated binaries to be installed by top KSFT script +TEST_GEN_PROGS := $(notdir $(PROGS))
+# Get Kernel headers installed and use them. +KSFT_KHDR_INSTALL := 1
+# This include mk will also mangle the TEST_GEN_PROGS list +# to account for any OUTPUT target-dirs optionally provided +# by the toplevel makefile +include ../../lib.mk
+$(TEST_GEN_PROGS): $(PROGS)
- -mkdir -p $(OUTPUT)
- -cp $(PROGS) $(OUTPUT)/
Why doesn't it matter if these fail?
(Maybe this was pasted from other kselftest Makefiles...)
+clean:
- $(CLEAN)
- rm -f $(PROGS)
+endif
+# Common test-unit targets to build common-layout test-cases executables +# Needs secondary expansion to properly include the testcase c-file in pre-reqs +.SECONDEXPANSION: +$(PROGS): test_signals.c test_signals_utils.c signals.S testcases/testcases.c $$@.c test_signals.h test_signals_utils.h testcases/testcases.h
- @if [ ! -d $(khdr_dir) ]; then \
echo -n "\n!!! WARNING: $(khdr_dir) NOT FOUND."; \
echo "===> Are you sure Kernel Headers have been installed properly ?\n"; \
- fi
- $(CC) $(CFLAGS) $^ -o $@
diff --git a/tools/testing/selftests/arm64/signal/README b/tools/testing/selftests/arm64/signal/README new file mode 100644 index 000000000000..315d77558e14 --- /dev/null +++ b/tools/testing/selftests/arm64/signal/README @@ -0,0 +1,56 @@ +KSelfTest arm64/signal/ +=======================
+Signals Tests ++++++++++++++
+- Tests are built around a common main compilation unit: such shared main
- enforces a standard sequence of operations needed to perform a single
- signal-test (setup/trigger/run/result/cleanup)
+- The above mentioned ops are configurable on a test-by-test basis: each test
- is described (and configured) using the descriptor signals.h::struct tdescr
+- Each signal testcase is compiled into its own executable: a separate
- executable is used for each test since many tests complete successfully
- by receiving some kind of 'Term' signal from the Kernel, so it's safer
Nit: to avoid confusion with SIGTERM, maybe say "some fatal signal" instead of 'Term' (although I can see what you mean).
- to run each test unit in its own standalone process, so as to start each
- test from a clean-slate.
Pedantic nit: "-" -> " "
+- New tests can be simply defined in testcases/ dir providing a proper struct
- tdescr overriding all the defaults we wish to change (as of now providing a
- custom run method is mandatory though)
+- Signals' test-cases hereafter defined belong currently to two
- principal families:
- 'mangle_' tests: a real signal (SIGUSR1) is raised and used as a trigger
- and then the test case code messes-up with the sigframe ucontext_t from
- inside the sighandler itself.
- 'fake_sigreturn_' tests: a brand new custom artificial sigframe structure
- is placed on the stack and a sigreturn syscall is called to simulate a
- real signal return. This kind of tests does not use a trigger usually and
- they are just fired using some simple included assembly trampoline code.
- Most of these tests are successfully passing if the process gets killed by
- some 'Term' signal: usually SIGSEGV or SIGBUS. Since, while writing this
Nit: "some fatal signal"
- kind of tests, it is extremely easy in fact to end-up injecting other
- unrelated SEGV bugs in the testcases, it becomes extremely tricky to
For tests that receive a fatal signal on success, is it always the same signal?
I'm wondering whether we can filter expected from unexpected signals using siginfo. For a bad sigreturn for example, I'd expect si_addr to point to (or at least somewhere within) the signal frame.
Maybe it's not that simple, though.
- be really sure that the tests are really addressing what they are meant
- to address and they are not instead falling apart due to unplanned bugs.
- In order to alleviate the misery of the life of such test-developer, a few
- helpers are provided:
- a couple of ASSERT_BAD/GOOD_CONTEXT() macros to easily parse a ucontext_t
and verify if it is indeed GOOD or BAD (depending on what we were
expecting), using the same logic/perspective as in the arm64 Kernel signals
routines.
- a sanity mechanism to be used in 'fake_sigreturn_'-alike tests: enabled by
default it takes care to verify that the test-execution had at least
successfully progressed upto the stage of triggering the fake sigreturn
Pedantic nit: "upto" -> "up to"
call.
- In both cases test results are expected in terms of some 'Term' signal sent
Nit: "some fatal signal"?
- by the Kernel to the test process, or analyzing some final regs state.
In general, putting comments nearer to the functions makes for easier reviewing, and makes it easier to keep the documentation in sync when the code changes.
Since you've written this documentation, we may as well keep it, though.
diff --git a/tools/testing/selftests/arm64/signal/signals.S b/tools/testing/selftests/arm64/signal/signals.S new file mode 100644 index 000000000000..4f510b3a3b4b --- /dev/null +++ b/tools/testing/selftests/arm64/signal/signals.S @@ -0,0 +1,64 @@ +/*
- SPDX-License-Identifier: GPL-2.0
- Copyright (C) 2019 ARM Limited
- */
+#include <asm/unistd.h>
+.section .rodata
This also needs to be marked allocatable (, "a")
You can also just say
.rodata
which gives you the sane default attributes for this section.
+call_fmt:
- .asciz "Calling sigreturn with fake sigframe sized:%zd at calculated SP @%08lX\n"
+.text
+.extern current
This is harmless, but people usually don't bother with .extern. It doesn't do anything IIUC.
+.globl fake_sigreturn
+/* fake_sigreturn x0:&sigframe x1:sigframe_size x2:alignment_SP */ +fake_sigreturn:
- stp x20, x21, [sp, #-16]!
- stp x22, x23, [sp, #-16]!
Nit: <tab> <mnemonic> <tab> [<operands> separated by ", " ...]
If this function doesn't return, does it need to save anything at all?
- mov x20, x0
- mov x21, x1
- mov x22, x2
- mov x23, sp
- /* create space on the stack for fake sigframe..."x22"-aligned */
- mov x0, #0
- add x0, x21, x22
- sub x22, x22, #1
- bic x0, x0, x22
- sub x23, x23, x0
- ldr x0, =call_fmt
- mov x1, x21
- mov x2, x23
- bl printf
- mov sp, x23
- /* now fill it with the provided content... */
- mov x0, sp
- mov x1, x20
- mov x2, x21
- bl memcpy
- /*
* Here saving a last minute SP to current->token acts as a marker:
* if we got here, we are successfully faking a sigreturn; in other
* words we are reasonably sure no bad Term signal has been raised
* till now for unrelated reasons, so we should consider the possibly
* observed SEGV coming from Kernel restore_sigframe() and triggered
* as expected from our test-case.
* For simplicity field 'token' is laid out as first in struct tdescr
*/
- ldr x0, current
Nit: this assumes that current is
- str x23, [x0, #0]
What's this for? Doesn't this overwrite the first bytes of siginfo or something?
Nit: The ", #0" is also redundant here.
- /* SP is already pointing back to the just built fake sigframe here */
- mov x8, #__NR_rt_sigreturn
- svc #0
- /* This should not return here...looping lead to a timeout */
- b .
Could we also return here, rather than relying on a timeout? This function strictly doesn't return when it succeeds, so this would provide an immediate indication of failure.
Maybe it's not worth it though, since some kinds of failure might lead to corruption or jumping to a random location -- which might only be detectable via a timeout in any case.
diff --git a/tools/testing/selftests/arm64/signal/test_arm64_signals.sh b/tools/testing/selftests/arm64/signal/test_arm64_signals.sh new file mode 100755 index 000000000000..ecaa5a67d3ca --- /dev/null +++ b/tools/testing/selftests/arm64/signal/test_arm64_signals.sh @@ -0,0 +1,44 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0 +# Copyright (C) 2019 ARM Limited
+ret=0 +keep_on_fail=0 +err_out="2> /dev/null"
+# avoiding getopt
It doesn't matter, but do we need to avoid getopt?
+while [ $# -gt 0 ] +do
- case $1 in
"-k")
keep_on_fail=1
shift
;;
"-v")
err_out=""
Or just
err_out=
shift
;;
*)
shift
Could you move the shift out of the switch here? Each case seems to end with it.
;;
- esac
+done
+TPROGS=
+i=0 +tot=$(echo $TPROGS | wc -w)
+for test in $TPROGS +do
- eval ./$test $err_out
- if [ $? != 0 ]; then
[ $keep_on_fail = 0 ] && echo "===>>> FAILED:: $test <<<===" && ret=1 && break
- else
i=$((i + 1))
- fi
+done
+echo "==>> PASSED: $i/$tot"
+exit $ret diff --git a/tools/testing/selftests/arm64/signal/test_signals.c b/tools/testing/selftests/arm64/signal/test_signals.c new file mode 100644 index 000000000000..afadb8ae33e4 --- /dev/null +++ b/tools/testing/selftests/arm64/signal/test_signals.c @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2019 ARM Limited */
+#include <stdio.h> +#include <unistd.h> +#include <stddef.h>
^ Are any of these needed? I don't see direct use of any libc stuff in this file...
+#include <kselftest.h>
+#include "test_signals.h" +#include "test_signals_utils.h"
+struct tdescr *current; +extern struct tdescr tde;
+int main(int argc, char *argv[]) +{
- current = &tde;
- ksft_print_msg("%s :: %s - SIG_TRIG:%d SIG_OK:%d -- current:%p\n",
current->name, current->descr, current->sig_trig,
current->sig_ok, current);
- if (test_setup(current)) {
if (test_run(current))
test_result(current);
test_cleanup(current);
- }
- return !current->pass;
+} diff --git a/tools/testing/selftests/arm64/signal/test_signals.h b/tools/testing/selftests/arm64/signal/test_signals.h new file mode 100644 index 000000000000..49536326db04 --- /dev/null +++ b/tools/testing/selftests/arm64/signal/test_signals.h @@ -0,0 +1,136 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2019 ARM Limited */
+#ifndef __TEST_SIGNALS_H__ +#define __TEST_SIGNALS_H__
+#include <assert.h> +#include <stdbool.h> +#include <signal.h> +#include <ucontext.h> +#include <stdint.h>
+/*
- Using ARCH specific and sanitized Kernel headers installed by KSFT
- framework since we asked for it by setting flag KSFT_KHDR_INSTALL
- in our Makefile.
- */
+#include <asm/ptrace.h> +#include <asm/hwcap.h>
+/* pasted from include/linux/stringify.h */ +#define __stringify_1(x...) #x +#define __stringify(x...) __stringify_1(x)
+#define FEAT_SSBS (1 << 0) +#define FEAT_PAN (1 << 1) +#define FEAT_UAO (1 << 2)
^ Comment to say what these are?
- /*
- Reads a sysreg using the, possibly provided, S3_ encoding in order to
- avoid inject any dependency on the used toolchain regarding possibly
- still unsupported ARMv8 extensions.
- Using a standard mnemonic here to indicate the specific sysreg (like SSBS)
- would introduce a compile-time dependency on possibly unsupported ARMv8
- Extensions: you could end-up failing to build the test depending on the
- available toolchain.
- This is undesirable since some tests, even if specifically targeted at some
- ARMv8 Extensions, can be plausibly run even on hardware lacking the above
- optional ARM features. (SSBS bit preservation is an example: Kernel handles
- it transparently not caring at all about the effective set of supported
- features).
- On the other side we will expect to observe different behaviours if the
- feature is supported or not: usually getting a SIGILL when trying to use
- unsupported features. For this reason we have anyway in place some
- preliminary run-time checks about the cpu effectively supported features.
- This helper macro is meant to be used for regs readable at EL0, BUT some
- EL1 sysregs are indeed readable too through MRS emulation Kernel-mechanism
- if the required reg is included in the supported encoding space:
- Documentation/arm64/cpu-feature-regsiters.txt
- "The infrastructure emulates only the following system register space:
- Op0=3, Op1=0, CRn=0, CRm=0,4,5,6,7
- */
+#define get_regval(regname, out) \
- asm volatile("mrs %0, " __stringify(regname) : "=r" (out) :: "memory")
+/* Regs encoding and masks naming copied in from sysreg.h */ +#define SYS_ID_AA64MMFR1_EL1 S3_0_C0_C7_1 /* MRS Emulated */ +#define SYS_ID_AA64MMFR2_EL1 S3_0_C0_C7_2 /* MRS Emulated */ +#define ID_AA64MMFR1_PAN_SHIFT 20 +#define ID_AA64MMFR2_UAO_SHIFT 4
+/* Local Helpers */ +#define IS_PAN_SUPPORTED(val) \
- (!!((val) & (0xf << ID_AA64MMFR1_PAN_SHIFT)))
+#define IS_UAO_SUPPORTED(val) \
- (!!((val) & (0xf << ID_AA64MMFR2_UAO_SHIFT)))
These should probably be 0xfUL. The ID regs are 64-bit, so a shift >= 28 would cause things to go wrong here.
+#define MRS_SSBS_SYSREG S3_3_C4_C2_6 /* EL0 supported */ +#define MRS_SSBS_BIT (1 << 12)
+/*
- A descriptor used to describe and configure a test case.
- Fields with a non-trivial meaning are described inline in the following.
- */
+struct tdescr {
- /* KEEP THIS FIELD FIRST for easier lookup from assembly */
- void *token;
- /* when disabled token based sanity checking is skipped in handler */
- bool sanity_disabled;
- /* just a name for the test-case; manadatory field */
- char *name;
- char *descr;
- unsigned long feats_required;
- /* bitmask of effectively supported feats: populated at run-time */
- unsigned long feats_supported;
- bool feats_ok;
- bool initialized;
- unsigned int minsigstksz;
- /* signum used as a test trigger. Zero if no trigger-signal is used */
- int sig_trig;
- /*
* signum considered as a successfull test completion.
successful
* Zero when no signal is expected on success
*/
- int sig_ok;
Could this be bool, or is it a signal number if nonzero?
- /* signum expected on unsupported CPU features. */
- int sig_unsupp;
- /*
* used to grab a sigcontext from a signal handler
* automatically set to SIGUSR2 if not configured
*/
- int sig_copyctx;
Is there any reason for this to be customisable?
- /* a timeout in second for test completion */
- unsigned int timeout;
- bool triggered;
- unsigned int handled;
bool, or otherwise what does the value for handled mean?
- bool pass;
- /* optional sa_flags for the installed handler */
- int sa_flags;
- ucontext_t saved_uc;
- /* used by copy_ctx */
- ucontext_t *live_uc;
- size_t live_sz;
- /* a setup function to be called before test starts */
- int (*setup)(struct tdescr *td);
- int (*cleanup)(struct tdescr *td);
- /* an optional function to be used as a trigger for test starting */
- int (*trigger)(struct tdescr *td);
- /*
* the actual test-core: invoked differently depending on the
* presence of the trigger function above; this is mandatory
*/
- int (*run)(struct tdescr *td, siginfo_t *si, ucontext_t *uc);
- /* an optional function for custom results' processing */
- int (*check_result)(struct tdescr *td);
- void *priv;
+}; +#endif diff --git a/tools/testing/selftests/arm64/signal/test_signals_utils.c b/tools/testing/selftests/arm64/signal/test_signals_utils.c new file mode 100644 index 000000000000..c00ba355dc1b --- /dev/null +++ b/tools/testing/selftests/arm64/signal/test_signals_utils.c @@ -0,0 +1,256 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2019 ARM Limited */
+#include <stdio.h> +#include <stdlib.h> +#include <signal.h> +#include <unistd.h> +#include <assert.h> +#include <sys/auxv.h> +#include <linux/auxvec.h>
+#include "test_signals.h" +#include "test_signals_utils.h" +#include "testcases/testcases.h"
+extern struct tdescr *current;
+void dump_uc(const char *prefix, ucontext_t *uc, int filter)
Should probably #include <ucontext.h> for this, even if we're getting it via some other header.
+{
- int i;
- if (prefix)
fprintf(stderr, "%s", prefix);
- if (filter & DUMP_REGS)
for (i = 0; i < 29; i++)
fprintf(stderr, "x%02d:%016llX\n",
i, uc->uc_mcontext.regs[i]);
- if (filter & DUMP_FP)
fprintf(stderr, "x%02d(fp):%016llX\n",
i, uc->uc_mcontext.regs[i]);
- i++;
- if (filter & DUMP_LR)
fprintf(stderr, "x%02d(lr):%016llX\n",
i, uc->uc_mcontext.regs[i]);
- if (filter & DUMP_SP)
fprintf(stderr, "sp:%016llX\n", uc->uc_mcontext.sp);
- if (filter & DUMP_PC)
fprintf(stderr, "pc:%016llX\n", uc->uc_mcontext.pc);
- if (filter & DUMP_PSTATE)
fprintf(stderr, "pstate:%016llX\n", uc->uc_mcontext.pstate);
- if (filter & DUMP_FAULT)
fprintf(stderr, "fault_address:%016llX\n",
uc->uc_mcontext.fault_address);
+}
+static void unblock_signal(int signum) +{
- sigset_t sset;
- sigemptyset(&sset);
- sigaddset(&sset, signum);
- sigprocmask(SIG_UNBLOCK, &sset, NULL);
+}
+static int default_result(struct tdescr *td, int force_exit);
Can we move default_result() here to avoid this forward declaration?
+static void default_handler(int signum, siginfo_t *si, void *uc) +{
- if (signum == current->sig_trig) {
SAFE_WRITE(2, "Handling SIG_TRIG\n");
current->triggered = 1;
/* ->run was asserted NON-NULL in test_setup() already */
current->run(current, si, uc);
- } else if (signum == SIGILL && !current->initialized) {
/*
* A SIGILL here while still not initialized means we fail
* to even asses the existence of feature
*/
SAFE_WRITE(1, "Marking UNSUPPORTED features.\n");
- } else if (current->sig_ok && signum == current->sig_ok) {
/* it's a bug in the test code when this assert fail */
assert(!current->sig_trig || current->triggered);
if (!current->sanity_disabled) {
fprintf(stderr,
"SIG_OK -- si_addr@:0x%p token@:0x%p\n",
si->si_addr, current->token);
assert(current->token);
}
SAFE_WRITE(2, "Handling SIG_OK\n");
current->pass = 1;
current->handled++;
/*
* Some tests can lead to SEGV loops: in such a case we want
* to terminate immediately exiting straight away
*/
default_result(current, 1);
- } else if (current->sig_copyctx && signum == current->sig_copyctx) {
memcpy(current->live_uc, uc, current->live_sz);
ASSERT_GOOD_CONTEXT(current->live_uc);
SAFE_WRITE(2,
"GOOD CONTEXT grabbed from sig_copyctx handler\n");
- } else {
if (signum == current->sig_unsupp && !current->feats_ok) {
SAFE_WRITE(2, "-- RX SIG_UNSUPP on unsupported feature...OK\n");
current->pass = 1;
} else if (signum == SIGALRM && current->timeout) {
SAFE_WRITE(2, "-- Timeout !\n");
} else {
SAFE_WRITE(2, "-- UNEXPECTED SIGNAL\n");
}
default_result(current, 1);
- }
+}
+static int default_setup(struct tdescr *td) +{
- struct sigaction sa;
- sa.sa_sigaction = default_handler;
- sa.sa_flags = SA_SIGINFO;
- if (td->sa_flags)
sa.sa_flags |= td->sa_flags;
- sigemptyset(&sa.sa_mask);
- /* uncatchable signals naturally skipped ... */
- for (int sig = 1; sig < 32; sig++)
sigaction(sig, &sa, NULL);
- /*
* RT Signals default disposition is Term but they cannot be
* generated by the Kernel in response to our tests; so just catch
* them all and report them as UNEXPECTED signals.
*/
- for (int sig = SIGRTMIN; sig <= SIGRTMAX; sig++)
sigaction(sig, &sa, NULL);
- /* just in case...unblock explicitly all we need */
- if (td->sig_trig)
unblock_signal(td->sig_trig);
- if (td->sig_ok)
unblock_signal(td->sig_ok);
- if (td->sig_unsupp)
unblock_signal(td->sig_unsupp);
- if (td->timeout) {
unblock_signal(SIGALRM);
alarm(td->timeout);
- }
- fprintf(stderr, "Registered handlers for all signals.\n");
- return 1;
+}
+static inline int default_trigger(struct tdescr *td) +{
- return !raise(td->sig_trig);
+}
+static int default_result(struct tdescr *td, int force_exit) +{
- if (td->pass)
SAFE_WRITE(2, "==>> completed. PASS(1)\n");
- else
SAFE_WRITE(1, "==>> completed. FAIL(0)\n");
- if (!force_exit)
return td->pass;
- else
exit(td->pass ? EXIT_SUCCESS : EXIT_FAILURE);
+}
+static int test_init(struct tdescr *td) +{
- td->minsigstksz = getauxval(AT_MINSIGSTKSZ);
- if (!td->minsigstksz)
td->minsigstksz = MINSIGSTKSZ;
- fprintf(stderr, "Detected MINSTKSIGSZ:%d\n", td->minsigstksz);
- if (td->feats_required) {
td->feats_supported = 0;
/*
* Checking for CPU required features using both the
* auxval and the arm64 MRS Emulation to read sysregs.
*/
if (getauxval(AT_HWCAP) & HWCAP_CPUID) {
uint64_t val = 0;
if (td->feats_required & FEAT_SSBS) {
if (getauxval(AT_HWCAP) & HWCAP_SSBS)
td->feats_supported |= FEAT_SSBS;
}
if (td->feats_required & FEAT_PAN) {
get_regval(SYS_ID_AA64MMFR1_EL1, val);
if (IS_PAN_SUPPORTED(val))
td->feats_supported |= FEAT_PAN;
}
if (td->feats_required & FEAT_UAO) {
get_regval(SYS_ID_AA64MMFR2_EL1 , val);
if (IS_UAO_SUPPORTED(val))
td->feats_supported |= FEAT_UAO;
}
} else {
fprintf(stderr,
"CPUID regs NOT available. Marking features unsupported\n");
}
td->feats_ok = td->feats_required == td->feats_supported;
fprintf(stderr, "Required Features %08lX are %ssupported\n",
td->feats_required, !td->feats_ok ? "NOT " : "");
- }
- if (!td->sig_copyctx) {
if (td->sig_trig != SIGUSR2)
td->sig_copyctx = SIGUSR2;
else if (td->sig_trig != SIGUSR1)
td->sig_copyctx = SIGUSR1;
else
td->sig_copyctx = 0;
- }
- if (td->sig_copyctx)
unblock_signal(td->sig_copyctx);
- td->initialized = 1;
- return 1;
+}
+int test_setup(struct tdescr *td) +{
- /* assert core invariants symptom of a rotten testcase */
- assert(current);
- assert(td);
- assert(td->name);
- assert(td->run);
- if (!test_init(td))
return 0;
- if (td->setup)
return td->setup(td);
- else
return default_setup(td);
+}
+int test_run(struct tdescr *td) +{
- if (td->sig_trig) {
if (td->trigger)
return td->trigger(td);
else
return default_trigger(td);
- } else {
return td->run(td, NULL, NULL);
- }
+}
+int test_result(struct tdescr *td) +{
- if (td->check_result)
td->check_result(td);
- return default_result(td, 0);
+}
+int test_cleanup(struct tdescr *td) +{
- if (td->cleanup)
return td->cleanup(td);
- else
return 1;
+}
Do we use the return value of either of these functions? Should we?
diff --git a/tools/testing/selftests/arm64/signal/test_signals_utils.h b/tools/testing/selftests/arm64/signal/test_signals_utils.h new file mode 100644 index 000000000000..e7ebae8e8077 --- /dev/null +++ b/tools/testing/selftests/arm64/signal/test_signals_utils.h @@ -0,0 +1,110 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2019 ARM Limited */
+#ifndef __TEST_SIGNALS_UTILS_H__ +#define __TEST_SIGNALS_UTILS_H__ +#include <stdio.h> +#include <signal.h> +#include <unistd.h> +#include <ucontext.h> +#include <string.h>
+#include <asm/unistd.h>
+#include "test_signals.h"
+#define DUMP_REGS (1 << 0) +#define DUMP_FP (1 << 1) +#define DUMP_LR (1 << 2) +#define DUMP_SP (1 << 3) +#define DUMP_PC (1 << 4) +#define DUMP_PSTATE (1 << 5) +#define DUMP_FAULT (1 << 6) +#define DUMP_ALL 0xffffffff
+/* Using a signal-safe function to write something out */ +#define SAFE_WRITE(fd, str) \
- do { \
int bytes = 0; \
bytes = write((fd), (str) + bytes, sizeof(str) - bytes); \
if (bytes < 0 || bytes == sizeof(str)) \
break; \
In the interest of readability, consider lining up the \ here. Doesn't matter that much though.
Also: won't we write the start of the string repeatedly here? bytes is never updated AFAICT.
- } while(1)
For block-like macros, I tend to write
#define FOO(bar) do { \ /* ... */ \ } while (0)
to save a line and reduce indentation. This is a matter of style though.
Finally, does this need to be a macro?
To avoid surprises, we could use strlen() for the string length. If the compiler is smart enough and the function is inlined, then strlen("foo") can be evaluated at compile time... but anyway, we are going to make syscalls, so the cost of an out-of-line strlen() is not a big deal.
+/* Be careful this helper is NOT signal-safe */ +void dump_uc(const char *prefix, ucontext_t *uc, int filter);
+int test_setup(struct tdescr *td); +int test_cleanup(struct tdescr *td); +int test_run(struct tdescr *td); +int test_result(struct tdescr *td); +int fake_sigreturn(void *sigframe, size_t sz, int alignment);
+/*
- 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.
- */
+static inline __attribute__((always_inline)) +int get_current_context(struct tdescr *td, ucontext_t *uc) +{
- static volatile int seen_already;
If there could be multiple users of this in the same program, it may make sense to make seen_already part of td.
Strictly speaking, this should be sig_atomic_t, not int (I think volatile int can't be torn into multiple accesses on any reasonable arch, but it depends which toolchain folks you talk to...)
- if (!td || !td->sig_copyctx || !uc) {
SAFE_WRITE(1, "Signal-based Context dumping NOT available\n");
return 0;
- }
- /* it's a genuine invokation..reinit */
- seen_already = 0;
- td->live_uc = uc;
- td->live_sz = sizeof(*uc);
- memset(td->live_uc, 0x00, td->live_sz);
Is this required? It is just here to help sanity-check whether live_uc is populatd with real data?
- /*
* Grab ucontext_t triggering a signal...
* ASM equivalent of raise(td->sig_copyctx);
*/
- asm volatile ("mov x8, %0\n\t"
"svc #0\n\t"
"mov x1, %1\n\t"
"mov x8, %2\n\t"
"svc #0" :
Nit: for readability, it's usual to put the : only at the start of lines when writing an asm on multiple lines.
: "r" (__NR_getpid),
"r" (td->sig_copyctx),
"r" (__NR_kill)
For the __NR_foo constants, you can use "i" to avoid wasting a register.
: "memory");
You need clobbers for x1 and x8, and also x0 (for the svc return value).
How does the compiler know that live_uc may be modified by this asm?
- /*
* 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) {
SAFE_WRITE(1, "....and we're back....seen_already !\n");
Can we make this message more self-explanatory?
Say "Can't populate already-populated ucontext" or somethine like that?
return 0;
- }
- seen_already = 1;
- return 1;
Do we have a way to detect that live_uc really was populated? I think that if sig_copyctx was blocked or handled inappropriately due to a bug elsewhere in the program, we could just fall through here and assume that td->live_uc contains valid data.
If we have a flag in td to indicate that live_uc was populated, we may be able to use it for this.
+}
+#endif diff --git a/tools/testing/selftests/arm64/signal/testcases/testcases.c b/tools/testing/selftests/arm64/signal/testcases/testcases.c new file mode 100644 index 000000000000..9f83f3517325 --- /dev/null +++ b/tools/testing/selftests/arm64/signal/testcases/testcases.c @@ -0,0 +1,123 @@ +#include "testcases.h"
Should <asm/sigcontext.h> be included for the arm64 sigcontext types here?
+struct _aarch64_ctx *get_header(struct _aarch64_ctx *head, uint32_t magic,
size_t resv_sz, size_t *offset)
+{
- size_t offs = 0;
- while (head && head->magic != magic && offs < resv_sz - HDR_SZ) {
Could resv_sz be < HDR_SZ?
Should head ever be NULL?
offs += head->size;
head = GET_RESV_NEXT_HEAD(head);
- }
- if (!head || head->magic != magic)
return NULL;
- else if (offset)
*offset = offs;
This could be probably be simplified a little by returning from inside the while loop when the match is found. It works either way, though.
- return head;
+}
+bool validate_extra_context(struct extra_context *extra, char **err) +{
- struct _aarch64_ctx *term;
- if (!extra || !err)
return false;
- fprintf(stderr, "Validating EXTRA...\n");
- term = GET_RESV_NEXT_HEAD(extra);
- if (!term || term->magic || term->size) {
SET_CTX_ERR("UN-Terminated EXTRA context");
return false;
- }
- if (extra->datap & ~0x10UL)
SET_CTX_ERR("Extra DATAP misaligned");
Did you mean & ~0xfUL?
Alternatively, datap % 0x10 == 0.
- else if (extra->size & ~0x10UL)
SET_CTX_ERR("Extra SIZE misaligned");
Similarly.
- else if (extra->datap != (uint64_t)term + sizeof(*term))
SET_CTX_ERR("Extra DATAP broken");
Make this more informative? Broken how?
- if (*err)
return false;
- fprintf(stderr, "GOOD EXTRA CONTEXT FOUND !\n");
Maybe we don't need to print anything on success here.
- return true;
+}
+bool validate_reserved(ucontext_t *uc, size_t resv_sz, char **err) +{
- bool terminated = false;
- size_t offs = 0;
- int flags = 0;
- struct extra_context *extra = NULL;
- struct _aarch64_ctx *head =
(struct _aarch64_ctx *)uc->uc_mcontext.__reserved;
- if (!err)
return false;
- /* Walk till the end terminator verifying __reserved contents */
- while (head && !terminated && offs < resv_sz) {
if ((uint64_t)head & 0x0fUL) {
SET_CTX_ERR("Misaligned HEAD");
return false;
}
switch (head->magic) {
case 0:
if (head->size)
SET_CTX_ERR("Bad size for MAGIC0");
I suggest just assigning *err rather than hiding this assignment in a macro, otherwise it's not clear that err (let alone *err) is touched by this.
else
terminated = true;
break;
case FPSIMD_MAGIC:
if (flags & FPSIMD_CTX)
SET_CTX_ERR("Multiple FPSIMD");
else if (head->size !=
sizeof(struct fpsimd_context))
SET_CTX_ERR("Bad size for FPSIMD context");
Can we use the names from sigcontext.h?
(Like "fpsimd_context" or FPSIMD_MAGIC?)
For the null record at the end, I have tended to write "terminator record" in the past.
flags |= FPSIMD_CTX;
break;
case ESR_MAGIC:
if (head->size != sizeof(struct esr_context))
SET_CTX_ERR("Bad size for ESR context");
break;
case SVE_MAGIC:
if (flags & SVE_CTX)
SET_CTX_ERR("Multiple SVE");
else if (head->size !=
sizeof(struct sve_context))
SET_CTX_ERR("Bad size for SVE context");
flags |= SVE_CTX;
break;
case EXTRA_MAGIC:
if (flags & EXTRA_CTX)
SET_CTX_ERR("Multiple EXTRA");
else if (head->size !=
sizeof(struct extra_context))
SET_CTX_ERR("Bad size for EXTRA context");
flags |= EXTRA_CTX;
extra = (struct extra_context *)head;
break;
default:
SET_CTX_ERR("Unknown MAGIC !");
This probably shouldn't be an error. If we hit this, it may mean that the kernel is newer than the kselftest build, but we can go ahead and test the records that we understand.
break;
}
if (*err)
return false;
We should do some sanity-checks on size here: for forward progress guarantees it should be >= sizeof(*head). To avoid ending up misaligned, it should also be a multiple of 16.
offs += head->size;
if (resv_sz - offs < sizeof(*head)) {
SET_CTX_ERR("Broken HEAD");
Maybe describe this an an overrun rather than a corrupt header.
return false;
}
if (flags & EXTRA_CTX)
if (!validate_extra_context(extra, err))
return false;
head = GET_RESV_NEXT_HEAD(head);
- }
- return true;
+} diff --git a/tools/testing/selftests/arm64/signal/testcases/testcases.h b/tools/testing/selftests/arm64/signal/testcases/testcases.h new file mode 100644 index 000000000000..4f704c1501aa --- /dev/null +++ b/tools/testing/selftests/arm64/signal/testcases/testcases.h @@ -0,0 +1,85 @@ +#ifndef __TESTCASES_H__ +#define __TESTCASES_H__
+#include <stdio.h> +#include <stdint.h> +#include <stdbool.h> +#include <unistd.h> +#include <ucontext.h>
+#include <asm/sigcontext.h>
Add a comment to explain what these are?
+#define FPSIMD_CTX (1 << 0) +#define SVE_CTX (1 << 1) +#define EXTRA_CTX (1 << 2)
+#define HDR_SZ \
- sizeof(struct _aarch64_ctx)
+#define GET_SF_RESV_HEAD(sf) \
- (struct _aarch64_ctx *)(&sf.uc.uc_mcontext.__reserved)
((struct _aarch64_ctx *)&(sf).uc.uc_mcontext.__reserved)
+#define GET_SF_RESV_SIZE(sf) \
- sizeof(sf.uc.uc_mcontext.__reserved)
(sf)
+#define GET_UCP_RESV_SIZE(ucp) \
- sizeof(((ucontext_t *)ucp)->uc_mcontext.__reserved)
(ucp)
Ideally, lose the cast and require ucp to be the correct type. Otherwise, the caller could pass any random type without the compiler complaining here.
+#define ASSERT_BAD_CONTEXT(uc) \
- do { \
char *err = NULL; \
assert(!validate_reserved((uc), GET_UCP_RESV_SIZE((uc)), &err)); \
if (err) \
fprintf(stderr, "Using badly built context - ERR: %s\n", err);\
- } while(0)
Using err implicitly may be confusing and make the code harder to maintain. Pass it in as an argument?
+#define ASSERT_GOOD_CONTEXT(uc) \
- do { \
char *err = NULL; \
if (!validate_reserved((uc), GET_UCP_RESV_SIZE((uc)), &err)) { \
if (err) \
fprintf(stderr, "Detected BAD context - ERR: %s\n", err);\
assert(0); \
} else { \
fprintf(stderr, "uc context validated.\n"); \
} \
- } while(0)
+/* head->size accounts both for payload and header _aarch64_ctx size ! */ +#define GET_RESV_NEXT_HEAD(h) \
- (struct _aarch64_ctx *)((uint8_t *)(h) + (h)->size)
Pedantic nit: maybe use (char *) here. The C standard is explicit about the magic properties of character types, including sizeof(char) == 1.
We "know" that uint8_t the same as unsigned char, but superficially this is just "whatever unsigned integer type is 8 bits in size".
+#define SET_CTX_ERR(str) \
- do { \
if (err) \
*err = str; \
- } while(0)
+struct a_sigframe {
Maybe fake_sigframe would be a better name? It depends on how you use this type, though.
- siginfo_t info;
- ucontext_t uc;
+};
+bool validate_reserved(ucontext_t *uc, size_t resv_sz, char **err);
+bool validate_extra_context(struct extra_context *extra, char **err);
+struct _aarch64_ctx *get_header(struct _aarch64_ctx *head, uint32_t magic,
size_t resv_sz, size_t *offset);
+static inline struct _aarch64_ctx *get_terminator(struct _aarch64_ctx *head,
size_t resv_sz,
size_t *offset)
+{
- return get_header(head, 0, resv_sz, offset);
+}
+static inline void write_terminator(struct _aarch64_ctx *tail) +{
- if (tail) {
tail->magic = 0;
tail->size = 0;
- }
+}
+#endif
2.17.1
linux-arm-kernel mailing list linux-arm-kernel@lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
Hi
Thanks for the review Dave
On 21/06/2019 11:35, Dave Martin wrote:
On Thu, Jun 13, 2019 at 12:13:24PM +0100, Cristian Marussi wrote:
Added some arm64/signal specific boilerplate and utility code to help further testcase development. Still no test case code included though.
There is a lot of code here, doing a variety of different things.
It may be more digestible if the common code is added in pieces, starting with whatever is needed for the simplest testcase, then adding that testcase, then extending the common code for the next case, and so on.
This would make it easier to see why each bit of common code is being added, and how it gets used.
I'll try to review as-is for now, but it might be worth splitting this patch up a bit when reposting.
I'll split support code to smaller chunks committed together with the test cases using them.
Signed-off-by: Cristian Marussi cristian.marussi@arm.com
tools/testing/selftests/arm64/Makefile | 2 +- .../testing/selftests/arm64/signal/.gitignore | 5 + tools/testing/selftests/arm64/signal/Makefile | 86 ++++++ tools/testing/selftests/arm64/signal/README | 56 ++++ .../testing/selftests/arm64/signal/signals.S | 64 +++++ .../arm64/signal/test_arm64_signals.sh | 44 +++ .../selftests/arm64/signal/test_signals.c | 30 ++ .../selftests/arm64/signal/test_signals.h | 136 ++++++++++ .../arm64/signal/test_signals_utils.c | 256 ++++++++++++++++++ .../arm64/signal/test_signals_utils.h | 110 ++++++++ .../arm64/signal/testcases/testcases.c | 123 +++++++++ .../arm64/signal/testcases/testcases.h | 85 ++++++ 12 files changed, 996 insertions(+), 1 deletion(-) create mode 100644 tools/testing/selftests/arm64/signal/.gitignore create mode 100644 tools/testing/selftests/arm64/signal/Makefile create mode 100644 tools/testing/selftests/arm64/signal/README create mode 100644 tools/testing/selftests/arm64/signal/signals.S create mode 100755 tools/testing/selftests/arm64/signal/test_arm64_signals.sh create mode 100644 tools/testing/selftests/arm64/signal/test_signals.c create mode 100644 tools/testing/selftests/arm64/signal/test_signals.h create mode 100644 tools/testing/selftests/arm64/signal/test_signals_utils.c create mode 100644 tools/testing/selftests/arm64/signal/test_signals_utils.h create mode 100644 tools/testing/selftests/arm64/signal/testcases/testcases.c create mode 100644 tools/testing/selftests/arm64/signal/testcases/testcases.h
diff --git a/tools/testing/selftests/arm64/Makefile b/tools/testing/selftests/arm64/Makefile index 03a0d4f71218..af59dc74e0dc 100644 --- a/tools/testing/selftests/arm64/Makefile +++ b/tools/testing/selftests/arm64/Makefile @@ -6,7 +6,7 @@ ARCH ?= $(shell uname -m) ARCH := $(shell echo $(ARCH) | sed -e s/aarch64/arm64/) ifeq ("x$(ARCH)", "xarm64") -SUBDIRS := +SUBDIRS := signal else SUBDIRS := endif diff --git a/tools/testing/selftests/arm64/signal/.gitignore b/tools/testing/selftests/arm64/signal/.gitignore new file mode 100644 index 000000000000..7234ebd99363 --- /dev/null +++ b/tools/testing/selftests/arm64/signal/.gitignore @@ -0,0 +1,5 @@ +# Helper script's internal testcases list (TPROGS) is regenerated +# each time by Makefile on standalone (non KSFT driven) runs. +# Committing such list creates a dependency between testcases +# patches such that they are no more easily revertable. Just ignore. +test_arm64_signals.sh diff --git a/tools/testing/selftests/arm64/signal/Makefile b/tools/testing/selftests/arm64/signal/Makefile new file mode 100644 index 000000000000..9518841124a3 --- /dev/null +++ b/tools/testing/selftests/arm64/signal/Makefile @@ -0,0 +1,86 @@ +# SPDX-License-Identifier: GPL-2.0 +# Copyright (C) 2019 ARM Limited
+# Supports also standalone invokation out of KSFT-tree. +# +# Build standalone with (issued from within the dir containing this makefile): +# +# host:~$ make clean && make INSTALL_PATH=<your-device-instdir> install
Minor nit: why clean?
just (bad) habit...not needed in fact
Minor nit: this won't work if run from ~. Maybe just say "$" for the prompt.
True, and I think I'll stick to the syntax used by KSFT doces using -C <dir> like invocation from linux/ topdir like in:
$ make -C tools/testing/selftests/arm64/signal INSTALL_PATH= install
(moreover I spotted a tricky bug in the test_arm64_signal.sh script which I'll fix)
+# +# Run standalone on device with: +# device:~# <your-device-instdir>/test_arm64_signals.sh [-k|-v]
Minor nit: do these tests actually need to run as root? If not (and for the above reasons) you could again just say "$".
root not really needed for arm64/signal, I'll fix.
+#
+# A proper top_srcdir is needed both by KSFT(lib.mk) +# and standalone builds +top_srcdir = ../../../../..
+CFLAGS += -std=gnu99 -I. -I$(top_srcdir)/tools/testing/selftests/ +SRCS := $(filter-out testcases/testcases.c,$(wildcard testcases/*.c)) +PROGS := $(patsubst %.c,%,$(SRCS))
+# Guessing as best as we can where the Kernel headers +# could have been installed depending on ENV config and +# type of invocation. +ifeq ($(KBUILD_OUTPUT),) +khdr_dir = $(top_srcdir)/usr/include +else +ifeq (0,$(MAKELEVEL)) +khdr_dir = $(KBUILD_OUTPUT)/usr/include +else +# the KSFT preferred location when KBUILD_OUTPUT is set +khdr_dir = $(KBUILD_OUTPUT)/kselftest/usr/include +endif +endif
+CFLAGS += -I$(khdr_dir)
+# Standalone run +ifeq (0,$(MAKELEVEL)) +CC := $(CROSS_COMPILE)gcc +RUNNER = test_arm64_signals.sh +INSTALL_PATH ?= install/
+all: $(RUNNER)
+$(RUNNER): $(PROGS)
- sed -i -e 's#PROGS=.*#PROGS="$(PROGS)"#' $@
+install: all
- mkdir -p $(INSTALL_PATH)/testcases
- cp $(PROGS) $(INSTALL_PATH)/testcases
- cp $(RUNNER) $(INSTALL_PATH)/
+.PHONY clean:
- rm -f $(PROGS)
+# KSFT run +else +# Generated binaries to be installed by top KSFT script +TEST_GEN_PROGS := $(notdir $(PROGS))
+# Get Kernel headers installed and use them. +KSFT_KHDR_INSTALL := 1
+# This include mk will also mangle the TEST_GEN_PROGS list +# to account for any OUTPUT target-dirs optionally provided +# by the toplevel makefile +include ../../lib.mk
+$(TEST_GEN_PROGS): $(PROGS)
- -mkdir -p $(OUTPUT)
- -cp $(PROGS) $(OUTPUT)/
Why doesn't it matter if these fail?
(Maybe this was pasted from other kselftest Makefiles...)
Unfortunately I think the evil comes from me, an ancient iteration of the patch where standalone and KSFT-integrated run were not splitted (nor working fully), so a bit of unclean machinery was needed to cope with non existent OUTPUT (anyway I was using mkdir -p so -mkdir was unneeded anyway...). OUTPUT dir is now properly created and propagated by upper layer Makefiles so no need to mkdir anything nor to ignore -cp. I'll cleanup.
+clean:
- $(CLEAN)
- rm -f $(PROGS)
+endif
+# Common test-unit targets to build common-layout test-cases executables +# Needs secondary expansion to properly include the testcase c-file in pre-reqs +.SECONDEXPANSION: +$(PROGS): test_signals.c test_signals_utils.c signals.S testcases/testcases.c $$@.c test_signals.h test_signals_utils.h testcases/testcases.h
- @if [ ! -d $(khdr_dir) ]; then \
echo -n "\n!!! WARNING: $(khdr_dir) NOT FOUND."; \
echo "===> Are you sure Kernel Headers have been installed properly ?\n"; \
- fi
- $(CC) $(CFLAGS) $^ -o $@
diff --git a/tools/testing/selftests/arm64/signal/README b/tools/testing/selftests/arm64/signal/README new file mode 100644 index 000000000000..315d77558e14 --- /dev/null +++ b/tools/testing/selftests/arm64/signal/README @@ -0,0 +1,56 @@ +KSelfTest arm64/signal/ +=======================
+Signals Tests ++++++++++++++
+- Tests are built around a common main compilation unit: such shared main
- enforces a standard sequence of operations needed to perform a single
- signal-test (setup/trigger/run/result/cleanup)
+- The above mentioned ops are configurable on a test-by-test basis: each test
- is described (and configured) using the descriptor signals.h::struct tdescr
+- Each signal testcase is compiled into its own executable: a separate
- executable is used for each test since many tests complete successfully
- by receiving some kind of 'Term' signal from the Kernel, so it's safer
Nit: to avoid confusion with SIGTERM, maybe say "some fatal signal" instead of 'Term' (although I can see what you mean).
Right, I was trying to avoid confusion following man pages signal(7) terminology for signal dispositions, but in fact it is far worse using Term here :D
- to run each test unit in its own standalone process, so as to start each
- test from a clean-slate.
Pedantic nit: "-" -> " "
Ok
+- New tests can be simply defined in testcases/ dir providing a proper struct
- tdescr overriding all the defaults we wish to change (as of now providing a
- custom run method is mandatory though)
+- Signals' test-cases hereafter defined belong currently to two
- principal families:
- 'mangle_' tests: a real signal (SIGUSR1) is raised and used as a trigger
- and then the test case code messes-up with the sigframe ucontext_t from
- inside the sighandler itself.
- 'fake_sigreturn_' tests: a brand new custom artificial sigframe structure
- is placed on the stack and a sigreturn syscall is called to simulate a
- real signal return. This kind of tests does not use a trigger usually and
- they are just fired using some simple included assembly trampoline code.
- Most of these tests are successfully passing if the process gets killed by
- some 'Term' signal: usually SIGSEGV or SIGBUS. Since, while writing this
Nit: "some fatal signal"
Ok
- kind of tests, it is extremely easy in fact to end-up injecting other
- unrelated SEGV bugs in the testcases, it becomes extremely tricky to
For tests that receive a fatal signal on success, is it always the same signal?
No and in fact it is configured in tdescr.sig_ok which signal the test case expects on success (if the acse)
I'm wondering whether we can filter expected from unexpected signals using siginfo. For a bad sigreturn for example, I'd expect si_addr to point to (or at least somewhere within) the signal frame.
Maybe it's not that simple, though.
This was my main concern in fact. At the end I double checked and verified all the here proposed test cases instrumenting the Kernel and seeing that in fact the test case was segfaulting from Kernel at the expected line (o_O) ... but this is clearly a poor approach for future tests...I would have like to have at least a check that ensures the crash come from the Kernel space inside the signal code.
For this reason at first I added the token field into tdescr, where I now save the SP used/calculated by fake_sigreturn ....my idea was to verify that once SEGV was hit, the test case handler could have verified this current->token saved-SP-value against the value hold in siginfo.si_addr, since, once the evil happens inside the kernel (and so the test is successful), Kernel calls:
arm64_notify_segfault(regs->sp)
with regs->sp finally put inside si_addr if you follow the call-chain.
Unfortunately in my tests I always found si_addr displaced from the saved SP by a few kbytes in my tests so I doubted this was a feasible approach and I instead reverted to the following best-effort strategy (as I tried poorly to explain in the README):
- at first I provided a couple of sanity macros ASSERT_GOOD/BAD_CONTEXT() which are supposed to check that at least the pre-requisite for the tests, in terms of built sigframe, are fine.
- then:
+ since I could NOT verify strictly the SEGV location was coming from Kernel (as above said), and also considering the fact that even if I could, I won't be ever able to track down the fault to the exact location in kernel code (as I manually did instrumenting with lovely printfs), and so I could not be automatically sure that what the testcase was effectively testing was what was meant to test
+ I decided to at least try to ensure that the crash happened after fake_sigreturn was issued...so reducing the uncertainty. I found this really useful while developing tests in order to quickly rule-out trivial SEGV coming from test code running well before the fake_sigreturn.
In order to do this:
1. as a last step in fake_sigreturn, just before calling sigreturn via SVC I save the used SP into current->token (which is norMa
2. in the SEGV handler, unless current->sanity_disabled was set in the tdescr config (set for mangle_ tests which do not use fake_sigreturn), I make sure that the token field is NOT ZERO...which means at least I arrived so far into fake_sigreturn and so I can reasonably assume fake_sigreturn was called
default_handler() while handling sig_ok (usually SEGV) signal ------------------------------------------------------------- if (!current->sanity_disabled) { fprintf(stderr, "SIG_OK -- si_addr@:0x%p token@:0x%p\n", si->si_addr, current->token); assert(current->token); }
with the assertion meant to cause an unexpected SIGABORT which warns the test-developer which something odd is happening and the test is flawed.
This approach is definitely a best effort, but if you could suggest some more strict and reliable test on si_addr value, I'm happy to review this thing.
- be really sure that the tests are really addressing what they are meant
- to address and they are not instead falling apart due to unplanned bugs.
- In order to alleviate the misery of the life of such test-developer, a few
- helpers are provided:
- a couple of ASSERT_BAD/GOOD_CONTEXT() macros to easily parse a ucontext_t
and verify if it is indeed GOOD or BAD (depending on what we were
expecting), using the same logic/perspective as in the arm64 Kernel signals
routines.
- a sanity mechanism to be used in 'fake_sigreturn_'-alike tests: enabled by
default it takes care to verify that the test-execution had at least
successfully progressed upto the stage of triggering the fake sigreturn
Pedantic nit: "upto" -> "up to"
Ok
call.
- In both cases test results are expected in terms of some 'Term' signal sent
Nit: "some fatal signal"?
Ok
- by the Kernel to the test process, or analyzing some final regs state.
In general, putting comments nearer to the functions makes for easier reviewing, and makes it easier to keep the documentation in sync when the code changes.
Since you've written this documentation, we may as well keep it, though.
Ok, I'll relocate or duplicate code-related and very specific comments into the codebase.
diff --git a/tools/testing/selftests/arm64/signal/signals.S b/tools/testing/selftests/arm64/signal/signals.S new file mode 100644 index 000000000000..4f510b3a3b4b --- /dev/null +++ b/tools/testing/selftests/arm64/signal/signals.S @@ -0,0 +1,64 @@ +/*
- SPDX-License-Identifier: GPL-2.0
- Copyright (C) 2019 ARM Limited
- */
+#include <asm/unistd.h>.
+.section .rodata
This also needs to be marked allocatable (, "a")
You can also just say
.rodata
which gives you the sane default attributes for this section.
I remember a compilation error while trying to use .rodata straight away, that's the reason I reverted to used section...
..yep...
/opt/toolchains/gcc-linaro-7.3.1-2018.05-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu-gcc -std=gnu99 -I. -I../../../../../tools/testing/selftests/ -I/home/crimar01/ARM/dev/src/pdsw/out_linux/usr/include test_signals.c test_signals_utils.c signals.S testcases/testcases.c testcases/fake_sigreturn_bad_size.c test_signals.h test_signals_utils.h testcases/testcases.h -o testcases/fake_sigreturn_bad_size signals.S: Assembler messages: signals.S:8: Error: unknown pseudo-op: `.rodata'
and I remember now a post from 2012 saying that the assembler could be too old....but I don't think so .... so I went for .section
in fact grepping for .rodata in arch/arm64 I can find a .rodata without section directive only in the linker script for vdso (arch/arm64/kernel/vdso/vdso.lds.S)
any thoughts ? what I'm getting wrong ?
+call_fmt:
- .asciz "Calling sigreturn with fake sigframe sized:%zd at calculated SP @%08lX\n"
+.text
+.extern current
This is harmless, but people usually don't bother with .extern. It doesn't do anything IIUC.
ah, ok.
+.globl fake_sigreturn
+/* fake_sigreturn x0:&sigframe x1:sigframe_size x2:alignment_SP */ +fake_sigreturn:
- stp x20, x21, [sp, #-16]!
- stp x22, x23, [sp, #-16]!
Nit: <tab> <mnemonic> <tab> [<operands> separated by ", " ...]
Messed again...sorry.
If this function doesn't return, does it need to save anything at all?
Good point
- mov x20, x0
- mov x21, x1
- mov x22, x2
- mov x23, sp
- /* create space on the stack for fake sigframe..."x22"-aligned */
- mov x0, #0
- add x0, x21, x22
- sub x22, x22, #1
- bic x0, x0, x22
- sub x23, x23, x0
- ldr x0, =call_fmt
- mov x1, x21
- mov x2, x23
- bl printf
- mov sp, x23
- /* now fill it with the provided content... */
- mov x0, sp
- mov x1, x20
- mov x2, x21
- bl memcpy
- /*
* Here saving a last minute SP to current->token acts as a marker:
* if we got here, we are successfully faking a sigreturn; in other
* words we are reasonably sure no bad Term signal has been raised
* till now for unrelated reasons, so we should consider the possibly
* observed SEGV coming from Kernel restore_sigframe() and triggered
* as expected from our test-case.
* For simplicity field 'token' is laid out as first in struct tdescr
*/
- ldr x0, current
Nit: this assumes that current is
ok
- str x23, [x0, #0]
What's this for? Doesn't this overwrite the first bytes of siginfo or something?
This is the step which I write the value of SP (cached also in x23) into the first field of tdescr (which is the first at offset 0 for simplicity) for the current test so in fact is equivalent to:
current->token = SP;
and it is part of the mechanism I described above to try to catch spurios SEGV
Nit: The ", #0" is also redundant here.
ok
- /* SP is already pointing back to the just built fake sigframe here */
- mov x8, #__NR_rt_sigreturn
- svc #0
- /* This should not return here...looping lead to a timeout */
- b .
Could we also return here, rather than relying on a timeout? This function strictly doesn't return when it succeeds, so this would provide an immediate indication of failure.
Maybe it's not worth it though, since some kinds of failure might lead to corruption or jumping to a random location -- which might only be detectable via a timeout in any case.
Good point...I'll have a thought about the opportunity of a return...
diff --git a/tools/testing/selftests/arm64/signal/test_arm64_signals.sh b/tools/testing/selftests/arm64/signal/test_arm64_signals.sh new file mode 100755 index 000000000000..ecaa5a67d3ca --- /dev/null +++ b/tools/testing/selftests/arm64/signal/test_arm64_signals.sh @@ -0,0 +1,44 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0 +# Copyright (C) 2019 ARM Limited
+ret=0 +keep_on_fail=0 +err_out="2> /dev/null"
+# avoiding getopt
It doesn't matter, but do we need to avoid getopt?
+while [ $# -gt 0 ] +do
- case $1 in
"-k")
keep_on_fail=1
shift
;;
"-v")
err_out=""
Or just
err_out=
shift
;;
*)
shift
Could you move the shift out of the switch here? Each case seems to end with it.
Ok I'll do
;;
- esac
+done
+TPROGS=
+i=0 +tot=$(echo $TPROGS | wc -w)
+for test in $TPROGS +do
- eval ./$test $err_out
- if [ $? != 0 ]; then
[ $keep_on_fail = 0 ] && echo "===>>> FAILED:: $test <<<===" && ret=1 && break
- else
i=$((i + 1))
- fi
+done
+echo "==>> PASSED: $i/$tot"
+exit $ret diff --git a/tools/testing/selftests/arm64/signal/test_signals.c b/tools/testing/selftests/arm64/signal/test_signals.c new file mode 100644 index 000000000000..afadb8ae33e4 --- /dev/null +++ b/tools/testing/selftests/arm64/signal/test_signals.c @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2019 ARM Limited */
+#include <stdio.h> +#include <unistd.h> +#include <stddef.h>
^ Are any of these needed? I don't see direct use of any libc stuff in this file...
I'll verify and cleanup
+#include <kselftest.h>
+#include "test_signals.h" +#include "test_signals_utils.h"
+struct tdescr *current; +extern struct tdescr tde;
+int main(int argc, char *argv[]) +{
- current = &tde;
- ksft_print_msg("%s :: %s - SIG_TRIG:%d SIG_OK:%d -- current:%p\n",
current->name, current->descr, current->sig_trig,
current->sig_ok, current);
- if (test_setup(current)) {
if (test_run(current))
test_result(current);
test_cleanup(current);
- }
- return !current->pass;
+} diff --git a/tools/testing/selftests/arm64/signal/test_signals.h b/tools/testing/selftests/arm64/signal/test_signals.h new file mode 100644 index 000000000000..49536326db04 --- /dev/null +++ b/tools/testing/selftests/arm64/signal/test_signals.h @@ -0,0 +1,136 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2019 ARM Limited */
+#ifndef __TEST_SIGNALS_H__ +#define __TEST_SIGNALS_H__
+#include <assert.h> +#include <stdbool.h> +#include <signal.h> +#include <ucontext.h> +#include <stdint.h>
+/*
- Using ARCH specific and sanitized Kernel headers installed by KSFT
- framework since we asked for it by setting flag KSFT_KHDR_INSTALL
- in our Makefile.
- */
+#include <asm/ptrace.h> +#include <asm/hwcap.h>
+/* pasted from include/linux/stringify.h */ +#define __stringify_1(x...) #x +#define __stringify(x...) __stringify_1(x)
+#define FEAT_SSBS (1 << 0) +#define FEAT_PAN (1 << 1) +#define FEAT_UAO (1 << 2)
^ Comment to say what these are?
Ok
- /*
- Reads a sysreg using the, possibly provided, S3_ encoding in order to
- avoid inject any dependency on the used toolchain regarding possibly
- still unsupported ARMv8 extensions.
- Using a standard mnemonic here to indicate the specific sysreg (like SSBS)
- would introduce a compile-time dependency on possibly unsupported ARMv8
- Extensions: you could end-up failing to build the test depending on the
- available toolchain.
- This is undesirable since some tests, even if specifically targeted at some
- ARMv8 Extensions, can be plausibly run even on hardware lacking the above
- optional ARM features. (SSBS bit preservation is an example: Kernel handles
- it transparently not caring at all about the effective set of supported
- features).
- On the other side we will expect to observe different behaviours if the
- feature is supported or not: usually getting a SIGILL when trying to use
- unsupported features. For this reason we have anyway in place some
- preliminary run-time checks about the cpu effectively supported features.
- This helper macro is meant to be used for regs readable at EL0, BUT some
- EL1 sysregs are indeed readable too through MRS emulation Kernel-mechanism
- if the required reg is included in the supported encoding space:
- Documentation/arm64/cpu-feature-regsiters.txt
- "The infrastructure emulates only the following system register space:
- Op0=3, Op1=0, CRn=0, CRm=0,4,5,6,7
- */
+#define get_regval(regname, out) \
- asm volatile("mrs %0, " __stringify(regname) : "=r" (out) :: "memory")
+/* Regs encoding and masks naming copied in from sysreg.h */ +#define SYS_ID_AA64MMFR1_EL1 S3_0_C0_C7_1 /* MRS Emulated */ +#define SYS_ID_AA64MMFR2_EL1 S3_0_C0_C7_2 /* MRS Emulated */ +#define ID_AA64MMFR1_PAN_SHIFT 20 +#define ID_AA64MMFR2_UAO_SHIFT 4
+/* Local Helpers */ +#define IS_PAN_SUPPORTED(val) \
- (!!((val) & (0xf << ID_AA64MMFR1_PAN_SHIFT)))
+#define IS_UAO_SUPPORTED(val) \
- (!!((val) & (0xf << ID_AA64MMFR2_UAO_SHIFT)))
These should probably be 0xfUL. The ID regs are 64-bit, so a shift >= 28 would cause things to go wrong here.
Missed that. Thanks
+#define MRS_SSBS_SYSREG S3_3_C4_C2_6 /* EL0 supported */ +#define MRS_SSBS_BIT (1 << 12)
+/*
- A descriptor used to describe and configure a test case.
- Fields with a non-trivial meaning are described inline in the following.
- */
+struct tdescr {
- /* KEEP THIS FIELD FIRST for easier lookup from assembly */
- void *token;
- /* when disabled token based sanity checking is skipped in handler */
- bool sanity_disabled;
- /* just a name for the test-case; manadatory field */
- char *name;
- char *descr;
- unsigned long feats_required;
- /* bitmask of effectively supported feats: populated at run-time */
- unsigned long feats_supported;
- bool feats_ok;
- bool initialized;
- unsigned int minsigstksz;
- /* signum used as a test trigger. Zero if no trigger-signal is used */
- int sig_trig;
- /*
* signum considered as a successfull test completion.
successful
ok
* Zero when no signal is expected on success
*/
- int sig_ok;
Could this be bool, or is it a signal number if nonzero?
No this holds the expected signal on a successfull test (SIGSEGV/SIGBUS...)
- /* signum expected on unsupported CPU features. */
- int sig_unsupp;
- /*
* used to grab a sigcontext from a signal handler
* automatically set to SIGUSR2 if not configured
*/
- int sig_copyctx;
Is there any reason for this to be customisable?
It is changeable, because on test init I take care to verify I'm not using a SIGUSR? which clashes with some other thing...like the configured sig_trig, not sure if the user should be allowed to configure it explicitly as it is now.
- /* a timeout in second for test completion *
- unsigned int timeout;
- bool triggered;
- unsigned int handled;
bool, or otherwise what does the value for handled mean?
This counts the times I hit the sig_ok...since I used to exit the program straight away once it counts than 1...to avoid infinite SEGV loops...but now is unused and I'll remove
- bool pass;
- /* optional sa_flags for the installed handler */
- int sa_flags;
- ucontext_t saved_uc;
- /* used by copy_ctx */
- ucontext_t *live_uc;
- size_t live_sz;
- /* a setup function to be called before test starts */
- int (*setup)(struct tdescr *td);
- int (*cleanup)(struct tdescr *td);
- /* an optional function to be used as a trigger for test starting */
- int (*trigger)(struct tdescr *td);
- /*
* the actual test-core: invoked differently depending on the
* presence of the trigger function above; this is mandatory
*/
- int (*run)(struct tdescr *td, siginfo_t *si, ucontext_t *uc);
- /* an optional function for custom results' processing */
- int (*check_result)(struct tdescr *td);
- void *priv;
+}; +#endif diff --git a/tools/testing/selftests/arm64/signal/test_signals_utils.c b/tools/testing/selftests/arm64/signal/test_signals_utils.c new file mode 100644 index 000000000000..c00ba355dc1b --- /dev/null +++ b/tools/testing/selftests/arm64/signal/test_signals_utils.c @@ -0,0 +1,256 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2019 ARM Limited */
+#include <stdio.h> +#include <stdlib.h> +#include <signal.h> +#include <unistd.h> +#include <assert.h> +#include <sys/auxv.h> +#include <linux/auxvec.h>
+#include "test_signals.h" +#include "test_signals_utils.h" +#include "testcases/testcases.h"
+extern struct tdescr *current;
+void dump_uc(const char *prefix, ucontext_t *uc, int filter)
Should probably #include <ucontext.h> for this, even if we're getting it via some other header.
Fine. This is for possible debug usage though, it was used and it could be used to debug the tests, but it is currently not used....so I wonder if I should just remove it.
+{
- int i;
- if (prefix)
fprintf(stderr, "%s", prefix);
- if (filter & DUMP_REGS)
for (i = 0; i < 29; i++)
fprintf(stderr, "x%02d:%016llX\n",
i, uc->uc_mcontext.regs[i]);
- if (filter & DUMP_FP)
fprintf(stderr, "x%02d(fp):%016llX\n",
i, uc->uc_mcontext.regs[i]);
- i++;
- if (filter & DUMP_LR)
fprintf(stderr, "x%02d(lr):%016llX\n",
i, uc->uc_mcontext.regs[i]);
- if (filter & DUMP_SP)
fprintf(stderr, "sp:%016llX\n", uc->uc_mcontext.sp);
- if (filter & DUMP_PC)
fprintf(stderr, "pc:%016llX\n", uc->uc_mcontext.pc);
- if (filter & DUMP_PSTATE)
fprintf(stderr, "pstate:%016llX\n", uc->uc_mcontext.pstate);
- if (filter & DUMP_FAULT)
fprintf(stderr, "fault_address:%016llX\n",
uc->uc_mcontext.fault_address);
+}
+static void unblock_signal(int signum) +{
- sigset_t sset;
- sigemptyset(&sset);
- sigaddset(&sset, signum);
- sigprocmask(SIG_UNBLOCK, &sset, NULL);
+}
+static int default_result(struct tdescr *td, int force_exit);
Can we move default_result() here to avoid this forward declaration?
Ok
+static void default_handler(int signum, siginfo_t *si, void *uc) +{
- if (signum == current->sig_trig) {
SAFE_WRITE(2, "Handling SIG_TRIG\n");
current->triggered = 1;
/* ->run was asserted NON-NULL in test_setup() already */
current->run(current, si, uc);
- } else if (signum == SIGILL && !current->initialized) {
/*
* A SIGILL here while still not initialized means we fail
* to even asses the existence of feature
*/
SAFE_WRITE(1, "Marking UNSUPPORTED features.\n");
- } else if (current->sig_ok && signum == current->sig_ok) {
/* it's a bug in the test code when this assert fail */
assert(!current->sig_trig || current->triggered);
if (!current->sanity_disabled) {
fprintf(stderr,
"SIG_OK -- si_addr@:0x%p token@:0x%p\n",
si->si_addr, current->token);
assert(current->token);
}
SAFE_WRITE(2, "Handling SIG_OK\n");
current->pass = 1;
current->handled++;
/*
* Some tests can lead to SEGV loops: in such a case we want
* to terminate immediately exiting straight away
*/
default_result(current, 1);
- } else if (current->sig_copyctx && signum == current->sig_copyctx) {
memcpy(current->live_uc, uc, current->live_sz);
ASSERT_GOOD_CONTEXT(current->live_uc);
SAFE_WRITE(2,
"GOOD CONTEXT grabbed from sig_copyctx handler\n");
- } else {
if (signum == current->sig_unsupp && !current->feats_ok) {
SAFE_WRITE(2, "-- RX SIG_UNSUPP on unsupported feature...OK\n");
current->pass = 1;
} else if (signum == SIGALRM && current->timeout) {
SAFE_WRITE(2, "-- Timeout !\n");
} else {
SAFE_WRITE(2, "-- UNEXPECTED SIGNAL\n");
}
default_result(current, 1);
- }
+}
+static int default_setup(struct tdescr *td) +{
- struct sigaction sa;
- sa.sa_sigaction = default_handler;
- sa.sa_flags = SA_SIGINFO;
- if (td->sa_flags)
sa.sa_flags |= td->sa_flags;
- sigemptyset(&sa.sa_mask);
- /* uncatchable signals naturally skipped ... */
- for (int sig = 1; sig < 32; sig++)
sigaction(sig, &sa, NULL);
- /*
* RT Signals default disposition is Term but they cannot be
* generated by the Kernel in response to our tests; so just catch
* them all and report them as UNEXPECTED signals.
*/
- for (int sig = SIGRTMIN; sig <= SIGRTMAX; sig++)
sigaction(sig, &sa, NULL);
- /* just in case...unblock explicitly all we need */
- if (td->sig_trig)
unblock_signal(td->sig_trig);
- if (td->sig_ok)
unblock_signal(td->sig_ok);
- if (td->sig_unsupp)
unblock_signal(td->sig_unsupp);
- if (td->timeout) {
unblock_signal(SIGALRM);
alarm(td->timeout);
- }
- fprintf(stderr, "Registered handlers for all signals.\n");
- return 1;
+}
+static inline int default_trigger(struct tdescr *td) +{
- return !raise(td->sig_trig);
+}
+static int default_result(struct tdescr *td, int force_exit) +{
- if (td->pass)
SAFE_WRITE(2, "==>> completed. PASS(1)\n");
- else
SAFE_WRITE(1, "==>> completed. FAIL(0)\n");
- if (!force_exit)
return td->pass;
- else
exit(td->pass ? EXIT_SUCCESS : EXIT_FAILURE);
+}
+static int test_init(struct tdescr *td) +{
- td->minsigstksz = getauxval(AT_MINSIGSTKSZ);
- if (!td->minsigstksz)
td->minsigstksz = MINSIGSTKSZ;
- fprintf(stderr, "Detected MINSTKSIGSZ:%d\n", td->minsigstksz);
- if (td->feats_required) {
td->feats_supported = 0;
/*
* Checking for CPU required features using both the
* auxval and the arm64 MRS Emulation to read sysregs.
*/
if (getauxval(AT_HWCAP) & HWCAP_CPUID) {
uint64_t val = 0;
if (td->feats_required & FEAT_SSBS) {
if (getauxval(AT_HWCAP) & HWCAP_SSBS)
td->feats_supported |= FEAT_SSBS;
}
if (td->feats_required & FEAT_PAN) {
get_regval(SYS_ID_AA64MMFR1_EL1, val);
if (IS_PAN_SUPPORTED(val))
td->feats_supported |= FEAT_PAN;
}
if (td->feats_required & FEAT_UAO) {
get_regval(SYS_ID_AA64MMFR2_EL1 , val);
if (IS_UAO_SUPPORTED(val))
td->feats_supported |= FEAT_UAO;
}
} else {
fprintf(stderr,
"CPUID regs NOT available. Marking features unsupported\n");
}
td->feats_ok = td->feats_required == td->feats_supported;
fprintf(stderr, "Required Features %08lX are %ssupported\n",
td->feats_required, !td->feats_ok ? "NOT " : "");
- }
- if (!td->sig_copyctx) {
if (td->sig_trig != SIGUSR2)
td->sig_copyctx = SIGUSR2;
else if (td->sig_trig != SIGUSR1)
td->sig_copyctx = SIGUSR1;
else
td->sig_copyctx = 0;
- }
- if (td->sig_copyctx)
unblock_signal(td->sig_copyctx);
- td->initialized = 1;
- return 1;
+}
+int test_setup(struct tdescr *td) +{
- /* assert core invariants symptom of a rotten testcase */
- assert(current);
- assert(td);
- assert(td->name);
- assert(td->run);
- if (!test_init(td))
return 0;
- if (td->setup)
return td->setup(td);
- else
return default_setup(td);
+}
+int test_run(struct tdescr *td) +{
- if (td->sig_trig) {
if (td->trigger)
return td->trigger(td);
else
return default_trigger(td);
- } else {
return td->run(td, NULL, NULL);
- }
+}
+int test_result(struct tdescr *td) +{
- if (td->check_result)
td->check_result(td);
- return default_result(td, 0);
+}
+int test_cleanup(struct tdescr *td) +{
- if (td->cleanup)
return td->cleanup(td);
- else
return 1;
+}
Do we use the return value of either of these functions? Should we?
Some of them are used for sure, I'll check if it really needed for all in this moment.
diff --git a/tools/testing/selftests/arm64/signal/test_signals_utils.h b/tools/testing/selftests/arm64/signal/test_signals_utils.h new file mode 100644 index 000000000000..e7ebae8e8077 --- /dev/null +++ b/tools/testing/selftests/arm64/signal/test_signals_utils.h @@ -0,0 +1,110 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2019 ARM Limited */
+#ifndef __TEST_SIGNALS_UTILS_H__ +#define __TEST_SIGNALS_UTILS_H__ +#include <stdio.h> +#include <signal.h> +#include <unistd.h> +#include <ucontext.h> +#include <string.h>
+#include <asm/unistd.h>
+#include "test_signals.h"
+#define DUMP_REGS (1 << 0) +#define DUMP_FP (1 << 1) +#define DUMP_LR (1 << 2) +#define DUMP_SP (1 << 3) +#define DUMP_PC (1 << 4) +#define DUMP_PSTATE (1 << 5) +#define DUMP_FAULT (1 << 6) +#define DUMP_ALL 0xffffffff
+/* Using a signal-safe function to write something out */ +#define SAFE_WRITE(fd, str) \
- do { \
int bytes = 0; \
bytes = write((fd), (str) + bytes, sizeof(str) - bytes); \
if (bytes < 0 || bytes == sizeof(str)) \
break; \
In the interest of readability, consider lining up the \ here. Doesn't matter that much though.
Also: won't we write the start of the string repeatedly here? bytes is never updated AFAICT.
You're right it will work only if INTR once. bytes += at least.
- } while(1)
For block-like macros, I tend to write
#define FOO(bar) do { \ /* ... */ \ } while (0)
to save a line and reduce indentation. This is a matter of style though.
Ok good to know
Finally, does this need to be a macro?
To avoid surprises, we could use strlen() for the string length. If the compiler is smart enough and the function is inlined, then strlen("foo") can be evaluated at compile time... but anyway, we are going to make syscalls, so the cost of an out-of-line strlen() is not a big deal.
Ok I'll review completely the approach and the need to these best-effort debug messages sent in the signal handlers...
+/* Be careful this helper is NOT signal-safe */ +void dump_uc(const char *prefix, ucontext_t *uc, int filter);
+int test_setup(struct tdescr *td); +int test_cleanup(struct tdescr *td); +int test_run(struct tdescr *td); +int test_result(struct tdescr *td); +int fake_sigreturn(void *sigframe, size_t sz, int alignment);
+/*
- 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.
- */
+static inline __attribute__((always_inline)) +int get_current_context(struct tdescr *td, ucontext_t *uc) +{
- static volatile int seen_already;
If there could be multiple users of this in the same program, it may make sense to make seen_already part of td.
Same program means same test though, so I'm not sure it would serve the same fucntionality...I have ot think about it.
Strictly speaking, this should be sig_atomic_t, not int (I think volatile int can't be torn into multiple accesses on any reasonable arch, but it depends which toolchain folks you talk to...)
ah...this was unexpected...
- if (!td || !td->sig_copyctx || !uc) {
SAFE_WRITE(1, "Signal-based Context dumping NOT available\n");
return 0;
- }
- /* it's a genuine invokation..reinit */
- seen_already = 0;
- td->live_uc = uc;
- td->live_sz = sizeof(*uc);
- memset(td->live_uc, 0x00, td->live_sz);
Is this required? It is just here to help sanity-check whether live_uc is populatd with real data?
Yes it is a safe net, I'll review if it is really needed.
- /*
* Grab ucontext_t triggering a signal...
* ASM equivalent of raise(td->sig_copyctx);
*/
- asm volatile ("mov x8, %0\n\t"
"svc #0\n\t"
"mov x1, %1\n\t"
"mov x8, %2\n\t"
"svc #0" :
Nit: for readability, it's usual to put the : only at the start of lines when writing an asm on multiple lines.
ok
: "r" (__NR_getpid),
"r" (td->sig_copyctx),
"r" (__NR_kill)
For the __NR_foo constants, you can use "i" to avoid wasting a register.
: "memory");
You need clobbers for x1 and x8, and also x0 (for the svc return value).
How does the compiler know that live_uc may be modified by this asm?
It does not in fact....
- /*
* 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) {
SAFE_WRITE(1, "....and we're back....seen_already !\n");
Can we make this message more self-explanatory?
Say "Can't populate already-populated ucontext" or somethine like that?
ok...it is on the failpath, but I suppose is better to explain why is this jump back considered symptom of failure.
return 0;
- }
- seen_already = 1;
- return 1;
Do we have a way to detect that live_uc really was populated? I think that if sig_copyctx was blocked or handled inappropriately due to a bug elsewhere in the program, we could just fall through here and assume that td->live_uc contains valid data.
If we have a flag in td to indicate that live_uc was populated, we may be able to use it for this.
good point...it's similar to the token usage to grab suprios SEGV conditions, if they're exclusive I could reuse token...maybe with a better naming...
+}
+#endif diff --git a/tools/testing/selftests/arm64/signal/testcases/testcases.c b/tools/testing/selftests/arm64/signal/testcases/testcases.c new file mode 100644 index 000000000000..9f83f3517325 --- /dev/null +++ b/tools/testing/selftests/arm64/signal/testcases/testcases.c @@ -0,0 +1,123 @@ +#include "testcases.h"
Should <asm/sigcontext.h> be included for the arm64 sigcontext types here?
It is in included testcases.h in fact:
#ifndef __TESTCASES_H__ #define __TESTCASES_H__
#include <stdio.h> #include <stdint.h> #include <stdbool.h> #include <unistd.h> #include <ucontext.h>
#include <asm/sigcontext.h>
+struct _aarch64_ctx *get_header(struct _aarch64_ctx *head, uint32_t magic,
size_t resv_sz, size_t *offset)
+{
- size_t offs = 0;
- while (head && head->magic != magic && offs < resv_sz - HDR_SZ) {
Could resv_sz be < HDR_SZ?
Should head ever be NULL?
probably not..overly defensive
offs += head->size;
head = GET_RESV_NEXT_HEAD(head);
- }
- if (!head || head->magic != magic)
return NULL;
- else if (offset)
*offset = offs;
This could be probably be simplified a little by returning from inside the while loop when the match is found. It works either way, though.
ok
- return head;
+}
+bool validate_extra_context(struct extra_context *extra, char **err) +{
- struct _aarch64_ctx *term;
- if (!extra || !err)
return false;
- fprintf(stderr, "Validating EXTRA...\n");
- term = GET_RESV_NEXT_HEAD(extra);
- if (!term || term->magic || term->size) {
SET_CTX_ERR("UN-Terminated EXTRA context");
return false;
- }
- if (extra->datap & ~0x10UL)
SET_CTX_ERR("Extra DATAP misaligned");
Did you mean & ~0xfUL?
yes...I'll fix.
Alternatively, datap % 0x10 == 0.
- else if (extra->size & ~0x10UL)
SET_CTX_ERR("Extra SIZE misaligned");
Similarly.
- else if (extra->datap != (uint64_t)term + sizeof(*term))
SET_CTX_ERR("Extra DATAP broken");
Make this more informative? Broken how?
ok
- if (*err)
return false;
- fprintf(stderr, "GOOD EXTRA CONTEXT FOUND !\n");
Maybe we don't need to print anything on success here.
In fact is debug in stderr, but KSFT main runner does not throw away stderr in fact (like my standalone script)..so it pollutes the output a bit...
- return true;
+}
+bool validate_reserved(ucontext_t *uc, size_t resv_sz, char **err) +{
- bool terminated = false;
- size_t offs = 0;
- int flags = 0;
- struct extra_context *extra = NULL;
- struct _aarch64_ctx *head =
(struct _aarch64_ctx *)uc->uc_mcontext.__reserved;
- if (!err)
return false;
- /* Walk till the end terminator verifying __reserved contents */
- while (head && !terminated && offs < resv_sz) {
if ((uint64_t)head & 0x0fUL) {
SET_CTX_ERR("Misaligned HEAD");
return false;
}
switch (head->magic) {
case 0:
if (head->size)
SET_CTX_ERR("Bad size for MAGIC0");
I suggest just assigning *err rather than hiding this assignment in a macro, otherwise it's not clear that err (let alone *err) is touched by this.
ok
else
terminated = true;
break;
case FPSIMD_MAGIC:
if (flags & FPSIMD_CTX)
SET_CTX_ERR("Multiple FPSIMD");
else if (head->size !=
sizeof(struct fpsimd_context))
SET_CTX_ERR("Bad size for FPSIMD context");
Can we use the names from sigcontext.h?
(Like "fpsimd_context" or FPSIMD_MAGIC?)
For the null record at the end, I have tended to write "terminator record" in the past.
ok
flags |= FPSIMD_CTX;
break;
case ESR_MAGIC:
if (head->size != sizeof(struct esr_context))
SET_CTX_ERR("Bad size for ESR context");
break;
case SVE_MAGIC:
if (flags & SVE_CTX)
SET_CTX_ERR("Multiple SVE");
else if (head->size !=
sizeof(struct sve_context))
SET_CTX_ERR("Bad size for SVE context");
flags |= SVE_CTX;
break;
case EXTRA_MAGIC:
if (flags & EXTRA_CTX)
SET_CTX_ERR("Multiple EXTRA");
else if (head->size !=
sizeof(struct extra_context))
SET_CTX_ERR("Bad size for EXTRA context");
flags |= EXTRA_CTX;
extra = (struct extra_context *)head;
break;
default:
SET_CTX_ERR("Unknown MAGIC !");
This probably shouldn't be an error. If we hit this, it may mean that the kernel is newer than the kselftest build, but we can go ahead and test the records that we understand.
Ah, yes true. I should log clearly though that we are missing something, and I should verify that size if fine and there is a properly chained context or a terminator in such a case.
break;
}
if (*err)
return false;
We should do some sanity-checks on size here: for forward progress guarantees it should be >= sizeof(*head). To avoid ending up misaligned, it should also be a multiple of 16.
ok
offs += head->size;
if (resv_sz - offs < sizeof(*head)) {
SET_CTX_ERR("Broken HEAD");
Maybe describe this an an overrun rather than a corrupt header.
ok
return false;
}
if (flags & EXTRA_CTX)
if (!validate_extra_context(extra, err))
return false;
head = GET_RESV_NEXT_HEAD(head);
- }
- return true;
+} diff --git a/tools/testing/selftests/arm64/signal/testcases/testcases.h b/tools/testing/selftests/arm64/signal/testcases/testcases.h new file mode 100644 index 000000000000..4f704c1501aa --- /dev/null +++ b/tools/testing/selftests/arm64/signal/testcases/testcases.h @@ -0,0 +1,85 @@ +#ifndef __TESTCASES_H__ +#define __TESTCASES_H__
+#include <stdio.h> +#include <stdint.h> +#include <stdbool.h> +#include <unistd.h> +#include <ucontext.h>
+#include <asm/sigcontext.h>
Add a comment to explain what these are?
ok
+#define FPSIMD_CTX (1 << 0) +#define SVE_CTX (1 << 1) +#define EXTRA_CTX (1 << 2)
+#define HDR_SZ \
- sizeof(struct _aarch64_ctx)
+#define GET_SF_RESV_HEAD(sf) \
- (struct _aarch64_ctx *)(&sf.uc.uc_mcontext.__reserved)
((struct _aarch64_ctx *)&(sf).uc.uc_mcontext.__reserved)
ok
+#define GET_SF_RESV_SIZE(sf) \
- sizeof(sf.uc.uc_mcontext.__reserved)
(sf)
+#define GET_UCP_RESV_SIZE(ucp) \
- sizeof(((ucontext_t *)ucp)->uc_mcontext.__reserved)
(ucp)
Ideally, lose the cast and require ucp to be the correct type. Otherwise, the caller could pass any random type without the compiler complaining here.
yes good point
+#define ASSERT_BAD_CONTEXT(uc) \
- do { \
char *err = NULL; \
assert(!validate_reserved((uc), GET_UCP_RESV_SIZE((uc)), &err)); \
if (err) \
fprintf(stderr, "Using badly built context - ERR: %s\n", err);\
- } while(0)
Using err implicitly may be confusing and make the code harder to maintain. Pass it in as an argument?
ok
+#define ASSERT_GOOD_CONTEXT(uc) \
- do { \
char *err = NULL; \
if (!validate_reserved((uc), GET_UCP_RESV_SIZE((uc)), &err)) { \
if (err) \
fprintf(stderr, "Detected BAD context - ERR: %s\n", err);\
assert(0); \
} else { \
fprintf(stderr, "uc context validated.\n"); \
} \
- } while(0)
+/* head->size accounts both for payload and header _aarch64_ctx size ! */ +#define GET_RESV_NEXT_HEAD(h) \
- (struct _aarch64_ctx *)((uint8_t *)(h) + (h)->size)
Pedantic nit: maybe use (char *) here. The C standard is explicit about the magic properties of character types, including sizeof(char) == 1.
We "know" that uint8_t the same as unsigned char, but superficially this is just "whatever unsigned integer type is 8 bits in size".
ok
+#define SET_CTX_ERR(str) \
- do { \
if (err) \
*err = str; \
- } while(0)
+struct a_sigframe {
Maybe fake_sigframe would be a better name? It depends on how you use this type, though.
Well yes, in fact it is a regular frame, it will be the content and the usage which will be fake....not the structure itself.
- siginfo_t info;
- ucontext_t uc;
+};
+bool validate_reserved(ucontext_t *uc, size_t resv_sz, char **err);
+bool validate_extra_context(struct extra_context *extra, char **err);
+struct _aarch64_ctx *get_header(struct _aarch64_ctx *head, uint32_t magic,
size_t resv_sz, size_t *offset);
+static inline struct _aarch64_ctx *get_terminator(struct _aarch64_ctx *head,
size_t resv_sz,
size_t *offset)
+{
- return get_header(head, 0, resv_sz, offset);
+}
+static inline void write_terminator(struct _aarch64_ctx *tail) +{
- if (tail) {
tail->magic = 0;
tail->size = 0;
- }
+}
+#endif
2.17.1
linux-arm-kernel mailing list linux-arm-kernel@lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
Added a simple mangle testcase which messes with the ucontext_t from within the sig_handler, trying to badly modify and misalign the SP. Expects SIGBUS on test PASS.
Signed-off-by: Cristian Marussi cristian.marussi@arm.com --- .../arm64/signal/testcases/.gitignore | 1 + .../signal/testcases/mangle_sp_misaligned.c | 24 +++++++++++++++++++ 2 files changed, 25 insertions(+) create mode 100644 tools/testing/selftests/arm64/signal/testcases/.gitignore create mode 100644 tools/testing/selftests/arm64/signal/testcases/mangle_sp_misaligned.c
diff --git a/tools/testing/selftests/arm64/signal/testcases/.gitignore b/tools/testing/selftests/arm64/signal/testcases/.gitignore new file mode 100644 index 000000000000..7f7414d241f2 --- /dev/null +++ b/tools/testing/selftests/arm64/signal/testcases/.gitignore @@ -0,0 +1 @@ +mangle_sp_misaligned diff --git a/tools/testing/selftests/arm64/signal/testcases/mangle_sp_misaligned.c b/tools/testing/selftests/arm64/signal/testcases/mangle_sp_misaligned.c new file mode 100644 index 000000000000..41bd27312e54 --- /dev/null +++ b/tools/testing/selftests/arm64/signal/testcases/mangle_sp_misaligned.c @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2019 ARM Limited */ + +#include "test_signals_utils.h" +#include "testcases.h" + +static int mangle_misaligned_sp_run(struct tdescr *td, siginfo_t *si, + ucontext_t *uc) +{ + ASSERT_GOOD_CONTEXT(uc); + + uc->uc_mcontext.sp += 3; + + return 1; +} + +struct tdescr tde = { + .sanity_disabled = true, + .name = "MANGLE_SP_MISALIGNED", + .descr = "Mangling uc_mcontext with MISALIGNED SP", + .sig_trig = SIGUSR1, + .sig_ok = SIGBUS, + .run = mangle_misaligned_sp_run, +};
On Thu, Jun 13, 2019 at 12:13:25PM +0100, Cristian Marussi wrote:
Added a simple mangle testcase which messes with the ucontext_t from within the sig_handler, trying to badly modify and misalign the SP. Expects SIGBUS on test PASS.
Signed-off-by: Cristian Marussi cristian.marussi@arm.com
.../arm64/signal/testcases/.gitignore | 1 + .../signal/testcases/mangle_sp_misaligned.c | 24 +++++++++++++++++++ 2 files changed, 25 insertions(+) create mode 100644 tools/testing/selftests/arm64/signal/testcases/.gitignore create mode 100644 tools/testing/selftests/arm64/signal/testcases/mangle_sp_misaligned.c
diff --git a/tools/testing/selftests/arm64/signal/testcases/.gitignore b/tools/testing/selftests/arm64/signal/testcases/.gitignore new file mode 100644 index 000000000000..7f7414d241f2 --- /dev/null +++ b/tools/testing/selftests/arm64/signal/testcases/.gitignore @@ -0,0 +1 @@ +mangle_sp_misaligned diff --git a/tools/testing/selftests/arm64/signal/testcases/mangle_sp_misaligned.c b/tools/testing/selftests/arm64/signal/testcases/mangle_sp_misaligned.c new file mode 100644 index 000000000000..41bd27312e54 --- /dev/null +++ b/tools/testing/selftests/arm64/signal/testcases/mangle_sp_misaligned.c @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2019 ARM Limited */
+#include "test_signals_utils.h" +#include "testcases.h"
+static int mangle_misaligned_sp_run(struct tdescr *td, siginfo_t *si,
ucontext_t *uc)
+{
- ASSERT_GOOD_CONTEXT(uc);
- uc->uc_mcontext.sp += 3;
What are we testing here?
It is archietcturally permitted (if unusual) to have a misaligned sp in userspace.
So are we just getting a SIGBUS after the sigreturn, when the thread tries to dereference sp? If so, we aren't really testing anything about sigreturn here -- I don't see any check in the kernel when restoring sp in sigreturn.
Even if there were no SIGBUS, the thread stack is now corrupt (due to wrong sp), so the interrupted code is unlikely to continue running successfully.
Am I missing something?
[...]
Cheers ---Dave
Hi
On 6/21/19 11:35 AM, Dave Martin wrote:
On Thu, Jun 13, 2019 at 12:13:25PM +0100, Cristian Marussi wrote:
Added a simple mangle testcase which messes with the ucontext_t from within the sig_handler, trying to badly modify and misalign the SP. Expects SIGBUS on test PASS.
Signed-off-by: Cristian Marussi cristian.marussi@arm.com
.../arm64/signal/testcases/.gitignore | 1 + .../signal/testcases/mangle_sp_misaligned.c | 24 +++++++++++++++++++ 2 files changed, 25 insertions(+) create mode 100644 tools/testing/selftests/arm64/signal/testcases/.gitignore create mode 100644 tools/testing/selftests/arm64/signal/testcases/mangle_sp_misaligned.c
diff --git a/tools/testing/selftests/arm64/signal/testcases/.gitignore b/tools/testing/selftests/arm64/signal/testcases/.gitignore new file mode 100644 index 000000000000..7f7414d241f2 --- /dev/null +++ b/tools/testing/selftests/arm64/signal/testcases/.gitignore @@ -0,0 +1 @@ +mangle_sp_misaligned diff --git a/tools/testing/selftests/arm64/signal/testcases/mangle_sp_misaligned.c b/tools/testing/selftests/arm64/signal/testcases/mangle_sp_misaligned.c new file mode 100644 index 000000000000..41bd27312e54 --- /dev/null +++ b/tools/testing/selftests/arm64/signal/testcases/mangle_sp_misaligned.c @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2019 ARM Limited */
+#include "test_signals_utils.h" +#include "testcases.h"
+static int mangle_misaligned_sp_run(struct tdescr *td, siginfo_t *si,
ucontext_t *uc)
+{
- ASSERT_GOOD_CONTEXT(uc);
- uc->uc_mcontext.sp += 3;
What are we testing here?
It is archietcturally permitted (if unusual) to have a misaligned sp in userspace.
So are we just getting a SIGBUS after the sigreturn, when the thread tries to dereference sp? If so, we aren't really testing anything about sigreturn here -- I don't see any check in the kernel when restoring sp in sigreturn.
Even if there were no SIGBUS, the thread stack is now corrupt (due to wrong sp), so the interrupted code is unlikely to continue running successfully.
Am I missing something?
The initial (flawed) attempt was to test the check in arm64 rt_sigreturn kernel code:
if (regs->sp & 15) goto badframe;
BUT in fact such initial check happens at the start of rt_sigreturn syscall well before the regs are restored from the uc context in the sigframe which I mangled
i.e. restore_sigframe() -->> __get_user_error(regs->sp...)
==>> uc.uc_mcontext.sp --> regs->sp
happens AFTER the above regs->sp alignment check.
So the check is performed on the effective SP value at the time of kernel entry of sigreturn NOT on the uc.uc_mcontext.sp MANGLED value, so this is not really a sigreturn related test at this point. (and hence the SIGBUS instead of the SEGV).
So an option could be as you proposed in another similarly flawed test to mangle uc.uc_mcontext.sp to point to something unreasonable and in Kernel space (at least virtually)
I'll give it a try.
Cristian
[...]
Cheers ---Dave
On Tue, Jul 02, 2019 at 04:39:44PM +0100, Cristian Marussi wrote:
Hi
On 6/21/19 11:35 AM, Dave Martin wrote:
On Thu, Jun 13, 2019 at 12:13:25PM +0100, Cristian Marussi wrote:
Added a simple mangle testcase which messes with the ucontext_t from within the sig_handler, trying to badly modify and misalign the SP. Expects SIGBUS on test PASS.
Signed-off-by: Cristian Marussi cristian.marussi@arm.com
.../arm64/signal/testcases/.gitignore | 1 + .../signal/testcases/mangle_sp_misaligned.c | 24 +++++++++++++++++++ 2 files changed, 25 insertions(+) create mode 100644 tools/testing/selftests/arm64/signal/testcases/.gitignore create mode 100644 tools/testing/selftests/arm64/signal/testcases/mangle_sp_misaligned.c
diff --git a/tools/testing/selftests/arm64/signal/testcases/.gitignore b/tools/testing/selftests/arm64/signal/testcases/.gitignore new file mode 100644 index 000000000000..7f7414d241f2 --- /dev/null +++ b/tools/testing/selftests/arm64/signal/testcases/.gitignore @@ -0,0 +1 @@ +mangle_sp_misaligned diff --git a/tools/testing/selftests/arm64/signal/testcases/mangle_sp_misaligned.c b/tools/testing/selftests/arm64/signal/testcases/mangle_sp_misaligned.c new file mode 100644 index 000000000000..41bd27312e54 --- /dev/null +++ b/tools/testing/selftests/arm64/signal/testcases/mangle_sp_misaligned.c @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2019 ARM Limited */
+#include "test_signals_utils.h" +#include "testcases.h"
+static int mangle_misaligned_sp_run(struct tdescr *td, siginfo_t *si,
ucontext_t *uc)
+{
- ASSERT_GOOD_CONTEXT(uc);
- uc->uc_mcontext.sp += 3;
What are we testing here?
It is archietcturally permitted (if unusual) to have a misaligned sp in userspace.
So are we just getting a SIGBUS after the sigreturn, when the thread tries to dereference sp? If so, we aren't really testing anything about sigreturn here -- I don't see any check in the kernel when restoring sp in sigreturn.
Even if there were no SIGBUS, the thread stack is now corrupt (due to wrong sp), so the interrupted code is unlikely to continue running successfully.
Am I missing something?
The initial (flawed) attempt was to test the check in arm64 rt_sigreturn kernel code:
if (regs->sp & 15) goto badframe;
BUT in fact such initial check happens at the start of rt_sigreturn syscall well before the regs are restored from the uc context in the sigframe which I mangled
i.e. restore_sigframe() -->> __get_user_error(regs->sp...)
==>> uc.uc_mcontext.sp --> regs->sp
happens AFTER the above regs->sp alignment check.
So the check is performed on the effective SP value at the time of kernel entry of sigreturn NOT on the uc.uc_mcontext.sp MANGLED value, so this is not really a sigreturn related test at this point. (and hence the SIGBUS instead of the SEGV).
So an option could be as you proposed in another similarly flawed test to mangle uc.uc_mcontext.sp to point to something unreasonable and in Kernel space (at least virtually)
I think a misaligned sp (i.e., the sp register, not uc_mcontext.sp) at sigreturn, and an sp that points to unmapped or kernel address space would be useful tests.
If those are already done elsewhere in the series, that's fine.
(There are many possible variations on this theme, but we have to stop somewhere.)
Cheers ---Dave IMPORTANT NOTICE: The contents of this email and any attachments are confidential and may also be privileged. If you are not the intended recipient, please notify the sender immediately and do not disclose the contents to any other person, use it for any purpose, or store or copy the information in any medium. Thank you.
Added a simple mangle testcase which messes with the ucontext_t from within the sig_handler, trying to badly modify the PC to point toward Kernel land. Expects SIGSEGV on test PASS.
Signed-off-by: Cristian Marussi cristian.marussi@arm.com --- .../arm64/signal/testcases/.gitignore | 1 + .../signal/testcases/mangle_pc_invalid.c | 24 +++++++++++++++++++ 2 files changed, 25 insertions(+) create mode 100644 tools/testing/selftests/arm64/signal/testcases/mangle_pc_invalid.c
diff --git a/tools/testing/selftests/arm64/signal/testcases/.gitignore b/tools/testing/selftests/arm64/signal/testcases/.gitignore index 7f7414d241f2..a87fb0f0d2cf 100644 --- a/tools/testing/selftests/arm64/signal/testcases/.gitignore +++ b/tools/testing/selftests/arm64/signal/testcases/.gitignore @@ -1 +1,2 @@ mangle_sp_misaligned +mangle_pc_invalid diff --git a/tools/testing/selftests/arm64/signal/testcases/mangle_pc_invalid.c b/tools/testing/selftests/arm64/signal/testcases/mangle_pc_invalid.c new file mode 100644 index 000000000000..0024032528bc --- /dev/null +++ b/tools/testing/selftests/arm64/signal/testcases/mangle_pc_invalid.c @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2019 ARM Limited */ + +#include "test_signals_utils.h" +#include "testcases.h" + +static int mangle_invalid_pc_run(struct tdescr *td, siginfo_t *si, + ucontext_t *uc) +{ + ASSERT_GOOD_CONTEXT(uc); + + uc->uc_mcontext.pc = 0xffffffffcccccccc; + + return 1; +} + +struct tdescr tde = { + .sanity_disabled = true, + .name = "MANGLE_PC_INVALID", + .descr = "Mangling uc_mcontext with INVALID PC", + .sig_trig = SIGUSR1, + .sig_ok = SIGSEGV, + .run = mangle_invalid_pc_run, +};
On Thu, Jun 13, 2019 at 12:13:26PM +0100, Cristian Marussi wrote:
Added a simple mangle testcase which messes with the ucontext_t from within the sig_handler, trying to badly modify the PC to point toward Kernel land. Expects SIGSEGV on test PASS.
Signed-off-by: Cristian Marussi cristian.marussi@arm.com
.../arm64/signal/testcases/.gitignore | 1 + .../signal/testcases/mangle_pc_invalid.c | 24 +++++++++++++++++++ 2 files changed, 25 insertions(+) create mode 100644 tools/testing/selftests/arm64/signal/testcases/mangle_pc_invalid.c
diff --git a/tools/testing/selftests/arm64/signal/testcases/.gitignore b/tools/testing/selftests/arm64/signal/testcases/.gitignore index 7f7414d241f2..a87fb0f0d2cf 100644 --- a/tools/testing/selftests/arm64/signal/testcases/.gitignore +++ b/tools/testing/selftests/arm64/signal/testcases/.gitignore @@ -1 +1,2 @@ mangle_sp_misaligned +mangle_pc_invalid diff --git a/tools/testing/selftests/arm64/signal/testcases/mangle_pc_invalid.c b/tools/testing/selftests/arm64/signal/testcases/mangle_pc_invalid.c new file mode 100644 index 000000000000..0024032528bc --- /dev/null +++ b/tools/testing/selftests/arm64/signal/testcases/mangle_pc_invalid.c @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2019 ARM Limited */
+#include "test_signals_utils.h" +#include "testcases.h"
+static int mangle_invalid_pc_run(struct tdescr *td, siginfo_t *si,
ucontext_t *uc)
+{
- ASSERT_GOOD_CONTEXT(uc);
- uc->uc_mcontext.pc = 0xffffffffcccccccc;
Similarly to the previous test, I don't think this is testing the sigreturn behaviour: the kernel just restores the bogus PC value and returns to userspace. We then rely on the architecture to generate a prefetch abort when trying to fetch from the bogus PC.
Am I missing anything?
Could we have a test that points SP to kernel memory and the calls sigreturn: there is a real behaviour to test there: we must not be able to trick the kernel into interpreting kernel memory as a signal frame.
[...]
Cheers --Dave
Added a simple mangle testcase which messes with the ucontext_t from within the sig_handler, trying to set PSTATE DAIF bits to an invalid value (masking everything). Expects SIGSEGV on test PASS.
Signed-off-by: Cristian Marussi cristian.marussi@arm.com --- .../arm64/signal/testcases/.gitignore | 1 + .../mangle_pstate_invalid_daif_bits.c | 25 +++++++++++++++++++ 2 files changed, 26 insertions(+) create mode 100644 tools/testing/selftests/arm64/signal/testcases/mangle_pstate_invalid_daif_bits.c
diff --git a/tools/testing/selftests/arm64/signal/testcases/.gitignore b/tools/testing/selftests/arm64/signal/testcases/.gitignore index a87fb0f0d2cf..a609a08b744f 100644 --- a/tools/testing/selftests/arm64/signal/testcases/.gitignore +++ b/tools/testing/selftests/arm64/signal/testcases/.gitignore @@ -1,2 +1,3 @@ mangle_sp_misaligned mangle_pc_invalid +mangle_pstate_invalid_daif_bits diff --git a/tools/testing/selftests/arm64/signal/testcases/mangle_pstate_invalid_daif_bits.c b/tools/testing/selftests/arm64/signal/testcases/mangle_pstate_invalid_daif_bits.c new file mode 100644 index 000000000000..710681cba59c --- /dev/null +++ b/tools/testing/selftests/arm64/signal/testcases/mangle_pstate_invalid_daif_bits.c @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2019 ARM Limited */ + +#include "test_signals_utils.h" +#include "testcases.h" + +static int mangle_invalid_pstate_run(struct tdescr *td, siginfo_t *si, + ucontext_t *uc) +{ + ASSERT_GOOD_CONTEXT(uc); + + /* This config should trigger a SIGSEGV by Kernel */ + uc->uc_mcontext.pstate |= PSR_D_BIT | PSR_A_BIT | PSR_I_BIT | PSR_F_BIT; + + return 1; +} + +struct tdescr tde = { + .sanity_disabled = true, + .name = "MANGLE_PSTATE_INVALID_DAIF_BITS", + .descr = "Mangling uc_mcontext with INVALID DAIF_BITS", + .sig_trig = SIGUSR1, + .sig_ok = SIGSEGV, + .run = mangle_invalid_pstate_run, +};
On Thu, Jun 13, 2019 at 12:13:27PM +0100, Cristian Marussi wrote:
Added a simple mangle testcase which messes with the ucontext_t from within the sig_handler, trying to set PSTATE DAIF bits to an invalid value (masking everything). Expects SIGSEGV on test PASS.
Signed-off-by: Cristian Marussi cristian.marussi@arm.com
.../arm64/signal/testcases/.gitignore | 1 + .../mangle_pstate_invalid_daif_bits.c | 25 +++++++++++++++++++ 2 files changed, 26 insertions(+) create mode 100644 tools/testing/selftests/arm64/signal/testcases/mangle_pstate_invalid_daif_bits.c
diff --git a/tools/testing/selftests/arm64/signal/testcases/.gitignore b/tools/testing/selftests/arm64/signal/testcases/.gitignore index a87fb0f0d2cf..a609a08b744f 100644 --- a/tools/testing/selftests/arm64/signal/testcases/.gitignore +++ b/tools/testing/selftests/arm64/signal/testcases/.gitignore @@ -1,2 +1,3 @@ mangle_sp_misaligned mangle_pc_invalid +mangle_pstate_invalid_daif_bits diff --git a/tools/testing/selftests/arm64/signal/testcases/mangle_pstate_invalid_daif_bits.c b/tools/testing/selftests/arm64/signal/testcases/mangle_pstate_invalid_daif_bits.c new file mode 100644 index 000000000000..710681cba59c --- /dev/null +++ b/tools/testing/selftests/arm64/signal/testcases/mangle_pstate_invalid_daif_bits.c @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2019 ARM Limited */
+#include "test_signals_utils.h" +#include "testcases.h"
+static int mangle_invalid_pstate_run(struct tdescr *td, siginfo_t *si,
ucontext_t *uc)
+{
- ASSERT_GOOD_CONTEXT(uc);
- /* This config should trigger a SIGSEGV by Kernel */
- uc->uc_mcontext.pstate |= PSR_D_BIT | PSR_A_BIT | PSR_I_BIT | PSR_F_BIT;
It may be worth commenting to mention what we're testing in the kernel here: in this case, we're checking that valid_native_regs() is called to police the new pstate value, and to check that it does the right thing.
Is there a way to check that the SIGSEGV comes from the sigreturn and not from something else?
It looks like the SIGSEGV should have si_code == SEGV_ACCERR and si_addr == <sigframe base address> in this case. uc_mcontext.pc will also point into sigtramp in the vdso, which we may be able to check somehow.
We don't have to have a bulletproof check here, but it would be nice to have some kind of sanity-check at least.
[...]
Cheers ---Dave
Added a simple mangle testcase which messes with the ucontext_t from within the sig_handler, trying to toggle PSTATE state bits to switch the system between 32bit/64bit execution state. Expects SIGSEGV on test PASS.
Signed-off-by: Cristian Marussi cristian.marussi@arm.com --- .../arm64/signal/testcases/.gitignore | 1 + .../mangle_pstate_invalid_state_toggle.c | 25 +++++++++++++++++++ 2 files changed, 26 insertions(+) create mode 100644 tools/testing/selftests/arm64/signal/testcases/mangle_pstate_invalid_state_toggle.c
diff --git a/tools/testing/selftests/arm64/signal/testcases/.gitignore b/tools/testing/selftests/arm64/signal/testcases/.gitignore index a609a08b744f..91f7aee4b666 100644 --- a/tools/testing/selftests/arm64/signal/testcases/.gitignore +++ b/tools/testing/selftests/arm64/signal/testcases/.gitignore @@ -1,3 +1,4 @@ mangle_sp_misaligned mangle_pc_invalid mangle_pstate_invalid_daif_bits +mangle_pstate_invalid_state_toggle diff --git a/tools/testing/selftests/arm64/signal/testcases/mangle_pstate_invalid_state_toggle.c b/tools/testing/selftests/arm64/signal/testcases/mangle_pstate_invalid_state_toggle.c new file mode 100644 index 000000000000..971193e7501b --- /dev/null +++ b/tools/testing/selftests/arm64/signal/testcases/mangle_pstate_invalid_state_toggle.c @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2019 ARM Limited */ + +#include "test_signals_utils.h" +#include "testcases.h" + +static int mangle_invalid_pstate_run(struct tdescr *td, siginfo_t *si, + ucontext_t *uc) +{ + ASSERT_GOOD_CONTEXT(uc); + + /* This config should trigger a SIGSEGV by Kernel */ + uc->uc_mcontext.pstate ^= PSR_MODE32_BIT; + + return 1; +} + +struct tdescr tde = { + .sanity_disabled = true, + .name = "MANGLE_PSTATE_INVALID_STATE_TOGGLE", + .descr = "Mangling uc_mcontext with INVALID STATE_TOGGLE", + .sig_trig = SIGUSR1, + .sig_ok = SIGSEGV, + .run = mangle_invalid_pstate_run, +};
On Thu, Jun 13, 2019 at 12:13:28PM +0100, Cristian Marussi wrote:
Added a simple mangle testcase which messes with the ucontext_t from within the sig_handler, trying to toggle PSTATE state bits to switch the system between 32bit/64bit execution state. Expects SIGSEGV on test PASS.
Maybe say "compat_toggle" instead of "state_toggle" in the test name. "state" is a bit of a generic term.
Once upon a time, the kernel didn't prohibit this toggle, which was a "cool feature" before compat existed for real. I think this probably got sorted before the initial arm64 port was upstreamed.
Signed-off-by: Cristian Marussi cristian.marussi@arm.com
.../arm64/signal/testcases/.gitignore | 1 + .../mangle_pstate_invalid_state_toggle.c | 25 +++++++++++++++++++ 2 files changed, 26 insertions(+) create mode 100644 tools/testing/selftests/arm64/signal/testcases/mangle_pstate_invalid_state_toggle.c
diff --git a/tools/testing/selftests/arm64/signal/testcases/.gitignore b/tools/testing/selftests/arm64/signal/testcases/.gitignore index a609a08b744f..91f7aee4b666 100644 --- a/tools/testing/selftests/arm64/signal/testcases/.gitignore +++ b/tools/testing/selftests/arm64/signal/testcases/.gitignore @@ -1,3 +1,4 @@ mangle_sp_misaligned mangle_pc_invalid mangle_pstate_invalid_daif_bits +mangle_pstate_invalid_state_toggle diff --git a/tools/testing/selftests/arm64/signal/testcases/mangle_pstate_invalid_state_toggle.c b/tools/testing/selftests/arm64/signal/testcases/mangle_pstate_invalid_state_toggle.c new file mode 100644 index 000000000000..971193e7501b --- /dev/null +++ b/tools/testing/selftests/arm64/signal/testcases/mangle_pstate_invalid_state_toggle.c @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2019 ARM Limited */
+#include "test_signals_utils.h" +#include "testcases.h"
+static int mangle_invalid_pstate_run(struct tdescr *td, siginfo_t *si,
ucontext_t *uc)
+{
- ASSERT_GOOD_CONTEXT(uc);
- /* This config should trigger a SIGSEGV by Kernel */
- uc->uc_mcontext.pstate ^= PSR_MODE32_BIT;
As for other tests, is there a way to sanity-check that the SIGSEGV was generated by sigreturn itself?
[...]
Cheers ---Dave
Added 3 simple mangle testcases that mess with the ucontext_t from within the sig_handler, trying to toggle PSTATE mode bits to trick the system into switching to EL1/EL2/EL3. Expects SIGSEGV on test PASS.
Signed-off-by: Cristian Marussi cristian.marussi@arm.com --- .../arm64/signal/testcases/.gitignore | 3 +++ .../mangle_pstate_invalid_mode_el1.c | 25 +++++++++++++++++++ .../mangle_pstate_invalid_mode_el2.c | 25 +++++++++++++++++++ .../mangle_pstate_invalid_mode_el3.c | 25 +++++++++++++++++++ 4 files changed, 78 insertions(+) create mode 100644 tools/testing/selftests/arm64/signal/testcases/mangle_pstate_invalid_mode_el1.c create mode 100644 tools/testing/selftests/arm64/signal/testcases/mangle_pstate_invalid_mode_el2.c create mode 100644 tools/testing/selftests/arm64/signal/testcases/mangle_pstate_invalid_mode_el3.c
diff --git a/tools/testing/selftests/arm64/signal/testcases/.gitignore b/tools/testing/selftests/arm64/signal/testcases/.gitignore index 91f7aee4b666..e7a1d998b650 100644 --- a/tools/testing/selftests/arm64/signal/testcases/.gitignore +++ b/tools/testing/selftests/arm64/signal/testcases/.gitignore @@ -2,3 +2,6 @@ mangle_sp_misaligned mangle_pc_invalid mangle_pstate_invalid_daif_bits mangle_pstate_invalid_state_toggle +mangle_pstate_invalid_mode_el1 +mangle_pstate_invalid_mode_el2 +mangle_pstate_invalid_mode_el3 diff --git a/tools/testing/selftests/arm64/signal/testcases/mangle_pstate_invalid_mode_el1.c b/tools/testing/selftests/arm64/signal/testcases/mangle_pstate_invalid_mode_el1.c new file mode 100644 index 000000000000..9f5bde2e287f --- /dev/null +++ b/tools/testing/selftests/arm64/signal/testcases/mangle_pstate_invalid_mode_el1.c @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2019 ARM Limited */ + +#include "test_signals_utils.h" +#include "testcases.h" + +static int mangle_invalid_pstate_run(struct tdescr *td, siginfo_t *si, + ucontext_t *uc) +{ + ASSERT_GOOD_CONTEXT(uc); + + /* This config should trigger a SIGSEGV by Kernel */ + uc->uc_mcontext.pstate |= PSR_MODE_EL1t; + + return 1; +} + +struct tdescr tde = { + .sanity_disabled = true, + .name = "MANGLE_PSTATE_INVALID_MODE_EL1t", + .descr = "Mangling uc_mcontext with INVALID MODE EL1t", + .sig_trig = SIGUSR1, + .sig_ok = SIGSEGV, + .run = mangle_invalid_pstate_run, +}; diff --git a/tools/testing/selftests/arm64/signal/testcases/mangle_pstate_invalid_mode_el2.c b/tools/testing/selftests/arm64/signal/testcases/mangle_pstate_invalid_mode_el2.c new file mode 100644 index 000000000000..667e04fadf7b --- /dev/null +++ b/tools/testing/selftests/arm64/signal/testcases/mangle_pstate_invalid_mode_el2.c @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2019 ARM Limited */ + +#include "test_signals_utils.h" +#include "testcases.h" + +static int mangle_invalid_pstate_run(struct tdescr *td, siginfo_t *si, + ucontext_t *uc) +{ + ASSERT_GOOD_CONTEXT(uc); + + /* This config should trigger a SIGSEGV by Kernel */ + uc->uc_mcontext.pstate |= PSR_MODE_EL2t; + + return 1; +} + +struct tdescr tde = { + .sanity_disabled = true, + .name = "MANGLE_PSTATE_INVALID_MODE_EL2t", + .descr = "Mangling uc_mcontext with INVALID MODE EL2t", + .sig_trig = SIGUSR1, + .sig_ok = SIGSEGV, + .run = mangle_invalid_pstate_run, +}; diff --git a/tools/testing/selftests/arm64/signal/testcases/mangle_pstate_invalid_mode_el3.c b/tools/testing/selftests/arm64/signal/testcases/mangle_pstate_invalid_mode_el3.c new file mode 100644 index 000000000000..17abde0c6883 --- /dev/null +++ b/tools/testing/selftests/arm64/signal/testcases/mangle_pstate_invalid_mode_el3.c @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2019 ARM Limited */ + +#include "test_signals_utils.h" +#include "testcases.h" + +static int mangle_invalid_pstate_run(struct tdescr *td, siginfo_t *si, + ucontext_t *uc) +{ + ASSERT_GOOD_CONTEXT(uc); + + /* This config should trigger a SIGSEGV by Kernel */ + uc->uc_mcontext.pstate |= PSR_MODE_EL3t; + + return 1; +} + +struct tdescr tde = { + .sanity_disabled = true, + .name = "MANGLE_PSTATE_INVALID_MODE_EL3t", + .descr = "Mangling uc_mcontext with INVALID MODE EL3t", + .sig_trig = SIGUSR1, + .sig_ok = SIGSEGV, + .run = mangle_invalid_pstate_run, +};
^ Subject line seems to end with a ?
Typo?
On Thu, Jun 13, 2019 at 12:13:29PM +0100, Cristian Marussi wrote:
Added 3 simple mangle testcases that mess with the ucontext_t from within the sig_handler, trying to toggle PSTATE mode bits to trick the system into switching to EL1/EL2/EL3. Expects SIGSEGV on test PASS.
For good measure, we may as well also test for the "h" modes.
I wonder whether this can be macro-ised somehow, since the tests are identical except for the pstate mode field value and the name?
Signed-off-by: Cristian Marussi cristian.marussi@arm.com
.../arm64/signal/testcases/.gitignore | 3 +++ .../mangle_pstate_invalid_mode_el1.c | 25 +++++++++++++++++++ .../mangle_pstate_invalid_mode_el2.c | 25 +++++++++++++++++++ .../mangle_pstate_invalid_mode_el3.c | 25 +++++++++++++++++++ 4 files changed, 78 insertions(+) create mode 100644 tools/testing/selftests/arm64/signal/testcases/mangle_pstate_invalid_mode_el1.c create mode 100644 tools/testing/selftests/arm64/signal/testcases/mangle_pstate_invalid_mode_el2.c create mode 100644 tools/testing/selftests/arm64/signal/testcases/mangle_pstate_invalid_mode_el3.c
diff --git a/tools/testing/selftests/arm64/signal/testcases/.gitignore b/tools/testing/selftests/arm64/signal/testcases/.gitignore index 91f7aee4b666..e7a1d998b650 100644 --- a/tools/testing/selftests/arm64/signal/testcases/.gitignore +++ b/tools/testing/selftests/arm64/signal/testcases/.gitignore @@ -2,3 +2,6 @@ mangle_sp_misaligned mangle_pc_invalid mangle_pstate_invalid_daif_bits mangle_pstate_invalid_state_toggle +mangle_pstate_invalid_mode_el1 +mangle_pstate_invalid_mode_el2 +mangle_pstate_invalid_mode_el3 diff --git a/tools/testing/selftests/arm64/signal/testcases/mangle_pstate_invalid_mode_el1.c b/tools/testing/selftests/arm64/signal/testcases/mangle_pstate_invalid_mode_el1.c new file mode 100644 index 000000000000..9f5bde2e287f --- /dev/null +++ b/tools/testing/selftests/arm64/signal/testcases/mangle_pstate_invalid_mode_el1.c @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2019 ARM Limited */
+#include "test_signals_utils.h" +#include "testcases.h"
+static int mangle_invalid_pstate_run(struct tdescr *td, siginfo_t *si,
ucontext_t *uc)
+{
- ASSERT_GOOD_CONTEXT(uc);
- /* This config should trigger a SIGSEGV by Kernel */
- uc->uc_mcontext.pstate |= PSR_MODE_EL1t;
For cleanliness, should we mask out the old mode field first, even if we expect it to be initiall zero?
[...]
Cheers ---Dave
Added a simple mangle testcase which messes with the ucontext_t from within the sig_handler, trying to toggle PSTATE SSBS bit. Expect SIGILL if SSBS feature unsupported or that the value set in PSTATE.SSBS is preserved on test PASS.
Signed-off-by: Cristian Marussi cristian.marussi@arm.com --- .../arm64/signal/testcases/.gitignore | 1 + .../testcases/mangle_pstate_ssbs_regs.c | 41 +++++++++++++++++++ 2 files changed, 42 insertions(+) create mode 100644 tools/testing/selftests/arm64/signal/testcases/mangle_pstate_ssbs_regs.c
diff --git a/tools/testing/selftests/arm64/signal/testcases/.gitignore b/tools/testing/selftests/arm64/signal/testcases/.gitignore index e7a1d998b650..c2972c3f33ca 100644 --- a/tools/testing/selftests/arm64/signal/testcases/.gitignore +++ b/tools/testing/selftests/arm64/signal/testcases/.gitignore @@ -5,3 +5,4 @@ mangle_pstate_invalid_state_toggle 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..d997ebf742d9 --- /dev/null +++ b/tools/testing/selftests/arm64/signal/testcases/mangle_pstate_ssbs_regs.c @@ -0,0 +1,41 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2019 ARM Limited */ + +#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); + + /* toggle bit value */ + uc->uc_mcontext.pstate ^= PSR_SSBS_BIT; + /* Save after mangling...it should be preserved */ + td->saved_uc = *uc; + + return 1; +} + +static int pstate_ssbs_bit_checks(struct tdescr *td) +{ + uint64_t val = 0; + + get_regval(MRS_SSBS_SYSREG, val); + /* pass when preserved */ + td->pass = (!!(val & MRS_SSBS_BIT) == + !!(td->saved_uc.uc_mcontext.pstate & PSR_SSBS_BIT)); + + return 1; +} + +struct tdescr tde = { + .sanity_disabled = true, + .name = "MANGLE_PSTATE_SSBS_REGS", + .descr = "Mangling uc_mcontext changing SSBS.(PRESERVE)", + .feats_required = FEAT_SSBS, + .sig_trig = SIGUSR1, + .sig_unsupp = SIGILL, + .run = mangle_invalid_pstate_ssbs_run, + .check_result = pstate_ssbs_bit_checks, +};
On Thu, Jun 13, 2019 at 12:13:30PM +0100, Cristian Marussi wrote:
Added a simple mangle testcase which messes with the ucontext_t from within the sig_handler, trying to toggle PSTATE SSBS bit. Expect SIGILL if SSBS feature unsupported or that the value set in PSTATE.SSBS is preserved on test PASS.
Signed-off-by: Cristian Marussi cristian.marussi@arm.com
.../arm64/signal/testcases/.gitignore | 1 + .../testcases/mangle_pstate_ssbs_regs.c | 41 +++++++++++++++++++ 2 files changed, 42 insertions(+) create mode 100644 tools/testing/selftests/arm64/signal/testcases/mangle_pstate_ssbs_regs.c
diff --git a/tools/testing/selftests/arm64/signal/testcases/.gitignore b/tools/testing/selftests/arm64/signal/testcases/.gitignore index e7a1d998b650..c2972c3f33ca 100644 --- a/tools/testing/selftests/arm64/signal/testcases/.gitignore +++ b/tools/testing/selftests/arm64/signal/testcases/.gitignore @@ -5,3 +5,4 @@ mangle_pstate_invalid_state_toggle 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..d997ebf742d9 --- /dev/null +++ b/tools/testing/selftests/arm64/signal/testcases/mangle_pstate_ssbs_regs.c @@ -0,0 +1,41 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2019 ARM Limited */
+#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);
- /* toggle bit value */
- uc->uc_mcontext.pstate ^= PSR_SSBS_BIT;
- /* Save after mangling...it should be preserved */
- td->saved_uc = *uc;
- return 1;
+}
+static int pstate_ssbs_bit_checks(struct tdescr *td) +{
- uint64_t val = 0;
- get_regval(MRS_SSBS_SYSREG, val);
- /* pass when preserved */
- td->pass = (!!(val & MRS_SSBS_BIT) ==
!!(td->saved_uc.uc_mcontext.pstate & PSR_SSBS_BIT));
Nit: there's a redundant level of ! here, and the outer () are unnecessary:
(!!a == !!b) -> !a == !b
[...]
Can we trigger a second signal after the first returns, to grab the updated ucontext and check SSBS in there directly?
Checking that the updated value is _also_ visible via MRS remains useful though, so we should keep that.
Cheers ---Dave
Hi
On 6/21/19 11:35 AM, Dave Martin wrote:
On Thu, Jun 13, 2019 at 12:13:30PM +0100, Cristian Marussi wrote:
Added a simple mangle testcase which messes with the ucontext_t from within the sig_handler, trying to toggle PSTATE SSBS bit. Expect SIGILL if SSBS feature unsupported or that the value set in PSTATE.SSBS is preserved on test PASS.
Signed-off-by: Cristian Marussi cristian.marussi@arm.com
.../arm64/signal/testcases/.gitignore | 1 + .../testcases/mangle_pstate_ssbs_regs.c | 41 +++++++++++++++++++ 2 files changed, 42 insertions(+) create mode 100644 tools/testing/selftests/arm64/signal/testcases/mangle_pstate_ssbs_regs.c
diff --git a/tools/testing/selftests/arm64/signal/testcases/.gitignore b/tools/testing/selftests/arm64/signal/testcases/.gitignore index e7a1d998b650..c2972c3f33ca 100644 --- a/tools/testing/selftests/arm64/signal/testcases/.gitignore +++ b/tools/testing/selftests/arm64/signal/testcases/.gitignore @@ -5,3 +5,4 @@ mangle_pstate_invalid_state_toggle 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..d997ebf742d9 --- /dev/null +++ b/tools/testing/selftests/arm64/signal/testcases/mangle_pstate_ssbs_regs.c @@ -0,0 +1,41 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2019 ARM Limited */
+#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);
- /* toggle bit value */
- uc->uc_mcontext.pstate ^= PSR_SSBS_BIT;
- /* Save after mangling...it should be preserved */
- td->saved_uc = *uc;
- return 1;
+}
+static int pstate_ssbs_bit_checks(struct tdescr *td) +{
- uint64_t val = 0;
- get_regval(MRS_SSBS_SYSREG, val);
- /* pass when preserved */
- td->pass = (!!(val & MRS_SSBS_BIT) ==
!!(td->saved_uc.uc_mcontext.pstate & PSR_SSBS_BIT));
Nit: there's a redundant level of ! here, and the outer () are unnecessary:
(!!a == !!b) -> !a == !b
This was me badly convinced (not sure where I got this) that the bitpos of PSR_SSBS_BIT in pstate was different from the bitpos as reported in the output of MRS SSBS, so I was trying to normalize the comparison to 1 == 1 or 0 == 0
...but in fact bitpos is the same between PSTATE and MSR SSBS so it can be compared directly.
[...]
Can we trigger a second signal after the first returns, to grab the updated ucontext and check SSBS in there directly?
Checking that the updated value is _also_ visible via MRS remains useful though, so we should keep that.
I have added an informational message that reports the PSTATE and the status of SSBS as grabbed from uc via an induced SIGUSR2. Test outcome is anyway determined on MRS SSBS result.
If HWCAP_SSBS is available the feature is considered available and so MRS SSBS MUST work. If instead feature is NOT supported as stated in HWCAP_SSBS the test is anyway run but the MRS SSBS is expected to cause a SIGILL (COULD not SHOULD...since HW_CAP could be reporting wrong caps and so MRS SSBS will still work)
Moreover I fixed a small glitch: I was toggling the SSBS bit in uc PSTATE, BUT this make no sense...toggling to zero there's NO way I can check if Kernel preserve it to zero...so now I'm setting to 1 and then check if it has been preserved by Kernel
Cristian
Cheers ---Dave
On Tue, Jul 02, 2019 at 04:51:38PM +0100, Cristian Marussi wrote:
Hi
On 6/21/19 11:35 AM, Dave Martin wrote:
On Thu, Jun 13, 2019 at 12:13:30PM +0100, Cristian Marussi wrote:
Added a simple mangle testcase which messes with the ucontext_t from within the sig_handler, trying to toggle PSTATE SSBS bit. Expect SIGILL if SSBS feature unsupported or that the value set in PSTATE.SSBS is preserved on test PASS.
Signed-off-by: Cristian Marussi cristian.marussi@arm.com
.../arm64/signal/testcases/.gitignore | 1 + .../testcases/mangle_pstate_ssbs_regs.c | 41 +++++++++++++++++++ 2 files changed, 42 insertions(+) create mode 100644 tools/testing/selftests/arm64/signal/testcases/mangle_pstate_ssbs_regs.c
diff --git a/tools/testing/selftests/arm64/signal/testcases/.gitignore b/tools/testing/selftests/arm64/signal/testcases/.gitignore index e7a1d998b650..c2972c3f33ca 100644 --- a/tools/testing/selftests/arm64/signal/testcases/.gitignore +++ b/tools/testing/selftests/arm64/signal/testcases/.gitignore @@ -5,3 +5,4 @@ mangle_pstate_invalid_state_toggle 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..d997ebf742d9 --- /dev/null +++ b/tools/testing/selftests/arm64/signal/testcases/mangle_pstate_ssbs_regs.c @@ -0,0 +1,41 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2019 ARM Limited */
+#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);
- /* toggle bit value */
- uc->uc_mcontext.pstate ^= PSR_SSBS_BIT;
- /* Save after mangling...it should be preserved */
- td->saved_uc = *uc;
- return 1;
+}
+static int pstate_ssbs_bit_checks(struct tdescr *td) +{
- uint64_t val = 0;
- get_regval(MRS_SSBS_SYSREG, val);
- /* pass when preserved */
- td->pass = (!!(val & MRS_SSBS_BIT) ==
!!(td->saved_uc.uc_mcontext.pstate & PSR_SSBS_BIT));
Nit: there's a redundant level of ! here, and the outer () are unnecessary:
(!!a == !!b) -> !a == !b
This was me badly convinced (not sure where I got this) that the bitpos of PSR_SSBS_BIT in pstate was different from the bitpos as reported in the output of MRS SSBS, so I was trying to normalize the comparison to 1 == 1 or 0 == 0
...but in fact bitpos is the same between PSTATE and MSR SSBS so it can be compared directly.
Since the #defines are separate, it may be more readable to avoid assuming that they have the same value (even if they do).
But I'm happy either way.
[...]
Can we trigger a second signal after the first returns, to grab the updated ucontext and check SSBS in there directly?
Checking that the updated value is _also_ visible via MRS remains useful though, so we should keep that.
I have added an informational message that reports the PSTATE and the status of SSBS as grabbed from uc via an induced SIGUSR2. Test outcome is anyway determined on MRS SSBS result.
OK, sounds reasonable.
If HWCAP_SSBS is available the feature is considered available and so MRS SSBS MUST work.
Yup
If instead feature is NOT supported as stated in HWCAP_SSBS the test is anyway run but the MRS SSBS is expected to cause a SIGILL (COULD not SHOULD...since HW_CAP could be reporting wrong caps and so MRS SSBS will still work)
Probably reasonable. I don't recall whether the architecture actually guarantees a SIGILL though, or whether other things could happen (such as executing as a NOP, or reading a fixed value such as 0).
Can this MRS be trapped? I'm wondering whether it will still just execute normally if running on hardware that has it, but on an old kernel that doesn't report HWCAP_SSBS. Some of the other tests may fail on such a kernel though -- valid_user_regs() would probaly mask the bit out when setting the register through sigreturn or ptrace.
Moreover I fixed a small glitch: I was toggling the SSBS bit in uc PSTATE, BUT this make no sense...toggling to zero there's NO way I can check if Kernel preserve it to zero...so now I'm setting to 1 and then check if it has been preserved by Kernel
Well, I guess you can check both ways, but the bit was previously RES0, so if the kernel masks it out it should be stuck at 0.
So agreed, attempting to set it to 1 is the more interesting test.
Cheers ---Dave
Added a simple fake_sigreturn testcase which builds a good ucontext_t and tries to place it onto the stack in a misaligned way. Expects a SIGSEGV on test PASS.
Signed-off-by: Cristian Marussi cristian.marussi@arm.com --- .../arm64/signal/testcases/.gitignore | 1 + .../testcases/fake_sigreturn_misaligned.c | 30 +++++++++++++++++++ 2 files changed, 31 insertions(+) create mode 100644 tools/testing/selftests/arm64/signal/testcases/fake_sigreturn_misaligned.c
diff --git a/tools/testing/selftests/arm64/signal/testcases/.gitignore b/tools/testing/selftests/arm64/signal/testcases/.gitignore index c2972c3f33ca..3e6b26be6727 100644 --- a/tools/testing/selftests/arm64/signal/testcases/.gitignore +++ b/tools/testing/selftests/arm64/signal/testcases/.gitignore @@ -6,3 +6,4 @@ mangle_pstate_invalid_mode_el1 mangle_pstate_invalid_mode_el2 mangle_pstate_invalid_mode_el3 mangle_pstate_ssbs_regs +fake_sigreturn_misaligned diff --git a/tools/testing/selftests/arm64/signal/testcases/fake_sigreturn_misaligned.c b/tools/testing/selftests/arm64/signal/testcases/fake_sigreturn_misaligned.c new file mode 100644 index 000000000000..d551858dd9dd --- /dev/null +++ b/tools/testing/selftests/arm64/signal/testcases/fake_sigreturn_misaligned.c @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2019 ARM Limited */ + +#include <ucontext.h> + +#include "test_signals_utils.h" +#include "testcases.h" + +struct a_sigframe sf; + +static int fake_sigreturn_misaligned_run(struct tdescr *td, + siginfo_t *si, ucontext_t *uc) +{ + /* just to fill the ucontext_t with something real */ + if (!get_current_context(td, &sf.uc)) + return 1; + + /* Forcing sigframe on misaligned (=!16) SP */ + fake_sigreturn(&sf, sizeof(sf), 8); + + return 1; +} + +struct tdescr tde = { + .name = "FAKE_SIGRETURN_MISALIGNED_SP", + .descr = "Triggers a fake sigreturn with a misaligned sigframe on SP", + .sig_ok = SIGSEGV, + .timeout = 3, + .run = fake_sigreturn_misaligned_run, +};
On Thu, Jun 13, 2019 at 12:13:31PM +0100, Cristian Marussi wrote:
Added a simple fake_sigreturn testcase which builds a good ucontext_t and tries to place it onto the stack in a misaligned way. Expects a SIGSEGV on test PASS.
Signed-off-by: Cristian Marussi cristian.marussi@arm.com
.../arm64/signal/testcases/.gitignore | 1 + .../testcases/fake_sigreturn_misaligned.c | 30 +++++++++++++++++++ 2 files changed, 31 insertions(+) create mode 100644 tools/testing/selftests/arm64/signal/testcases/fake_sigreturn_misaligned.c
diff --git a/tools/testing/selftests/arm64/signal/testcases/.gitignore b/tools/testing/selftests/arm64/signal/testcases/.gitignore index c2972c3f33ca..3e6b26be6727 100644 --- a/tools/testing/selftests/arm64/signal/testcases/.gitignore +++ b/tools/testing/selftests/arm64/signal/testcases/.gitignore @@ -6,3 +6,4 @@ mangle_pstate_invalid_mode_el1 mangle_pstate_invalid_mode_el2 mangle_pstate_invalid_mode_el3 mangle_pstate_ssbs_regs +fake_sigreturn_misaligned diff --git a/tools/testing/selftests/arm64/signal/testcases/fake_sigreturn_misaligned.c b/tools/testing/selftests/arm64/signal/testcases/fake_sigreturn_misaligned.c new file mode 100644 index 000000000000..d551858dd9dd --- /dev/null +++ b/tools/testing/selftests/arm64/signal/testcases/fake_sigreturn_misaligned.c @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2019 ARM Limited */
+#include <ucontext.h>
+#include "test_signals_utils.h" +#include "testcases.h"
+struct a_sigframe sf;
+static int fake_sigreturn_misaligned_run(struct tdescr *td,
siginfo_t *si, ucontext_t *uc)
+{
- /* just to fill the ucontext_t with something real */
- if (!get_current_context(td, &sf.uc))
return 1;
- /* Forcing sigframe on misaligned (=!16) SP */
- fake_sigreturn(&sf, sizeof(sf), 8);
Does this do the right thing? From the asm code, it looks like fake_sigreturn will ensure that SP % 8 == 0, but that may still be fine (i.e., SP % 16 == 0 and SP % 8 == 0 can both be true, depending on the precise value of sizeof(sf)).
Maybe I misunderstood what fake_sigreturn is doing.
Instead, do we want to ensure that SP % 16 != 0 here?
[...]
Cheers ---Dave
Added a simple fake_sigreturn testcase which builds a ucontext_t with a bad magic header and place it onto the stack. Expects a SIGSEGV on test PASS.
Signed-off-by: Cristian Marussi cristian.marussi@arm.com --- .../arm64/signal/testcases/.gitignore | 1 + .../testcases/fake_sigreturn_bad_magic.c | 42 +++++++++++++++++++ 2 files changed, 43 insertions(+) create mode 100644 tools/testing/selftests/arm64/signal/testcases/fake_sigreturn_bad_magic.c
diff --git a/tools/testing/selftests/arm64/signal/testcases/.gitignore b/tools/testing/selftests/arm64/signal/testcases/.gitignore index 3e6b26be6727..c353b7bd899d 100644 --- a/tools/testing/selftests/arm64/signal/testcases/.gitignore +++ b/tools/testing/selftests/arm64/signal/testcases/.gitignore @@ -7,3 +7,4 @@ mangle_pstate_invalid_mode_el2 mangle_pstate_invalid_mode_el3 mangle_pstate_ssbs_regs fake_sigreturn_misaligned +fake_sigreturn_bad_magic diff --git a/tools/testing/selftests/arm64/signal/testcases/fake_sigreturn_bad_magic.c b/tools/testing/selftests/arm64/signal/testcases/fake_sigreturn_bad_magic.c new file mode 100644 index 000000000000..de81ea10393f --- /dev/null +++ b/tools/testing/selftests/arm64/signal/testcases/fake_sigreturn_bad_magic.c @@ -0,0 +1,42 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2019 ARM Limited */ + +#include <asm/sigcontext.h> +#include <ucontext.h> +#include <stdio.h> + +#include "test_signals_utils.h" +#include "testcases.h" + +struct a_sigframe sf; + +static int fake_sigreturn_bad_magic_run(struct tdescr *td, + siginfo_t *si, ucontext_t *uc) +{ + struct _aarch64_ctx *head = GET_SF_RESV_HEAD(sf); + + /* just to fill the ucontext_t with something real */ + if (!get_current_context(td, &sf.uc)) + return 1; + + /* find the terminator, preserving existig headers */ + head = get_terminator(head, GET_SF_RESV_SIZE(sf), NULL); + if (head) { + head->magic = 0xdeadbeef; + head->size = 128; + write_terminator(GET_RESV_NEXT_HEAD(head)); + + ASSERT_BAD_CONTEXT(&sf.uc); + fake_sigreturn(&sf, sizeof(sf), 16); + } + + return 1; +} + +struct tdescr tde = { + .name = "FAKE_SIGRETURN_BAD_MAGIC", + .descr = "Triggers a fake sigreturn with a sigframe including a bad non-existent magic", + .sig_ok = SIGSEGV, + .timeout = 3, + .run = fake_sigreturn_bad_magic_run, +};
On Thu, Jun 13, 2019 at 12:13:32PM +0100, Cristian Marussi wrote:
Added a simple fake_sigreturn testcase which builds a ucontext_t with a bad magic header and place it onto the stack. Expects a SIGSEGV on test PASS.
Signed-off-by: Cristian Marussi cristian.marussi@arm.com
.../arm64/signal/testcases/.gitignore | 1 + .../testcases/fake_sigreturn_bad_magic.c | 42 +++++++++++++++++++ 2 files changed, 43 insertions(+) create mode 100644 tools/testing/selftests/arm64/signal/testcases/fake_sigreturn_bad_magic.c
diff --git a/tools/testing/selftests/arm64/signal/testcases/.gitignore b/tools/testing/selftests/arm64/signal/testcases/.gitignore index 3e6b26be6727..c353b7bd899d 100644 --- a/tools/testing/selftests/arm64/signal/testcases/.gitignore +++ b/tools/testing/selftests/arm64/signal/testcases/.gitignore @@ -7,3 +7,4 @@ mangle_pstate_invalid_mode_el2 mangle_pstate_invalid_mode_el3 mangle_pstate_ssbs_regs fake_sigreturn_misaligned +fake_sigreturn_bad_magic diff --git a/tools/testing/selftests/arm64/signal/testcases/fake_sigreturn_bad_magic.c b/tools/testing/selftests/arm64/signal/testcases/fake_sigreturn_bad_magic.c new file mode 100644 index 000000000000..de81ea10393f --- /dev/null +++ b/tools/testing/selftests/arm64/signal/testcases/fake_sigreturn_bad_magic.c @@ -0,0 +1,42 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2019 ARM Limited */
+#include <asm/sigcontext.h> +#include <ucontext.h> +#include <stdio.h>
+#include "test_signals_utils.h" +#include "testcases.h"
+struct a_sigframe sf;
+static int fake_sigreturn_bad_magic_run(struct tdescr *td,
siginfo_t *si, ucontext_t *uc)
+{
- struct _aarch64_ctx *head = GET_SF_RESV_HEAD(sf);
- /* just to fill the ucontext_t with something real */
- if (!get_current_context(td, &sf.uc))
return 1;
- /* find the terminator, preserving existig headers */
- head = get_terminator(head, GET_SF_RESV_SIZE(sf), NULL);
- if (head) {
head->magic = 0xdeadbeef;
head->size = 128;
How can we be sure this won't overrun the end of the sigframe, once the new terminator is appended?
If there is extra_context in the frame, the frame is only as large as needed, so adding another record will always overrun the size of the frame in that case.
I suggest splitting this test into a few cases, something like the following.
(Note, some of these are already covered by your tests. I'm just trying to give the broad view here.)
1) Add a bogus record that doesn't overrun the frame.
2) Add a bogus record that does overrun the frame.
3) Add a record with a bogus size (i.e., not a multiple of 16 bytes, or smaller than _aarch64_ctx).
3) Hack the size field of an existing record so that it overruns the frame. Thee are two subcases here: a record big enough that the next _aarch64_ctx doesn't fit (probably already tested by fake_sigreturn_overflow_reserved), and a record that overruns the frame all by itself (tested by fake_sigreturn_bad_size, but it would be good to check the exact boundary condition -- see my comments on that patch).
When checking that the kernel rejects an _aarch64_ctx header that overruns the end of the frame, we should nonetheless write data for it that looks valid, so that we know the kernel is rejecting it because of the overrun and not because the header contents are invalid.
4) Delete a required record from the frame (say, fpsimd_context). Or just delete everything.
For this series, maybe just do (1): this test nearly does that.
If there is extra_context, we can delete it and replace it with our bogus record: this can't overrun if the bogus record's size is no bigger than extra_context.
We should have tests to cover the other cases, but they can be TODO for now.
[...]
Cheers ---Dave
Hi Dave
a few updates related to v2 of this patchset
On 6/21/19 11:36 AM, Dave Martin wrote:
On Thu, Jun 13, 2019 at 12:13:32PM +0100, Cristian Marussi wrote:
Added a simple fake_sigreturn testcase which builds a ucontext_t with a bad magic header and place it onto the stack. Expects a SIGSEGV on test PASS.
Signed-off-by: Cristian Marussi cristian.marussi@arm.com
.../arm64/signal/testcases/.gitignore | 1 + .../testcases/fake_sigreturn_bad_magic.c | 42 +++++++++++++++++++ 2 files changed, 43 insertions(+) create mode 100644 tools/testing/selftests/arm64/signal/testcases/fake_sigreturn_bad_magic.c
diff --git a/tools/testing/selftests/arm64/signal/testcases/.gitignore b/tools/testing/selftests/arm64/signal/testcases/.gitignore index 3e6b26be6727..c353b7bd899d 100644 --- a/tools/testing/selftests/arm64/signal/testcases/.gitignore +++ b/tools/testing/selftests/arm64/signal/testcases/.gitignore @@ -7,3 +7,4 @@ mangle_pstate_invalid_mode_el2 mangle_pstate_invalid_mode_el3 mangle_pstate_ssbs_regs fake_sigreturn_misaligned +fake_sigreturn_bad_magic diff --git a/tools/testing/selftests/arm64/signal/testcases/fake_sigreturn_bad_magic.c b/tools/testing/selftests/arm64/signal/testcases/fake_sigreturn_bad_magic.c new file mode 100644 index 000000000000..de81ea10393f --- /dev/null +++ b/tools/testing/selftests/arm64/signal/testcases/fake_sigreturn_bad_magic.c @@ -0,0 +1,42 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2019 ARM Limited */
+#include <asm/sigcontext.h> +#include <ucontext.h> +#include <stdio.h>
+#include "test_signals_utils.h" +#include "testcases.h"
+struct a_sigframe sf;
+static int fake_sigreturn_bad_magic_run(struct tdescr *td,
siginfo_t *si, ucontext_t *uc)
+{
- struct _aarch64_ctx *head = GET_SF_RESV_HEAD(sf);
- /* just to fill the ucontext_t with something real */
- if (!get_current_context(td, &sf.uc))
return 1;
- /* find the terminator, preserving existig headers */
- head = get_terminator(head, GET_SF_RESV_SIZE(sf), NULL);
- if (head) {
head->magic = 0xdeadbeef;
head->size = 128;
How can we be sure this won't overrun the end of the sigframe, once the new terminator is appended?
If there is extra_context in the frame, the frame is only as large as needed, so adding another record will always overrun the size of the frame in that case.
I underestimated this, since extra is currently never found and the __reserved area is mostly empty.
I have fixed in these regards some tests in V2, taking care to verify the available space and throwing away EXTRA to make room as advised.
I suggest splitting this test into a few cases, something like the following.
(Note, some of these are already covered by your tests. I'm just trying to give the broad view here.)
Add a bogus record that doesn't overrun the frame.
Add a bogus record that does overrun the frame.
Add a record with a bogus size (i.e., not a multiple of 16 bytes, or
smaller than _aarch64_ctx).
These are tests TBD...in V2 I did something similar with wrong sizes and aligments (and overrun) but not full cases. I'll add something or some TODO notes in V3.
- Hack the size field of an existing record so that it overruns the
frame. Thee are two subcases here: a record big enough that the next _aarch64_ctx doesn't fit (probably already tested by fake_sigreturn_overflow_reserved),
fake_sigreturn_overflow_reserved has been removed in V2, will fix and re-enable in V3
and a record that overruns the frame
all by itself (tested by fake_sigreturn_bad_size, but it would be good to check the exact boundary condition -- see my comments on that patch).
This should have been fixed now in fake_sigreturn_bad_size following advice (hopefuly)
When checking that the kernel rejects an _aarch64_ctx header that overruns the end of the frame, we should nonetheless write data for it that looks valid, so that we know the kernel is rejecting it because of the overrun and not because the header contents are invalid.
- Delete a required record from the frame (say, fpsimd_context).
Or just delete everything.
2 tests added in V2 about fpsimd: mising and duplicated
For this series, maybe just do (1): this test nearly does that.
If there is extra_context, we can delete it and replace it with our bogus record: this can't overrun if the bogus record's size is no bigger than extra_context.
Followed this approach in v2
We should have tests to cover the other cases, but they can be TODO for now.
[...]
Cheers ---Dave
Cheers
Cristian
Added a simple fake_sigreturn testcase which builds a ucontext_t with a badly sized header and place it onto the stack. Expects a SIGSEGV on test PASS.
Signed-off-by: Cristian Marussi cristian.marussi@arm.com --- .../arm64/signal/testcases/.gitignore | 1 + .../testcases/fake_sigreturn_bad_size.c | 40 +++++++++++++++++++ 2 files changed, 41 insertions(+) create mode 100644 tools/testing/selftests/arm64/signal/testcases/fake_sigreturn_bad_size.c
diff --git a/tools/testing/selftests/arm64/signal/testcases/.gitignore b/tools/testing/selftests/arm64/signal/testcases/.gitignore index c353b7bd899d..9ad1735e0018 100644 --- a/tools/testing/selftests/arm64/signal/testcases/.gitignore +++ b/tools/testing/selftests/arm64/signal/testcases/.gitignore @@ -8,3 +8,4 @@ mangle_pstate_invalid_mode_el3 mangle_pstate_ssbs_regs fake_sigreturn_misaligned fake_sigreturn_bad_magic +fake_sigreturn_bad_size diff --git a/tools/testing/selftests/arm64/signal/testcases/fake_sigreturn_bad_size.c b/tools/testing/selftests/arm64/signal/testcases/fake_sigreturn_bad_size.c new file mode 100644 index 000000000000..d54f79d63bf6 --- /dev/null +++ b/tools/testing/selftests/arm64/signal/testcases/fake_sigreturn_bad_size.c @@ -0,0 +1,40 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2019 ARM Limited */ + +#include <asm/sigcontext.h> +#include <ucontext.h> + +#include "test_signals_utils.h" +#include "testcases.h" + +struct a_sigframe sf; + +static int fake_sigreturn_bad_size_run(struct tdescr *td, + siginfo_t *si, ucontext_t *uc) +{ + struct _aarch64_ctx *head = GET_SF_RESV_HEAD(sf); + + /* just to fill the ucontext_t with something real */ + if (!get_current_context(td, &sf.uc)) + return 1; + + /* Tampering size to an unplausible number should cause a SEGV */ + head = get_terminator(head, GET_SF_RESV_SIZE(sf), NULL); + if (head) { + head->magic = ESR_MAGIC; + head->size = GET_SF_RESV_SIZE(sf) * 2; + + ASSERT_BAD_CONTEXT(&sf.uc); + fake_sigreturn(&sf, sizeof(sf), 16); + } + + return 1; +} + +struct tdescr tde = { + .name = "FAKE_SIGRETURN_BAD_SIZE", + .descr = "Triggers a fake sigreturn with a sigframe including a bad sized head on __reserved", + .sig_ok = SIGSEGV, + .timeout = 3, + .run = fake_sigreturn_bad_size_run, +};
On Thu, Jun 13, 2019 at 12:13:33PM +0100, Cristian Marussi wrote:
Added a simple fake_sigreturn testcase which builds a ucontext_t with a badly sized header and place it onto the stack. Expects a SIGSEGV on test PASS.
Signed-off-by: Cristian Marussi cristian.marussi@arm.com
.../arm64/signal/testcases/.gitignore | 1 + .../testcases/fake_sigreturn_bad_size.c | 40 +++++++++++++++++++ 2 files changed, 41 insertions(+) create mode 100644 tools/testing/selftests/arm64/signal/testcases/fake_sigreturn_bad_size.c
diff --git a/tools/testing/selftests/arm64/signal/testcases/.gitignore b/tools/testing/selftests/arm64/signal/testcases/.gitignore index c353b7bd899d..9ad1735e0018 100644 --- a/tools/testing/selftests/arm64/signal/testcases/.gitignore +++ b/tools/testing/selftests/arm64/signal/testcases/.gitignore @@ -8,3 +8,4 @@ mangle_pstate_invalid_mode_el3 mangle_pstate_ssbs_regs fake_sigreturn_misaligned fake_sigreturn_bad_magic +fake_sigreturn_bad_size diff --git a/tools/testing/selftests/arm64/signal/testcases/fake_sigreturn_bad_size.c b/tools/testing/selftests/arm64/signal/testcases/fake_sigreturn_bad_size.c new file mode 100644 index 000000000000..d54f79d63bf6 --- /dev/null +++ b/tools/testing/selftests/arm64/signal/testcases/fake_sigreturn_bad_size.c @@ -0,0 +1,40 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2019 ARM Limited */
+#include <asm/sigcontext.h> +#include <ucontext.h>
+#include "test_signals_utils.h" +#include "testcases.h"
+struct a_sigframe sf;
+static int fake_sigreturn_bad_size_run(struct tdescr *td,
siginfo_t *si, ucontext_t *uc)
+{
- struct _aarch64_ctx *head = GET_SF_RESV_HEAD(sf);
- /* just to fill the ucontext_t with something real */
- if (!get_current_context(td, &sf.uc))
return 1;
- /* Tampering size to an unplausible number should cause a SEGV */
- head = get_terminator(head, GET_SF_RESV_SIZE(sf), NULL);
- if (head) {
head->magic = ESR_MAGIC;
Add a comment to say why we're using this magic?
head->size = GET_SF_RESV_SIZE(sf) * 2;
If we have extra_context, this might not be an invalid size, because extra_context can be much larger than sizeof(__reserved) ... but also we will overrun anyway, because extra_context should already be only just large enough to accommodate the original terminator.
As with the previous test, we should probably throw away the extra_context record (if any) and replace it with this new record.
We should check the actual boundary condition here, rather than just testing with a giant size: we want to make the kernel detects any overrun, however small.
We should also write a valid terminator after the new record, so that we know the kernel is detecting the size overrun instead of rejecting the next record because it is garbage.
ASSERT_BAD_CONTEXT(&sf.uc);
fake_sigreturn(&sf, sizeof(sf), 16);
- }
- return 1;
+}
[...]
Cheers ---Dave
Hi
On 6/21/19 11:36 AM, Dave Martin wrote:
On Thu, Jun 13, 2019 at 12:13:33PM +0100, Cristian Marussi wrote:
Added a simple fake_sigreturn testcase which builds a ucontext_t with a badly sized header and place it onto the stack. Expects a SIGSEGV on test PASS.
Signed-off-by: Cristian Marussi cristian.marussi@arm.com
.../arm64/signal/testcases/.gitignore | 1 + .../testcases/fake_sigreturn_bad_size.c | 40 +++++++++++++++++++ 2 files changed, 41 insertions(+) create mode 100644 tools/testing/selftests/arm64/signal/testcases/fake_sigreturn_bad_size.c
diff --git a/tools/testing/selftests/arm64/signal/testcases/.gitignore b/tools/testing/selftests/arm64/signal/testcases/.gitignore index c353b7bd899d..9ad1735e0018 100644 --- a/tools/testing/selftests/arm64/signal/testcases/.gitignore +++ b/tools/testing/selftests/arm64/signal/testcases/.gitignore @@ -8,3 +8,4 @@ mangle_pstate_invalid_mode_el3 mangle_pstate_ssbs_regs fake_sigreturn_misaligned fake_sigreturn_bad_magic +fake_sigreturn_bad_size diff --git a/tools/testing/selftests/arm64/signal/testcases/fake_sigreturn_bad_size.c b/tools/testing/selftests/arm64/signal/testcases/fake_sigreturn_bad_size.c new file mode 100644 index 000000000000..d54f79d63bf6 --- /dev/null +++ b/tools/testing/selftests/arm64/signal/testcases/fake_sigreturn_bad_size.c @@ -0,0 +1,40 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2019 ARM Limited */
+#include <asm/sigcontext.h> +#include <ucontext.h>
+#include "test_signals_utils.h" +#include "testcases.h"
+struct a_sigframe sf;
+static int fake_sigreturn_bad_size_run(struct tdescr *td,
siginfo_t *si, ucontext_t *uc)
+{
- struct _aarch64_ctx *head = GET_SF_RESV_HEAD(sf);
- /* just to fill the ucontext_t with something real */
- if (!get_current_context(td, &sf.uc))
return 1;
- /* Tampering size to an unplausible number should cause a SEGV */
- head = get_terminator(head, GET_SF_RESV_SIZE(sf), NULL);
- if (head) {
head->magic = ESR_MAGIC;
Add a comment to say why we're using this magic?
head->size = GET_SF_RESV_SIZE(sf) * 2;
If we have extra_context, this might not be an invalid size, because extra_context can be much larger than sizeof(__reserved) ... but also we will overrun anyway, because extra_context should already be only just large enough to accommodate the original terminator.
As with the previous test, we should probably throw away the extra_context record (if any) and replace it with this new record.
We should check the actual boundary condition here, rather than just testing with a giant size: we want to make the kernel detects any overrun, however small.
We should also write a valid terminator after the new record, so that we know the kernel is detecting the size overrun instead of rejecting the next record because it is garbage.
Fixed in V2: throwing away the extra when needed, and adding a bogus ESR_MAGIC with a bogus size, large enough to fit all the remaining free __reserved (- NULL terminator space) + 16 bytes (to be still aligned and crashing out on an overrun of the least minimum size) and terminated with another NULL
Cristian
ASSERT_BAD_CONTEXT(&sf.uc);
fake_sigreturn(&sf, sizeof(sf), 16);
- }
- return 1;
+}
[...]
Cheers ---Dave
Added a simple fake_sigreturn testcase which builds a ucontext_t with a badly sized magic0 header and place it onto the stack. Expects a SIGSEGV on test PASS.
Signed-off-by: Cristian Marussi cristian.marussi@arm.com --- .../arm64/signal/testcases/.gitignore | 1 + .../fake_sigreturn_bad_size_for_magic0.c | 44 +++++++++++++++++++ 2 files changed, 45 insertions(+) create mode 100644 tools/testing/selftests/arm64/signal/testcases/fake_sigreturn_bad_size_for_magic0.c
diff --git a/tools/testing/selftests/arm64/signal/testcases/.gitignore b/tools/testing/selftests/arm64/signal/testcases/.gitignore index 9ad1735e0018..66ba865b3b7e 100644 --- a/tools/testing/selftests/arm64/signal/testcases/.gitignore +++ b/tools/testing/selftests/arm64/signal/testcases/.gitignore @@ -9,3 +9,4 @@ mangle_pstate_ssbs_regs fake_sigreturn_misaligned fake_sigreturn_bad_magic fake_sigreturn_bad_size +fake_sigreturn_bad_size_for_magic0 diff --git a/tools/testing/selftests/arm64/signal/testcases/fake_sigreturn_bad_size_for_magic0.c b/tools/testing/selftests/arm64/signal/testcases/fake_sigreturn_bad_size_for_magic0.c new file mode 100644 index 000000000000..703909959473 --- /dev/null +++ b/tools/testing/selftests/arm64/signal/testcases/fake_sigreturn_bad_size_for_magic0.c @@ -0,0 +1,44 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2019 ARM Limited */ + +#include <asm/sigcontext.h> +#include <ucontext.h> +#include <stdio.h> + +#include "test_signals_utils.h" +#include "testcases.h" + +struct a_sigframe sf; + +static int fake_sigreturn_bad_size_for_magic0_run(struct tdescr *td, + siginfo_t *si, ucontext_t *uc) +{ + struct _aarch64_ctx *head = GET_SF_RESV_HEAD(sf); + + /* just to fill the ucontext_t with something real */ + if (!get_current_context(td, &sf.uc)) + return 1; + + /* + * Jump to the free slot...we must preserve existing + * magics like fpsimd in order no to SEGV anyway + */ + head = get_terminator(head, GET_SF_RESV_SIZE(sf), NULL); + if (head) { + head->magic = 0; + head->size = 256; + + ASSERT_BAD_CONTEXT(&sf.uc); + fake_sigreturn(&sf, sizeof(sf), 16); + } + + return 1; +} + +struct tdescr tde = { + .name = "FAKE_SIGRETURN_BAD_SIZE_FOR_MAGIC0", + .descr = "Triggers a fake sigreturn with a sigframe including a bad non-zero size magic0", + .sig_ok = SIGSEGV, + .timeout = 3, + .run = fake_sigreturn_bad_size_for_magic0_run, +};
On Thu, Jun 13, 2019 at 12:13:34PM +0100, Cristian Marussi wrote:
Added a simple fake_sigreturn testcase which builds a ucontext_t with a badly sized magic0 header and place it onto the stack. Expects a SIGSEGV on test PASS.
Signed-off-by: Cristian Marussi cristian.marussi@arm.com
.../arm64/signal/testcases/.gitignore | 1 + .../fake_sigreturn_bad_size_for_magic0.c | 44 +++++++++++++++++++ 2 files changed, 45 insertions(+) create mode 100644 tools/testing/selftests/arm64/signal/testcases/fake_sigreturn_bad_size_for_magic0.c
diff --git a/tools/testing/selftests/arm64/signal/testcases/.gitignore b/tools/testing/selftests/arm64/signal/testcases/.gitignore index 9ad1735e0018..66ba865b3b7e 100644 --- a/tools/testing/selftests/arm64/signal/testcases/.gitignore +++ b/tools/testing/selftests/arm64/signal/testcases/.gitignore @@ -9,3 +9,4 @@ mangle_pstate_ssbs_regs fake_sigreturn_misaligned fake_sigreturn_bad_magic fake_sigreturn_bad_size +fake_sigreturn_bad_size_for_magic0 diff --git a/tools/testing/selftests/arm64/signal/testcases/fake_sigreturn_bad_size_for_magic0.c b/tools/testing/selftests/arm64/signal/testcases/fake_sigreturn_bad_size_for_magic0.c new file mode 100644 index 000000000000..703909959473 --- /dev/null +++ b/tools/testing/selftests/arm64/signal/testcases/fake_sigreturn_bad_size_for_magic0.c @@ -0,0 +1,44 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2019 ARM Limited */
+#include <asm/sigcontext.h> +#include <ucontext.h> +#include <stdio.h>
+#include "test_signals_utils.h" +#include "testcases.h"
+struct a_sigframe sf;
+static int fake_sigreturn_bad_size_for_magic0_run(struct tdescr *td,
siginfo_t *si, ucontext_t *uc)
+{
- struct _aarch64_ctx *head = GET_SF_RESV_HEAD(sf);
- /* just to fill the ucontext_t with something real */
- if (!get_current_context(td, &sf.uc))
return 1;
- /*
* Jump to the free slot...we must preserve existing
* magics like fpsimd in order no to SEGV anyway
*/
- head = get_terminator(head, GET_SF_RESV_SIZE(sf), NULL);
- if (head) {
head->magic = 0;
head->size = 256;
What if this overruns the signal frame? We want to know that the kernel is rejecting the terminator because its size is wrong, not because the size overruns the frame or is invalid is some other way.
For example, if the kernel code were refactored to check that the record fits in the frame first, before looking at the magic, then this test could pass for the wrong reason.
[...]
Cheers ---Dave
Hi
On 6/21/19 11:36 AM, Dave Martin wrote:
On Thu, Jun 13, 2019 at 12:13:34PM +0100, Cristian Marussi wrote:
Added a simple fake_sigreturn testcase which builds a ucontext_t with a badly sized magic0 header and place it onto the stack. Expects a SIGSEGV on test PASS.
Signed-off-by: Cristian Marussi cristian.marussi@arm.com
.../arm64/signal/testcases/.gitignore | 1 + .../fake_sigreturn_bad_size_for_magic0.c | 44 +++++++++++++++++++ 2 files changed, 45 insertions(+) create mode 100644 tools/testing/selftests/arm64/signal/testcases/fake_sigreturn_bad_size_for_magic0.c
diff --git a/tools/testing/selftests/arm64/signal/testcases/.gitignore b/tools/testing/selftests/arm64/signal/testcases/.gitignore index 9ad1735e0018..66ba865b3b7e 100644 --- a/tools/testing/selftests/arm64/signal/testcases/.gitignore +++ b/tools/testing/selftests/arm64/signal/testcases/.gitignore @@ -9,3 +9,4 @@ mangle_pstate_ssbs_regs fake_sigreturn_misaligned fake_sigreturn_bad_magic fake_sigreturn_bad_size +fake_sigreturn_bad_size_for_magic0 diff --git a/tools/testing/selftests/arm64/signal/testcases/fake_sigreturn_bad_size_for_magic0.c b/tools/testing/selftests/arm64/signal/testcases/fake_sigreturn_bad_size_for_magic0.c new file mode 100644 index 000000000000..703909959473 --- /dev/null +++ b/tools/testing/selftests/arm64/signal/testcases/fake_sigreturn_bad_size_for_magic0.c @@ -0,0 +1,44 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2019 ARM Limited */
+#include <asm/sigcontext.h> +#include <ucontext.h> +#include <stdio.h>
+#include "test_signals_utils.h" +#include "testcases.h"
+struct a_sigframe sf;
+static int fake_sigreturn_bad_size_for_magic0_run(struct tdescr *td,
siginfo_t *si, ucontext_t *uc)
+{
- struct _aarch64_ctx *head = GET_SF_RESV_HEAD(sf);
- /* just to fill the ucontext_t with something real */
- if (!get_current_context(td, &sf.uc))
return 1;
- /*
* Jump to the free slot...we must preserve existing
* magics like fpsimd in order no to SEGV anyway
*/
- head = get_terminator(head, GET_SF_RESV_SIZE(sf), NULL);
- if (head) {
head->magic = 0;
head->size = 256;
What if this overruns the signal frame? We want to know that the kernel is rejecting the terminator because its size is wrong, not because the size overruns the frame or is invalid is some other way.
For example, if the kernel code were refactored to check that the record fits in the frame first, before looking at the magic, then this test could pass for the wrong reason.
Right. Fixed in V2 to account of available space and avoding overrun (throwing away EXTRA if neeeded): moreover bad size is now 16 aligned to avoid crashing out in size % 16 chcecks
Thanks
Cristian
[...]
Cheers ---Dave
Added a simple fake_sigreturn testcase which builds a ucontext_t with the whole __reserved area overflowed with ESR_MAGIC headers and place it onto the stack. Expects a SIGSEGV on test PASS.
Signed-off-by: Cristian Marussi cristian.marussi@arm.com --- .../arm64/signal/testcases/.gitignore | 1 + .../fake_sigreturn_overflow_reserved.c | 48 +++++++++++++++++++ 2 files changed, 49 insertions(+) create mode 100644 tools/testing/selftests/arm64/signal/testcases/fake_sigreturn_overflow_reserved.c
diff --git a/tools/testing/selftests/arm64/signal/testcases/.gitignore b/tools/testing/selftests/arm64/signal/testcases/.gitignore index 66ba865b3b7e..69a18391c220 100644 --- a/tools/testing/selftests/arm64/signal/testcases/.gitignore +++ b/tools/testing/selftests/arm64/signal/testcases/.gitignore @@ -10,3 +10,4 @@ fake_sigreturn_misaligned fake_sigreturn_bad_magic fake_sigreturn_bad_size fake_sigreturn_bad_size_for_magic0 +fake_sigreturn_overflow_reserved diff --git a/tools/testing/selftests/arm64/signal/testcases/fake_sigreturn_overflow_reserved.c b/tools/testing/selftests/arm64/signal/testcases/fake_sigreturn_overflow_reserved.c new file mode 100644 index 000000000000..eba109453a65 --- /dev/null +++ b/tools/testing/selftests/arm64/signal/testcases/fake_sigreturn_overflow_reserved.c @@ -0,0 +1,48 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2019 ARM Limited */ + +#include <asm/sigcontext.h> +#include <ucontext.h> +#include <stdio.h> + +#include "test_signals_utils.h" +#include "testcases.h" + +struct a_sigframe sf; + +static int fake_sigreturn_overflow_reserved_run(struct tdescr *td, + siginfo_t *si, + ucontext_t *uc) +{ + size_t offset = 0; + struct _aarch64_ctx *head = GET_SF_RESV_HEAD(sf); + + /* just to fill the ucontext_t with something real */ + if (!get_current_context(td, &sf.uc)) + return 1; + + /* find the terminator, preserving existig headers */ + head = get_terminator(head, GET_SF_RESV_SIZE(sf), &offset); + if (head) { + /* fill the __reserved area till the end */ + do { + head->magic = ESR_MAGIC; + head->size = sizeof(struct esr_context); + offset += head->size; + head = GET_RESV_NEXT_HEAD(head); + } while (offset < GET_SF_RESV_SIZE(sf)); + + ASSERT_BAD_CONTEXT(&sf.uc); + fake_sigreturn(&sf, sizeof(sf), 16); + } + + return 1; +} + +struct tdescr tde = { + .name = "FAKE_SIGRETURN_OVERFLOW_RESERVED", + .descr = "Triggers a fake sigreturn with an overflowed sigframe", + .sig_ok = SIGSEGV, + .timeout = 3, + .run = fake_sigreturn_overflow_reserved_run, +};
On Thu, Jun 13, 2019 at 12:13:35PM +0100, Cristian Marussi wrote:
Added a simple fake_sigreturn testcase which builds a ucontext_t with the whole __reserved area overflowed with ESR_MAGIC headers and place it onto the stack. Expects a SIGSEGV on test PASS.
Signed-off-by: Cristian Marussi cristian.marussi@arm.com
.../arm64/signal/testcases/.gitignore | 1 + .../fake_sigreturn_overflow_reserved.c | 48 +++++++++++++++++++ 2 files changed, 49 insertions(+) create mode 100644 tools/testing/selftests/arm64/signal/testcases/fake_sigreturn_overflow_reserved.c
diff --git a/tools/testing/selftests/arm64/signal/testcases/.gitignore b/tools/testing/selftests/arm64/signal/testcases/.gitignore index 66ba865b3b7e..69a18391c220 100644 --- a/tools/testing/selftests/arm64/signal/testcases/.gitignore +++ b/tools/testing/selftests/arm64/signal/testcases/.gitignore @@ -10,3 +10,4 @@ fake_sigreturn_misaligned fake_sigreturn_bad_magic fake_sigreturn_bad_size fake_sigreturn_bad_size_for_magic0 +fake_sigreturn_overflow_reserved diff --git a/tools/testing/selftests/arm64/signal/testcases/fake_sigreturn_overflow_reserved.c b/tools/testing/selftests/arm64/signal/testcases/fake_sigreturn_overflow_reserved.c new file mode 100644 index 000000000000..eba109453a65 --- /dev/null +++ b/tools/testing/selftests/arm64/signal/testcases/fake_sigreturn_overflow_reserved.c @@ -0,0 +1,48 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2019 ARM Limited */
+#include <asm/sigcontext.h> +#include <ucontext.h> +#include <stdio.h>
+#include "test_signals_utils.h" +#include "testcases.h"
+struct a_sigframe sf;
+static int fake_sigreturn_overflow_reserved_run(struct tdescr *td,
siginfo_t *si,
ucontext_t *uc)
+{
- size_t offset = 0;
- struct _aarch64_ctx *head = GET_SF_RESV_HEAD(sf);
- /* just to fill the ucontext_t with something real */
- if (!get_current_context(td, &sf.uc))
return 1;
- /* find the terminator, preserving existig headers */
- head = get_terminator(head, GET_SF_RESV_SIZE(sf), &offset);
- if (head) {
/* fill the __reserved area till the end */
do {
head->magic = ESR_MAGIC;
Comment on why this magic is chosen.
Since it is useful for test purposes to have a record that the kernel ignores completely, it might be worth dropping in comment in parse_user_sigframe() so that we don't add stricter checks by accident.
We could alternatively add a special-purpose dummy record with its own magic. This might be better than abusing esr_context ... but I could be persuaded either way.
head->size = sizeof(struct esr_context);
Can we end up with a hole at the end of the frame that is smaller than esr_context and so can't be filled properly? If not, why not?
offset += head->size;
head = GET_RESV_NEXT_HEAD(head);
} while (offset < GET_SF_RESV_SIZE(sf));
ASSERT_BAD_CONTEXT(&sf.uc);
fake_sigreturn(&sf, sizeof(sf), 16);
We should have something that looks like a valid terminator after the end of the signal frame, so that we can be sure the overrun is detected, rather than the kernel just rejecting the garbage after the last record.
- }
- return 1;
+}
[...]
Cheers ---Dave
Hi
On 6/21/19 11:36 AM, Dave Martin wrote:
On Thu, Jun 13, 2019 at 12:13:35PM +0100, Cristian Marussi wrote:
Added a simple fake_sigreturn testcase which builds a ucontext_t with the whole __reserved area overflowed with ESR_MAGIC headers and place it onto the stack. Expects a SIGSEGV on test PASS.
Signed-off-by: Cristian Marussi cristian.marussi@arm.com
.../arm64/signal/testcases/.gitignore | 1 + .../fake_sigreturn_overflow_reserved.c | 48 +++++++++++++++++++ 2 files changed, 49 insertions(+) create mode 100644 tools/testing/selftests/arm64/signal/testcases/fake_sigreturn_overflow_reserved.c
diff --git a/tools/testing/selftests/arm64/signal/testcases/.gitignore b/tools/testing/selftests/arm64/signal/testcases/.gitignore index 66ba865b3b7e..69a18391c220 100644 --- a/tools/testing/selftests/arm64/signal/testcases/.gitignore +++ b/tools/testing/selftests/arm64/signal/testcases/.gitignore @@ -10,3 +10,4 @@ fake_sigreturn_misaligned fake_sigreturn_bad_magic fake_sigreturn_bad_size fake_sigreturn_bad_size_for_magic0 +fake_sigreturn_overflow_reserved diff --git a/tools/testing/selftests/arm64/signal/testcases/fake_sigreturn_overflow_reserved.c b/tools/testing/selftests/arm64/signal/testcases/fake_sigreturn_overflow_reserved.c new file mode 100644 index 000000000000..eba109453a65 --- /dev/null +++ b/tools/testing/selftests/arm64/signal/testcases/fake_sigreturn_overflow_reserved.c @@ -0,0 +1,48 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2019 ARM Limited */
+#include <asm/sigcontext.h> +#include <ucontext.h> +#include <stdio.h>
+#include "test_signals_utils.h" +#include "testcases.h"
+struct a_sigframe sf;
+static int fake_sigreturn_overflow_reserved_run(struct tdescr *td,
siginfo_t *si,
ucontext_t *uc)
+{
- size_t offset = 0;
- struct _aarch64_ctx *head = GET_SF_RESV_HEAD(sf);
- /* just to fill the ucontext_t with something real */
- if (!get_current_context(td, &sf.uc))
return 1;
- /* find the terminator, preserving existig headers */
- head = get_terminator(head, GET_SF_RESV_SIZE(sf), &offset);
- if (head) {
/* fill the __reserved area till the end */
do {
head->magic = ESR_MAGIC;
Comment on why this magic is chosen.
Since it is useful for test purposes to have a record that the kernel ignores completely, it might be worth dropping in comment in parse_user_sigframe() so that we don't add stricter checks by accident.
We could alternatively add a special-purpose dummy record with its own magic. This might be better than abusing esr_context ... but I could be persuaded either way.
head->size = sizeof(struct esr_context);
Can we end up with a hole at the end of the frame that is smaller than esr_context and so can't be filled properly? If not, why not?
offset += head->size;
head = GET_RESV_NEXT_HEAD(head);
} while (offset < GET_SF_RESV_SIZE(sf));
ASSERT_BAD_CONTEXT(&sf.uc);
fake_sigreturn(&sf, sizeof(sf), 16);
We should have something that looks like a valid terminator after the end of the signal frame, so that we can be sure the overrun is detected, rather than the kernel just rejecting the garbage after the last record.
This testcase issues still to be addressed. Disabled in V2...want to re-enable in V3.
Cristian
- }
- return 1;
+}
[...]
Cheers ---Dave
On Thu, Jun 13, 2019 at 12:13:22PM +0100, Cristian Marussi wrote:
Hi
this patch aims to add the initial arch-specific arm64 support to kselftest starting with signals-related test-cases. A common internal test-case layout is proposed which then it is anyway wired-up to the toplevel kselftest Makefile, so that it should be possible at the end to run it on an arm64 target in the usual way with KSFT.
~/linux# make TARGETS=arm64 kselftest
Looks like a good initial set of dests overall. There are various details that need attention, but once those are sorted out I think this will be a decent base to build more tests on in the future.
See comments on the individual patches.
[...]
Cheers ---Dave
linux-kselftest-mirror@lists.linaro.org