[PATCH] target/arm: Fix BTI versus CF_PCREL

Richard Henderson posted 1 patch 3 months, 3 weeks ago
There is a newer version of this series
target/arm/tcg/helper-a64.h    |  3 ++
target/arm/tcg/translate.h     |  2 --
target/arm/tcg/helper-a64.c    | 38 ++++++++++++++++++++
target/arm/tcg/translate-a64.c | 64 ++++++++--------------------------
4 files changed, 55 insertions(+), 52 deletions(-)
[PATCH] target/arm: Fix BTI versus CF_PCREL
Posted by Richard Henderson 3 months, 3 weeks ago
With pcrel, we cannot check the guarded page bit at translation
time, as different mappings of the same physical page may or may
not have the GP bit set.

Instead, add a couple of helpers to check the page at runtime,
after all other filters that might obviate the need for the check.

The set_btype_for_br call must be moved after the gen_a64_set_pc
call to ensure the current pc can still be computed.

Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
---
 target/arm/tcg/helper-a64.h    |  3 ++
 target/arm/tcg/translate.h     |  2 --
 target/arm/tcg/helper-a64.c    | 38 ++++++++++++++++++++
 target/arm/tcg/translate-a64.c | 64 ++++++++--------------------------
 4 files changed, 55 insertions(+), 52 deletions(-)

diff --git a/target/arm/tcg/helper-a64.h b/target/arm/tcg/helper-a64.h
index 371388f61b..481007bf39 100644
--- a/target/arm/tcg/helper-a64.h
+++ b/target/arm/tcg/helper-a64.h
@@ -133,6 +133,9 @@ DEF_HELPER_4(cpyfp, void, env, i32, i32, i32)
 DEF_HELPER_4(cpyfm, void, env, i32, i32, i32)
 DEF_HELPER_4(cpyfe, void, env, i32, i32, i32)
 
+DEF_HELPER_FLAGS_1(guarded_page_check, TCG_CALL_NO_WG, void, env)
+DEF_HELPER_FLAGS_2(guarded_page_br, TCG_CALL_NO_RWG, void, env, tl)
+
 DEF_HELPER_FLAGS_5(gvec_fdiv_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32)
 DEF_HELPER_FLAGS_5(gvec_fdiv_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32)
 DEF_HELPER_FLAGS_5(gvec_fdiv_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32)
