Path: blob/master/tools/testing/selftests/arm64/gcs/basic-gcs.c
29270 views
// SPDX-License-Identifier: GPL-2.0-only1/*2* Copyright (C) 2023 ARM Limited.3*/45#include <limits.h>6#include <stdbool.h>78#include <linux/prctl.h>910#include <sys/mman.h>11#include <asm/mman.h>12#include <asm/hwcap.h>13#include <linux/sched.h>1415#include "kselftest.h"16#include "gcs-util.h"1718/* nolibc doesn't have sysconf(), just hard code the maximum */19static size_t page_size = 65536;2021static __attribute__((noinline)) void valid_gcs_function(void)22{23/* Do something the compiler can't optimise out */24my_syscall1(__NR_prctl, PR_SVE_GET_VL);25}2627static inline int gcs_set_status(unsigned long mode)28{29bool enabling = mode & PR_SHADOW_STACK_ENABLE;30int ret;31unsigned long new_mode;3233/*34* The prctl takes 1 argument but we need to ensure that the35* other 3 values passed in registers to the syscall are zero36* since the kernel validates them.37*/38ret = my_syscall5(__NR_prctl, PR_SET_SHADOW_STACK_STATUS, mode,390, 0, 0);4041if (ret == 0) {42ret = my_syscall5(__NR_prctl, PR_GET_SHADOW_STACK_STATUS,43&new_mode, 0, 0, 0);44if (ret == 0) {45if (new_mode != mode) {46ksft_print_msg("Mode set to %lx not %lx\n",47new_mode, mode);48ret = -EINVAL;49}50} else {51ksft_print_msg("Failed to validate mode: %d\n", ret);52}5354if (enabling != chkfeat_gcs()) {55ksft_print_msg("%senabled by prctl but %senabled in CHKFEAT\n",56enabling ? "" : "not ",57chkfeat_gcs() ? "" : "not ");58ret = -EINVAL;59}60}6162return ret;63}6465/* Try to read the status */66static bool read_status(void)67{68unsigned long state;69int ret;7071ret = my_syscall5(__NR_prctl, PR_GET_SHADOW_STACK_STATUS,72&state, 0, 0, 0);73if (ret != 0) {74ksft_print_msg("Failed to read state: %d\n", ret);75return false;76}7778return state & PR_SHADOW_STACK_ENABLE;79}8081/* Just a straight enable */82static bool base_enable(void)83{84int ret;8586ret = gcs_set_status(PR_SHADOW_STACK_ENABLE);87if (ret) {88ksft_print_msg("PR_SHADOW_STACK_ENABLE failed %d\n", ret);89return false;90}9192return true;93}9495/* Check we can read GCSPR_EL0 when GCS is enabled */96static bool read_gcspr_el0(void)97{98unsigned long *gcspr_el0;99100ksft_print_msg("GET GCSPR\n");101gcspr_el0 = get_gcspr();102ksft_print_msg("GCSPR_EL0 is %p\n", gcspr_el0);103104return true;105}106107/* Also allow writes to stack */108static bool enable_writeable(void)109{110int ret;111112ret = gcs_set_status(PR_SHADOW_STACK_ENABLE | PR_SHADOW_STACK_WRITE);113if (ret) {114ksft_print_msg("PR_SHADOW_STACK_ENABLE writeable failed: %d\n", ret);115return false;116}117118ret = gcs_set_status(PR_SHADOW_STACK_ENABLE);119if (ret) {120ksft_print_msg("failed to restore plain enable %d\n", ret);121return false;122}123124return true;125}126127/* Also allow writes to stack */128static bool enable_push_pop(void)129{130int ret;131132ret = gcs_set_status(PR_SHADOW_STACK_ENABLE | PR_SHADOW_STACK_PUSH);133if (ret) {134ksft_print_msg("PR_SHADOW_STACK_ENABLE with push failed: %d\n",135ret);136return false;137}138139ret = gcs_set_status(PR_SHADOW_STACK_ENABLE);140if (ret) {141ksft_print_msg("failed to restore plain enable %d\n", ret);142return false;143}144145return true;146}147148/* Enable GCS and allow everything */149static bool enable_all(void)150{151int ret;152153ret = gcs_set_status(PR_SHADOW_STACK_ENABLE | PR_SHADOW_STACK_PUSH |154PR_SHADOW_STACK_WRITE);155if (ret) {156ksft_print_msg("PR_SHADOW_STACK_ENABLE with everything failed: %d\n",157ret);158return false;159}160161ret = gcs_set_status(PR_SHADOW_STACK_ENABLE);162if (ret) {163ksft_print_msg("failed to restore plain enable %d\n", ret);164return false;165}166167return true;168}169170static bool enable_invalid(void)171{172int ret = gcs_set_status(ULONG_MAX);173if (ret == 0) {174ksft_print_msg("GCS_SET_STATUS %lx succeeded\n", ULONG_MAX);175return false;176}177178return true;179}180181/* Map a GCS */182static bool map_guarded_stack(void)183{184int ret;185uint64_t *buf;186uint64_t expected_cap;187int elem;188bool pass = true;189190buf = (void *)my_syscall3(__NR_map_shadow_stack, 0, page_size,191SHADOW_STACK_SET_MARKER |192SHADOW_STACK_SET_TOKEN);193if (buf == MAP_FAILED) {194ksft_print_msg("Failed to map %lu byte GCS: %d\n",195page_size, errno);196return false;197}198ksft_print_msg("Mapped GCS at %p-%p\n", buf,199(void *)((uint64_t)buf + page_size));200201/* The top of the newly allocated region should be 0 */202elem = (page_size / sizeof(uint64_t)) - 1;203if (buf[elem]) {204ksft_print_msg("Last entry is 0x%llx not 0x0\n", buf[elem]);205pass = false;206}207208/* Then a valid cap token */209elem--;210expected_cap = ((uint64_t)buf + page_size - 16);211expected_cap &= GCS_CAP_ADDR_MASK;212expected_cap |= GCS_CAP_VALID_TOKEN;213if (buf[elem] != expected_cap) {214ksft_print_msg("Cap entry is 0x%llx not 0x%llx\n",215buf[elem], expected_cap);216pass = false;217}218ksft_print_msg("cap token is 0x%llx\n", buf[elem]);219220/* The rest should be zeros */221for (elem = 0; elem < page_size / sizeof(uint64_t) - 2; elem++) {222if (!buf[elem])223continue;224ksft_print_msg("GCS slot %d is 0x%llx not 0x0\n",225elem, buf[elem]);226pass = false;227}228229ret = munmap(buf, page_size);230if (ret != 0) {231ksft_print_msg("Failed to unmap %ld byte GCS: %d\n",232page_size, errno);233pass = false;234}235236return pass;237}238239/* A fork()ed process can run */240static bool test_fork(void)241{242unsigned long child_mode;243int ret, status;244pid_t pid;245bool pass = true;246247pid = fork();248if (pid == -1) {249ksft_print_msg("fork() failed: %d\n", errno);250pass = false;251goto out;252}253if (pid == 0) {254/* In child, make sure we can call a function, read255* the GCS pointer and status and then exit */256valid_gcs_function();257get_gcspr();258259ret = my_syscall5(__NR_prctl, PR_GET_SHADOW_STACK_STATUS,260&child_mode, 0, 0, 0);261if (ret == 0 && !(child_mode & PR_SHADOW_STACK_ENABLE)) {262ksft_print_msg("GCS not enabled in child\n");263ret = -EINVAL;264}265266exit(ret);267}268269/*270* In parent, check we can still do function calls then block271* for the child.272*/273valid_gcs_function();274275ksft_print_msg("Waiting for child %d\n", pid);276277ret = waitpid(pid, &status, 0);278if (ret == -1) {279ksft_print_msg("Failed to wait for child: %d\n",280errno);281return false;282}283284if (!WIFEXITED(status)) {285ksft_print_msg("Child exited due to signal %d\n",286WTERMSIG(status));287pass = false;288} else {289if (WEXITSTATUS(status)) {290ksft_print_msg("Child exited with status %d\n",291WEXITSTATUS(status));292pass = false;293}294}295296out:297298return pass;299}300301/* A vfork()ed process can run and exit */302static bool test_vfork(void)303{304unsigned long child_mode;305int ret, status;306pid_t pid;307bool pass = true;308309pid = vfork();310if (pid == -1) {311ksft_print_msg("vfork() failed: %d\n", errno);312pass = false;313goto out;314}315if (pid == 0) {316/*317* In child, make sure we can call a function, read318* the GCS pointer and status and then exit.319*/320valid_gcs_function();321get_gcspr();322323ret = my_syscall5(__NR_prctl, PR_GET_SHADOW_STACK_STATUS,324&child_mode, 0, 0, 0);325if (ret == 0 && !(child_mode & PR_SHADOW_STACK_ENABLE)) {326ksft_print_msg("GCS not enabled in child\n");327ret = EXIT_FAILURE;328}329330_exit(ret);331}332333/*334* In parent, check we can still do function calls then check335* on the child.336*/337valid_gcs_function();338339ksft_print_msg("Waiting for child %d\n", pid);340341ret = waitpid(pid, &status, 0);342if (ret == -1) {343ksft_print_msg("Failed to wait for child: %d\n",344errno);345return false;346}347348if (!WIFEXITED(status)) {349ksft_print_msg("Child exited due to signal %d\n",350WTERMSIG(status));351pass = false;352} else if (WEXITSTATUS(status)) {353ksft_print_msg("Child exited with status %d\n",354WEXITSTATUS(status));355pass = false;356}357358out:359360return pass;361}362363typedef bool (*gcs_test)(void);364365static struct {366char *name;367gcs_test test;368bool needs_enable;369} tests[] = {370{ "read_status", read_status },371{ "base_enable", base_enable, true },372{ "read_gcspr_el0", read_gcspr_el0 },373{ "enable_writeable", enable_writeable, true },374{ "enable_push_pop", enable_push_pop, true },375{ "enable_all", enable_all, true },376{ "enable_invalid", enable_invalid, true },377{ "map_guarded_stack", map_guarded_stack },378{ "fork", test_fork },379{ "vfork", test_vfork },380};381382int main(void)383{384int i, ret;385unsigned long gcs_mode;386387ksft_print_header();388389if (!(getauxval(AT_HWCAP) & HWCAP_GCS))390ksft_exit_skip("SKIP GCS not supported\n");391392ret = my_syscall5(__NR_prctl, PR_GET_SHADOW_STACK_STATUS,393&gcs_mode, 0, 0, 0);394if (ret != 0)395ksft_exit_fail_msg("Failed to read GCS state: %d\n", ret);396397if (!(gcs_mode & PR_SHADOW_STACK_ENABLE)) {398gcs_mode = PR_SHADOW_STACK_ENABLE;399ret = my_syscall5(__NR_prctl, PR_SET_SHADOW_STACK_STATUS,400gcs_mode, 0, 0, 0);401if (ret != 0)402ksft_exit_fail_msg("Failed to enable GCS: %d\n", ret);403}404405ksft_set_plan(ARRAY_SIZE(tests));406407for (i = 0; i < ARRAY_SIZE(tests); i++) {408ksft_test_result((*tests[i].test)(), "%s\n", tests[i].name);409}410411/* One last test: disable GCS, we can do this one time */412ret = my_syscall5(__NR_prctl, PR_SET_SHADOW_STACK_STATUS, 0, 0, 0, 0);413if (ret != 0)414ksft_print_msg("Failed to disable GCS: %d\n", ret);415416ksft_finished();417418return 0;419}420421422