kselftest: arm64: fake_sigreturn_bad_magic

Add 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.

Introduce a common utility assembly trampoline function to invoke a
sigreturn while placing the provided sigframe at wanted alignment and
also an helper to make space when needed inside the sigframe reserved
area.

Reviewed-by: Dave Martin <Dave.Martin@arm.com>
Signed-off-by: Cristian Marussi <cristian.marussi@arm.com>
Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
This commit is contained in:
Cristian Marussi 2019-10-25 18:57:12 +01:00 committed by Catalin Marinas
parent 34306b05d3
commit 6c2aa42845
6 changed files with 169 additions and 1 deletions

View File

@ -28,5 +28,5 @@ clean:
# 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 testcases/testcases.c $$@.c test_signals.h test_signals_utils.h testcases/testcases.h
$(PROGS): test_signals.c test_signals_utils.c testcases/testcases.c signals.S $$@.c test_signals.h test_signals_utils.h testcases/testcases.h
$(CC) $(CFLAGS) $^ -o $@

View File

@ -0,0 +1,64 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (C) 2019 ARM Limited */
#include <asm/unistd.h>
.section .rodata, "a"
call_fmt:
.asciz "Calling sigreturn with fake sigframe sized:%zd at SP @%08lX\n"
.text
.globl fake_sigreturn
/* fake_sigreturn x0:&sigframe, x1:sigframe_size, x2:misalign_bytes */
fake_sigreturn:
stp x29, x30, [sp, #-16]!
mov x29, sp
mov x20, x0
mov x21, x1
mov x22, x2
/* create space on the stack for fake sigframe 16 bytes-aligned */
add x0, x21, x22
add x0, x0, #15
bic x0, x0, #15 /* round_up(sigframe_size + misalign_bytes, 16) */
sub sp, sp, x0
add x23, sp, x22 /* new sigframe base with misaligment if any */
ldr x0, =call_fmt
mov x1, x21
mov x2, x23
bl printf
/* memcpy the provided content, while still keeping SP aligned */
mov x0, x23
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 sure no bad fatal signal has been raised till now
* for unrelated reasons, so we should consider the possibly observed
* fatal signal like SEGV coming from Kernel restore_sigframe() and
* triggered as expected from our test-case.
* For simplicity this assumes that current field 'token' is laid out
* as first in struct tdescr
*/
ldr x0, current
str x23, [x0]
/* finally move SP to misaligned address...if any requested */
mov sp, x23
mov x8, #__NR_rt_sigreturn
svc #0
/*
* Above sigreturn should not return...looping here leads to a timeout
* and ensure proper and clean test failure, instead of jumping around
* on a potentially corrupted stack.
*/
b .

View File

@ -115,4 +115,6 @@ static __always_inline bool get_current_context(struct tdescr *td,
return td->live_uc_valid;
}
int fake_sigreturn(void *sigframe, size_t sz, int misalign_bytes);
#endif

View File

@ -0,0 +1,52 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2019 ARM Limited
*
* Place a fake sigframe on the stack including a BAD Unknown magic
* record: on sigreturn Kernel must spot this attempt and the test
* case is expected to be terminated via SEGV.
*/
#include <signal.h>
#include <ucontext.h>
#include "test_signals_utils.h"
#include "testcases.h"
struct fake_sigframe sf;
static int fake_sigreturn_bad_magic_run(struct tdescr *td,
siginfo_t *si, ucontext_t *uc)
{
struct _aarch64_ctx *shead = GET_SF_RESV_HEAD(sf), *head;
/* just to fill the ucontext_t with something real */
if (!get_current_context(td, &sf.uc))
return 1;
/* need at least 2*HDR_SZ space: KSFT_BAD_MAGIC + terminator. */
head = get_starting_head(shead, HDR_SZ * 2, GET_SF_RESV_SIZE(sf), NULL);
if (!head)
return 0;
/*
* use a well known NON existent bad magic...something
* we should pretty sure won't be ever defined in Kernel
*/
head->magic = KSFT_BAD_MAGIC;
head->size = HDR_SZ;
write_terminator_record(GET_RESV_NEXT_HEAD(head));
ASSERT_BAD_CONTEXT(&sf.uc);
fake_sigreturn(&sf, sizeof(sf), 0);
return 1;
}
struct tdescr tde = {
.name = "FAKE_SIGRETURN_BAD_MAGIC",
.descr = "Trigger a sigreturn with a sigframe with a bad magic",
.sig_ok = SIGSEGV,
.timeout = 3,
.run = fake_sigreturn_bad_magic_run,
};

View File

@ -148,3 +148,49 @@ bool validate_reserved(ucontext_t *uc, size_t resv_sz, char **err)
return true;
}
/*
* This function walks through the records inside the provided reserved area
* trying to find enough space to fit @need_sz bytes: if not enough space is
* available and an extra_context record is present, it throws away the
* extra_context record.
*
* It returns a pointer to a new header where it is possible to start storing
* our need_sz bytes.
*
* @shead: points to the start of reserved area
* @need_sz: needed bytes
* @resv_sz: reserved area size in bytes
* @offset: if not null, this will be filled with the offset of the return
* head pointer from @shead
*
* @return: pointer to a new head where to start storing need_sz bytes, or
* NULL if space could not be made available.
*/
struct _aarch64_ctx *get_starting_head(struct _aarch64_ctx *shead,
size_t need_sz, size_t resv_sz,
size_t *offset)
{
size_t offs = 0;
struct _aarch64_ctx *head;
head = get_terminator(shead, resv_sz, &offs);
/* not found a terminator...no need to update offset if any */
if (!head)
return head;
if (resv_sz - offs < need_sz) {
fprintf(stderr, "Low on space:%zd. Discarding extra_context.\n",
resv_sz - offs);
head = get_header(shead, EXTRA_MAGIC, resv_sz, &offs);
if (!head || resv_sz - offs < need_sz) {
fprintf(stderr,
"Failed to reclaim space on sigframe.\n");
return NULL;
}
}
fprintf(stderr, "Available space:%zd\n", resv_sz - offs);
if (offset)
*offset = offs;
return head;
}

View File

@ -97,4 +97,8 @@ static inline void write_terminator_record(struct _aarch64_ctx *tail)
tail->size = 0;
}
}
struct _aarch64_ctx *get_starting_head(struct _aarch64_ctx *shead,
size_t need_sz, size_t resv_sz,
size_t *offset);
#endif