diff --git a/target/arm/tcg/translate.h b/target/arm/tcg/translate.h
index a8672c857c..01c217f4a4 100644
--- a/target/arm/tcg/translate.h
+++ b/target/arm/tcg/translate.h
@@ -163,8 +163,6 @@ typedef struct DisasContext {
     uint8_t dcz_blocksize;
     /* A copy of cpu->gm_blocksize. */
     uint8_t gm_blocksize;
-    /* True if this page is guarded.  */
-    bool guarded_page;
     /* True if the current insn_start has been updated. */
     bool insn_start_updated;
     /* Bottom two bits of XScale c15_cpar coprocessor access control reg */
diff --git a/target/arm/tcg/helper-a64.c b/target/arm/tcg/helper-a64.c
index c60d2a7ec9..424fe927b4 100644
--- a/target/arm/tcg/helper-a64.c
+++ b/target/arm/tcg/helper-a64.c
@@ -1877,3 +1877,41 @@ void HELPER(cpyfe)(CPUARMState *env, uint32_t syndrome, uint32_t wdesc,
 {
     do_cpye(env, syndrome, wdesc, rdesc, false, GETPC());
 }
+
+static bool is_guarded_page(CPUARMState *env, target_ulong addr)
+{
+#ifdef CONFIG_USER_ONLY
+    return page_get_flags(addr) & PAGE_BTI;
+#else
+    CPUTLBEntryFull *full;
+    void *host;
+    int mmu_idx = cpu_mmu_index(env_cpu(env), true);
+    int flags = probe_access_full(env, addr, 0, MMU_INST_FETCH, mmu_idx,
+                                  false, &host, &full, 0);
+
+    assert(!(flags & TLB_INVALID_MASK));
+    return full->extra.arm.guarded;
+#endif
+}
+
+void HELPER(guarded_page_check)(CPUARMState *env)
+{
+    /*
+     * We have already verified that bti is enabled, and that the
+     * instruction at PC is not ok for BTYPE.  This is always at
+     * the beginning of a block, so PC is always up-to-date.
+     */
+    if (is_guarded_page(env, env->pc)) {
+        raise_exception(env, EXCP_UDEF, syn_btitrap(env->btype),
+                        exception_target_el(env));
+    }
+}
+
+void HELPER(guarded_page_br)(CPUARMState *env, target_ulong pc)
+{
+    /*
+     * We have already checked for branch via x16 and x17.
+     * What remains for choosing BTYPE is checking for a guarded page.
+     */
+    env->btype = is_guarded_page(env, pc) ? 3 : 1;
+}
diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c
index 148be2826e..28a1013503 100644
--- a/target/arm/tcg/translate-a64.c
+++ b/target/arm/tcg/translate-a64.c
@@ -1507,7 +1507,14 @@ static void set_btype_for_br(DisasContext *s, int rn)
 {
     if (dc_isar_feature(aa64_bti, s)) {
         /* BR to {x16,x17} or !guard -> 1, else 3.  */
-        set_btype(s, rn == 16 || rn == 17 || !s->guarded_page ? 1 : 3);
+        if (rn == 16 || rn == 17) {
+            set_btype(s, 1);
+        } else {
+            TCGv_i64 pc = tcg_temp_new_i64();
+            gen_pc_plus_diff(s, pc, 0);
+            gen_helper_guarded_page_br(tcg_env, pc);
+            s->btype = -1;
+        }
     }
 }
 
@@ -1521,8 +1528,8 @@ static void set_btype_for_blr(DisasContext *s)
 
 static bool trans_BR(DisasContext *s, arg_r *a)
 {
-    gen_a64_set_pc(s, cpu_reg(s, a->rn));
     set_btype_for_br(s, a->rn);
+    gen_a64_set_pc(s, cpu_reg(s, a->rn));
     s->base.is_jmp = DISAS_JUMP;
     return true;
 }
@@ -1581,8 +1588,8 @@ static bool trans_BRAZ(DisasContext *s, arg_braz *a)
     }
 
     dst = auth_branch_target(s, cpu_reg(s, a->rn), tcg_constant_i64(0), !a->m);
-    gen_a64_set_pc(s, dst);
     set_btype_for_br(s, a->rn);
+    gen_a64_set_pc(s, dst);
     s->base.is_jmp = DISAS_JUMP;
     return true;
 }
@@ -11878,37 +11885,6 @@ static bool trans_FAIL(DisasContext *s, arg_OK *a)
     return true;
 }
 
-/**
- * is_guarded_page:
- * @env: The cpu environment
- * @s: The DisasContext
- *
- * Return true if the page is guarded.
- */
-static bool is_guarded_page(CPUARMState *env, DisasContext *s)
-{
-    uint64_t addr = s->base.pc_first;
-#ifdef CONFIG_USER_ONLY
-    return page_get_flags(addr) & PAGE_BTI;
-#else
-    CPUTLBEntryFull *full;
-    void *host;
-    int mmu_idx = arm_to_core_mmu_idx(s->mmu_idx);
-    int flags;
-
-    /*
-     * We test this immediately after reading an insn, which means
-     * that the TLB entry must be present and valid, and thus this
-     * access will never raise an exception.
-     */
-    flags = probe_access_full(env, addr, 0, MMU_INST_FETCH, mmu_idx,
-                              false, &host, &full, 0);
-    assert(!(flags & TLB_INVALID_MASK));
-
-    return full->extra.arm.guarded;
-#endif
-}
-
 /**
  * btype_destination_ok:
  * @insn: The instruction at the branch destination
@@ -12151,19 +12127,6 @@ static void aarch64_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu)
 
     if (dc_isar_feature(aa64_bti, s)) {
         if (s->base.num_insns == 1) {
-            /*
-             * At the first insn of the TB, compute s->guarded_page.
-             * We delayed computing this until successfully reading
-             * the first insn of the TB, above.  This (mostly) ensures
-             * that the softmmu tlb entry has been populated, and the
-             * page table GP bit is available.
-             *
-             * Note that we need to compute this even if btype == 0,
-             * because this value is used for BR instructions later
-             * where ENV is not available.
-             */
-            s->guarded_page = is_guarded_page(env, s);
-
             /* First insn can have btype set to non-zero.  */
             tcg_debug_assert(s->btype >= 0);
 
