-
Notifications
You must be signed in to change notification settings - Fork 679
Aarch64/gcs #2725
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: criu-dev
Are you sure you want to change the base?
Aarch64/gcs #2725
Changes from all commits
7360c0a
74d8b3b
b82ad2b
304e07f
55072e1
067d88e
94a04e1
a2e70ff
d2fd607
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,48 @@ | ||
| #ifndef __UAPI_ASM_GCS_TYPES_H__ | ||
| #define __UAPI_ASM_GCS_TYPES_H__ | ||
|
|
||
| #ifndef NT_ARM_GCS | ||
| #define NT_ARM_GCS 0x410 /* ARM GCS state */ | ||
| #endif | ||
|
|
||
| /* Shadow Stack/Guarded Control Stack interface */ | ||
| #define PR_GET_SHADOW_STACK_STATUS 74 | ||
| #define PR_SET_SHADOW_STACK_STATUS 75 | ||
| #define PR_LOCK_SHADOW_STACK_STATUS 76 | ||
|
|
||
| /* When set PR_SHADOW_STACK_ENABLE flag allocates a Guarded Control Stack */ | ||
| #ifndef PR_SHADOW_STACK_ENABLE | ||
| #define PR_SHADOW_STACK_ENABLE (1UL << 0) | ||
| #endif | ||
|
|
||
| /* Allows explicit GCS stores (eg. using GCSSTR) */ | ||
| #ifndef PR_SHADOW_STACK_WRITE | ||
| #define PR_SHADOW_STACK_WRITE (1UL << 1) | ||
| #endif | ||
|
|
||
| /* Allows explicit GCS pushes (eg. using GCSPUSHM) */ | ||
| #ifndef PR_SHADOW_STACK_PUSH | ||
| #define PR_SHADOW_STACK_PUSH (1UL << 2) | ||
| #endif | ||
|
|
||
| #ifndef SHADOW_STACK_SET_TOKEN | ||
| #define SHADOW_STACK_SET_TOKEN 0x1 /* Set up a restore token in the shadow stack */ | ||
| #endif | ||
|
|
||
| #define PR_SHADOW_STACK_ALL_MODES \ | ||
| PR_SHADOW_STACK_ENABLE | PR_SHADOW_STACK_WRITE | PR_SHADOW_STACK_PUSH | ||
|
|
||
| /* copied from: arch/arm64/include/asm/sysreg.h */ | ||
| #define GCS_CAP_VALID_TOKEN 0x1 | ||
| #define GCS_CAP_ADDR_MASK 0xFFFFFFFFFFFFF000ULL | ||
| #define GCS_CAP(x) ((((unsigned long)x) & GCS_CAP_ADDR_MASK) | GCS_CAP_VALID_TOKEN) | ||
| #define GCS_SIGNAL_CAP(addr) (((unsigned long)addr) & GCS_CAP_ADDR_MASK) | ||
|
|
||
| #include <asm/hwcap.h> | ||
|
|
||
| #ifndef HWCAP_GCS | ||
| #else | ||
| #define HWCAP_GCS (1UL << 32) | ||
| #endif | ||
|
|
||
| #endif /* __UAPI_ASM_GCS_TYPES_H__ */ | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -13,6 +13,8 @@ | |
| #include "infect.h" | ||
| #include "infect-priv.h" | ||
| #include "asm/breakpoints.h" | ||
| #include "asm/gcs-types.h" | ||
svilenkov marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| #include <linux/prctl.h> | ||
|
|
||
| unsigned __page_size = 0; | ||
| unsigned __page_shift = 0; | ||
|
|
@@ -36,21 +38,37 @@ static inline void __always_unused __check_code_syscall(void) | |
| int sigreturn_prep_regs_plain(struct rt_sigframe *sigframe, user_regs_struct_t *regs, user_fpregs_struct_t *fpregs) | ||
| { | ||
| struct fpsimd_context *fpsimd = RT_SIGFRAME_FPU(sigframe); | ||
| struct gcs_context *gcs = RT_SIGFRAME_GCS(sigframe); | ||
|
|
||
| memcpy(sigframe->uc.uc_mcontext.regs, regs->regs, sizeof(regs->regs)); | ||
|
|
||
| pr_debug("sigreturn_prep_regs_plain: sp %lx pc %lx\n", (long)regs->sp, (long)regs->pc); | ||
|
|
||
| sigframe->uc.uc_mcontext.sp = regs->sp; | ||
| sigframe->uc.uc_mcontext.pc = regs->pc; | ||
| sigframe->uc.uc_mcontext.pstate = regs->pstate; | ||
|
|
||
| memcpy(fpsimd->vregs, fpregs->vregs, 32 * sizeof(__uint128_t)); | ||
| memcpy(fpsimd->vregs, fpregs->fpstate.vregs, 32 * sizeof(__uint128_t)); | ||
|
|
||
| fpsimd->fpsr = fpregs->fpsr; | ||
| fpsimd->fpcr = fpregs->fpcr; | ||
| fpsimd->fpsr = fpregs->fpstate.fpsr; | ||
| fpsimd->fpcr = fpregs->fpstate.fpcr; | ||
|
|
||
| fpsimd->head.magic = FPSIMD_MAGIC; | ||
| fpsimd->head.size = sizeof(*fpsimd); | ||
|
|
||
| if (__compel_gcs_enabled(&fpregs->gcs)) { | ||
| gcs->head.magic = GCS_MAGIC; | ||
| gcs->head.size = sizeof(*gcs); | ||
| gcs->reserved = 0; | ||
| gcs->gcspr = fpregs->gcs.gcspr_el0 - 8; | ||
| gcs->features_enabled = fpregs->gcs.features_enabled; | ||
|
|
||
| pr_debug("sigframe gcspr=%llx features_enabled=%llx\n", fpregs->gcs.gcspr_el0 - 8, fpregs->gcs.features_enabled); | ||
| } else { | ||
| pr_debug("sigframe gcspr=[disabled]\n"); | ||
svilenkov marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| memset(gcs, 0, sizeof(*gcs)); | ||
| } | ||
|
|
||
| return 0; | ||
| } | ||
|
|
||
|
|
@@ -74,13 +92,27 @@ int compel_get_task_regs(pid_t pid, user_regs_struct_t *regs, user_fpregs_struct | |
| goto err; | ||
| } | ||
|
|
||
| iov.iov_base = fpsimd; | ||
| iov.iov_len = sizeof(*fpsimd); | ||
| iov.iov_base = &fpsimd->fpstate; | ||
| iov.iov_len = sizeof(fpsimd->fpstate); | ||
| if ((ret = ptrace(PTRACE_GETREGSET, pid, NT_PRFPREG, &iov))) { | ||
| pr_perror("Failed to obtain FPU registers for %d", pid); | ||
| goto err; | ||
| } | ||
|
|
||
| memset(&fpsimd->gcs, 0, sizeof(fpsimd->gcs)); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we don't memset(0) other register structs, why there is a need to clear fpsimd->gcs?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've explicitly zeroed out to be sure GCS fields won't contain garbage values that would falsely indicate it's enabled |
||
|
|
||
| iov.iov_base = &fpsimd->gcs; | ||
| iov.iov_len = sizeof(fpsimd->gcs); | ||
| if (ptrace(PTRACE_GETREGSET, pid, NT_ARM_GCS, &iov) == 0) { | ||
| pr_info("gcs: GCSPR_EL0 for %d: 0x%llx, features: 0x%llx\n", | ||
| pid, fpsimd->gcs.gcspr_el0, fpsimd->gcs.features_enabled); | ||
|
|
||
| if (!__compel_gcs_enabled(&fpsimd->gcs)) | ||
| pr_info("gcs: GCS is NOT enabled\n"); | ||
| } else { | ||
| pr_info("gcs: GCS state not available for %d\n", pid); | ||
| } | ||
|
|
||
| ret = save(pid, arg, regs, fpsimd); | ||
| err: | ||
| return ret; | ||
|
|
@@ -101,6 +133,26 @@ int compel_set_task_ext_regs(pid_t pid, user_fpregs_struct_t *ext_regs) | |
| return 0; | ||
| } | ||
|
|
||
| int compel_set_task_gcs_regs(pid_t pid, user_fpregs_struct_t *ext_regs) | ||
svilenkov marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| { | ||
| struct iovec iov; | ||
|
|
||
| pr_info("gcs: restoring GCS registers for %d\n", pid); | ||
| pr_info("gcs: restoring GCS: gcspr=%llx features=%llx\n", | ||
| ext_regs->gcs.gcspr_el0, ext_regs->gcs.features_enabled); | ||
|
|
||
| iov.iov_base = &ext_regs->gcs; | ||
| iov.iov_len = sizeof(ext_regs->gcs); | ||
|
|
||
| if (ptrace(PTRACE_SETREGSET, pid, NT_ARM_GCS, &iov)) { | ||
| pr_perror("gcs: Failed to set GCS registers for %d", pid); | ||
| return -1; | ||
| } | ||
|
|
||
| return 0; | ||
| } | ||
|
|
||
|
|
||
| int compel_syscall(struct parasite_ctl *ctl, int nr, long *ret, unsigned long arg1, unsigned long arg2, | ||
| unsigned long arg3, unsigned long arg4, unsigned long arg5, unsigned long arg6) | ||
| { | ||
|
|
@@ -286,3 +338,70 @@ int ptrace_flush_breakpoints(pid_t pid) | |
|
|
||
| return 0; | ||
| } | ||
|
|
||
| bool __compel_gcs_enabled(struct user_gcs *gcs) | ||
| { | ||
| return gcs && (gcs->features_enabled & PR_SHADOW_STACK_ENABLE) != 0; | ||
| } | ||
|
|
||
| int inject_gcs_cap_token(struct parasite_ctl *ctl, pid_t pid, struct user_gcs *gcs) | ||
| { | ||
| struct iovec gcs_iov = { .iov_base = gcs, .iov_len = sizeof(*gcs) }; | ||
|
|
||
| uint64_t token_addr = gcs->gcspr_el0 - 8; | ||
| uint64_t sigtramp_addr = gcs->gcspr_el0 - 16; | ||
|
|
||
| uint64_t cap_token = ALIGN_DOWN(GCS_SIGNAL_CAP(token_addr), 8); | ||
| unsigned long restorer_addr; | ||
|
|
||
| pr_info("gcs: (setup) CAP token: 0x%lx at addr: 0x%lx\n", cap_token, token_addr); | ||
|
|
||
| /* Inject capability token at gcspr_el0 - 8 */ | ||
| if (ptrace(PTRACE_POKEDATA, pid, (void*)token_addr, cap_token)) { | ||
| pr_perror("gcs: (setup) Inject GCS cap token failed"); | ||
| return -1; | ||
| } | ||
|
|
||
| /* Inject restorer trampoline address (gcspr_el0 - 16) */ | ||
| restorer_addr = ctl->parasite_ip; | ||
| if (ptrace(PTRACE_POKEDATA, pid, (void*)sigtramp_addr, restorer_addr)) { | ||
| pr_perror("gcs: (setup) Inject GCS restorer failed"); | ||
| return -1; | ||
| } | ||
|
|
||
| /* Update GCSPR_EL0 */ | ||
| gcs->gcspr_el0 = token_addr; | ||
| if(ptrace(PTRACE_SETREGSET, pid, NT_ARM_GCS, &gcs_iov)) { | ||
| pr_perror("gcs: PTRACE_SETREGS FAILED"); | ||
| return -1; | ||
| } | ||
|
|
||
| pr_debug("gcs: parasite_ip=%#lx sp=%#llx gcspr_el0=%#llx\n", | ||
| ctl->parasite_ip, ctl->orig.regs.sp, gcs->gcspr_el0); | ||
|
|
||
| return 0; | ||
| } | ||
|
|
||
| int parasite_setup_shstk(struct parasite_ctl *ctl, user_fpregs_struct_t *ext_regs) | ||
| { | ||
| struct user_gcs gcs; | ||
| struct iovec gcs_iov = { .iov_base = &gcs, .iov_len = sizeof(gcs) }; | ||
| pid_t pid = ctl->rpid; | ||
|
|
||
| if (ptrace(PTRACE_GETREGSET, pid, NT_ARM_GCS, &gcs_iov) != 0) { | ||
| pr_perror("GCS state not available for %d\n", pid); | ||
| return -1; | ||
| } | ||
|
|
||
| if (!__compel_gcs_enabled(&gcs)) | ||
| return 0; | ||
|
|
||
| if (inject_gcs_cap_token(ctl, pid, &gcs)) { | ||
| pr_perror("Failed to inject GCS cap token for %d\n", pid); | ||
| return -1; | ||
| } | ||
|
|
||
| pr_info("gcs: GCS enabled for %d\n", pid); | ||
|
|
||
| return 0; | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -761,7 +761,7 @@ bool __compel_shstk_enabled(user_fpregs_struct_t *ext_regs) | |
| return false; | ||
| } | ||
|
|
||
| int parasite_setup_shstk(struct parasite_ctl *ctl, user_fpregs_struct_t *ext_regs) | ||
| int parasite_setup_shstk(struct parasite_ctl *ctl, __maybe_unused user_fpregs_struct_t *ext_regs) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. looks like ext_regs are really unused :) |
||
| { | ||
| pid_t pid = ctl->rpid; | ||
| unsigned long sa_restorer = ctl->parasite_ip; | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -209,4 +209,12 @@ static inline int parasite_setup_shstk(struct parasite_ctl *ctl, | |
| #define parasite_setup_shstk parasite_setup_shstk | ||
| #endif | ||
|
|
||
| #ifndef compel_gcs_enabled | ||
| static inline bool compel_gcs_enabled(struct user_gcs *gcs) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It looks like we don't need neither compel_gcs_enabled() nor compel_shstk_enabled(). |
||
| { | ||
| return false; | ||
| } | ||
| #define compel_gcs_enabled compel_gcs_enabled | ||
| #endif | ||
|
|
||
| #endif | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -19,6 +19,7 @@ | |
| #include <compel/plugins/std/asm/syscall-types.h> | ||
| #include "uapi/compel/plugins/std/syscall.h" | ||
| #include "asm/infect-types.h" | ||
| #include "asm/gcs-types.h" | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. won't compile on !aarch64 |
||
| #include "asm/sigframe.h" | ||
| #include "infect.h" | ||
| #include "ptrace.h" | ||
|
|
@@ -552,6 +553,9 @@ static int restore_thread_ctx(int pid, struct thread_ctx *ctx, bool restore_ext_ | |
| { | ||
| int ret = 0; | ||
|
|
||
| struct user_gcs gcs; | ||
| struct iovec gcs_iov = { .iov_base = &gcs, .iov_len = sizeof(gcs) }; | ||
|
|
||
| if (ptrace_set_regs(pid, &ctx->regs)) { | ||
| pr_perror("Can't restore registers (pid: %d)", pid); | ||
| ret = -1; | ||
|
|
@@ -560,6 +564,13 @@ static int restore_thread_ctx(int pid, struct thread_ctx *ctx, bool restore_ext_ | |
| if (restore_ext_regs && compel_set_task_ext_regs(pid, &ctx->ext_regs)) | ||
| ret = -1; | ||
|
|
||
| if (ptrace(PTRACE_GETREGSET, pid, NT_ARM_GCS, &gcs_iov) < 0) { | ||
| pr_warn("gcs: Failed to get GCS for %d", pid); | ||
| } else { | ||
| ctx->ext_regs.gcs = gcs; | ||
| compel_set_task_gcs_regs(pid, &ctx->ext_regs); | ||
| } | ||
|
|
||
| if (ptrace(PTRACE_SETSIGMASK, pid, sizeof(k_rtsigset_t), &ctx->sigmask)) { | ||
| pr_perror("Can't block signals"); | ||
| ret = -1; | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -6,3 +6,4 @@ obj-y += cpu.o | |
| obj-y += crtools.o | ||
| obj-y += sigframe.o | ||
| obj-y += bitops.o | ||
| obj-y += gcs.o | ||
Uh oh!
There was an error while loading. Please reload this page.