Re: [PATCH 02/13] kselftest: arm64: adds arm64/signal support code

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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@xxxxxxx>
>> ---
>>  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:
>> + *
>> + * 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 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@xxxxxxxxxxxxxxxxxxx
>> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel





[Index of Archives]     [Linux Wireless]     [Linux Kernel]     [ATH6KL]     [Linux Bluetooth]     [Linux Netdev]     [Kernel Newbies]     [Share Photos]     [IDE]     [Security]     [Git]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux ATA RAID]     [Samba]     [Device Mapper]

  Powered by Linux