linux-user/aarch64: Generate GCS signal records
Here we must push and pop a cap on the GCS stack as well as the gcs record on the normal stack. Reviewed-by: Pierrick Bouvier <pierrick.bouvier@linaro.org> Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Message-id: 20251008215613.300150-70-richard.henderson@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
parent
37897b29b3
commit
ef110c3070
1 changed files with 132 additions and 6 deletions
|
|
@ -22,6 +22,7 @@
|
|||
#include "signal-common.h"
|
||||
#include "linux-user/trace.h"
|
||||
#include "target/arm/cpu-features.h"
|
||||
#include "gcs-internal.h"
|
||||
|
||||
struct target_sigcontext {
|
||||
uint64_t fault_address;
|
||||
|
|
@ -152,6 +153,16 @@ struct target_zt_context {
|
|||
QEMU_BUILD_BUG_ON(TARGET_ZT_SIG_REG_BYTES != \
|
||||
sizeof_field(CPUARMState, za_state.zt0));
|
||||
|
||||
#define TARGET_GCS_MAGIC 0x47435300
|
||||
#define GCS_SIGNAL_CAP(X) ((X) & TARGET_PAGE_MASK)
|
||||
|
||||
struct target_gcs_context {
|
||||
struct target_aarch64_ctx head;
|
||||
uint64_t gcspr;
|
||||
uint64_t features_enabled;
|
||||
uint64_t reserved;
|
||||
};
|
||||
|
||||
struct target_rt_sigframe {
|
||||
struct target_siginfo info;
|
||||
struct target_ucontext uc;
|
||||
|
|
@ -322,6 +333,35 @@ static void target_setup_zt_record(struct target_zt_context *zt,
|
|||
}
|
||||
}
|
||||
|
||||
static bool target_setup_gcs_record(struct target_gcs_context *ctx,
|
||||
CPUARMState *env, uint64_t return_addr)
|
||||
{
|
||||
uint64_t mode = gcs_get_el0_mode(env);
|
||||
uint64_t gcspr = env->cp15.gcspr_el[0];
|
||||
|
||||
if (mode & PR_SHADOW_STACK_ENABLE) {
|
||||
/* Push a cap for the signal frame. */
|
||||
gcspr -= 8;
|
||||
if (put_user_u64(GCS_SIGNAL_CAP(gcspr), gcspr)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Push a gcs entry for the trampoline. */
|
||||
if (put_user_u64(return_addr, gcspr - 8)) {
|
||||
return false;
|
||||
}
|
||||
env->cp15.gcspr_el[0] = gcspr - 8;
|
||||
}
|
||||
|
||||
__put_user(TARGET_GCS_MAGIC, &ctx->head.magic);
|
||||
__put_user(sizeof(*ctx), &ctx->head.size);
|
||||
__put_user(gcspr, &ctx->gcspr);
|
||||
__put_user(mode, &ctx->features_enabled);
|
||||
__put_user(0, &ctx->reserved);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void target_restore_general_frame(CPUARMState *env,
|
||||
struct target_rt_sigframe *sf)
|
||||
{
|
||||
|
|
@ -502,6 +542,64 @@ static bool target_restore_zt_record(CPUARMState *env,
|
|||
return true;
|
||||
}
|
||||
|
||||
static bool target_restore_gcs_record(CPUARMState *env,
|
||||
struct target_gcs_context *ctx,
|
||||
bool *rebuild_hflags)
|
||||
{
|
||||
TaskState *ts = get_task_state(env_cpu(env));
|
||||
uint64_t cur_mode = gcs_get_el0_mode(env);
|
||||
uint64_t new_mode, gcspr;
|
||||
|
||||
__get_user(new_mode, &ctx->features_enabled);
|
||||
__get_user(gcspr, &ctx->gcspr);
|
||||
|
||||
/*
|
||||
* The kernel pushes the value through the hw register:
|
||||
* write_sysreg_s(gcspr, SYS_GCSPR_EL0) in restore_gcs_context,
|
||||
* then read_sysreg_s(SYS_GCSPR_EL0) in gcs_restore_signal.
|
||||
* Since the bottom 3 bits are RES0, this can (CONSTRAINED UNPREDICTABLE)
|
||||
* force align the value. Mirror the choice from gcspr_write().
|
||||
*/
|
||||
gcspr &= ~7;
|
||||
|
||||
if (new_mode & ~(PR_SHADOW_STACK_ENABLE |
|
||||
PR_SHADOW_STACK_WRITE |
|
||||
PR_SHADOW_STACK_PUSH)) {
|
||||
return false;
|
||||
}
|
||||
if ((new_mode ^ cur_mode) & ts->gcs_el0_locked) {
|
||||
return false;
|
||||
}
|
||||
if (new_mode & ~cur_mode & PR_SHADOW_STACK_ENABLE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (new_mode & PR_SHADOW_STACK_ENABLE) {
|
||||
uint64_t cap;
|
||||
|
||||
/* Pop and clear the signal cap. */
|
||||
if (get_user_u64(cap, gcspr)) {
|
||||
return false;
|
||||
}
|
||||
if (cap != GCS_SIGNAL_CAP(gcspr)) {
|
||||
return false;
|
||||
}
|
||||
if (put_user_u64(0, gcspr)) {
|
||||
return false;
|
||||
}
|
||||
gcspr += 8;
|
||||
} else {
|
||||
new_mode = 0;
|
||||
}
|
||||
|
||||
env->cp15.gcspr_el[0] = gcspr;
|
||||
if (new_mode != cur_mode) {
|
||||
*rebuild_hflags = true;
|
||||
gcs_set_el0_mode(env, new_mode);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static int target_restore_sigframe(CPUARMState *env,
|
||||
struct target_rt_sigframe *sf)
|
||||
{
|
||||
|
|
@ -511,8 +609,10 @@ static int target_restore_sigframe(CPUARMState *env,
|
|||
struct target_za_context *za = NULL;
|
||||
struct target_tpidr2_context *tpidr2 = NULL;
|
||||
struct target_zt_context *zt = NULL;
|
||||
struct target_gcs_context *gcs = NULL;
|
||||
uint64_t extra_datap = 0;
|
||||
bool used_extra = false;
|
||||
bool rebuild_hflags = false;
|
||||
int sve_size = 0;
|
||||
int za_size = 0;
|
||||
int zt_size = 0;
|
||||
|
|
@ -582,6 +682,15 @@ static int target_restore_sigframe(CPUARMState *env,
|
|||
zt_size = size;
|
||||
break;
|
||||
|
||||
case TARGET_GCS_MAGIC:
|
||||
if (gcs
|
||||
|| size != sizeof(struct target_gcs_context)
|
||||
|| !cpu_isar_feature(aa64_gcs, env_archcpu(env))) {
|
||||
goto err;
|
||||
}
|
||||
gcs = (struct target_gcs_context *)ctx;
|
||||
break;
|
||||
|
||||
case TARGET_EXTRA_MAGIC:
|
||||
if (extra || size != sizeof(struct target_extra_context)) {
|
||||
goto err;
|
||||
|
|
@ -612,6 +721,10 @@ static int target_restore_sigframe(CPUARMState *env,
|
|||
goto err;
|
||||
}
|
||||
|
||||
if (gcs && !target_restore_gcs_record(env, gcs, &rebuild_hflags)) {
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* SVE data, if present, overwrites FPSIMD data. */
|
||||
if (sve && !target_restore_sve_record(env, sve, sve_size, &svcr)) {
|
||||
goto err;
|
||||
|
|
@ -631,6 +744,9 @@ static int target_restore_sigframe(CPUARMState *env,
|
|||
}
|
||||
if (env->svcr != svcr) {
|
||||
env->svcr = svcr;
|
||||
rebuild_hflags = true;
|
||||
}
|
||||
if (rebuild_hflags) {
|
||||
arm_rebuild_hflags(env);
|
||||
}
|
||||
unlock_user(extra, extra_datap, 0);
|
||||
|
|
@ -701,7 +817,7 @@ static void target_setup_frame(int usig, struct target_sigaction *ka,
|
|||
uc.tuc_mcontext.__reserved),
|
||||
};
|
||||
int fpsimd_ofs, fr_ofs, sve_ofs = 0, za_ofs = 0, tpidr2_ofs = 0;
|
||||
int zt_ofs = 0, esr_ofs = 0;
|
||||
int zt_ofs = 0, esr_ofs = 0, gcs_ofs = 0;
|
||||
int sve_size = 0, za_size = 0, tpidr2_size = 0, zt_size = 0;
|
||||
struct target_rt_sigframe *frame;
|
||||
struct target_rt_frame_record *fr;
|
||||
|
|
@ -720,6 +836,11 @@ static void target_setup_frame(int usig, struct target_sigaction *ka,
|
|||
&layout);
|
||||
}
|
||||
|
||||
if (env->cp15.gcspr_el[0]) {
|
||||
gcs_ofs = alloc_sigframe_space(sizeof(struct target_gcs_context),
|
||||
&layout);
|
||||
}
|
||||
|
||||
/* SVE state needs saving only if it exists. */
|
||||
if (cpu_isar_feature(aa64_sve, env_archcpu(env)) ||
|
||||
cpu_isar_feature(aa64_sme, env_archcpu(env))) {
|
||||
|
|
@ -779,6 +900,12 @@ static void target_setup_frame(int usig, struct target_sigaction *ka,
|
|||
goto give_sigsegv;
|
||||
}
|
||||
|
||||
if (ka->sa_flags & TARGET_SA_RESTORER) {
|
||||
return_addr = ka->sa_restorer;
|
||||
} else {
|
||||
return_addr = default_rt_sigreturn;
|
||||
}
|
||||
|
||||
target_setup_general_frame(frame, env, set);
|
||||
target_setup_fpsimd_record((void *)frame + fpsimd_ofs, env);
|
||||
if (esr_ofs) {
|
||||
|
|
@ -786,6 +913,10 @@ static void target_setup_frame(int usig, struct target_sigaction *ka,
|
|||
/* Leave ESR_EL1 clear while it's not relevant. */
|
||||
env->cp15.esr_el[1] = 0;
|
||||
}
|
||||
if (gcs_ofs &&
|
||||
!target_setup_gcs_record((void *)frame + gcs_ofs, env, return_addr)) {
|
||||
goto give_sigsegv;
|
||||
}
|
||||
target_setup_end_record((void *)frame + layout.std_end_ofs);
|
||||
if (layout.extra_ofs) {
|
||||
target_setup_extra_record((void *)frame + layout.extra_ofs,
|
||||
|
|
@ -811,11 +942,6 @@ static void target_setup_frame(int usig, struct target_sigaction *ka,
|
|||
__put_user(env->xregs[29], &fr->fp);
|
||||
__put_user(env->xregs[30], &fr->lr);
|
||||
|
||||
if (ka->sa_flags & TARGET_SA_RESTORER) {
|
||||
return_addr = ka->sa_restorer;
|
||||
} else {
|
||||
return_addr = default_rt_sigreturn;
|
||||
}
|
||||
env->xregs[0] = usig;
|
||||
env->xregs[29] = frame_addr + fr_ofs;
|
||||
env->xregs[30] = return_addr;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue