[PATCH v2 03/11] arm64: Add logic to fully remove features from sanitised id registers

Suzuki K Poulose suzuki.poulose at arm.com
Mon Mar 2 05:35:41 PST 2026


On 02/03/2026 11:56, Marc Zyngier 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 at kernel.org>
> ---
>   arch/arm64/include/asm/cpufeature.h | 17 +++++++++------
>   arch/arm64/kernel/cpufeature.c      | 32 ++++++++++++++++++++++-------
>   2 files changed, 36 insertions(+), 13 deletions(-)
> 
> diff --git a/arch/arm64/include/asm/cpufeature.h b/arch/arm64/include/asm/cpufeature.h
> index 4de51f8d92cba..e853a0ac7db38 100644
> --- a/arch/arm64/include/asm/cpufeature.h
> +++ b/arch/arm64/include/asm/cpufeature.h
> @@ -53,17 +53,22 @@ 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:8;
>   	bool		strict;	/* CPU Sanity check: strict matching required ? */
> -	enum ftr_type	type;
> +	enum ftr_type	type:8;
>   	u8		shift;
>   	u8		width;
>   	s64		safe_val; /* safe value for FTR_EXACT features */
> diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c
> index 102c5bac4d502..965dd2acf0640 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,				\
> @@ -1063,16 +1063,33 @@ 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)
>   			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:
> +			/*
> +			 * ALL_HIDDEN and HIGHER_SAFE are incompatible.
> +			 * Only hide from userspace, and log the oddity.
> +			 */
> +			if (WARN_ON(ftrp->type == FTR_HIGHER_SAFE))
> +				val = arm64_ftr_set_value(ftrp, val, ftr_new);
> +			else
> +				val = arm64_ftr_set_safe_value(ftrp, val);
>   			reg->user_val = arm64_ftr_set_safe_value(ftrp,
>   								 reg->user_val);
> +			break;
> +		case FTR_HIDDEN:
> +			val = arm64_ftr_set_value(ftrp, val, ftr_new);
> +			reg->user_val = arm64_ftr_set_safe_value(ftrp,
> +								 reg->user_val);
> +			break;
> +		}
>   	}
>   
>   	val &= valid_mask;
> @@ -1230,9 +1247,10 @@ static void update_cpu_ftr_reg(struct arm64_ftr_reg *reg, u64 new)
>   
>   		/*
>   		 * Don't alter the initial value that has been forced
> -		 * by an override.
> +		 * by an override or a disabled feature.
>   		 */
> -		if ((reg->override->mask & arm64_ftr_mask(ftrp)) == arm64_ftr_mask(ftrp))
> +		if (ftrp->visibility == FTR_ALL_HIDDEN ||
> +		    (reg->override->mask & arm64_ftr_mask(ftrp)) == arm64_ftr_mask(ftrp))
>   			continue;
>   
>   		if (ftr_cur == ftr_new)


Looks good to me.

Reviewed-by: Suzuki K Poulose <suzuki.poulose at arm.com>




More information about the linux-arm-kernel mailing list