@@ -12172,12 +12135,13 @@ static void aarch64_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu)
              * priority -- below debugging exceptions but above most
              * everything else.  This allows us to handle this now
              * instead of waiting until the insn is otherwise decoded.
+             *
+             * We can check all but the guarded page check here;
+             * defer the latter to a helper.
              */
             if (s->btype != 0
-                && s->guarded_page
                 && !btype_destination_ok(insn, s->bt, s->btype)) {
-                gen_exception_insn(s, 0, EXCP_UDEF, syn_btitrap(s->btype));
-                return;
+                gen_helper_guarded_page_check(tcg_env);
             }
         } else {
             /* Not the first insn: btype must be 0.  */
-- 
2.43.0
Re: [PATCH] target/arm: Fix BTI versus CF_PCREL
Posted by Peter Maydell 3 months, 3 weeks ago
On Tue, 30 Jul 2024 at 03:07, Richard Henderson
<richard.henderson@linaro.org> wrote:
>
> With pcrel, we cannot check the guarded page bit at translation
> time, as different mappings of the same physical page may or may
> not have the GP bit set.
>
> Instead, add a couple of helpers to check the page at runtime,
> after all other filters that might obviate the need for the check.
>
> The set_btype_for_br call must be moved after the gen_a64_set_pc
> call to ensure the current pc can still be computed.
>
> Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
> ---
>  target/arm/tcg/helper-a64.h    |  3 ++
>  target/arm/tcg/translate.h     |  2 --
>  target/arm/tcg/helper-a64.c    | 38 ++++++++++++++++++++
>  target/arm/tcg/translate-a64.c | 64 ++++++++--------------------------
>  4 files changed, 55 insertions(+), 52 deletions(-)
>
> diff --git a/target/arm/tcg/helper-a64.h b/target/arm/tcg/helper-a64.h
> index 371388f61b..481007bf39 100644
> --- a/target/arm/tcg/helper-a64.h
> +++ b/target/arm/tcg/helper-a64.h
> @@ -133,6 +133,9 @@ DEF_HELPER_4(cpyfp, void, env, i32, i32, i32)
>  DEF_HELPER_4(cpyfm, void, env, i32, i32, i32)
>  DEF_HELPER_4(cpyfe, void, env, i32, i32, i32)
>
> +DEF_HELPER_FLAGS_1(guarded_page_check, TCG_CALL_NO_WG, void, env)
> +DEF_HELPER_FLAGS_2(guarded_page_br, TCG_CALL_NO_RWG, void, env, tl)
> +
>  DEF_HELPER_FLAGS_5(gvec_fdiv_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32)
>  DEF_HELPER_FLAGS_5(gvec_fdiv_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32)
>  DEF_HELPER_FLAGS_5(gvec_fdiv_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32)
> diff --git a/target/arm/tcg/translate.h b/target/arm/tcg/translate.h
> index a8672c857c..01c217f4a4 100644
> --- a/target/arm/tcg/translate.h
> +++ b/target/arm/tcg/translate.h
> @@ -163,8 +163,6 @@ typedef struct DisasContext {
>      uint8_t dcz_blocksize;
>      /* A copy of cpu->gm_blocksize. */
>      uint8_t gm_blocksize;
> -    /* True if this page is guarded.  */
> -    bool guarded_page;
>      /* True if the current insn_start has been updated. */
>      bool insn_start_updated;
>      /* Bottom two bits of XScale c15_cpar coprocessor access control reg */
> diff --git a/target/arm/tcg/helper-a64.c b/target/arm/tcg/helper-a64.c
> index c60d2a7ec9..424fe927b4 100644
> --- a/target/arm/tcg/helper-a64.c
> +++ b/target/arm/tcg/helper-a64.c
> @@ -1877,3 +1877,41 @@ void HELPER(cpyfe)(CPUARMState *env, uint32_t syndrome, uint32_t wdesc,
>  {
>      do_cpye(env, syndrome, wdesc, rdesc, false, GETPC());
>  }
> +
> +static bool is_guarded_page(CPUARMState *env, target_ulong addr)
> +{
> +#ifdef CONFIG_USER_ONLY
> +    return page_get_flags(addr) & PAGE_BTI;
> +#else
> +    CPUTLBEntryFull *full;
> +    void *host;
> +    int mmu_idx = cpu_mmu_index(env_cpu(env), true);
> +    int flags = probe_access_full(env, addr, 0, MMU_INST_FETCH, mmu_idx,
> +                                  false, &host, &full, 0);
> +
> +    assert(!(flags & TLB_INVALID_MASK));

Is there a race condition here where some other vCPU
knocks this entry out of the TLB between the point when
we started executing the TB and when we made this helper
function call ?

> +    return full->extra.arm.guarded;
> +#endif
> +}

thanks
-- PMM
Re: [PATCH] target/arm: Fix BTI versus CF_PCREL
Posted by Richard Henderson 3 months, 3 weeks ago
On 7/30/24 19:30, Peter Maydell wrote:
>> +static bool is_guarded_page(CPUARMState *env, target_ulong addr)
>> +{
>> +#ifdef CONFIG_USER_ONLY
>> +    return page_get_flags(addr) & PAGE_BTI;
>> +#else
>> +    CPUTLBEntryFull *full;
>> +    void *host;
>> +    int mmu_idx = cpu_mmu_index(env_cpu(env), true);
>> +    int flags = probe_access_full(env, addr, 0, MMU_INST_FETCH, mmu_idx,
>> +                                  false, &host, &full, 0);
>> +
>> +    assert(!(flags & TLB_INVALID_MASK));
> 
> Is there a race condition here where some other vCPU
> knocks this entry out of the TLB between the point when
> we started executing the TB and when we made this helper
> function call ?

I don't think so, because cross-cpu flushes use async_safe_run_on_cpu, which will wait 
until this cpu returns to the main loop.  But it's just as easy to allow this probe to 
fault and unwind, Just In Case.


r~
Re: [PATCH] target/arm: Fix BTI versus CF_PCREL
Posted by Philippe Mathieu-Daudé 3 months, 3 weeks ago
On 30/7/24 03:39, Richard Henderson wrote:
> With pcrel, we cannot check the guarded page bit at translation
> time, as different mappings of the same physical page may or may
> not have the GP bit set.
> 
> Instead, add a couple of helpers to check the page at runtime,
> after all other filters that might obviate the need for the check.
> 
> The set_btype_for_br call must be moved after the gen_a64_set_pc
> call to ensure the current pc can still be computed.
> 
> Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
> ---
>   target/arm/tcg/helper-a64.h    |  3 ++
>   target/arm/tcg/translate.h     |  2 --
>   target/arm/tcg/helper-a64.c    | 38 ++++++++++++++++++++
>   target/arm/tcg/translate-a64.c | 64 ++++++++--------------------------
>   4 files changed, 55 insertions(+), 52 deletions(-)

Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>