Re: [PATCH 1/9] arm64: Add logic to fully remove features from sanitised id registers

Fuad Tabba
2026-02-20T08:36:42Z

Hi Marc,

On Thu, 19 Feb 2026 at 19:55, Marc Zyngier <maz@kernel.org> wrote:
>
> We currently make support for some features such as Pointer Auth,
> SVE or S1POE a compile time decision.
>
> However, while we hide that feature from userspace when such support
> is disabled, we still leave the value provided by the HW visible to
> the rest of the kernel, including KVM.
>
> This has the potential to result in ugly state leakage, as half of
> the kernel knows about the feature, and the other doesn't.
>
> Short of completely banning such compilation options and restore
> universal knowledge, introduce the possibility to fully remove such
> knowledge from the sanitised id registers.
>
> This has more or less the same effect as the idreg override that
> a user can pass on the command-line, only defined at build-time.
>
> For that purpose, we provide a new macro (FTR_CONFIG()) that defines
> the behaviour of a feature, both when enabled and disabled.
>
> At this stage, nothing is making use of this anti-feature.
>
> Signed-off-by: Marc Zyngier <maz@kernel.org>
> ---
> arch/arm64/include/asm/cpufeature.h | 15 ++++++++++-----
> arch/arm64/kernel/cpufeature.c | 21 ++++++++++++++++-----
> 2 files changed, 26 insertions(+), 10 deletions(-)
>
> diff --git a/arch/arm64/include/asm/cpufeature.h b/arch/arm64/include/asm/cpufeature.h
> index 4de51f8d92cba..2731ea13c2c86 100644
> --- a/arch/arm64/include/asm/cpufeature.h
> +++ b/arch/arm64/include/asm/cpufeature.h
> @@ -53,15 +53,20 @@ enum ftr_type {
> #define FTR_SIGNED true /* Value should be treated as signed */
> #define FTR_UNSIGNED false /* Value should be treated as unsigned */
>
> -#define FTR_VISIBLE true /* Feature visible to the user space */
> -#define FTR_HIDDEN false /* Feature is hidden from the user */
> +enum ftr_visibility {
> + FTR_HIDDEN, /* Feature hidden from the user */
> + FTR_ALL_HIDDEN, /* Feature hidden from kernel, user and KVM */
> + FTR_VISIBLE, /* Feature visible to all observers */
> +};
> +
> +#define FTR_CONFIG(c, e, d) \
> + (IS_ENABLED(c) ? FTR_ ## e : FTR_ ## d)
>
> -#define FTR_VISIBLE_IF_IS_ENABLED(config) \
> - (IS_ENABLED(config) ? FTR_VISIBLE : FTR_HIDDEN)
> +#define FTR_VISIBLE_IF_IS_ENABLED(c) FTR_CONFIG(c, VISIBLE, HIDDEN)
>
> struct arm64_ftr_bits {
> bool sign; /* Value is signed ? */
> - bool visible;
> + enum ftr_visibility visibility;
> bool strict; /* CPU Sanity check: strict matching required ? */
> enum ftr_type type;
> u8 shift;

This introduces bloat. Should you group the bools together and the
enums together?

> diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c
> index c840a93b9ef95..b34a39967d111 100644
> --- a/arch/arm64/kernel/cpufeature.c
> +++ b/arch/arm64/kernel/cpufeature.c
> @@ -192,7 +192,7 @@ void dump_cpu_features(void)
> #define __ARM64_FTR_BITS(SIGNED, VISIBLE, STRICT, TYPE, SHIFT, WIDTH, SAFE_VAL) \
> { \
> .sign = SIGNED, \
> - .visible = VISIBLE, \
> + .visibility = VISIBLE, \
> .strict = STRICT, \
> .type = TYPE, \
> .shift = SHIFT, \
> @@ -1057,17 +1057,28 @@ static void init_cpu_ftr_reg(u32 sys_reg, u64 new)
> ftrp->shift);
> }
>
> - val = arm64_ftr_set_value(ftrp, val, ftr_new);
> -
> valid_mask |= ftr_mask;
> if (!ftrp->strict)

Should FTR_ALL_HIDDEN also be removed from strict_mask? i.e.

- if (!ftrp->strict)
+ if (!ftrp->strict || || ftrp->visibility == FTR_ALL_HIDDEN)

(or under the ALL_HIDDEN case below).

> strict_mask &= ~ftr_mask;
> - if (ftrp->visible)
> +
> + switch (ftrp->visibility) {
> + case FTR_VISIBLE:
> + val = arm64_ftr_set_value(ftrp, val, ftr_new);
> user_mask |= ftr_mask;
> - else
> + break;
> + case FTR_ALL_HIDDEN:
> + val = arm64_ftr_set_value(ftrp, val, ftrp->safe_val);
> + reg->user_val = arm64_ftr_set_value(ftrp,
> + reg->user_val,
> + ftrp->safe_val);

Should we also take the safe value in update_cpu_ftr_reg() for FTR_ALL_HIDDEN?

Cheers,
/fuad

> + break;
> + case FTR_HIDDEN:
> + val = arm64_ftr_set_value(ftrp, val, ftr_new);
> reg->user_val = arm64_ftr_set_value(ftrp,
> reg->user_val,
> ftrp->safe_val);
> + break;
> + }
> }
>
> val &= valid_mask;
> --
> 2.47.3
>