Path: blob/master/tools/testing/selftests/arm64/gcs/basic-gcs.c
26292 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 <linux/sched.h>1314#include "kselftest.h"15#include "gcs-util.h"1617/* nolibc doesn't have sysconf(), just hard code the maximum */18static size_t page_size = 65536;1920static __attribute__((noinline)) void valid_gcs_function(void)21{22/* Do something the compiler can't optimise out */23my_syscall1(__NR_prctl, PR_SVE_GET_VL);24}2526static inline int gcs_set_status(unsigned long mode)27{28bool enabling = mode & PR_SHADOW_STACK_ENABLE;29int ret;30unsigned long new_mode;3132/*33* The prctl takes 1 argument but we need to ensure that the34* other 3 values passed in registers to the syscall are zero35* since the kernel validates them.36*/37ret = my_syscall5(__NR_prctl, PR_SET_SHADOW_STACK_STATUS, mode,380, 0, 0);3940if (ret == 0) {41ret = my_syscall5(__NR_prctl, PR_GET_SHADOW_STACK_STATUS,42&new_mode, 0, 0, 0);43if (ret == 0) {44if (new_mode != mode) {45ksft_print_msg("Mode set to %lx not %lx\n",46new_mode, mode);47ret = -EINVAL;48}49} else {50ksft_print_msg("Failed to validate mode: %d\n", ret);51}5253if (enabling != chkfeat_gcs()) {54ksft_print_msg("%senabled by prctl but %senabled in CHKFEAT\n",55enabling ? "" : "not ",56chkfeat_gcs() ? "" : "not ");57ret = -EINVAL;58}59}6061return ret;62}6364/* Try to read the status */65static bool read_status(void)66{67unsigned long state;68int ret;6970ret = my_syscall5(__NR_prctl, PR_GET_SHADOW_STACK_STATUS,71&state, 0, 0, 0);72if (ret != 0) {73ksft_print_msg("Failed to read state: %d\n", ret);74return false;75}7677return state & PR_SHADOW_STACK_ENABLE;78}7980/* Just a straight enable */81static bool base_enable(void)82{83int ret;8485ret = gcs_set_status(PR_SHADOW_STACK_ENABLE);86if (ret) {87ksft_print_msg("PR_SHADOW_STACK_ENABLE failed %d\n", ret);88return false;89}9091return true;92}9394/* Check we can read GCSPR_EL0 when GCS is enabled */95static bool read_gcspr_el0(void)96{97unsigned long *gcspr_el0;9899ksft_print_msg("GET GCSPR\n");100gcspr_el0 = get_gcspr();101ksft_print_msg("GCSPR_EL0 is %p\n", gcspr_el0);102103return true;104}105106/* Also allow writes to stack */107static bool enable_writeable(void)108{109int ret;110111ret = gcs_set_status(PR_SHADOW_STACK_ENABLE | PR_SHADOW_STACK_WRITE);112if (ret) {113ksft_print_msg("PR_SHADOW_STACK_ENABLE writeable failed: %d\n", ret);114return false;115}116117ret = gcs_set_status(PR_SHADOW_STACK_ENABLE);118if (ret) {119ksft_print_msg("failed to restore plain enable %d\n", ret);120return false;121}122123return true;124}125126/* Also allow writes to stack */127static bool enable_push_pop(void)128{129int ret;130131ret = gcs_set_status(PR_SHADOW_STACK_ENABLE | PR_SHADOW_STACK_PUSH);132if (ret) {133ksft_print_msg("PR_SHADOW_STACK_ENABLE with push failed: %d\n",134ret);135return false;136}137138ret = gcs_set_status(PR_SHADOW_STACK_ENABLE);139if (ret) {140ksft_print_msg("failed to restore plain enable %d\n", ret);141return false;142}143144return true;145}146147/* Enable GCS and allow everything */148static bool enable_all(void)149{150int ret;151152ret = gcs_set_status(PR_SHADOW_STACK_ENABLE | PR_SHADOW_STACK_PUSH |153PR_SHADOW_STACK_WRITE);154if (ret) {155ksft_print_msg("PR_SHADOW_STACK_ENABLE with everything failed: %d\n",156ret);157return false;158}159160ret = gcs_set_status(PR_SHADOW_STACK_ENABLE);161if (ret) {162ksft_print_msg("failed to restore plain enable %d\n", ret);163return false;164}165166return true;167}168169static bool enable_invalid(void)170{171int ret = gcs_set_status(ULONG_MAX);172if (ret == 0) {173ksft_print_msg("GCS_SET_STATUS %lx succeeded\n", ULONG_MAX);174return false;175}176177return true;178}179180/* Map a GCS */181static bool map_guarded_stack(void)182{183int ret;184uint64_t *buf;185uint64_t expected_cap;186int elem;187bool pass = true;188189buf = (void *)my_syscall3(__NR_map_shadow_stack, 0, page_size,190SHADOW_STACK_SET_MARKER |191SHADOW_STACK_SET_TOKEN);192if (buf == MAP_FAILED) {193ksft_print_msg("Failed to map %lu byte GCS: %d\n",194page_size, errno);195return false;196}197ksft_print_msg("Mapped GCS at %p-%p\n", buf,198(void *)((uint64_t)buf + page_size));199200/* The top of the newly allocated region should be 0 */201elem = (page_size / sizeof(uint64_t)) - 1;202if (buf[elem]) {203ksft_print_msg("Last entry is 0x%llx not 0x0\n", buf[elem]);204pass = false;205}206207/* Then a valid cap token */208elem--;209expected_cap = ((uint64_t)buf + page_size - 16);210expected_cap &= GCS_CAP_ADDR_MASK;211expected_cap |= GCS_CAP_VALID_TOKEN;212if (buf[elem] != expected_cap) {213ksft_print_msg("Cap entry is 0x%llx not 0x%llx\n",214buf[elem], expected_cap);215pass = false;216}217ksft_print_msg("cap token is 0x%llx\n", buf[elem]);218219/* The rest should be zeros */220for (elem = 0; elem < page_size / sizeof(uint64_t) - 2; elem++) {221if (!buf[elem])222continue;223ksft_print_msg("GCS slot %d is 0x%llx not 0x0\n",224elem, buf[elem]);225pass = false;226}227228ret = munmap(buf, page_size);229if (ret != 0) {230ksft_print_msg("Failed to unmap %ld byte GCS: %d\n",231page_size, errno);232pass = false;233}234235return pass;236}237238/* A fork()ed process can run */239static bool test_fork(void)240{241unsigned long child_mode;242int ret, status;243pid_t pid;244bool pass = true;245246pid = fork();247if (pid == -1) {248ksft_print_msg("fork() failed: %d\n", errno);249pass = false;250goto out;251}252if (pid == 0) {253/* In child, make sure we can call a function, read254* the GCS pointer and status and then exit */255valid_gcs_function();256get_gcspr();257258ret = my_syscall5(__NR_prctl, PR_GET_SHADOW_STACK_STATUS,259&child_mode, 0, 0, 0);260if (ret == 0 && !(child_mode & PR_SHADOW_STACK_ENABLE)) {261ksft_print_msg("GCS not enabled in child\n");262ret = -EINVAL;263}264265exit(ret);266}267268/*269* In parent, check we can still do function calls then block270* for the child.271*/272valid_gcs_function();273274ksft_print_msg("Waiting for child %d\n", pid);275276ret = waitpid(pid, &status, 0);277if (ret == -1) {278ksft_print_msg("Failed to wait for child: %d\n",279errno);280return false;281}282283if (!WIFEXITED(status)) {284ksft_print_msg("Child exited due to signal %d\n",285WTERMSIG(status));286pass = false;287} else {288if (WEXITSTATUS(status)) {289ksft_print_msg("Child exited with status %d\n",290WEXITSTATUS(status));291pass = false;292}293}294295out:296297return pass;298}299300/* A vfork()ed process can run and exit */301static bool test_vfork(void)302{303unsigned long child_mode;304int ret, status;305pid_t pid;306bool pass = true;307308pid = vfork();309if (pid == -1) {310ksft_print_msg("vfork() failed: %d\n", errno);311pass = false;312goto out;313}314if (pid == 0) {315/*316* In child, make sure we can call a function, read317* the GCS pointer and status and then exit.318*/319valid_gcs_function();320get_gcspr();321322ret = my_syscall5(__NR_prctl, PR_GET_SHADOW_STACK_STATUS,323&child_mode, 0, 0, 0);324if (ret == 0 && !(child_mode & PR_SHADOW_STACK_ENABLE)) {325ksft_print_msg("GCS not enabled in child\n");326ret = EXIT_FAILURE;327}328329_exit(ret);330}331332/*333* In parent, check we can still do function calls then check334* on the child.335*/336valid_gcs_function();337338ksft_print_msg("Waiting for child %d\n", pid);339340ret = waitpid(pid, &status, 0);341if (ret == -1) {342ksft_print_msg("Failed to wait for child: %d\n",343errno);344return false;345}346347if (!WIFEXITED(status)) {348ksft_print_msg("Child exited due to signal %d\n",349WTERMSIG(status));350pass = false;351} else if (WEXITSTATUS(status)) {352ksft_print_msg("Child exited with status %d\n",353WEXITSTATUS(status));354pass = false;355}356357out:358359return pass;360}361362typedef bool (*gcs_test)(void);363364static struct {365char *name;366gcs_test test;367bool needs_enable;368} tests[] = {369{ "read_status", read_status },370{ "base_enable", base_enable, true },371{ "read_gcspr_el0", read_gcspr_el0 },372{ "enable_writeable", enable_writeable, true },373{ "enable_push_pop", enable_push_pop, true },374{ "enable_all", enable_all, true },375{ "enable_invalid", enable_invalid, true },376{ "map_guarded_stack", map_guarded_stack },377{ "fork", test_fork },378{ "vfork", test_vfork },379};380381int main(void)382{383int i, ret;384unsigned long gcs_mode;385386ksft_print_header();387388/*389* We don't have getauxval() with nolibc so treat a failure to390* read GCS state as a lack of support and skip.391*/392ret = my_syscall5(__NR_prctl, PR_GET_SHADOW_STACK_STATUS,393&gcs_mode, 0, 0, 0);394if (ret != 0)395ksft_exit_skip("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 */412my_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