mirror of
https://github.com/checkpoint-restore/criu.git
synced 2026-01-23 02:14:37 +00:00
compel: gcs: set up GCS token/restorer for rt_sigreturn
When GCS is enabled, the kernel expects a capability token at GCSPR_EL0-8 and sa_restorer at GCSPR_EL0-16 on rt_sigreturn. The sigframe must be consistent with the kernel’s expectations, with GCSPR_EL0 advanced by -8 having it point to the token on signal entry. On rt_sigreturn, the kernel verifies the cap at GCSPR_EL0, invalidates it and increments GCSPR_EL0 by 8 at the end of gcs_restore_signal() . Implement parasite_setup_gcs() to: - read NT_ARM_GCS via ptrace(PTRACE_GETREGSET) - write (via ptrace) the computed capability token and restorer address - update GCSPR_EL0 to point to the token's location Call parasite_setup_gcs() into parasite_start_daemon() so the sigreturn frame satisfies kernel's expectation Tests with GCS remain opt‑in: make -C compel/test/infect GCS_ENABLE=1 && make -C compel/test/infect run Signed-off-by: Igor Svilenkov Bozic <svilenkov@gmail.com> [ alex: cleanup fixes ] Signed-off-by: Alexander Mikhalitsyn <aleksandr.mikhalitsyn@canonical.com> Acked-by: Mike Rapoport <rppt@kernel.org>
This commit is contained in:
parent
5817983089
commit
6b6382f136
3 changed files with 84 additions and 5 deletions
|
|
@ -57,4 +57,9 @@ typedef struct user_fpregs_struct user_fpregs_struct_t;
|
|||
extern bool __compel_host_supports_gcs(void);
|
||||
#define compel_host_supports_gcs __compel_host_supports_gcs
|
||||
|
||||
struct parasite_ctl;
|
||||
extern int __parasite_setup_shstk(struct parasite_ctl *ctl,
|
||||
user_fpregs_struct_t *ext_regs);
|
||||
#define parasite_setup_shstk __parasite_setup_shstk
|
||||
|
||||
#endif /* UAPI_COMPEL_ASM_TYPES_H__ */
|
||||
|
|
|
|||
|
|
@ -46,10 +46,7 @@ static bool __compel_gcs_enabled(struct user_gcs *gcs)
|
|||
if (!compel_host_supports_gcs())
|
||||
return false;
|
||||
|
||||
if (gcs->features_enabled & PR_SHADOW_STACK_ENABLE)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
return gcs && (gcs->features_enabled & PR_SHADOW_STACK_ENABLE) != 0;
|
||||
}
|
||||
|
||||
int sigreturn_prep_regs_plain(struct rt_sigframe *sigframe, user_regs_struct_t *regs, user_fpregs_struct_t *fpregs)
|
||||
|
|
@ -118,6 +115,18 @@ int compel_get_task_regs(pid_t pid, user_regs_struct_t *regs, user_fpregs_struct
|
|||
|
||||
memset(&ext_regs->gcs, 0, sizeof(ext_regs->gcs));
|
||||
|
||||
iov.iov_base = &ext_regs->gcs;
|
||||
iov.iov_len = sizeof(ext_regs->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, ext_regs->gcs.gcspr_el0, ext_regs->gcs.features_enabled);
|
||||
|
||||
if (!__compel_gcs_enabled(&ext_regs->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, ext_regs);
|
||||
err:
|
||||
return ret;
|
||||
|
|
@ -323,3 +332,68 @@ int ptrace_flush_breakpoints(pid_t pid)
|
|||
|
||||
return 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(!__compel_host_supports_gcs())
|
||||
return 0;
|
||||
|
||||
if (ptrace(PTRACE_GETREGSET, pid, NT_ARM_GCS, &gcs_iov) != 0) {
|
||||
pr_perror("GCS state not available for %d", 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", pid);
|
||||
return -1;
|
||||
}
|
||||
|
||||
pr_info("gcs: GCS enabled for %d\n", pid);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
pid_t pid = ctl->rpid;
|
||||
unsigned long sa_restorer = ctl->parasite_ip;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue