Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

kselftest/arm64: Add GCS signal tests

Do some testing of the signal handling for GCS, checking that a GCS
frame has the expected information in it and that the expected signals
are delivered with invalid operations.

Reviewed-by: Thiago Jung Bauermann <thiago.bauermann@linaro.org>
Tested-by: Thiago Jung Bauermann <thiago.bauermann@linaro.org>
Signed-off-by: Mark Brown <broonie@kernel.org>
Link: https://lore.kernel.org/r/20241001-arm64-gcs-v13-37-222b78d87eee@kernel.org
Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>

authored by

Mark Brown and committed by
Catalin Marinas
794b64ca 58d69a3e

+228
+1
tools/testing/selftests/arm64/signal/.gitignore
··· 3 3 fake_sigreturn_* 4 4 fpmr_* 5 5 poe_* 6 + gcs_* 6 7 sme_* 7 8 ssve_* 8 9 sve_*
+10
tools/testing/selftests/arm64/signal/test_signals_utils.h
··· 6 6 7 7 #include <assert.h> 8 8 #include <stdio.h> 9 + #include <stdint.h> 9 10 #include <string.h> 10 11 11 12 #include <linux/compiler.h> ··· 47 46 ); \ 48 47 _arg1; \ 49 48 }) 49 + 50 + static inline __attribute__((always_inline)) uint64_t get_gcspr_el0(void) 51 + { 52 + uint64_t val; 53 + 54 + asm volatile("mrs %0, S3_3_C2_C5_1" : "=r" (val)); 55 + 56 + return val; 57 + } 50 58 51 59 static inline bool feats_ok(struct tdescr *td) 52 60 {
+62
tools/testing/selftests/arm64/signal/testcases/gcs_exception_fault.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Copyright (C) 2023 ARM Limited 4 + */ 5 + 6 + #include <errno.h> 7 + #include <signal.h> 8 + #include <unistd.h> 9 + 10 + #include <sys/mman.h> 11 + #include <sys/prctl.h> 12 + 13 + #include "test_signals_utils.h" 14 + #include "testcases.h" 15 + 16 + /* 17 + * We should get this from asm/siginfo.h but the testsuite is being 18 + * clever with redefining siginfo_t. 19 + */ 20 + #ifndef SEGV_CPERR 21 + #define SEGV_CPERR 10 22 + #endif 23 + 24 + static inline void gcsss1(uint64_t Xt) 25 + { 26 + asm volatile ( 27 + "sys #3, C7, C7, #2, %0\n" 28 + : 29 + : "rZ" (Xt) 30 + : "memory"); 31 + } 32 + 33 + static int gcs_op_fault_trigger(struct tdescr *td) 34 + { 35 + /* 36 + * The slot below our current GCS should be in a valid GCS but 37 + * must not have a valid cap in it. 38 + */ 39 + gcsss1(get_gcspr_el0() - 8); 40 + 41 + return 0; 42 + } 43 + 44 + static int gcs_op_fault_signal(struct tdescr *td, siginfo_t *si, 45 + ucontext_t *uc) 46 + { 47 + ASSERT_GOOD_CONTEXT(uc); 48 + 49 + return 1; 50 + } 51 + 52 + struct tdescr tde = { 53 + .name = "Invalid GCS operation", 54 + .descr = "An invalid GCS operation generates the expected signal", 55 + .feats_required = FEAT_GCS, 56 + .timeout = 3, 57 + .sig_ok = SIGSEGV, 58 + .sig_ok_code = SEGV_CPERR, 59 + .sanity_disabled = true, 60 + .trigger = gcs_op_fault_trigger, 61 + .run = gcs_op_fault_signal, 62 + };
+88
tools/testing/selftests/arm64/signal/testcases/gcs_frame.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Copyright (C) 2023 ARM Limited 4 + */ 5 + 6 + #include <signal.h> 7 + #include <ucontext.h> 8 + #include <sys/prctl.h> 9 + 10 + #include "test_signals_utils.h" 11 + #include "testcases.h" 12 + 13 + static union { 14 + ucontext_t uc; 15 + char buf[1024 * 64]; 16 + } context; 17 + 18 + static int gcs_regs(struct tdescr *td, siginfo_t *si, ucontext_t *uc) 19 + { 20 + size_t offset; 21 + struct _aarch64_ctx *head = GET_BUF_RESV_HEAD(context); 22 + struct gcs_context *gcs; 23 + unsigned long expected, gcspr; 24 + uint64_t *u64_val; 25 + int ret; 26 + 27 + ret = prctl(PR_GET_SHADOW_STACK_STATUS, &expected, 0, 0, 0); 28 + if (ret != 0) { 29 + fprintf(stderr, "Unable to query GCS status\n"); 30 + return 1; 31 + } 32 + 33 + /* We expect a cap to be added to the GCS in the signal frame */ 34 + gcspr = get_gcspr_el0(); 35 + gcspr -= 8; 36 + fprintf(stderr, "Expecting GCSPR_EL0 %lx\n", gcspr); 37 + 38 + if (!get_current_context(td, &context.uc, sizeof(context))) { 39 + fprintf(stderr, "Failed getting context\n"); 40 + return 1; 41 + } 42 + 43 + /* Ensure that the signal restore token was consumed */ 44 + u64_val = (uint64_t *)get_gcspr_el0() + 1; 45 + if (*u64_val) { 46 + fprintf(stderr, "GCS value at %p is %lx not 0\n", 47 + u64_val, *u64_val); 48 + return 1; 49 + } 50 + 51 + fprintf(stderr, "Got context\n"); 52 + 53 + head = get_header(head, GCS_MAGIC, GET_BUF_RESV_SIZE(context), 54 + &offset); 55 + if (!head) { 56 + fprintf(stderr, "No GCS context\n"); 57 + return 1; 58 + } 59 + 60 + gcs = (struct gcs_context *)head; 61 + 62 + /* Basic size validation is done in get_current_context() */ 63 + 64 + if (gcs->features_enabled != expected) { 65 + fprintf(stderr, "Features enabled %llx but expected %lx\n", 66 + gcs->features_enabled, expected); 67 + return 1; 68 + } 69 + 70 + if (gcs->gcspr != gcspr) { 71 + fprintf(stderr, "Got GCSPR %llx but expected %lx\n", 72 + gcs->gcspr, gcspr); 73 + return 1; 74 + } 75 + 76 + fprintf(stderr, "GCS context validated\n"); 77 + td->pass = 1; 78 + 79 + return 0; 80 + } 81 + 82 + struct tdescr tde = { 83 + .name = "GCS basics", 84 + .descr = "Validate a GCS signal context", 85 + .feats_required = FEAT_GCS, 86 + .timeout = 3, 87 + .run = gcs_regs, 88 + };
+67
tools/testing/selftests/arm64/signal/testcases/gcs_write_fault.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Copyright (C) 2023 ARM Limited 4 + */ 5 + 6 + #include <errno.h> 7 + #include <signal.h> 8 + #include <unistd.h> 9 + 10 + #include <sys/mman.h> 11 + #include <sys/prctl.h> 12 + 13 + #include "test_signals_utils.h" 14 + #include "testcases.h" 15 + 16 + static uint64_t *gcs_page; 17 + 18 + #ifndef __NR_map_shadow_stack 19 + #define __NR_map_shadow_stack 453 20 + #endif 21 + 22 + static bool alloc_gcs(struct tdescr *td) 23 + { 24 + long page_size = sysconf(_SC_PAGE_SIZE); 25 + 26 + gcs_page = (void *)syscall(__NR_map_shadow_stack, 0, 27 + page_size, 0); 28 + if (gcs_page == MAP_FAILED) { 29 + fprintf(stderr, "Failed to map %ld byte GCS: %d\n", 30 + page_size, errno); 31 + return false; 32 + } 33 + 34 + return true; 35 + } 36 + 37 + static int gcs_write_fault_trigger(struct tdescr *td) 38 + { 39 + /* Verify that the page is readable (ie, not completely unmapped) */ 40 + fprintf(stderr, "Read value 0x%lx\n", gcs_page[0]); 41 + 42 + /* A regular write should trigger a fault */ 43 + gcs_page[0] = EINVAL; 44 + 45 + return 0; 46 + } 47 + 48 + static int gcs_write_fault_signal(struct tdescr *td, siginfo_t *si, 49 + ucontext_t *uc) 50 + { 51 + ASSERT_GOOD_CONTEXT(uc); 52 + 53 + return 1; 54 + } 55 + 56 + 57 + struct tdescr tde = { 58 + .name = "GCS write fault", 59 + .descr = "Normal writes to a GCS segfault", 60 + .feats_required = FEAT_GCS, 61 + .timeout = 3, 62 + .sig_ok = SIGSEGV, 63 + .sanity_disabled = true, 64 + .init = alloc_gcs, 65 + .trigger = gcs_write_fault_trigger, 66 + .run = gcs_write_fault_signal, 67 + };