/* * The prctl takes 1 argument but we need to ensure that the * other 3 values passed in registers to the syscall are zero * since the kernel validates them.
*/
ret = my_syscall5(__NR_prctl, PR_SET_SHADOW_STACK_STATUS, mode,
0, 0, 0);
if (ret == 0) {
ret = my_syscall5(__NR_prctl, PR_GET_SHADOW_STACK_STATUS,
&new_mode, 0, 0, 0); if (ret == 0) { if (new_mode != mode) {
ksft_print_msg("Mode set to %lx not %lx\n",
new_mode, mode);
ret = -EINVAL;
}
} else {
ksft_print_msg("Failed to validate mode: %d\n", ret);
}
if (enabling != chkfeat_gcs()) {
ksft_print_msg("%senabled by prctl but %senabled in CHKFEAT\n",
enabling ? "" : "not ",
chkfeat_gcs() ? "" : "not ");
ret = -EINVAL;
}
}
return ret;
}
/* Try to read the status */ staticbool read_status(void)
{ unsignedlong state; int ret;
ret = my_syscall5(__NR_prctl, PR_GET_SHADOW_STACK_STATUS,
&state, 0, 0, 0); if (ret != 0) {
ksft_print_msg("Failed to read state: %d\n", ret); returnfalse;
}
return state & PR_SHADOW_STACK_ENABLE;
}
/* Just a straight enable */ staticbool base_enable(void)
{ int ret;
ret = gcs_set_status(PR_SHADOW_STACK_ENABLE); if (ret) {
ksft_print_msg("PR_SHADOW_STACK_ENABLE failed %d\n", ret); returnfalse;
}
returntrue;
}
/* Check we can read GCSPR_EL0 when GCS is enabled */ staticbool read_gcspr_el0(void)
{ unsignedlong *gcspr_el0;
ksft_print_msg("GET GCSPR\n");
gcspr_el0 = get_gcspr();
ksft_print_msg("GCSPR_EL0 is %p\n", gcspr_el0);
returntrue;
}
/* Also allow writes to stack */ staticbool enable_writeable(void)
{ int ret;
ret = gcs_set_status(PR_SHADOW_STACK_ENABLE | PR_SHADOW_STACK_WRITE); if (ret) {
ksft_print_msg("PR_SHADOW_STACK_ENABLE writeable failed: %d\n", ret); returnfalse;
}
ret = gcs_set_status(PR_SHADOW_STACK_ENABLE); if (ret) {
ksft_print_msg("failed to restore plain enable %d\n", ret); returnfalse;
}
returntrue;
}
/* Also allow writes to stack */ staticbool enable_push_pop(void)
{ int ret;
ret = gcs_set_status(PR_SHADOW_STACK_ENABLE | PR_SHADOW_STACK_PUSH); if (ret) {
ksft_print_msg("PR_SHADOW_STACK_ENABLE with push failed: %d\n",
ret); returnfalse;
}
ret = gcs_set_status(PR_SHADOW_STACK_ENABLE); if (ret) {
ksft_print_msg("failed to restore plain enable %d\n", ret); returnfalse;
}
returntrue;
}
/* Enable GCS and allow everything */ staticbool enable_all(void)
{ int ret;
ret = gcs_set_status(PR_SHADOW_STACK_ENABLE | PR_SHADOW_STACK_PUSH |
PR_SHADOW_STACK_WRITE); if (ret) {
ksft_print_msg("PR_SHADOW_STACK_ENABLE with everything failed: %d\n",
ret); returnfalse;
}
ret = gcs_set_status(PR_SHADOW_STACK_ENABLE); if (ret) {
ksft_print_msg("failed to restore plain enable %d\n", ret); returnfalse;
}
returntrue;
}
staticbool enable_invalid(void)
{ int ret = gcs_set_status(ULONG_MAX); if (ret == 0) {
ksft_print_msg("GCS_SET_STATUS %lx succeeded\n", ULONG_MAX); returnfalse;
}
returntrue;
}
/* Map a GCS */ staticbool map_guarded_stack(void)
{ int ret;
uint64_t *buf;
uint64_t expected_cap; int elem; bool pass = true;
/* The top of the newly allocated region should be 0 */
elem = (page_size / sizeof(uint64_t)) - 1; if (buf[elem]) {
ksft_print_msg("Last entry is 0x%llx not 0x0\n", buf[elem]);
pass = false;
}
/* Then a valid cap token */
elem--;
expected_cap = ((uint64_t)buf + page_size - 16);
expected_cap &= GCS_CAP_ADDR_MASK;
expected_cap |= GCS_CAP_VALID_TOKEN; if (buf[elem] != expected_cap) {
ksft_print_msg("Cap entry is 0x%llx not 0x%llx\n",
buf[elem], expected_cap);
pass = false;
}
ksft_print_msg("cap token is 0x%llx\n", buf[elem]);
/* The rest should be zeros */ for (elem = 0; elem < page_size / sizeof(uint64_t) - 2; elem++) { if (!buf[elem]) continue;
ksft_print_msg("GCS slot %d is 0x%llx not 0x0\n",
elem, buf[elem]);
pass = false;
}
ret = munmap(buf, page_size); if (ret != 0) {
ksft_print_msg("Failed to unmap %ld byte GCS: %d\n",
page_size, errno);
pass = false;
}
return pass;
}
/* A fork()ed process can run */ staticbool test_fork(void)
{ unsignedlong child_mode; int ret, status;
pid_t pid; bool pass = true;
pid = fork(); if (pid == -1) {
ksft_print_msg("fork() failed: %d\n", errno);
pass = false; goto out;
} if (pid == 0) { /* In child, make sure we can call a function, read
* the GCS pointer and status and then exit */
valid_gcs_function();
get_gcspr();
ret = my_syscall5(__NR_prctl, PR_GET_SHADOW_STACK_STATUS,
&child_mode, 0, 0, 0); if (ret == 0 && !(child_mode & PR_SHADOW_STACK_ENABLE)) {
ksft_print_msg("GCS not enabled in child\n");
ret = -EINVAL;
}
exit(ret);
}
/* * In parent, check we can still do function calls then block * for the child.
*/
valid_gcs_function();
ksft_print_msg("Waiting for child %d\n", pid);
ret = waitpid(pid, &status, 0); if (ret == -1) {
ksft_print_msg("Failed to wait for child: %d\n",
errno); returnfalse;
}
if (!WIFEXITED(status)) {
ksft_print_msg("Child exited due to signal %d\n",
WTERMSIG(status));
pass = false;
} else { if (WEXITSTATUS(status)) {
ksft_print_msg("Child exited with status %d\n",
WEXITSTATUS(status));
pass = false;
}
}
out:
return pass;
}
/* A vfork()ed process can run and exit */ staticbool test_vfork(void)
{ unsignedlong child_mode; int ret, status;
pid_t pid; bool pass = true;
pid = vfork(); if (pid == -1) {
ksft_print_msg("vfork() failed: %d\n", errno);
pass = false; goto out;
} if (pid == 0) { /* * In child, make sure we can call a function, read * the GCS pointer and status and then exit.
*/
valid_gcs_function();
get_gcspr();
ret = my_syscall5(__NR_prctl, PR_GET_SHADOW_STACK_STATUS,
&child_mode, 0, 0, 0); if (ret == 0 && !(child_mode & PR_SHADOW_STACK_ENABLE)) {
ksft_print_msg("GCS not enabled in child\n");
ret = EXIT_FAILURE;
}
_exit(ret);
}
/* * In parent, check we can still do function calls then check * on the child.
*/
valid_gcs_function();
ksft_print_msg("Waiting for child %d\n", pid);
ret = waitpid(pid, &status, 0); if (ret == -1) {
ksft_print_msg("Failed to wait for child: %d\n",
errno); returnfalse;
}
if (!WIFEXITED(status)) {
ksft_print_msg("Child exited due to signal %d\n",
WTERMSIG(status));
pass = false;
} elseif (WEXITSTATUS(status)) {
ksft_print_msg("Child exited with status %d\n",
WEXITSTATUS(status));
pass = false;
}
int main(void)
{ int i, ret; unsignedlong gcs_mode;
ksft_print_header();
/* * We don't have getauxval() with nolibc so treat a failure to * read GCS state as a lack of support and skip.
*/
ret = my_syscall5(__NR_prctl, PR_GET_SHADOW_STACK_STATUS,
&gcs_mode, 0, 0, 0); if (ret != 0)
ksft_exit_skip("Failed to read GCS state: %d\n", ret);
if (!(gcs_mode & PR_SHADOW_STACK_ENABLE)) {
gcs_mode = PR_SHADOW_STACK_ENABLE;
ret = my_syscall5(__NR_prctl, PR_SET_SHADOW_STACK_STATUS,
gcs_mode, 0, 0, 0); if (ret != 0)
ksft_exit_fail_msg("Failed to enable GCS: %d\n", ret);
}
ksft_set_plan(ARRAY_SIZE(tests));
for (i = 0; i < ARRAY_SIZE(tests); i++) {
ksft_test_result((*tests[i].test)(), "%s\n", tests[i].name);
}
/* One last test: disable GCS, we can do this one time */
ret = my_syscall5(__NR_prctl, PR_SET_SHADOW_STACK_STATUS, 0, 0, 0, 0); if (ret != 0)
ksft_print_msg("Failed to disable GCS: %d\n", ret);
ksft_finished();
return 0;
}
Messung V0.5
¤ Dauer der Verarbeitung: 0.12 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.