Linux-ARM-Kernel Archive on lore.kernel.org
 help / color / mirror / Atom feed
* Re: [PATCH net 1/2] net: airoha: Fix use-after-free in metadata dst teardown
From: Lorenzo Bianconi @ 2026-06-04 21:23 UTC (permalink / raw)
  To: Jacob Keller
  Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Felix Fietkau, Matthias Brugger,
	AngeloGioacchino Del Regno, Florian Westphal, linux-arm-kernel,
	linux-mediatek, netdev
In-Reply-To: <21810a20-abe6-4490-969c-cfd62c4c082a@intel.com>

[-- Attachment #1: Type: text/plain, Size: 3295 bytes --]

> On 6/2/2026 2:21 AM, Lorenzo Bianconi wrote:
> > airoha_metadata_dst_free() runs metadata_dst_free() which frees the
> > metadata_dst with kfree() immediately, bypassing the RCU grace period.
> > In the RX path, skb_dst_set_noref() sets a non-refcounted pointer from
> > the skb to the metadata_dst. This function requires RCU read-side
> > protection and the dst must remain valid until all RCU readers complete.
> > Since metadata_dst_free() calls kfree() directly, an use-after-free can
> > occur if any skb still holds a noref pointer to the dst when the driver
> > tears it down.
> > Replace metadata_dst_free() with dst_release() which properly goes
> > through the refcount path: when the refcount drops to zero, it schedules
> > the actual free via call_rcu_hurry(), ensuring all RCU readers have
> > completed before the memory is freed.
> > 
> > Fixes: af3cf757d5c9 ("net: airoha: Move DSA tag in DMA descriptor")
> > Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
> > ---
> >  drivers/net/ethernet/airoha/airoha_eth.c | 2 +-
> >  1 file changed, 1 insertion(+), 1 deletion(-)
> > 
> > diff --git a/drivers/net/ethernet/airoha/airoha_eth.c b/drivers/net/ethernet/airoha/airoha_eth.c
> > index cecd66251dba..eab6a98d62b9 100644
> > --- a/drivers/net/ethernet/airoha/airoha_eth.c
> > +++ b/drivers/net/ethernet/airoha/airoha_eth.c
> > @@ -2936,7 +2936,7 @@ static void airoha_metadata_dst_free(struct airoha_gdm_port *port)
> >  		if (!port->dsa_meta[i])
> >  			continue;
> >  
> > -		metadata_dst_free(port->dsa_meta[i]);
> > +		dst_release(&port->dsa_meta[i]->dst);
> >  	}
> >  }
> >  
> > 
> 
> the port->dsa_meta is allocated using metadata_dst_alloc().. how is it
> safe to use dst_release here? Seems like we should be calling dst_alloc
> instead of metadata_dst_alloc in order to use dst_release??

We need to allocate the metadata_dst using metadata_dst_alloc() since
md_dst->u.port_info.port_id is consumed in dsa_switch_rcv() to get the
switch conduit port.
I guess it is fine to free metadata_dst running dst_release() since dst_init()
sets DST_METADATA flag and so dst_destroy() runs metadata_dst_free() after the
RCU grace period.

> 
> metadata_dst_alloc does call __metadata_dst_init which calls dst_init..
> 
> I guess the start of the metadata_dst structure is also the same address
> as the internal dst_entry struct...
> 
> But dst_destroy does a whole lot more than metadata_dst_release so I
> don't feel confident in this actually being a drop-in replacement... It
> calls netdev_put, it calls the dst->ops->destroy, it releases child
> refs.. Or for metadata dst entries is that all basically a no-op??

__metadata_dst_init() calls dst_init() with dev = NULL so netdev_put() is a
no-op. Same for dst->ops is dst_blackhole_ops and and dst_blackhole_ops has no
destroy callback.

> 
> I feel like I'm missing something here.. The driver also calls
> metadata_dst_free in the remove path and that wasn't changed by this
> patch either.

can you please explain what you mean here? we do not run metadata_dst_free()
anymore.

Regards,
Lorenzo

> 
> Generally it seems like we should be using the same API to allocate as
> to release the object... This is confusing. What am I missing?

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

^ permalink raw reply

* [PATCH] KVM: arm64: vgic: Use list_del_rcu() when flushing pending LPIs
From: Hyunwoo Kim @ 2026-06-04 21:16 UTC (permalink / raw)
  To: maz, oupton, joey.gouly, seiden, suzuki.poulose, yuzenghui,
	catalin.marinas, will, Sascha.Bischoff, jic23
  Cc: linux-arm-kernel, kvmarm, imv4bel

vgic_v3_fold_lr_state() walks the ap_list from last_lr_irq without holding
the ap_list_lock, relying on vgic_irq being freed via kfree_rcu() and on
interrupts being disabled. vgic_flush_pending_lpis() removes entries with
list_del(), which clobbers a node's next pointer, so when another vCPU
disables LPIs via GICR_CTLR the walk can follow the clobbered next pointer
from a removed node, or from the node that last_lr_irq points to.

Remove entries with list_del_rcu() so that the next pointer stays valid
until the walk completes.

Fixes: 3cfd59f81e0f ("KVM: arm64: GICv3: Handle LR overflow when EOImode==0")
Signed-off-by: Hyunwoo Kim <imv4bel@gmail.com>
---
 arch/arm64/kvm/vgic/vgic.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/arch/arm64/kvm/vgic/vgic.c b/arch/arm64/kvm/vgic/vgic.c
index 1e9fe8764584d..73efc0f95bfb1 100644
--- a/arch/arm64/kvm/vgic/vgic.c
+++ b/arch/arm64/kvm/vgic/vgic.c
@@ -204,7 +204,7 @@ void vgic_flush_pending_lpis(struct kvm_vcpu *vcpu)
 	list_for_each_entry_safe(irq, tmp, &vgic_cpu->ap_list_head, ap_list) {
 		if (irq_is_lpi(vcpu->kvm, irq->intid)) {
 			raw_spin_lock(&irq->irq_lock);
-			list_del(&irq->ap_list);
+			list_del_rcu(&irq->ap_list);
 			irq->vcpu = NULL;
 			raw_spin_unlock(&irq->irq_lock);
 			deleted |= vgic_put_irq_norelease(vcpu->kvm, irq);
-- 
2.43.0



^ permalink raw reply related

* Re: [PATCH bpf-next v2 4/8] bpf, x86: refactor BPF_ST management in do_jit
From: bot+bpf-ci @ 2026-06-04 21:13 UTC (permalink / raw)
  To: alexis.lothore, ast, daniel, andrii, martin.lau, eddyz87, memxor,
	song, yonghong.song, jolsa, john.fastabend, tglx, mingo, bp,
	dave.hansen, x86, hpa, shuah, mcoquelin.stm32, alexandre.torgue,
	ihor.solodrai
  Cc: ebpf, bastien.curutchet, thomas.petazzoni, bpf, linux-kernel,
	linux-kselftest, linux-stm32, linux-arm-kernel, alexis.lothore,
	ast, andrii, daniel, martin.lau, eddyz87, yonghong.song, clm,
	ihor.solodrai
In-Reply-To: <20260604-kasan-v2-4-c066e627fda8@bootlin.com>

[-- Attachment #1: Type: text/plain, Size: 3412 bytes --]

> diff --git a/arch/x86/net/bpf_jit_comp.c b/arch/x86/net/bpf_jit_comp.c
> index 0981791014eb..943a0f315cf2 100644
> --- a/arch/x86/net/bpf_jit_comp.c
> +++ b/arch/x86/net/bpf_jit_comp.c
> @@ -2300,41 +2300,50 @@ static int do_jit(struct bpf_verifier_env *env, struct bpf_prog *bpf_prog, int *
>  			EMIT_LFENCE();
>  			break;
>
> -			/* ST: *(u8*)(dst_reg + off) = imm */
>  		case BPF_ST | BPF_MEM | BPF_B:

[ ... ]

>  		case BPF_ST | BPF_MEM | BPF_DW:
> -			if (dst_reg == BPF_REG_PARAMS && insn->off == -8) {
> -				/* Arg 6: store immediate in r9 register */
> -				emit_mov_imm64(&prog, X86_REG_R9, imm32 >> 31, (u32)imm32);
> +			switch (BPF_SIZE(insn->code)) {

[ ... ]

> +			case BPF_DW:
> +				if (dst_reg == BPF_REG_PARAMS &&
> +				    insn->off == -8) {
> +					/* Arg 6: store immediate in r9 register */
> +					emit_mov_imm64(&prog, X86_REG_R9,
> +						       imm32 >> 31, (u32)imm32);
> +					break;
> +				}
> +				EMIT2(add_1mod(0x48, dst_reg), 0xC7);
>  				break;
>  			}
> -			EMIT2(add_1mod(0x48, dst_reg), 0xC7);
>
> -st:			insn_off = insn->off;
> +			insn_off = insn->off;

Has the scope of the Arg 6 break changed here?

In the original code the BPF_DW handling lived directly under the case
label, and its break left the whole outer instruction switch:

	case BPF_ST | BPF_MEM | BPF_DW:
		if (dst_reg == BPF_REG_PARAMS && insn->off == -8) {
			/* Arg 6: store immediate in r9 register */
			emit_mov_imm64(&prog, X86_REG_R9, imm32 >> 31, (u32)imm32);
			break;
		}
		EMIT2(add_1mod(0x48, dst_reg), 0xC7);
	st:	insn_off = insn->off;
		...

So for the Arg 6 case it emitted exactly the movabs into r9 and stopped,
never reaching the st: addressing and immediate emission.

After the refactor, the Arg 6 branch lives inside the inner switch on
BPF_SIZE(), so its break only exits that inner switch:

	case BPF_DW:
		if (dst_reg == BPF_REG_PARAMS &&
		    insn->off == -8) {
			emit_mov_imm64(&prog, X86_REG_R9,
				       imm32 >> 31, (u32)imm32);
			break;
		}
		EMIT2(add_1mod(0x48, dst_reg), 0xC7);
		break;
	}

	insn_off = insn->off;
	if (dst_reg == BPF_REG_PARAMS) {
		insn_off = outgoing_arg_base - outgoing_rsp -
			   insn_off - 16;
		dst_reg = BPF_REG_FP;
	}
	if (is_imm8(insn_off))
		EMIT2(add_1reg(0x40, dst_reg), insn_off);
	else
		EMIT1_off32(add_1reg(0x80, dst_reg), insn_off);
	EMIT(imm32, bpf_size_to_x86_bytes(BPF_SIZE(insn->code)));

After emitting the movabs into r9, does control now fall through into
the common insn_off block and emit an additional ModRM/displacement byte
sequence plus a 4-byte immediate that do not belong to any instruction?

Would those extra bytes corrupt the emitted machine code stream for the
Arg 6 path, since dst_reg == BPF_REG_PARAMS with insn->off == -8 is a
reachable case used to pass the 6th outgoing call argument in r9?

Could the original break be preserved here, for example by breaking out
of the outer switch (or skipping the insn_off block) once the Arg 6
movabs has been emitted?

The commit message describes this as grouping the BPF_ST instructions
"in a single block of fall-through cases", implying no behavioral change,
but the Arg 6 path appears to gain extra emitted bytes.


---
AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md

CI run summary: https://github.com/kernel-patches/bpf/actions/runs/26978380520

^ permalink raw reply

* Re: [PATCH bpf-next v2 2/8] bpf: add BPF_JIT_KASAN for KASAN instrumentation of JITed programs
From: bot+bpf-ci @ 2026-06-04 21:13 UTC (permalink / raw)
  To: alexis.lothore, ast, daniel, andrii, martin.lau, eddyz87, memxor,
	song, yonghong.song, jolsa, john.fastabend, tglx, mingo, bp,
	dave.hansen, x86, hpa, shuah, mcoquelin.stm32, alexandre.torgue,
	ihor.solodrai
  Cc: ebpf, bastien.curutchet, thomas.petazzoni, bpf, linux-kernel,
	linux-kselftest, linux-stm32, linux-arm-kernel, alexis.lothore,
	ast, andrii, daniel, martin.lau, eddyz87, yonghong.song, clm,
	ihor.solodrai
In-Reply-To: <20260604-kasan-v2-2-c066e627fda8@bootlin.com>

[-- Attachment #1: Type: text/plain, Size: 1563 bytes --]

> diff --git a/kernel/bpf/Kconfig b/kernel/bpf/Kconfig
> index eb3de35734f0..a8e004f88b92 100644
> --- a/kernel/bpf/Kconfig
> +++ b/kernel/bpf/Kconfig

[ ... ]

> @@ -101,4 +105,9 @@ config BPF_LSM
>
>  	  If you are unsure how to answer this question, answer N.
>
> +config BPF_JIT_KASAN
> +	bool
> +	depends on HAVE_EBPF_JIT_KASAN
> +	default y if BPF_JIT && KASAN_GENERIC && KASAN_VMALLOC && VMAP_STACK
> +
>  endmenu # "BPF subsystem"

This isn't a bug, but a design concern raised on an earlier posting of
this work doesn't look fully resolved.

In the v1 (RFC) discussion, the suggestion was to not add a config knob
at all and instead do the instrumentation inside the JIT whenever
KASAN_GENERIC is set:

  "Let's not add more config knobs. Even this patch looks redundant.
   Inside JIT do instrumentation when KASAN_GENERIC is set."

  https://lore.kernel.org/bpf/CAADnVQLj=UosnsC-4V2+mN92Fe0-kW++U+m-O9c93kk6BwiXgw@mail.gmail.com/

The reasoning given for keeping the internal Kconfig was that it lets
selftests adapt their behavior without sprinkling arch and KASAN
conditionals, and another reviewer agreed that approach was reasonable.

That "this patch looks redundant" objection was never explicitly
retracted or re-acked, yet the BPF_JIT_KASAN config is retained here.

Has this design point been settled?


---
AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md

CI run summary: https://github.com/kernel-patches/bpf/actions/runs/26978380520

^ permalink raw reply

* Re: [PATCH bpf-next v2 1/8] bpf: mark instructions accessing program stack
From: bot+bpf-ci @ 2026-06-04 21:13 UTC (permalink / raw)
  To: alexis.lothore, ast, daniel, andrii, martin.lau, eddyz87, memxor,
	song, yonghong.song, jolsa, john.fastabend, tglx, mingo, bp,
	dave.hansen, x86, hpa, shuah, mcoquelin.stm32, alexandre.torgue,
	ihor.solodrai
  Cc: ebpf, bastien.curutchet, thomas.petazzoni, bpf, linux-kernel,
	linux-kselftest, linux-stm32, linux-arm-kernel, alexis.lothore,
	ast, andrii, daniel, martin.lau, eddyz87, yonghong.song, clm,
	ihor.solodrai
In-Reply-To: <20260604-kasan-v2-1-c066e627fda8@bootlin.com>

[-- Attachment #1: Type: text/plain, Size: 3627 bytes --]

> diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c
> index a656a8572bdb..393d9eacd215 100644
> --- a/kernel/bpf/core.c
> +++ b/kernel/bpf/core.c

[ ... ]

> @@ -1583,6 +1583,22 @@ bool bpf_insn_is_indirect_target(const struct bpf_verifier_env *env, const struc
>  	return env->insn_aux_data[insn_idx].indirect_target;
>  }
>
> +bool bpf_insn_accesses_stack_only(const struct bpf_verifier_env *env,
> +				  const struct bpf_prog *prog, int insn_idx)
> +{
> +	struct bpf_insn *insn;
> +
> +	/* cBPF: we have no verifier state, do a best-effort check based on
> +	 * dst/src reg
> +	 */
> +	insn_idx += prog->aux->subprog_start;
> +	insn = (struct bpf_insn *)prog->insnsi + insn_idx;
> +	if (!env)
> +		return insn->dst_reg == BPF_REG_FP ||
> +		       insn->src_reg == BPF_REG_FP;
> +	return !env->insn_aux_data[insn_idx].non_stack_access;
> +}
> +

Sashiko bot raised a concern about this code in v2 review: When JIT
compiling an eBPF subprogram, prog points to the subprogram and
prog->insnsi only spans the subprogram's instructions.

By adding prog->aux->subprog_start to insn_idx, doesn't this result in
out-of-bounds pointer arithmetic for insn? Even though the pointer is not
dereferenced here because of the if (!env) check, could this trigger UBSAN
bounds-checking traps?

Should the insn pointer calculation be deferred until inside the !env block
where it is genuinely needed for cBPF?

Reference: https://lore.kernel.org/bpf/20260604203644.81EF51F00893@smtp.kernel.org/

> diff --git a/kernel/bpf/fixups.c b/kernel/bpf/fixups.c
> index 5aa3f7d99ac9..5228c910fbf5 100644
> --- a/kernel/bpf/fixups.c
> +++ b/kernel/bpf/fixups.c

[ ... ]

> @@ -185,16 +185,22 @@ static void adjust_insn_aux_data(struct bpf_verifier_env *env,
>  	}
>
>  	/*
> -	 * The indirect_target flag of the original instruction was moved to the last of the
> -	 * new instructions by the above memmove and memset, but the indirect jump target is
> -	 * actually the first instruction, so move it back. This also matches with the behavior
> -	 * of bpf_insn_array_adjust(), which preserves xlated_off to point to the first new
> -	 * instruction.
> +	 * The indirect_target and non_stack_access flags of the original
> +	 * instruction were moved to the last of the new instructions by the
> +	 * above memmove and memset, but those actually match the first
> +	 * instruction, so move them back. This also matches with the behavior
> +	 * of bpf_insn_array_adjust(), which preserves xlated_off to point to
> +	 * the first new instruction.
>  	 */
>  	if (data[off + cnt - 1].indirect_target) {
>  		data[off].indirect_target = 1;
>  		data[off + cnt - 1].indirect_target = 0;
>  	}
> +
> +	if (data[off + cnt - 1].non_stack_access) {
> +		data[off].non_stack_access = 1;
> +		data[off + cnt - 1].non_stack_access = 0;
> +	}
>  }

Sashiko bot raised a concern about this logic in v2 review: When
bpf_patch_insn_data() expands a single instruction into multiple
instructions, this logic restricts the non_stack_access flag to only the
first instruction of the patched sequence and clears it for the rest.

Since the actual memory load or store in a patched sequence often occurs on
subsequent instructions rather than the first, wouldn't they lose this flag
and incorrectly bypass KASAN checks during JIT compilation?

Reference: https://lore.kernel.org/bpf/20260604203644.81EF51F00893@smtp.kernel.org/



---
AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md

CI run summary: https://github.com/kernel-patches/bpf/actions/runs/26978380520

^ permalink raw reply

* [PATCH] KVM: arm64: vgic: Check the interrupt is still ours before migrating it
From: Hyunwoo Kim @ 2026-06-04 20:59 UTC (permalink / raw)
  To: maz, oupton, joey.gouly, seiden, suzuki.poulose, yuzenghui,
	catalin.marinas, will, Sascha.Bischoff, jic23, timothy.hayes,
	eric.auger, christoffer.dall, andre.przywara
  Cc: linux-arm-kernel, kvmarm, imv4bel

vgic_prune_ap_list() drops both ap_list_lock and irq_lock while migrating
an interrupt to another vCPU. After reacquiring the locks it only checks
that the affinity is unchanged (target_vcpu == vgic_target_oracle(irq))
before moving the interrupt, which assumes that an interrupt whose affinity
is preserved is still queued on this vCPU's ap_list.

That assumption no longer holds if the interrupt is taken off the ap_list
while the locks are dropped. vgic_flush_pending_lpis() removes the
interrupt from the list and sets irq->vcpu to NULL, but leaves
enabled/pending/target_vcpu untouched. As the interrupt is still enabled
and pending, vgic_target_oracle() returns the same target_vcpu, so the
affinity check passes and list_del() is run a second time on an entry that
has already been removed.

Also check that the interrupt is still assigned to this vCPU
(irq->vcpu == vcpu) before moving it.

Fixes: 0919e84c0fc1 ("KVM: arm/arm64: vgic-new: Add IRQ sync/flush framework")
Signed-off-by: Hyunwoo Kim <imv4bel@gmail.com>
---
 arch/arm64/kvm/vgic/vgic.c | 11 ++++++-----
 1 file changed, 6 insertions(+), 5 deletions(-)

diff --git a/arch/arm64/kvm/vgic/vgic.c b/arch/arm64/kvm/vgic/vgic.c
index 1e9fe8764584..18b280de9a29 100644
--- a/arch/arm64/kvm/vgic/vgic.c
+++ b/arch/arm64/kvm/vgic/vgic.c
@@ -818,15 +818,16 @@ static void vgic_prune_ap_list(struct kvm_vcpu *vcpu)
 		raw_spin_lock(&irq->irq_lock);
 
 		/*
-		 * If the affinity has been preserved, move the
-		 * interrupt around. Otherwise, it means things have
-		 * changed while the interrupt was unlocked, and we
-		 * need to replay this.
+		 * If the interrupt is still ours and its affinity has
+		 * been preserved, move it around. Otherwise, it means
+		 * things have changed while the interrupt was unlocked
+		 * (it may even have been taken off the list with its
+		 * affinity left untouched), and we need to replay this.
 		 *
 		 * In all cases, we cannot trust the list not to have
 		 * changed, so we restart from the beginning.
 		 */
-		if (target_vcpu == vgic_target_oracle(irq)) {
+		if (irq->vcpu == vcpu && target_vcpu == vgic_target_oracle(irq)) {
 			struct vgic_cpu *new_cpu = &target_vcpu->arch.vgic_cpu;
 
 			list_del(&irq->ap_list);
-- 
2.43.0



^ permalink raw reply related

* Re: [PATCH v2] regulator: dt-bindings: mt6311: Convert to DT schema
From: Rob Herring (Arm) @ 2026-06-04 20:57 UTC (permalink / raw)
  To: Ninad Naik
  Cc: broonie, devicetree, linux-arm-kernel, conor+dt, krzk+dt,
	matthias.bgg, angelogioacchino.delregno, me, linux-kernel-mentees,
	skhan, linux-mediatek, lgirdwood, linux-kernel
In-Reply-To: <20260604162624.644241-1-ninadnaik07@gmail.com>


On Thu, 04 Jun 2026 21:56:24 +0530, Ninad Naik wrote:
> Convert mediatek,mt6311 to DT schema.
> 
> Signed-off-by: Ninad Naik <ninadnaik07@gmail.com>
> ---
> Changes in v2:
> - Correct "MediaTek" in the title.
> - Drop "|" in the top-level description.
> - Remove unnecessary regulator node description.
> - Remove unused labels from example.
> 
>  .../regulator/mediatek,mt6311-regulator.yaml  | 70 +++++++++++++++++++
>  .../bindings/regulator/mt6311-regulator.txt   | 35 ----------
>  2 files changed, 70 insertions(+), 35 deletions(-)
>  create mode 100644 Documentation/devicetree/bindings/regulator/mediatek,mt6311-regulator.yaml
>  delete mode 100644 Documentation/devicetree/bindings/regulator/mt6311-regulator.txt
> 

Reviewed-by: Rob Herring (Arm) <robh@kernel.org>



^ permalink raw reply

* [PATCH v1] spi: Use named initializers for platform_device_id arrays
From: Uwe Kleine-König (The Capable Hub) @ 2026-06-04 20:55 UTC (permalink / raw)
  To: Mark Brown
  Cc: Jonas Gorski, David Rhodes, Richard Fitzgerald, Andi Shyti,
	Tudor Ambarus, Krzysztof Kozlowski, Peter Griffin, Alim Akhtar,
	linux-spi, linux-kernel, patches, linux-samsung-soc,
	linux-arm-kernel

Named initializers are better readable and more robust to changes of the
struct definition. This robustness is relevant for a planned change to
struct platform_device_id replacing .driver_data by an anonymous union.

While touching these arrays unify spacing and usage of commas.

Signed-off-by: Uwe Kleine-König (The Capable Hub) <u.kleine-koenig@baylibre.com>
---
Hello,

see e.g.
https://lore.kernel.org/all/cover.1779893336.git.u.kleine-koenig@baylibre.com/
for details about my quest to modify platform_device_id.

Best regards
Uwe

 drivers/spi/spi-altera-platform.c | 4 ++--
 drivers/spi/spi-bcm63xx.c         | 3 +--
 drivers/spi/spi-cs42l43.c         | 4 ++--
 drivers/spi/spi-rspi.c            | 4 ++--
 drivers/spi/spi-s3c64xx.c         | 6 +++---
 drivers/spi/spi-sh-msiof.c        | 4 ++--
 6 files changed, 12 insertions(+), 13 deletions(-)

diff --git a/drivers/spi/spi-altera-platform.c b/drivers/spi/spi-altera-platform.c
index 3ee5d3480bb4..3de7df73f216 100644
--- a/drivers/spi/spi-altera-platform.c
+++ b/drivers/spi/spi-altera-platform.c
@@ -139,8 +139,8 @@ MODULE_DEVICE_TABLE(of, altera_spi_match);
 #endif /* CONFIG_OF */
 
 static const struct platform_device_id altera_spi_ids[] = {
-	{ DRV_NAME,		ALTERA_SPI_TYPE_UNKNOWN },
-	{ "subdev_spi_altera",	ALTERA_SPI_TYPE_SUBDEV },
+	{ .name = DRV_NAME,		.driver_data = ALTERA_SPI_TYPE_UNKNOWN },
+	{ .name = "subdev_spi_altera",	.driver_data = ALTERA_SPI_TYPE_SUBDEV },
 	{ }
 };
 MODULE_DEVICE_TABLE(platform, altera_spi_ids);
diff --git a/drivers/spi/spi-bcm63xx.c b/drivers/spi/spi-bcm63xx.c
index f8cfe535b2a3..43d7b54e3ae8 100644
--- a/drivers/spi/spi-bcm63xx.c
+++ b/drivers/spi/spi-bcm63xx.c
@@ -477,8 +477,7 @@ static const struct platform_device_id bcm63xx_spi_dev_match[] = {
 		.name = "bcm6358-spi",
 		.driver_data = (unsigned long)bcm6358_spi_reg_offsets,
 	},
-	{
-	},
+	{ }
 };
 MODULE_DEVICE_TABLE(platform, bcm63xx_spi_dev_match);
 
diff --git a/drivers/spi/spi-cs42l43.c b/drivers/spi/spi-cs42l43.c
index 68f208ef1e01..6961e36b89d1 100644
--- a/drivers/spi/spi-cs42l43.c
+++ b/drivers/spi/spi-cs42l43.c
@@ -438,8 +438,8 @@ static int cs42l43_spi_probe(struct platform_device *pdev)
 }
 
 static const struct platform_device_id cs42l43_spi_id_table[] = {
-	{ "cs42l43-spi", },
-	{}
+	{ .name = "cs42l43-spi" },
+	{ }
 };
 MODULE_DEVICE_TABLE(platform, cs42l43_spi_id_table);
 
diff --git a/drivers/spi/spi-rspi.c b/drivers/spi/spi-rspi.c
index a0c77e02bc90..38df676774ee 100644
--- a/drivers/spi/spi-rspi.c
+++ b/drivers/spi/spi-rspi.c
@@ -1377,8 +1377,8 @@ static int rspi_probe(struct platform_device *pdev)
 }
 
 static const struct platform_device_id spi_driver_ids[] = {
-	{ "rspi",	(kernel_ulong_t)&rspi_ops },
-	{},
+	{ .name = "rspi", .driver_data = (kernel_ulong_t)&rspi_ops },
+	{ }
 };
 
 MODULE_DEVICE_TABLE(platform, spi_driver_ids);
diff --git a/drivers/spi/spi-s3c64xx.c b/drivers/spi/spi-s3c64xx.c
index 37176e557099..28c56b06fa99 100644
--- a/drivers/spi/spi-s3c64xx.c
+++ b/drivers/spi/spi-s3c64xx.c
@@ -1613,10 +1613,10 @@ static const struct s3c64xx_spi_port_config gs101_spi_port_config = {
 
 static const struct platform_device_id s3c64xx_spi_driver_ids[] = {
 	{
-		.name		= "s3c6410-spi",
-		.driver_data	= (kernel_ulong_t)&s3c6410_spi_port_config,
+		.name = "s3c6410-spi",
+		.driver_data = (kernel_ulong_t)&s3c6410_spi_port_config,
 	},
-	{ },
+	{ }
 };
 MODULE_DEVICE_TABLE(platform, s3c64xx_spi_driver_ids);
 
diff --git a/drivers/spi/spi-sh-msiof.c b/drivers/spi/spi-sh-msiof.c
index 070e16bc764f..f23db85a1889 100644
--- a/drivers/spi/spi-sh-msiof.c
+++ b/drivers/spi/spi-sh-msiof.c
@@ -1310,8 +1310,8 @@ static void sh_msiof_spi_remove(struct platform_device *pdev)
 }
 
 static const struct platform_device_id spi_driver_ids[] = {
-	{ "spi_sh_msiof",	(kernel_ulong_t)&sh_data },
-	{},
+	{ .name = "spi_sh_msiof", .driver_data = (kernel_ulong_t)&sh_data },
+	{ }
 };
 MODULE_DEVICE_TABLE(platform, spi_driver_ids);
 

base-commit: a225caacc36546a09586e3ece36c0313146e7da9
-- 
2.47.3



^ permalink raw reply related

* [PATCH v01] mailbox/pcc.c:  add query channel function
From: Adam Young @ 2026-06-04 20:37 UTC (permalink / raw)
  To: Sudeep Holla, Jassi Brar, Rafael J. Wysocki, Saket Dumbre,
	Len Brown
  Cc: linux-kernel, linux-hwmon, linux-acpi, Andi Shyti, Guenter Roeck,
	Huisong Li, MyungJoo Ham, Kyungmin Park, Chanwoo Choi,
	linux-arm-kernel

Drivers need information about a channel prior to creating a channel
or they risk triggering message delivery on the remote side of a
connection.

One of those pieces of infomration is the type of channel.

Add PCC channel type to records and expose PCC channel type to client.

Signed-off-by: Adam Young <admiyo@os.amperecomputing.com>
---
 drivers/mailbox/pcc.c | 39 +++++++++++++++++++++++++++++++++++++++
 include/acpi/pcc.h    | 12 ++++++++++++
 2 files changed, 51 insertions(+)

diff --git a/drivers/mailbox/pcc.c b/drivers/mailbox/pcc.c
index 0deaf7907ed6..c27bea426967 100644
--- a/drivers/mailbox/pcc.c
+++ b/drivers/mailbox/pcc.c
@@ -348,6 +348,44 @@ static irqreturn_t pcc_mbox_irq(int irq, void *p)
 	return IRQ_HANDLED;
 }
 
+/**
+ * pcc_mbox_query_channel - returns information about the channel
+ *              without activating the channel.
+ *
+ * @q_chan a pointer to an already allocated struct pcc_mbox_chan
+ *              that will be populated with the channel data.
+ *
+ * Return: 0 upon success or non-zero upon error.
+ */
+int
+pcc_mbox_query_channel(struct pcc_mbox_chan *q_chan, int subspace_id)
+{
+	struct pcc_mbox_chan *pcc_mchan;
+	struct pcc_chan_info *pchan;
+	struct mbox_chan *chan;
+
+	if (subspace_id < 0 || subspace_id >= pcc_chan_count)
+		return -ENOENT;
+	pchan = chan_info + subspace_id;
+	chan = pchan->chan.mchan;
+	if (IS_ERR(chan)) {
+		pr_err("Channel not found for idx: %d\n", subspace_id);
+		return -EBUSY;
+	}
+	pcc_mchan = &pchan->chan;
+
+	q_chan->shmem_base_addr = pcc_mchan->shmem_base_addr;
+	q_chan->shmem = NULL;
+	q_chan->shmem_size = pcc_mchan->shmem_size;
+	q_chan->latency = pcc_mchan->latency;
+	q_chan->max_access_rate = pcc_mchan->max_access_rate;
+	q_chan->min_turnaround_time = pcc_mchan->min_turnaround_time;
+	q_chan->type = pcc_mchan->type;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(pcc_mbox_query_channel);
+
 /**
  * pcc_mbox_request_channel - PCC clients call this function to
  *		request a pointer to their PCC subspace, from which they
@@ -834,6 +872,7 @@ static int pcc_mbox_probe(struct platform_device *pdev)
 		pcc_parse_subspace_shmem(pchan, pcct_entry);
 
 		pchan->type = pcct_entry->type;
+		pchan->chan.type = pcct_entry->type;
 		pcct_entry = (struct acpi_subtable_header *)
 			((unsigned long) pcct_entry + pcct_entry->length);
 	}
diff --git a/include/acpi/pcc.h b/include/acpi/pcc.h
index 840bfc95bae3..8d0fada6e31f 100644
--- a/include/acpi/pcc.h
+++ b/include/acpi/pcc.h
@@ -8,6 +8,10 @@
 
 #include <linux/mailbox_controller.h>
 #include <linux/mailbox_client.h>
+#include <linux/acpi.h>
+//#include <acpi/actypes.h>
+//#include <acpi/actbl.h>
+//#include <acpi/actbl2.h>
 
 struct pcc_mbox_chan {
 	struct mbox_chan *mchan;
@@ -17,6 +21,7 @@ struct pcc_mbox_chan {
 	u32 latency;
 	u32 max_access_rate;
 	u16 min_turnaround_time;
+	enum acpi_pcct_type type;
 };
 
 /* Generic Communications Channel Shared Memory Region */
@@ -37,6 +42,8 @@ struct pcc_mbox_chan {
 extern struct pcc_mbox_chan *
 pcc_mbox_request_channel(struct mbox_client *cl, int subspace_id);
 extern void pcc_mbox_free_channel(struct pcc_mbox_chan *chan);
+extern int
+pcc_mbox_query_channel(struct pcc_mbox_chan *q_chan, int subspace_id);
 #else
 static inline struct pcc_mbox_chan *
 pcc_mbox_request_channel(struct mbox_client *cl, int subspace_id)
@@ -44,6 +51,11 @@ pcc_mbox_request_channel(struct mbox_client *cl, int subspace_id)
 	return ERR_PTR(-ENODEV);
 }
 static inline void pcc_mbox_free_channel(struct pcc_mbox_chan *chan) { }
+static inline int
+pcc_mbox_query_channel(struct pcc_mbox_chan *q_chan, int subspace_id)
+{
+	return -ENODEV;
+}
 #endif
 
 #endif /* _PCC_H */
-- 
2.43.0



^ permalink raw reply related

* [RFC PATCH 2/2] ASoC: atmel: ac97c: Fix use-after-free on driver teardown
From: Manish Baing @ 2026-06-04 20:36 UTC (permalink / raw)
  To: perex, tiwai, nicolas.ferre, alexandre.belloni, claudiu.beznea
  Cc: linux-sound, linux-arm-kernel, linux-kernel, manishbaing2789,
	Sashiko AI
In-Reply-To: <20260604203623.162640-1-manishbaing2789@gmail.com>

In atmel_ac97c_remove() and the probe error path, the driver disables
clocks and unmaps memory before freeing the IRQ. If a stray interrupt
fires during this window, the handler will attempt to access unmapped
memory or unclocked hardware, resulting in a kernel panic.

Reorder the teardown sequence to call free_irq() first, adhering to
the standard reverse-initialization order.

Running make W=1 returns no errors. I was unable to test the patch
because I do not have the hardware.The issue was flagged by the
Sashiko AI bot.

Link: https://sashiko.dev/#/patchset/20260530052812.115994-1-manishbaing2789@gmail.com?part=1
Reported-by: Sashiko AI <sashiko-bot@kernel.org>
Signed-off-by: Manish Baing <manishbaing2789@gmail.com>
---
 sound/atmel/ac97c.c | 9 +++++----
 1 file changed, 5 insertions(+), 4 deletions(-)

diff --git a/sound/atmel/ac97c.c b/sound/atmel/ac97c.c
index cd74395dd222..b9280b644f26 100644
--- a/sound/atmel/ac97c.c
+++ b/sound/atmel/ac97c.c
@@ -790,7 +790,7 @@ static int atmel_ac97c_probe(struct platform_device *pdev)
 	retval = snd_card_register(card);
 	if (retval) {
 		dev_dbg(&pdev->dev, "could not register sound card\n");
-		goto err_ac97_bus;
+		goto err_snd_card_register;
 	}
 
 	platform_set_drvdata(pdev, card);
@@ -800,11 +800,12 @@ static int atmel_ac97c_probe(struct platform_device *pdev)
 
 	return 0;
 
+err_snd_card_register:
+	free_irq(irq, chip);
 err_ac97_bus:
+err_request_irq:
 	iounmap(chip->regs);
 err_ioremap:
-	free_irq(irq, chip);
-err_request_irq:
 	snd_card_free(card);
 err_snd_card_new:
 	clk_disable_unprepare(pclk);
@@ -842,10 +843,10 @@ static void atmel_ac97c_remove(struct platform_device *pdev)
 	ac97c_writel(chip, COMR, 0);
 	ac97c_writel(chip, MR,   0);
 
+	free_irq(chip->irq, chip);
 	clk_disable_unprepare(chip->pclk);
 	clk_put(chip->pclk);
 	iounmap(chip->regs);
-	free_irq(chip->irq, chip);
 
 	snd_card_free(card);
 }
-- 
2.43.0



^ permalink raw reply related

* [RFC PATCH 1/2] ASoC: sound: atmel_ac97c: Fix IRQ handler null pointer dereference
From: Manish Baing @ 2026-06-04 20:36 UTC (permalink / raw)
  To: perex, tiwai, nicolas.ferre, alexandre.belloni, claudiu.beznea
  Cc: linux-sound, linux-arm-kernel, linux-kernel, manishbaing2789,
	Sashiko AI
In-Reply-To: <20260604203623.162640-1-manishbaing2789@gmail.com>

In atmel_ac97c_probe(), request_irq() is called before ioremap().
If an interrupt fires immediately, the handler atmel_ac97c_interrupt()
will attempt to dereference chip->regs via ac97c_readl(), leading to
a null pointer dereference and kernel panic.

Move request_irq() to the end of the probe function, after memory
is mapped and clocks are enabled, ensuring the hardware is fully
ready before interrupts are serviced.

Running make W=1 returns no errors. I was unable to test the patch
because I do not have the hardware.The issue was flagged by the
Sashiko AI bot.

Link: https://sashiko.dev/#/patchset/20260530052812.115994-1-manishbaing2789@gmail.com?part=1
Reported-by: Sashiko AI <sashiko-bot@kernel.org>
Signed-off-by: Manish Baing <manishbaing2789@gmail.com>
---
 sound/atmel/ac97c.c | 11 ++++++-----
 1 file changed, 6 insertions(+), 5 deletions(-)

diff --git a/sound/atmel/ac97c.c b/sound/atmel/ac97c.c
index df0a049192de..cd74395dd222 100644
--- a/sound/atmel/ac97c.c
+++ b/sound/atmel/ac97c.c
@@ -734,11 +734,6 @@ static int atmel_ac97c_probe(struct platform_device *pdev)
 
 	chip = get_chip(card);
 
-	retval = request_irq(irq, atmel_ac97c_interrupt, 0, "AC97C", chip);
-	if (retval) {
-		dev_dbg(&pdev->dev, "unable to request irq %d\n", irq);
-		goto err_request_irq;
-	}
 	chip->irq = irq;
 
 	spin_lock_init(&chip->lock);
@@ -786,6 +781,12 @@ static int atmel_ac97c_probe(struct platform_device *pdev)
 		goto err_ac97_bus;
 	}
 
+	retval = request_irq(irq, atmel_ac97c_interrupt, 0, "AC97C", chip);
+	if (retval) {
+		dev_dbg(&pdev->dev, "unable to request irq %d\n", irq);
+		goto err_request_irq;
+	}
+
 	retval = snd_card_register(card);
 	if (retval) {
 		dev_dbg(&pdev->dev, "could not register sound card\n");
-- 
2.43.0



^ permalink raw reply related

* [RFC PATCH 0/2] ASoC: atmel: ac97c: Fix IRQ handling sequences
From: Manish Baing @ 2026-06-04 20:36 UTC (permalink / raw)
  To: perex, tiwai, nicolas.ferre, alexandre.belloni, claudiu.beznea
  Cc: linux-sound, linux-arm-kernel, linux-kernel, manishbaing2789

This series addresses two hardware initialization and teardown issues in 
the atmel_ac97c driver flagged by the Sashiko AI bot.
The original report can be found here:
https://sashiko.dev/#/patchset/20260530052812.115994-1-manishbaing2789@gmail.com?part=1

- Patch 1 moves request_irq() to the end of probe to prevent a null pointer
  dereference if an interrupt fires early.
- Patch 2 reorders the teardown sequence to free the IRQ before disabling
  clocks and unmapping memory, preventing a use-after-free.

I am submitting this as an RFC because I do not have the physical hardware
to test these changes, However, my manual analysis indicates these are 
valid bugs, and the series compiles cleanly with W=1.

Manish Baing (2):
  ASoC: sound: atmel_ac97c: Fix IRQ handler null pointer dereference
  ASoC: atmel: ac97c: Fix use-after-free on driver teardown

 sound/atmel/ac97c.c | 20 +++++++++++---------
 1 file changed, 11 insertions(+), 9 deletions(-)

-- 
2.43.0



^ permalink raw reply

* [PATCH bpf-next v2 8/8] selftests/bpf: add tests to validate KASAN on JIT programs
From: Alexis Lothoré (eBPF Foundation) @ 2026-06-04 20:22 UTC (permalink / raw)
  To: Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko,
	Martin KaFai Lau, Eduard Zingerman, Kumar Kartikeya Dwivedi,
	Song Liu, Yonghong Song, Jiri Olsa, John Fastabend,
	Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, x86,
	H. Peter Anvin, Shuah Khan, Maxime Coquelin, Alexandre Torgue,
	Ihor Solodrai
  Cc: ebpf, Bastien Curutchet, Thomas Petazzoni, bpf, linux-kernel,
	linux-kselftest, linux-stm32, linux-arm-kernel,
	Alexis Lothoré (eBPF Foundation)
In-Reply-To: <20260604-kasan-v2-0-c066e627fda8@bootlin.com>

Add a basic KASAN test runner that loads and test-run programs that can
trigger memory management bugs. The test captures kernel logs and ensure
that the expected KASAN splat is emitted by searching for the
corresponding first lines in the report, hence validated that the needed
instrumentation has been inserted by the JIT compiler before the
relevant memory accesses.

The runner covers different cases and settings: in the nominal case, it
validates kasan reports on basic instructions (on all supported accesses
sizes) but also when report _should not_ be emitted (eg: for accesses on
program stack). The runner also comes with a few specialized tests that
are then not executed for all sizes/locations. A few of those tests
depends on cpuv4 (load_acquire and store_release).

  # ./test_progs -a kasan
  #164/1   kasan/st_1_not_on_stack:OK
  #164/2   kasan/st_1_on_stack:OK
  #164/3   kasan/st_2_not_on_stack:OK
  #164/4   kasan/st_2_on_stack:OK
  #164/5   kasan/st_4_not_on_stack:OK
  #164/6   kasan/st_4_on_stack:OK
  #164/7   kasan/st_8_not_on_stack:OK
  #164/8   kasan/st_8_on_stack:OK
  #164/9   kasan/stx_1_not_on_stack:OK
  #164/10  kasan/stx_1_on_stack:OK
  #164/11  kasan/stx_2_not_on_stack:OK
  #164/12  kasan/stx_2_on_stack:OK
  #164/13  kasan/stx_4_not_on_stack:OK
  #164/14  kasan/stx_4_on_stack:OK
  #164/15  kasan/stx_8_not_on_stack:OK
  #164/16  kasan/stx_8_on_stack:OK
  #164/17  kasan/ldx_1_not_on_stack:OK
  #164/18  kasan/ldx_1_on_stack:OK
  #164/19  kasan/ldx_2_not_on_stack:OK
  #164/20  kasan/ldx_2_on_stack:OK
  #164/21  kasan/ldx_4_not_on_stack:OK
  #164/22  kasan/ldx_4_on_stack:OK
  #164/23  kasan/ldx_8_not_on_stack:OK
  #164/24  kasan/ldx_8_on_stack:OK
  #164/25  kasan/simple_atomic_4_not_on_stack:OK
  #164/26  kasan/simple_atomic_4_on_stack:OK
  #164/27  kasan/simple_atomic_8_not_on_stack:OK
  #164/28  kasan/simple_atomic_8_on_stack:OK
  #164/29  kasan/load_acquire_1_not_on_stack:SKIP
  #164/30  kasan/load_acquire_1_on_stack:SKIP
  #164/31  kasan/load_acquire_2_not_on_stack:SKIP
  #164/32  kasan/load_acquire_2_on_stack:SKIP
  #164/33  kasan/load_acquire_4_not_on_stack:SKIP
  #164/34  kasan/load_acquire_4_on_stack:SKIP
  #164/35  kasan/load_acquire_8_not_on_stack:SKIP
  #164/36  kasan/load_acquire_8_on_stack:SKIP
  #164/37  kasan/store_release_1_not_on_stack:SKIP
  #164/38  kasan/store_release_1_on_stack:SKIP
  #164/39  kasan/store_release_2_not_on_stack:SKIP
  #164/40  kasan/store_release_2_on_stack:SKIP
  #164/41  kasan/store_release_4_not_on_stack:SKIP
  #164/42  kasan/store_release_4_on_stack:SKIP
  #164/43  kasan/store_release_8_not_on_stack:SKIP
  #164/44  kasan/store_release_8_on_stack:SKIP
  #164/45  kasan/ldx_patched:OK
  #164/46  kasan/stack_and_non_stack:OK
  #164     kasan:OK (SKIP: 16/46)
  Summary: 1/30 PASSED, 16 SKIPPED, 0 FAILED

Signed-off-by: Alexis Lothoré (eBPF Foundation) <alexis.lothore@bootlin.com>
---
Changes in v2:
- simplify tests by just manually poisoning test areas with a dedicated
  kfunc
- introduce one prog per covered instruction family
- make sure that tests do not consume kernel logs (use /dev/kmgs rather
  than klogctl)
- add tests for stack accesses:
  - marking correctly set when there are diverging verifier states
    leading to different memory types
  - marking kept in sync with prog when it is patched
---
 tools/testing/selftests/bpf/prog_tests/kasan.c     | 356 +++++++++++++++++++
 tools/testing/selftests/bpf/progs/kasan.c          | 382 +++++++++++++++++++++
 .../testing/selftests/bpf/test_kmods/bpf_testmod.c |  22 ++
 3 files changed, 760 insertions(+)

diff --git a/tools/testing/selftests/bpf/prog_tests/kasan.c b/tools/testing/selftests/bpf/prog_tests/kasan.c
new file mode 100644
index 000000000000..adf61e230ec9
--- /dev/null
+++ b/tools/testing/selftests/bpf/prog_tests/kasan.c
@@ -0,0 +1,356 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+#include <bpf/bpf.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <linux/if_ether.h>
+#include <unistd.h>
+#include <test_progs.h>
+#include <unpriv_helpers.h>
+#include "kasan.skel.h"
+
+#define SUBTEST_NAME_MAX_LEN	128
+#define PROG_NAME_MAX_LEN	128
+
+#define MAX_LOG_SIZE		(8 * 1024)
+#define READ_CHUNK_SIZE		256
+
+#define KASAN_PATTERN_SLAB_UAF "BUG: KASAN: slab-use-after-free " \
+	"in bpf_prog_%02x%02x%02x%02x%02x%02x%02x%02x_%s"
+#define KASAN_PATTERN_REPORT "%s of size %d at addr"
+
+static char klog_buffer[MAX_LOG_SIZE];
+
+struct test_spec {
+	char *prog_type;
+	bool is_write;
+	bool only_32_or_64;
+	bool needs_load_acq_store_rel;
+	bool skip_multi_size_testing;
+	bool skip_on_stack_testing;
+	int run_size;
+	bool expect_no_report;
+	bool rnd_hi32;
+};
+
+struct kasan_write_val {
+	__u8 data_1;
+	__u16 data_2;
+	__u32 data_4;
+	__u64 data_8;
+};
+
+struct test_ctx {
+	__u8  prog_tag[BPF_TAG_SIZE];
+	struct kasan *skel;
+	struct bpf_program *prog;
+	char prog_name[SUBTEST_NAME_MAX_LEN];
+	int klog_fd;
+};
+
+static int open_kernel_logs(void)
+{
+	int fd;
+
+	fd = open("/dev/kmsg", O_RDONLY | O_NONBLOCK);
+
+	return fd;
+}
+
+static void skip_kernel_logs(int fd)
+{
+	lseek(fd, 0, SEEK_END);
+}
+
+static int read_kernel_logs(int fd, char *buf, size_t max_len)
+{
+	char record[512];
+	size_t total = 0;
+	ssize_t n;
+
+	buf[0] = '\0';
+	while (1) {
+		char *msg, *eol;
+		size_t len;
+
+		n = read(fd, record, sizeof(record) - 1);
+		if (n < 0) {
+			if (errno == EAGAIN)
+				break;
+			return n;
+		}
+		record[n] = '\0';
+
+		/* Each kmsg record starts with some metadata, separated
+		 * from the actual content by a semi-colon
+		 */
+		msg = strchr(record, ';');
+		if (!msg)
+			continue;
+		msg++;
+		eol = strchr(msg, '\n');
+		if (eol)
+			*eol = '\0';
+
+		len = strlen(msg);
+		if (total + len + 2 > max_len)
+			break;
+		memcpy(buf + total, msg, len);
+		total += len;
+		buf[total++] = '\n';
+		buf[total] = '\0';
+	}
+
+	return total;
+}
+
+static int check_kasan_report_in_kernel_logs(char *buf, struct test_ctx *ctx,
+					     bool is_write, int size)
+{
+	char *access_desc_start, *access_desc_end, *tmp;
+	char access_log[READ_CHUNK_SIZE];
+	char *kasan_report_start;
+	int nsize;
+
+	snprintf(access_log, READ_CHUNK_SIZE, KASAN_PATTERN_SLAB_UAF,
+		 ctx->prog_tag[0], ctx->prog_tag[1], ctx->prog_tag[2],
+		 ctx->prog_tag[3], ctx->prog_tag[4], ctx->prog_tag[5],
+		 ctx->prog_tag[6], ctx->prog_tag[7], ctx->prog_name);
+	/* Searched kasan report is valid if
+	 * - it contains the expected kasan pattern
+	 * - the next line is the description of the faulty access
+	 * - faulty access properties match the tested type and size
+	 */
+	kasan_report_start = strstr(buf, access_log);
+
+	if (!kasan_report_start)
+		return 1;
+
+	/* Find next line */
+	access_desc_start = strchr(kasan_report_start, '\n');
+	if (!access_desc_start)
+		return 1;
+	access_desc_start++;
+
+	access_desc_end = strchr(access_desc_start, '\n');
+	if (!access_desc_end)
+		return 1;
+
+	nsize = snprintf(access_log, READ_CHUNK_SIZE, KASAN_PATTERN_REPORT,
+		 is_write ? "Write" : "Read", size);
+
+	tmp = memmem(access_desc_start, access_desc_end - access_desc_start,
+		     access_log, nsize);
+
+	if (!tmp)
+		return 1;
+
+	return 0;
+}
+
+static void run_subtest_with_size_and_location(struct test_ctx *ctx,
+					       struct test_spec *test,
+					       int access_size,
+					       bool on_stack)
+{
+	char subtest_name[SUBTEST_NAME_MAX_LEN];
+	char prog_name[PROG_NAME_MAX_LEN];
+	struct bpf_prog_info info;
+	uint8_t buf[ETH_HLEN];
+	__u32 info_len;
+	int ret;
+
+	if (test->skip_multi_size_testing) {
+		snprintf(subtest_name, SUBTEST_NAME_MAX_LEN, "%s",
+			 test->prog_type);
+		strncpy(prog_name, test->prog_type, PROG_NAME_MAX_LEN);
+	} else {
+		snprintf(subtest_name, SUBTEST_NAME_MAX_LEN, "%s_%d_%s",
+			 test->prog_type, access_size,
+			 on_stack ? "on_stack" : "not_on_stack");
+		snprintf(prog_name, PROG_NAME_MAX_LEN, "%s_%s", test->prog_type,
+				on_stack ? "on_stack" : "not_on_stack");
+	}
+
+	if (!test__start_subtest(subtest_name))
+		return;
+
+	if (test->needs_load_acq_store_rel &&
+	    ctx->skel->data->skip_load_acq_store_rel_tests) {
+		test__skip();
+		return;
+	}
+
+	ctx->prog = bpf_object__find_program_by_name(ctx->skel->obj, prog_name);
+	if (!ASSERT_OK_PTR(ctx->prog, "find test prog"))
+		return;
+
+	info_len = sizeof(info);
+	memset(&info, 0, info_len);
+	ret = bpf_prog_get_info_by_fd(bpf_program__fd(ctx->prog), &info,
+				      &info_len);
+	if (!ASSERT_OK(ret, "fetch loaded program info"))
+		return;
+	memcpy(ctx->prog_tag, info.tag, BPF_TAG_SIZE);
+
+	skip_kernel_logs(ctx->klog_fd);
+
+	LIBBPF_OPTS(bpf_test_run_opts, topts);
+	topts.sz = sizeof(struct bpf_test_run_opts);
+	topts.data_size_in = ETH_HLEN;
+	topts.data_in = buf;
+	ctx->skel->bss->access_size = access_size;
+	ret = bpf_prog_test_run_opts(bpf_program__fd(ctx->prog),
+				     &topts);
+	if (!ASSERT_OK(ret, "run prog"))
+		return;
+
+	ret = read_kernel_logs(ctx->klog_fd, klog_buffer, MAX_LOG_SIZE);
+	if (!ASSERT_GE(ret, 0, "read kernel logs"))
+		return;
+
+	ret = check_kasan_report_in_kernel_logs(klog_buffer, ctx,
+						test->is_write, access_size);
+	if (on_stack || test->expect_no_report)
+		ASSERT_NEQ(ret, 0, "no report should be generated");
+	else
+		ASSERT_OK(ret, "report should be generated");
+}
+
+static void run_subtest_with_size(struct test_ctx *ctx, struct test_spec *test,
+				  int size)
+{
+	run_subtest_with_size_and_location(ctx, test, size, false);
+	if (!test->skip_on_stack_testing)
+		run_subtest_with_size_and_location(ctx, test, size, true);
+}
+
+static void run_subtest(struct test_ctx *ctx, struct test_spec *test)
+{
+	if (test->skip_multi_size_testing) {
+		run_subtest_with_size(ctx, test, test->run_size);
+		return;
+	}
+
+	if (!test->only_32_or_64) {
+		run_subtest_with_size(ctx, test, 1);
+		run_subtest_with_size(ctx, test, 2);
+	}
+	run_subtest_with_size(ctx, test, 4);
+	run_subtest_with_size(ctx, test, 8);
+}
+
+static struct test_spec tests[] = {
+	{
+		.prog_type = "st",
+		.is_write = true
+	},
+	{
+		.prog_type = "stx",
+		.is_write = true
+	},
+	{
+		.prog_type = "ldx",
+		.is_write = false
+	},
+	{
+		.prog_type = "simple_atomic",
+		.is_write = false,
+		.only_32_or_64 = true
+	},
+	{
+		.prog_type = "load_acquire",
+		.is_write = false,
+		.needs_load_acq_store_rel = true
+	},
+	{
+		.prog_type = "store_release",
+		.is_write = true,
+		.needs_load_acq_store_rel = true
+	},
+	{
+		.prog_type = "ldx_patched",
+		.is_write = false,
+		.skip_multi_size_testing = true,
+		.skip_on_stack_testing = true,
+		.run_size = 4,
+		/* Make the verifier patch instruction to test
+		 * adjust_insn_aux_data logic
+		 */
+		.rnd_hi32 = true
+	},
+	{
+		.prog_type = "stack_and_non_stack",
+		.is_write = true,
+		.skip_multi_size_testing = true,
+		.skip_on_stack_testing = true,
+		.run_size = 1
+	}
+};
+
+void test_kasan(void)
+{
+	struct kasan_write_val val;
+	struct test_spec *test;
+	struct test_ctx *ctx;
+	__u32 key = 0;
+	int i, ret;
+
+	ctx = calloc(1, sizeof(struct test_ctx));
+	if (!ASSERT_OK_PTR(ctx, "alloc test ctx"))
+		return;
+
+	if (!is_jit_enabled() || !get_kasan_jit_enabled()) {
+		test__skip();
+		goto end;
+	}
+
+	ctx->skel = kasan__open();
+	if (!ASSERT_OK_PTR(ctx->skel, "open prog"))
+		goto end;
+
+	for (i = 0; i < ARRAY_SIZE(tests); i++) {
+		struct bpf_program *prog;
+
+		if (!tests[i].rnd_hi32)
+			continue;
+
+		prog = bpf_object__find_program_by_name(ctx->skel->obj,
+							tests[i].prog_type);
+		if (!ASSERT_OK_PTR(prog, "find rnd_hi32 prog"))
+			goto destroy;
+		bpf_program__set_flags(prog, BPF_F_TEST_RND_HI32);
+	}
+
+	if (!ASSERT_OK(kasan__load(ctx->skel), "load prog"))
+		goto destroy;
+
+	ctx->klog_fd = open_kernel_logs();
+	if (!ASSERT_OK_FD(ctx->klog_fd, "open kernel logs"))
+		goto destroy;
+
+	/* Fill map with recognizable values */
+	ret = bpf_map__lookup_elem(ctx->skel->maps.test_map, &key, sizeof(key),
+				   &val, sizeof(val), 0);
+	if (!ASSERT_OK(ret, "get map"))
+		goto close;
+	val.data_1 = 0xAA;
+	val.data_2 = 0xBBBB;
+	val.data_4 = 0xCCCCCCCC;
+	val.data_8 = 0xDDDDDDDDDDDDDDDD;
+	ret = bpf_map__update_elem(ctx->skel->maps.test_map, &key, sizeof(key),
+				   &val, sizeof(val), 0);
+	if (!ASSERT_OK(ret, "set map"))
+		goto close;
+
+	for (i = 0; i < ARRAY_SIZE(tests); i++) {
+		test = &tests[i];
+		run_subtest(ctx, test);
+	}
+
+close:
+	close(ctx->klog_fd);
+destroy:
+	kasan__destroy(ctx->skel);
+end:
+	free(ctx);
+}
diff --git a/tools/testing/selftests/bpf/progs/kasan.c b/tools/testing/selftests/bpf/progs/kasan.c
new file mode 100644
index 000000000000..670318a956a4
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/kasan.c
@@ -0,0 +1,382 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+
+#include <linux/bpf.h>
+#include <bpf/bpf_helpers.h>
+#include <bpf/bpf_tracing.h>
+#include "bpf_misc.h"
+#include <stdbool.h>
+
+extern void bpf_kfunc_kasan_poison(void *mem, __u32 mem__sz) __ksym;
+extern void bpf_kfunc_kasan_unpoison(void *mem, __u32 mem__sz) __ksym;
+
+int access_size;
+
+struct kasan_write_val {
+	__u8 data_1;
+	__u16 data_2;
+	__u32 data_4;
+	__u64 data_8;
+};
+
+struct {
+	__uint(type, BPF_MAP_TYPE_ARRAY);
+	__uint(max_entries, 1);
+	__type(key, __u32);
+	__type(value, struct kasan_write_val);
+} test_map SEC(".maps");
+
+SEC("tcx/ingress")
+int st_on_stack(struct __sk_buff *skb)
+{
+	struct kasan_write_val val;
+
+	bpf_kfunc_kasan_poison(&val, sizeof(struct kasan_write_val));
+	switch (access_size) {
+	case 1:
+		val.data_1 = 0xAA;
+		break;
+	case 2:
+		val.data_2 = 0xAA;
+		break;
+	case 4:
+		val.data_4 = 0xAA;
+		break;
+	case 8:
+		val.data_8 = 0xAA;
+		break;
+	}
+	bpf_kfunc_kasan_unpoison(&val, sizeof(struct kasan_write_val));
+	return 0;
+}
+
+SEC("tcx/ingress")
+int st_not_on_stack(struct __sk_buff *skb)
+{
+	struct kasan_write_val *val;
+	__u32 key = 0;
+
+	val = bpf_map_lookup_elem(&test_map, &key);
+	if (!val)
+		return 0;
+
+	bpf_kfunc_kasan_poison(val, sizeof(struct kasan_write_val));
+	switch (access_size) {
+	case 1:
+		val->data_1 = 0xAA;
+		break;
+	case 2:
+		val->data_2 = 0xAA;
+		break;
+	case 4:
+		val->data_4 = 0xAA;
+		break;
+	case 8:
+		val->data_8 = 0xAA;
+		break;
+	}
+	bpf_kfunc_kasan_unpoison(val, sizeof(struct kasan_write_val));
+	return 0;
+}
+
+SEC("tcx/ingress")
+int stx_on_stack(struct __sk_buff *skb)
+{
+	struct kasan_write_val val;
+
+	bpf_kfunc_kasan_poison(&val, sizeof(struct kasan_write_val));
+	switch (access_size) {
+	case 1:
+		val.data_1 = access_size;
+		break;
+	case 2:
+		val.data_2 = access_size;
+		break;
+	case 4:
+		val.data_4 = access_size;
+		break;
+	case 8:
+		val.data_8 = access_size;
+		break;
+	}
+	bpf_kfunc_kasan_poison(&val, sizeof(struct kasan_write_val));
+	return 0;
+}
+
+SEC("tcx/ingress")
+int stx_not_on_stack(struct __sk_buff *skb)
+{
+	struct kasan_write_val *val;
+	__u32 key = 0;
+
+	val = bpf_map_lookup_elem(&test_map, &key);
+	if (!val)
+		return 0;
+
+	bpf_kfunc_kasan_poison(val, sizeof(struct kasan_write_val));
+	switch (access_size) {
+	case 1:
+		val->data_1 = access_size;
+		break;
+	case 2:
+		val->data_2 = access_size;
+		break;
+	case 4:
+		val->data_4 = access_size;
+		break;
+	case 8:
+		val->data_8 = access_size;
+		break;
+	}
+	bpf_kfunc_kasan_poison(val, sizeof(struct kasan_write_val));
+	return 0;
+}
+
+SEC("tcx/ingress")
+int ldx_on_stack(struct __sk_buff *skb)
+{
+	struct kasan_write_val val;
+
+	bpf_kfunc_kasan_poison(&val, sizeof(struct kasan_write_val));
+	switch (access_size) {
+	case 1:
+		__sink(val.data_1);
+		break;
+	case 2:
+		__sink(val.data_2);
+		break;
+	case 4:
+		__sink(val.data_4);
+		break;
+	case 8:
+		__sink(val.data_8);
+		break;
+	}
+	bpf_kfunc_kasan_unpoison(&val, sizeof(struct kasan_write_val));
+	return 0;
+}
+
+SEC("tcx/ingress")
+int ldx_not_on_stack(struct __sk_buff *skb)
+{
+	struct kasan_write_val *val;
+	__u32 key = 0;
+
+	val = bpf_map_lookup_elem(&test_map, &key);
+	if (!val)
+		return 0;
+
+	bpf_kfunc_kasan_poison(val, sizeof(struct kasan_write_val));
+	switch (access_size) {
+	case 1:
+		__sink(val->data_1);
+		break;
+	case 2:
+		__sink(val->data_2);
+		break;
+	case 4:
+		__sink(val->data_4);
+		break;
+	case 8:
+		__sink(val->data_8);
+		break;
+	}
+	bpf_kfunc_kasan_unpoison(val, sizeof(struct kasan_write_val));
+	return 0;
+}
+
+SEC("tcx/ingress")
+int ldx_patched(struct __sk_buff *skb)
+{
+	struct kasan_write_val *val;
+	__u32 key = 0;
+
+	val = bpf_map_lookup_elem(&test_map, &key);
+	if (!val)
+		return 0;
+
+	bpf_kfunc_kasan_poison(val, sizeof(struct kasan_write_val));
+	__sink(val->data_4);
+	bpf_kfunc_kasan_unpoison(val, sizeof(struct kasan_write_val));
+
+	return 0;
+}
+
+SEC("tcx/ingress")
+int simple_atomic_on_stack(struct __sk_buff *skb)
+{
+	struct kasan_write_val val;
+
+	bpf_kfunc_kasan_poison(&val, sizeof(struct kasan_write_val));
+	switch (access_size) {
+	case 4:
+		__sync_fetch_and_add(&val.data_4, 4);
+		break;
+	case 8:
+		__sync_fetch_and_add(&val.data_8, 8);
+		break;
+	}
+	bpf_kfunc_kasan_unpoison(&val, sizeof(struct kasan_write_val));
+	return 0;
+}
+
+SEC("tcx/ingress")
+int simple_atomic_not_on_stack(struct __sk_buff *skb)
+{
+	struct kasan_write_val *val;
+	__u32 key = 0;
+
+	val = bpf_map_lookup_elem(&test_map, &key);
+	if (!val)
+		return 0;
+
+	bpf_kfunc_kasan_poison(val, sizeof(struct kasan_write_val));
+	switch (access_size) {
+	case 4:
+		__sync_fetch_and_add(&val->data_4, 4);
+		break;
+	case 8:
+		__sync_fetch_and_add(&val->data_8, 8);
+		break;
+	}
+	bpf_kfunc_kasan_unpoison(val, sizeof(struct kasan_write_val));
+	return 0;
+}
+
+#ifdef __BPF_FEATURE_LOAD_ACQ_STORE_REL
+bool skip_load_acq_store_rel_tests __attribute__((section(".data"))) = 0;
+
+SEC("tcx/ingress")
+int load_acquire_on_stack(struct __sk_buff *skb)
+{
+	struct kasan_write_val val;
+
+	bpf_kfunc_kasan_poison(&val, sizeof(struct kasan_write_val));
+	switch (access_size) {
+	case 1:
+		__atomic_load_n(&val.data_1, __ATOMIC_ACQUIRE);
+		break;
+	case 2:
+		__atomic_load_n(&val.data_2, __ATOMIC_ACQUIRE);
+		break;
+	case 4:
+		__atomic_load_n(&val.data_4, __ATOMIC_ACQUIRE);
+		break;
+	case 8:
+		__atomic_load_n(&val.data_8, __ATOMIC_ACQUIRE);
+		break;
+	}
+	bpf_kfunc_kasan_unpoison(&val, sizeof(struct kasan_write_val));
+	return 0;
+}
+
+SEC("tcx/ingress")
+int load_acquire_not_on_stack(struct __sk_buff *skb)
+{
+	struct kasan_write_val *val;
+	__u32 key = 0;
+
+	val = bpf_map_lookup_elem(&test_map, &key);
+	if (!val)
+		return 0;
+
+	bpf_kfunc_kasan_poison(val, sizeof(struct kasan_write_val));
+	switch (access_size) {
+	case 1:
+		__atomic_load_n(&val->data_1, __ATOMIC_ACQUIRE);
+		break;
+	case 2:
+		__atomic_load_n(&val->data_2, __ATOMIC_ACQUIRE);
+		break;
+	case 4:
+		__atomic_load_n(&val->data_4, __ATOMIC_ACQUIRE);
+		break;
+	case 8:
+		__atomic_load_n(&val->data_8, __ATOMIC_ACQUIRE);
+		break;
+	}
+	bpf_kfunc_kasan_unpoison(val, sizeof(struct kasan_write_val));
+	return 0;
+}
+
+SEC("tcx/ingress")
+int store_release_on_stack(struct __sk_buff *skb)
+{
+	struct kasan_write_val val;
+
+	bpf_kfunc_kasan_poison(&val, sizeof(struct kasan_write_val));
+	switch (access_size) {
+	case 1:
+		__atomic_store_n(&val.data_1, 0xAA, __ATOMIC_RELEASE);
+		break;
+	case 2:
+		__atomic_store_n(&val.data_2, 0xBBBB, __ATOMIC_RELEASE);
+		break;
+	case 4:
+		__atomic_store_n(&val.data_4, 0xCCCCCCCC, __ATOMIC_RELEASE);
+		break;
+	case 8:
+		__atomic_store_n(&val.data_8, 0xDDDDDDDDDDDDDDDD,
+				 __ATOMIC_RELEASE);
+		break;
+	}
+	bpf_kfunc_kasan_unpoison(&val, sizeof(struct kasan_write_val));
+	return 0;
+}
+
+SEC("tcx/ingress")
+int store_release_not_on_stack(struct __sk_buff *skb)
+{
+	struct kasan_write_val *val;
+	__u32 key = 0;
+
+	val = bpf_map_lookup_elem(&test_map, &key);
+	if (!val)
+		return 0;
+
+	bpf_kfunc_kasan_poison(val, sizeof(struct kasan_write_val));
+	switch (access_size) {
+	case 1:
+		__atomic_store_n(&val->data_1, 0xAA, __ATOMIC_RELEASE);
+		break;
+	case 2:
+		__atomic_store_n(&val->data_2, 0xBBBB, __ATOMIC_RELEASE);
+		break;
+	case 4:
+		__atomic_store_n(&val->data_4, 0xCCCCCCCC, __ATOMIC_RELEASE);
+		break;
+	case 8:
+		__atomic_store_n(&val->data_8, 0xDDDDDDDDDDDDDDDD,
+				 __ATOMIC_RELEASE);
+		break;
+	}
+	bpf_kfunc_kasan_unpoison(val, sizeof(struct kasan_write_val));
+	return 0;
+}
+#else
+bool skip_load_acq_store_rel_tests __attribute__((section(".data"))) = 1;
+#endif
+
+SEC("tcx/ingress")
+int stack_and_non_stack(struct __sk_buff *skb)
+{
+	struct kasan_write_val stack_val = {};
+	struct kasan_write_val *val;
+	void *ptr;
+	__u32 key = 0;
+
+	val = bpf_map_lookup_elem(&test_map, &key);
+	if (!val)
+		return 0;
+
+	if (access_size)
+		ptr = val;
+	else
+		ptr = &stack_val;
+
+	bpf_kfunc_kasan_poison(val, sizeof(*val));
+	*(__u8 *)ptr = 0xAA;
+	bpf_kfunc_kasan_unpoison(val, sizeof(*val));
+	return 0;
+}
+
+char LICENSE[] SEC("license") = "GPL";
diff --git a/tools/testing/selftests/bpf/test_kmods/bpf_testmod.c b/tools/testing/selftests/bpf/test_kmods/bpf_testmod.c
index 30f1cd23093c..09a502a1742f 100644
--- a/tools/testing/selftests/bpf/test_kmods/bpf_testmod.c
+++ b/tools/testing/selftests/bpf/test_kmods/bpf_testmod.c
@@ -271,6 +271,26 @@ __bpf_kfunc void bpf_kfunc_put_default_trusted_ptr_test(struct prog_test_member
 	 */
 }
 
+#ifdef CONFIG_KASAN_GENERIC
+
+extern void kasan_poison(const void *addr, size_t size, u8 value, bool init);
+
+#define KASAN_SLAB_FREE 0xFB
+
+__bpf_kfunc void bpf_kfunc_kasan_poison(void *mem, u32 mem__sz)
+{
+	kasan_poison(mem, mem__sz, KASAN_SLAB_FREE, false);
+}
+
+__bpf_kfunc void bpf_kfunc_kasan_unpoison(void *mem, u32 mem__sz)
+{
+	kasan_poison(mem, mem__sz, 0x00, false);
+}
+#else
+__bpf_kfunc void bpf_kfunc_kasan_poison(void *mem, u32 mem__sz) { }
+__bpf_kfunc void bpf_kfunc_kasan_unpoison(void *mem, u32 mem__sz) { }
+#endif
+
 __bpf_kfunc struct bpf_testmod_ctx *
 bpf_testmod_ctx_create(int *err)
 {
@@ -740,6 +760,8 @@ BTF_ID_FLAGS(func, bpf_testmod_ops3_call_test_1)
 BTF_ID_FLAGS(func, bpf_testmod_ops3_call_test_2)
 BTF_ID_FLAGS(func, bpf_kfunc_get_default_trusted_ptr_test);
 BTF_ID_FLAGS(func, bpf_kfunc_put_default_trusted_ptr_test);
+BTF_ID_FLAGS(func, bpf_kfunc_kasan_poison)
+BTF_ID_FLAGS(func, bpf_kfunc_kasan_unpoison)
 BTF_KFUNCS_END(bpf_testmod_common_kfunc_ids)
 
 BTF_ID_LIST(bpf_testmod_dtor_ids)

-- 
2.54.0



^ permalink raw reply related

* [PATCH bpf-next v2 7/8] selftests/bpf: add helper to check whether eBPF KASAN is active
From: Alexis Lothoré (eBPF Foundation) @ 2026-06-04 20:22 UTC (permalink / raw)
  To: Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko,
	Martin KaFai Lau, Eduard Zingerman, Kumar Kartikeya Dwivedi,
	Song Liu, Yonghong Song, Jiri Olsa, John Fastabend,
	Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, x86,
	H. Peter Anvin, Shuah Khan, Maxime Coquelin, Alexandre Torgue,
	Ihor Solodrai
  Cc: ebpf, Bastien Curutchet, Thomas Petazzoni, bpf, linux-kernel,
	linux-kselftest, linux-stm32, linux-arm-kernel,
	Alexis Lothoré (eBPF Foundation)
In-Reply-To: <20260604-kasan-v2-0-c066e627fda8@bootlin.com>

Add a simple helper checking whether JIT compiler is able to insert
KASAN checks in programs. This will allow to conditionally run
selftests for KASAN checks in JITed programs.

Signed-off-by: Alexis Lothoré (eBPF Foundation) <alexis.lothore@bootlin.com>
---
Changes in v2:
- fix condition
---
 tools/testing/selftests/bpf/unpriv_helpers.c | 5 +++++
 tools/testing/selftests/bpf/unpriv_helpers.h | 1 +
 2 files changed, 6 insertions(+)

diff --git a/tools/testing/selftests/bpf/unpriv_helpers.c b/tools/testing/selftests/bpf/unpriv_helpers.c
index f997d7ec8fd0..11201b65a3d4 100644
--- a/tools/testing/selftests/bpf/unpriv_helpers.c
+++ b/tools/testing/selftests/bpf/unpriv_helpers.c
@@ -142,3 +142,8 @@ bool get_unpriv_disabled(void)
 	}
 	return mitigations_off;
 }
+
+bool get_kasan_jit_enabled(void)
+{
+	return config_contains("CONFIG_BPF_JIT_KASAN=y") == 1;
+}
diff --git a/tools/testing/selftests/bpf/unpriv_helpers.h b/tools/testing/selftests/bpf/unpriv_helpers.h
index 151f67329665..bc5f4c953c9d 100644
--- a/tools/testing/selftests/bpf/unpriv_helpers.h
+++ b/tools/testing/selftests/bpf/unpriv_helpers.h
@@ -5,3 +5,4 @@
 #define UNPRIV_SYSCTL "kernel/unprivileged_bpf_disabled"
 
 bool get_unpriv_disabled(void);
+bool get_kasan_jit_enabled(void);

-- 
2.54.0



^ permalink raw reply related

* [PATCH bpf-next v2 6/8] bpf, x86: enable KASAN for JITed programs on x86
From: Alexis Lothoré (eBPF Foundation) @ 2026-06-04 20:22 UTC (permalink / raw)
  To: Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko,
	Martin KaFai Lau, Eduard Zingerman, Kumar Kartikeya Dwivedi,
	Song Liu, Yonghong Song, Jiri Olsa, John Fastabend,
	Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, x86,
	H. Peter Anvin, Shuah Khan, Maxime Coquelin, Alexandre Torgue,
	Ihor Solodrai
  Cc: ebpf, Bastien Curutchet, Thomas Petazzoni, bpf, linux-kernel,
	linux-kselftest, linux-stm32, linux-arm-kernel,
	Alexis Lothoré (eBPF Foundation)
In-Reply-To: <20260604-kasan-v2-0-c066e627fda8@bootlin.com>

Mark x86 as supporting KASAN checks in JITed programs so that the
corresponding JIT compiler inserts checks on the translated
instructions.

Signed-off-by: Alexis Lothoré (eBPF Foundation) <alexis.lothore@bootlin.com>
---
 arch/x86/Kconfig | 1 +
 1 file changed, 1 insertion(+)

diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index f3f7cb01d69d..cc140108b74c 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -235,6 +235,7 @@ config X86
 	select HAVE_SAMPLE_FTRACE_DIRECT	if X86_64
 	select HAVE_SAMPLE_FTRACE_DIRECT_MULTI	if X86_64
 	select HAVE_EBPF_JIT
+	select HAVE_EBPF_JIT_KASAN		if X86_64
 	select HAVE_EFFICIENT_UNALIGNED_ACCESS
 	select HAVE_EISA			if X86_32
 	select HAVE_EXIT_THREAD

-- 
2.54.0



^ permalink raw reply related

* [PATCH bpf-next v2 5/8] bpf, x86: emit KASAN checks into x86 JITed programs
From: Alexis Lothoré (eBPF Foundation) @ 2026-06-04 20:22 UTC (permalink / raw)
  To: Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko,
	Martin KaFai Lau, Eduard Zingerman, Kumar Kartikeya Dwivedi,
	Song Liu, Yonghong Song, Jiri Olsa, John Fastabend,
	Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, x86,
	H. Peter Anvin, Shuah Khan, Maxime Coquelin, Alexandre Torgue,
	Ihor Solodrai
  Cc: ebpf, Bastien Curutchet, Thomas Petazzoni, bpf, linux-kernel,
	linux-kselftest, linux-stm32, linux-arm-kernel,
	Alexis Lothoré (eBPF Foundation)
In-Reply-To: <20260604-kasan-v2-0-c066e627fda8@bootlin.com>

Insert KASAN shadow memory checks before memory load and store
operations in JIT-compiled BPF programs. This helps detect memory safety
bugs such as use-after-free and out-of-bounds accesses at runtime.

The main instructions being targeted are BPF_ST, BPF_STX and BPF_LDX,
but not all of them are being instrumented:
- if the load/store instruction is in fact accessing the program stack,
  emit_kasan_check silently skips the instrumentation, as we already
  have page guards to monitor stack accesses.
- if the load/store instruction is a BPF_PROBE_MEM or a BPF_PROBE_ATOMIC
  instruction, we do not instrument it, as the passed address can fault
  (hence the custom fault management with BPF_PROBE_XXX instructions),
  and so the corresponding kasan check could fault as well.

Signed-off-by: Alexis Lothoré (eBPF Foundation) <alexis.lothore@bootlin.com>
---
Changes in v2:
- support BPF_ATOMICS
- support BPF_ST
- make sure to systematically pass correct instruction to kasan check
---
 arch/x86/net/bpf_jit_comp.c | 63 ++++++++++++++++++++++++++++++++++++++-------
 1 file changed, 53 insertions(+), 10 deletions(-)

diff --git a/arch/x86/net/bpf_jit_comp.c b/arch/x86/net/bpf_jit_comp.c
index 943a0f315cf2..cb3c03edc4bd 100644
--- a/arch/x86/net/bpf_jit_comp.c
+++ b/arch/x86/net/bpf_jit_comp.c
@@ -1516,17 +1516,30 @@ static int emit_atomic_rmw_index(u8 **pprog, u32 atomic_op, u32 size,
 	return 0;
 }
 
-static int emit_atomic_ld_st(u8 **pprog, u32 atomic_op, u32 dst_reg,
-			     u32 src_reg, s16 off, u8 bpf_size)
+static int emit_atomic_ld_st(u8 **pprog, struct bpf_insn *insn, u8 *ip,
+			     u32 dst_reg, u32 src_reg, bool accesses_stack_only)
 {
+	u32 atomic_op = insn->imm;
+	int err;
+
 	switch (atomic_op) {
 	case BPF_LOAD_ACQ:
+		err = emit_kasan_check(pprog, src_reg, insn, ip, false,
+				       accesses_stack_only);
+		if (err)
+			return err;
 		/* dst_reg = smp_load_acquire(src_reg + off16) */
-		emit_ldx(pprog, bpf_size, dst_reg, src_reg, off);
+		emit_ldx(pprog, BPF_SIZE(insn->code), dst_reg, src_reg,
+			 insn->off);
 		break;
 	case BPF_STORE_REL:
+		err = emit_kasan_check(pprog, dst_reg, insn, ip, true,
+				       accesses_stack_only);
+		if (err)
+			return err;
 		/* smp_store_release(dst_reg + off16, src_reg) */
-		emit_stx(pprog, bpf_size, dst_reg, src_reg, off);
+		emit_stx(pprog, BPF_SIZE(insn->code), dst_reg, src_reg,
+			 insn->off);
 		break;
 	default:
 		pr_err("bpf_jit: unknown atomic load/store opcode %02x\n",
@@ -1904,6 +1917,7 @@ static int do_jit(struct bpf_verifier_env *env, struct bpf_prog *bpf_prog, int *
 		const s32 imm32 = insn->imm;
 		u32 dst_reg = insn->dst_reg;
 		u32 src_reg = insn->src_reg;
+		bool accesses_stack_only;
 		u8 b2 = 0, b3 = 0;
 		u8 *start_of_ldx;
 		s64 jmp_offset;
@@ -1924,6 +1938,8 @@ static int do_jit(struct bpf_verifier_env *env, struct bpf_prog *bpf_prog, int *
 			EMIT_ENDBR();
 
 		ip = image + addrs[i - 1] + (prog - temp);
+		accesses_stack_only =
+			bpf_insn_accesses_stack_only(env, bpf_prog, i - 1);
 
 		switch (insn->code) {
 			/* ALU */
@@ -2304,6 +2320,10 @@ static int do_jit(struct bpf_verifier_env *env, struct bpf_prog *bpf_prog, int *
 		case BPF_ST | BPF_MEM | BPF_H:
 		case BPF_ST | BPF_MEM | BPF_W:
 		case BPF_ST | BPF_MEM | BPF_DW:
+			err = emit_kasan_check(&prog, dst_reg, insn, ip, true,
+					       accesses_stack_only);
+			if (err)
+				return err;
 			switch (BPF_SIZE(insn->code)) {
 			case BPF_B:
 				if (is_ereg(dst_reg))
@@ -2369,6 +2389,10 @@ static int do_jit(struct bpf_verifier_env *env, struct bpf_prog *bpf_prog, int *
 				insn_off = outgoing_arg_base - outgoing_rsp - insn_off - 16;
 				dst_reg = BPF_REG_FP;
 			}
+			err = emit_kasan_check(&prog, dst_reg, insn, ip, true,
+					       accesses_stack_only);
+			if (err)
+				return err;
 			emit_stx(&prog, BPF_SIZE(insn->code), dst_reg, src_reg, insn_off);
 			break;
 
@@ -2530,6 +2554,12 @@ static int do_jit(struct bpf_verifier_env *env, struct bpf_prog *bpf_prog, int *
 				/* populate jmp_offset for JAE above to jump to start_of_ldx */
 				start_of_ldx = prog;
 				end_of_jmp[-1] = start_of_ldx - end_of_jmp;
+			} else {
+				err = emit_kasan_check(&prog, src_reg, insn, ip,
+						       false,
+						       accesses_stack_only);
+				if (err)
+					return err;
 			}
 			if (BPF_MODE(insn->code) == BPF_PROBE_MEMSX ||
 			    BPF_MODE(insn->code) == BPF_MEMSX)
@@ -2592,13 +2622,13 @@ static int do_jit(struct bpf_verifier_env *env, struct bpf_prog *bpf_prog, int *
 			fallthrough;
 		case BPF_STX | BPF_ATOMIC | BPF_W:
 		case BPF_STX | BPF_ATOMIC | BPF_DW:
+			bool is64 = BPF_SIZE(insn->code) == BPF_DW;
+			u32 real_src_reg = src_reg;
+			u32 real_dst_reg = dst_reg;
+			u8 *branch_target;
 			if (insn->imm == (BPF_AND | BPF_FETCH) ||
 			    insn->imm == (BPF_OR | BPF_FETCH) ||
 			    insn->imm == (BPF_XOR | BPF_FETCH)) {
-				bool is64 = BPF_SIZE(insn->code) == BPF_DW;
-				u32 real_src_reg = src_reg;
-				u32 real_dst_reg = dst_reg;
-				u8 *branch_target;
 
 				/*
 				 * Can't be implemented with a single x86 insn.
@@ -2612,7 +2642,19 @@ static int do_jit(struct bpf_verifier_env *env, struct bpf_prog *bpf_prog, int *
 				if (dst_reg == BPF_REG_0)
 					real_dst_reg = BPF_REG_AX;
 
+				ip += 3;
+			}
+			if (!bpf_atomic_is_load_store(insn)) {
+				err = emit_kasan_check(&prog, real_dst_reg,
+						       insn, ip, false,
+						       accesses_stack_only);
+				if (err)
+					return err;
 				branch_target = prog;
+			}
+			if (insn->imm == (BPF_AND | BPF_FETCH) ||
+			    insn->imm == (BPF_OR | BPF_FETCH) ||
+			    insn->imm == (BPF_XOR | BPF_FETCH)) {
 				/* Load old value */
 				emit_ldx(&prog, BPF_SIZE(insn->code),
 					 BPF_REG_0, real_dst_reg, insn->off);
@@ -2644,8 +2686,9 @@ static int do_jit(struct bpf_verifier_env *env, struct bpf_prog *bpf_prog, int *
 			}
 
 			if (bpf_atomic_is_load_store(insn))
-				err = emit_atomic_ld_st(&prog, insn->imm, dst_reg, src_reg,
-							insn->off, BPF_SIZE(insn->code));
+				err = emit_atomic_ld_st(&prog, insn, ip,
+							dst_reg, src_reg,
+							accesses_stack_only);
 			else
 				err = emit_atomic_rmw(&prog, insn->imm, dst_reg, src_reg,
 						      insn->off, BPF_SIZE(insn->code));

-- 
2.54.0



^ permalink raw reply related

* [PATCH bpf-next v2 4/8] bpf, x86: refactor BPF_ST management in do_jit
From: Alexis Lothoré (eBPF Foundation) @ 2026-06-04 20:22 UTC (permalink / raw)
  To: Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko,
	Martin KaFai Lau, Eduard Zingerman, Kumar Kartikeya Dwivedi,
	Song Liu, Yonghong Song, Jiri Olsa, John Fastabend,
	Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, x86,
	H. Peter Anvin, Shuah Khan, Maxime Coquelin, Alexandre Torgue,
	Ihor Solodrai
  Cc: ebpf, Bastien Curutchet, Thomas Petazzoni, bpf, linux-kernel,
	linux-kselftest, linux-stm32, linux-arm-kernel,
	Alexis Lothoré (eBPF Foundation)
In-Reply-To: <20260604-kasan-v2-0-c066e627fda8@bootlin.com>

In order to prepare for KASAN checks insertion before every
memory-related load or store, group all BPF_ST instructions that indeed
access memory in a single block of fall-through cases to allow
instrumenting those in one call, rather than having to instrument all
cases individually.

Signed-off-by: Alexis Lothoré (eBPF Foundation) <alexis.lothore@bootlin.com>
---
 arch/x86/net/bpf_jit_comp.c | 53 ++++++++++++++++++++++++++-------------------
 1 file changed, 31 insertions(+), 22 deletions(-)

diff --git a/arch/x86/net/bpf_jit_comp.c b/arch/x86/net/bpf_jit_comp.c
index 0981791014eb..943a0f315cf2 100644
--- a/arch/x86/net/bpf_jit_comp.c
+++ b/arch/x86/net/bpf_jit_comp.c
@@ -2300,41 +2300,50 @@ static int do_jit(struct bpf_verifier_env *env, struct bpf_prog *bpf_prog, int *
 			EMIT_LFENCE();
 			break;
 
-			/* ST: *(u8*)(dst_reg + off) = imm */
 		case BPF_ST | BPF_MEM | BPF_B:
-			if (is_ereg(dst_reg))
-				EMIT2(0x41, 0xC6);
-			else
-				EMIT1(0xC6);
-			goto st;
 		case BPF_ST | BPF_MEM | BPF_H:
-			if (is_ereg(dst_reg))
-				EMIT3(0x66, 0x41, 0xC7);
-			else
-				EMIT2(0x66, 0xC7);
-			goto st;
 		case BPF_ST | BPF_MEM | BPF_W:
-			if (is_ereg(dst_reg))
-				EMIT2(0x41, 0xC7);
-			else
-				EMIT1(0xC7);
-			goto st;
 		case BPF_ST | BPF_MEM | BPF_DW:
-			if (dst_reg == BPF_REG_PARAMS && insn->off == -8) {
-				/* Arg 6: store immediate in r9 register */
-				emit_mov_imm64(&prog, X86_REG_R9, imm32 >> 31, (u32)imm32);
+			switch (BPF_SIZE(insn->code)) {
+			case BPF_B:
+				if (is_ereg(dst_reg))
+					EMIT2(0x41, 0xC6);
+				else
+					EMIT1(0xC6);
+				break;
+			case BPF_H:
+				if (is_ereg(dst_reg))
+					EMIT3(0x66, 0x41, 0xC7);
+				else
+					EMIT2(0x66, 0xC7);
+				break;
+			case BPF_W:
+				if (is_ereg(dst_reg))
+					EMIT2(0x41, 0xC7);
+				else
+					EMIT1(0xC7);
+				break;
+			case BPF_DW:
+				if (dst_reg == BPF_REG_PARAMS &&
+				    insn->off == -8) {
+					/* Arg 6: store immediate in r9 register */
+					emit_mov_imm64(&prog, X86_REG_R9,
+						       imm32 >> 31, (u32)imm32);
+					break;
+				}
+				EMIT2(add_1mod(0x48, dst_reg), 0xC7);
 				break;
 			}
-			EMIT2(add_1mod(0x48, dst_reg), 0xC7);
 
-st:			insn_off = insn->off;
+			insn_off = insn->off;
 			if (dst_reg == BPF_REG_PARAMS) {
 				/*
 				 * Args 7+: reverse BPF negative offsets to
 				 * x86 positive rsp offsets.
 				 * BPF off=-16 → [rsp+0], off=-24 → [rsp+8], ...
 				 */
-				insn_off = outgoing_arg_base - outgoing_rsp - insn_off - 16;
+				insn_off = outgoing_arg_base - outgoing_rsp -
+					   insn_off - 16;
 				dst_reg = BPF_REG_FP;
 			}
 			if (is_imm8(insn_off))

-- 
2.54.0



^ permalink raw reply related

* [PATCH bpf-next v2 2/8] bpf: add BPF_JIT_KASAN for KASAN instrumentation of JITed programs
From: Alexis Lothoré (eBPF Foundation) @ 2026-06-04 20:22 UTC (permalink / raw)
  To: Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko,
	Martin KaFai Lau, Eduard Zingerman, Kumar Kartikeya Dwivedi,
	Song Liu, Yonghong Song, Jiri Olsa, John Fastabend,
	Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, x86,
	H. Peter Anvin, Shuah Khan, Maxime Coquelin, Alexandre Torgue,
	Ihor Solodrai
  Cc: ebpf, Bastien Curutchet, Thomas Petazzoni, bpf, linux-kernel,
	linux-kselftest, linux-stm32, linux-arm-kernel,
	Alexis Lothoré (eBPF Foundation)
In-Reply-To: <20260604-kasan-v2-0-c066e627fda8@bootlin.com>

Add a new Kconfig option CONFIG_BPF_JIT_KASAN that automatically enables
generic KASAN (Kernel Address SANitizer) memory access checks for
JIT-compiled BPF programs as well, when both KASAN_GENERIC and JIT
compiler are enabled. This new Kconfig is not a user selectable one: it
is either automatically enabled if KASAN is enabled on a compatible
platform, or disabled. When enabled, the JIT compiler will emit shadow
memory checks before memory loads and stores to detect use-after-free or
out-of-bounds accesses at runtime. The option is gated behind
HAVE_EBPF_JIT_KASAN, as it needs proper arch-specific implementation.

As KASAN instrumentation for eBPF program will depend on the info that
can be accessed during each instruction verification, there may be
instructions that will be instrumented even if they don't really need to
(eg: global subprograms that access caller stack memory passed as
argument). To make sure that those additional checks do not trigger any
crash, make sure that VMAP_STACK is enabled so that programs stack has
shadow memory allocated.

Signed-off-by: Alexis Lothoré (eBPF Foundation) <alexis.lothore@bootlin.com>
---
Changes in v2:
- add dependency on kasan for vmalloc and vmalloc'ed stack
---
 kernel/bpf/Kconfig | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/kernel/bpf/Kconfig b/kernel/bpf/Kconfig
index eb3de35734f0..a8e004f88b92 100644
--- a/kernel/bpf/Kconfig
+++ b/kernel/bpf/Kconfig
@@ -17,6 +17,10 @@ config HAVE_CBPF_JIT
 config HAVE_EBPF_JIT
 	bool
 
+# KASAN support for JIT compiler
+config HAVE_EBPF_JIT_KASAN
+	bool
+
 # Used by archs to tell that they want the BPF JIT compiler enabled by
 # default for kernels that were compiled with BPF JIT support.
 config ARCH_WANT_DEFAULT_BPF_JIT
@@ -101,4 +105,9 @@ config BPF_LSM
 
 	  If you are unsure how to answer this question, answer N.
 
+config BPF_JIT_KASAN
+	bool
+	depends on HAVE_EBPF_JIT_KASAN
+	default y if BPF_JIT && KASAN_GENERIC && KASAN_VMALLOC && VMAP_STACK
+
 endmenu # "BPF subsystem"

-- 
2.54.0



^ permalink raw reply related

* [PATCH bpf-next v2 3/8] bpf, x86: add helper to emit kasan checks in x86 JITed programs
From: Alexis Lothoré (eBPF Foundation) @ 2026-06-04 20:22 UTC (permalink / raw)
  To: Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko,
	Martin KaFai Lau, Eduard Zingerman, Kumar Kartikeya Dwivedi,
	Song Liu, Yonghong Song, Jiri Olsa, John Fastabend,
	Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, x86,
	H. Peter Anvin, Shuah Khan, Maxime Coquelin, Alexandre Torgue,
	Ihor Solodrai
  Cc: ebpf, Bastien Curutchet, Thomas Petazzoni, bpf, linux-kernel,
	linux-kselftest, linux-stm32, linux-arm-kernel,
	Alexis Lothoré (eBPF Foundation)
In-Reply-To: <20260604-kasan-v2-0-c066e627fda8@bootlin.com>

Add the emit_kasan_check() function that emits KASAN shadow memory
checks before memory accesses in JIT-compiled BPF programs. The
implementation relies on the existing __asan_{load,store}X functions
from KASAN subsystem. The helper:
- ensures that the kasan instrumention is actually needed: if the
  instruction being processed accesses the program stack, we skip the
  instrumentation, as those accesses are already protected with page
  guards
- saves registers. This includes caller-saved registers, but also
  temporary registers, as those were possibly used by the
  affected program
- computes the accessed address and stores it in %rdi
- calls the relevant function, depending on the instruction being a load
  or a store, and the size of the access.
- restores registers

The special care needed when inserting this instrumentation comes at the
cost of a non negligeable increase in JITed code size. For example, a
bare

  mov 	0x0(%si),rbx # Load in rbx content at address stored in rsi

becomes

  push    %rax
  push    %rcx
  push    %rdx
  push    %rsi
  push    %rdi
  push    %r8
  push    %r9
  mov     %rsi,%rdi
  call    0xffffffff81da0a60 <__asan_load8>
  pop     %r9
  pop     %r8
  pop     %rdi
  pop     %rsi
  pop     %rdx
  pop     %rcx
  pop     %rax
  mov     0x0(%rsi),rbx

Signed-off-by: Alexis Lothoré (eBPF Foundation) <alexis.lothore@bootlin.com>
---
Changes in v2:
- move asan functions declaration directly into jit compiler, and guard
  them with IS_ENABLED
- remove faulty stack alignment, no arg is passed to kasan funcs on the
  stack anyway
- make sure to emit call depth accounting code
- do not save unneeded registers
- update helper signature to let caller configure some values (eg:
  is_write)

Signed-off-by: Alexis Lothoré (eBPF Foundation) <alexis.lothore@bootlin.com>
---
 arch/x86/net/bpf_jit_comp.c | 93 +++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 93 insertions(+)

diff --git a/arch/x86/net/bpf_jit_comp.c b/arch/x86/net/bpf_jit_comp.c
index a0c541a441cf..0981791014eb 100644
--- a/arch/x86/net/bpf_jit_comp.c
+++ b/arch/x86/net/bpf_jit_comp.c
@@ -21,6 +21,19 @@
 #include <asm/unwind.h>
 #include <asm/cfi.h>
 
+#if IS_ENABLED(CONFIG_BPF_JIT_KASAN)
+void __asan_load1(void *p);
+void __asan_store1(void *p);
+void __asan_load2(void *p);
+void __asan_store2(void *p);
+void __asan_load4(void *p);
+void __asan_store4(void *p);
+void __asan_load8(void *p);
+void __asan_store8(void *p);
+void __asan_load16(void *p);
+void __asan_store16(void *p);
+#endif
+
 static bool all_callee_regs_used[4] = {true, true, true, true};
 
 static u8 *emit_code(u8 *ptr, u32 bytes, unsigned int len)
@@ -1330,6 +1343,86 @@ static void emit_store_stack_imm64(u8 **pprog, int reg, int stack_off, u64 imm64
 	emit_stx(pprog, BPF_DW, BPF_REG_FP, reg, stack_off);
 }
 
+static int emit_kasan_check(u8 **pprog, u32 addr_reg, struct bpf_insn *insn,
+			    u8 *ip, bool is_write, bool accesses_stack_only)
+{
+#ifdef CONFIG_BPF_JIT_KASAN
+	u32 bpf_size = BPF_SIZE(insn->code);
+	s32 off = insn->off;
+	u8 *prog = *pprog;
+	void *kasan_func;
+
+	if (accesses_stack_only)
+		return 0;
+
+	/* Derive KASAN check function from access type and size */
+	switch (bpf_size) {
+	case BPF_B:
+		kasan_func = is_write ? __asan_store1 : __asan_load1;
+		break;
+	case BPF_H:
+		kasan_func = is_write ? __asan_store2 : __asan_load2;
+		break;
+	case BPF_W:
+		kasan_func = is_write ? __asan_store4 : __asan_load4;
+		break;
+	case BPF_DW:
+		kasan_func = is_write ? __asan_store8 : __asan_load8;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	/* Save rax */
+	EMIT1(0x50);
+	/* Save rcx */
+	EMIT1(0x51);
+	/* Save rdx */
+	EMIT1(0x52);
+	/* Save rsi */
+	EMIT1(0x56);
+	/* Save rdi */
+	EMIT1(0x57);
+	/* Save r8 */
+	EMIT2(0x41, 0x50);
+	/* Save r9 */
+	EMIT2(0x41, 0x51);
+
+	/* mov rdi, addr_reg */
+	EMIT_mov(BPF_REG_1, addr_reg);
+
+	/* add rdi, off (if offset is non-zero) */
+	if (off) {
+		if (is_imm8(off)) {
+			/* add rdi, imm8 */
+			EMIT4(0x48, 0x83, 0xC7, (u8)off);
+		} else {
+			/* add rdi, imm32 */
+			EMIT3_off32(0x48, 0x81, 0xC7, off);
+		}
+	}
+
+	/* Adjust ip to account for the instrumentation generated so far */
+	ip += (prog - *pprog);
+	/* We emit a call, so update call depth counting */
+	ip += x86_call_depth_emit_accounting(&prog, kasan_func, ip);
+	/* call kasan_func */
+	if (emit_call(&prog, kasan_func, ip))
+		return -ERANGE;
+
+	EMIT2(0x41, 0x59);
+	EMIT2(0x41, 0x58);
+	EMIT1(0x5F);
+	EMIT1(0x5E);
+	EMIT1(0x5A);
+	EMIT1(0x59);
+	EMIT1(0x58);
+
+	*pprog = prog;
+#endif /* CONFIG_BPF_JIT_KASAN */
+	return 0;
+}
+
 static int emit_atomic_rmw(u8 **pprog, u32 atomic_op,
 			   u32 dst_reg, u32 src_reg, s16 off, u8 bpf_size)
 {

-- 
2.54.0



^ permalink raw reply related

* [PATCH bpf-next v2 0/8] bpf: add support for KASAN checks in JITed programs
From: Alexis Lothoré (eBPF Foundation) @ 2026-06-04 20:21 UTC (permalink / raw)
  To: Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko,
	Martin KaFai Lau, Eduard Zingerman, Kumar Kartikeya Dwivedi,
	Song Liu, Yonghong Song, Jiri Olsa, John Fastabend,
	Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, x86,
	H. Peter Anvin, Shuah Khan, Maxime Coquelin, Alexandre Torgue,
	Ihor Solodrai
  Cc: ebpf, Bastien Curutchet, Thomas Petazzoni, bpf, linux-kernel,
	linux-kselftest, linux-stm32, linux-arm-kernel,
	Alexis Lothoré (eBPF Foundation)

Hello,
this series aims to bring basic support for KASAN checks to BPF JITed
programs. This v2 drops the RFC prefix and brings many updates regarding
the topics and issues mentioned on the RFC or at LSFMMBPF. Thanks to
Ihor's update on CI, the instrumentation can now trigger properly in CI
as well.

"Traditional" KASAN allows to spot memory management mistakes by
reserving a fraction of memory as "shadow memory" that will map to the
rest of the memory and allow its monitoring. Each memory-accessing
instruction is then instrumented at build time to call some ASAN check
function, that will analyze the corresponding bits in shadow memory, and
if it detects the access as invalid, trigger a detailed report. The goal
of this series is to replicate this mechanism for BPF programs when they
are being JITed into native instructions: that's then the JIT compiler
that is in charge of inserting calls to the corresponding kasan checks,
when a program is being loaded into the kernel. This task involves:
- identifying at program load time the instructions performing memory
  accesses
- identifying those accesses properties (size ? read or write ?) to
  define the relevant kasan check function to call
- just before the identified instructions:
  - perform the basic context saving (ie: saving registers)
  - inserting a call to the relevant kasan check function
  - restore context
- whenever the instrumented program executes, if it performs an invalid
  access, it triggers a kasan report identical to those instrumented on
  kernel side at build time.

As discussed in [1], this series is based on some choices and
assumptions:
- it focuses on x86_64 for now, and so only on KASAN_GENERIC
- not all memory accessing BPF instructions are being instrumented:
  - it discards instructions accessing BPF program stack (already
    monitored by page guards)
  - it discards possibly faulting instructions, like BPF_PROBE_MEM or
    BPF_PROBE_ATOMIC insns

---
Changes in v2:
- declare asan functions as extern in JIT compiler rather than exposing
  them in kasan header
- invert stack-accessing instructions marking to make sure not to skip
  instructions that could end up accessing to-be-checked memory
- fix stack accesses marking when verifier patches instructions
- add best effort marking for cBPF
- add missing call depth accounting in jited instrumentation
- skip unused registers in kasan instrumentation save/restore
- remove faulty stack align in kasan instrumentation
- drop commit skipping some jit-related tests
- cover missing instructions: BPF_ST and atomics
- completely rework tests: directly tune shadow memory, increase
  coverage, do not consume kernel logs
- Link to v1: https://patch.msgid.link/20260413-kasan-v1-0-1a5831230821@bootlin.com

To: Alexei Starovoitov <ast@kernel.org>
To: Daniel Borkmann <daniel@iogearbox.net>
To: Andrii Nakryiko <andrii@kernel.org>
To: Martin KaFai Lau <martin.lau@linux.dev>
To: Eduard Zingerman <eddyz87@gmail.com>
To: Kumar Kartikeya Dwivedi <memxor@gmail.com>
To: Song Liu <song@kernel.org>
To: Yonghong Song <yonghong.song@linux.dev>
To: Jiri Olsa <jolsa@kernel.org>
To: John Fastabend <john.fastabend@gmail.com>
To: Thomas Gleixner <tglx@kernel.org>
To: Ingo Molnar <mingo@redhat.com>
To: Borislav Petkov <bp@alien8.de>
To: Dave Hansen <dave.hansen@linux.intel.com>
To: x86@kernel.org
To: "H. Peter Anvin" <hpa@zytor.com>
To: Shuah Khan <shuah@kernel.org>
To: Maxime Coquelin <mcoquelin.stm32@gmail.com>
To: Alexandre Torgue <alexandre.torgue@foss.st.com>
To: Ihor Solodrai <ihor.solodrai@linux.dev>
Cc: ebpf@linuxfoundation.org
Cc: Bastien Curutchet <bastien.curutchet@bootlin.com>
Cc: Thomas Petazzoni <thomas.petazzoni@bootlin.com>
Cc: bpf@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Cc: linux-kselftest@vger.kernel.org
Cc: linux-stm32@st-md-mailman.stormreply.com
Cc: linux-arm-kernel@lists.infradead.org

---
Alexis Lothoré (eBPF Foundation) (8):
      bpf: mark instructions accessing program stack
      bpf: add BPF_JIT_KASAN for KASAN instrumentation of JITed programs
      bpf, x86: add helper to emit kasan checks in x86 JITed programs
      bpf, x86: refactor BPF_ST management in do_jit
      bpf, x86: emit KASAN checks into x86 JITed programs
      bpf, x86: enable KASAN for JITed programs on x86
      selftests/bpf: add helper to check whether eBPF KASAN is active
      selftests/bpf: add tests to validate KASAN on JIT programs

 arch/x86/Kconfig                                   |   1 +
 arch/x86/net/bpf_jit_comp.c                        | 209 +++++++++--
 include/linux/bpf.h                                |   2 +
 include/linux/bpf_verifier.h                       |   2 +
 kernel/bpf/Kconfig                                 |   9 +
 kernel/bpf/core.c                                  |  17 +
 kernel/bpf/fixups.c                                |  16 +-
 kernel/bpf/verifier.c                              |   9 +
 tools/testing/selftests/bpf/prog_tests/kasan.c     | 356 +++++++++++++++++++
 tools/testing/selftests/bpf/progs/kasan.c          | 382 +++++++++++++++++++++
 .../testing/selftests/bpf/test_kmods/bpf_testmod.c |  22 ++
 tools/testing/selftests/bpf/unpriv_helpers.c       |   5 +
 tools/testing/selftests/bpf/unpriv_helpers.h       |   1 +
 13 files changed, 994 insertions(+), 37 deletions(-)
---
base-commit: b1c85ee71e2ab9ed7a12d7f3ee38988509baa368
change-id: 20260126-kasan-fcd68f64cd7b

Best regards,
--  
Alexis Lothoré (eBPF Foundation) <alexis.lothore@bootlin.com>



^ permalink raw reply

* [PATCH bpf-next v2 1/8] bpf: mark instructions accessing program stack
From: Alexis Lothoré (eBPF Foundation) @ 2026-06-04 20:21 UTC (permalink / raw)
  To: Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko,
	Martin KaFai Lau, Eduard Zingerman, Kumar Kartikeya Dwivedi,
	Song Liu, Yonghong Song, Jiri Olsa, John Fastabend,
	Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, x86,
	H. Peter Anvin, Shuah Khan, Maxime Coquelin, Alexandre Torgue,
	Ihor Solodrai
  Cc: ebpf, Bastien Curutchet, Thomas Petazzoni, bpf, linux-kernel,
	linux-kselftest, linux-stm32, linux-arm-kernel,
	Alexis Lothoré (eBPF Foundation)
In-Reply-To: <20260604-kasan-v2-0-c066e627fda8@bootlin.com>

In order to prepare to emit KASAN checks in JITed programs, JIT
compilers need to be aware about whether some load/store instructions
are targeting the bpf program stack, as those should not be monitored
(we already have guard pages for that, and it is difficult anyway to
correctly monitor any kind of data passed on stack).

To support this need, make the BPF verifier mark the instructions
depending on whether they could access or not memory other than stack:
- add a setter that allows the verifier to mark instructions accessing
  non-stack memory
- add a getter that allows JIT compilers to check whether instructions
  being JITed are accessing the stack _and only_ the stack. If no env is
  provided (eg this is a cBPF program), do a best-effort check based on
  source and destination registers.

As different states in the verifier could lead to different memory types
for the same access, just marking an instruction as accessing stack only
is not enough (it could be some other memory type in another verifier
state), so the algorithm rather sets by default any load/store
instruction as stack only, and if _any_ state leads to any memory access
type other than PTR_TO_STACK, it overrides this setting. It also takes
care about shifting back the instruction marking in adjust_insn_aux_data
if the verifier patches instructions.

Signed-off-by: Alexis Lothoré (eBPF Foundation) <alexis.lothore@bootlin.com>
---
Changes in v2:
- invert marking logic to cover possible different reg types when the
  verifier covers different states
- add a best-effort processing for classical bpf programs, inspecting
  directly src and dst registers since we don't have verifier env
- make sure to keep marking in sync with prog when it is patched by
  verifier
---
 include/linux/bpf.h          |  2 ++
 include/linux/bpf_verifier.h |  2 ++
 kernel/bpf/core.c            | 17 +++++++++++++++++
 kernel/bpf/fixups.c          | 16 +++++++++++-----
 kernel/bpf/verifier.c        |  9 +++++++++
 5 files changed, 41 insertions(+), 5 deletions(-)

diff --git a/include/linux/bpf.h b/include/linux/bpf.h
index 8599b451dd7a..ff80d1d62bff 100644
--- a/include/linux/bpf.h
+++ b/include/linux/bpf.h
@@ -1560,6 +1560,8 @@ void bpf_jit_uncharge_modmem(u32 size);
 bool bpf_prog_has_trampoline(const struct bpf_prog *prog);
 bool bpf_insn_is_indirect_target(const struct bpf_verifier_env *env, const struct bpf_prog *prog,
 				 int insn_idx);
+bool bpf_insn_accesses_stack_only(const struct bpf_verifier_env *env,
+				  const struct bpf_prog *prog, int insn_idx);
 u16 bpf_out_stack_arg_cnt(const struct bpf_verifier_env *env, const struct bpf_prog *prog);
 #else
 static inline int bpf_trampoline_link_prog(struct bpf_tramp_link *link,
diff --git a/include/linux/bpf_verifier.h b/include/linux/bpf_verifier.h
index c248ff41f42a..0f3f055d6c14 100644
--- a/include/linux/bpf_verifier.h
+++ b/include/linux/bpf_verifier.h
@@ -722,6 +722,8 @@ struct bpf_insn_aux_data {
 	u16 const_reg_map_mask;
 	u16 const_reg_subprog_mask;
 	u32 const_reg_vals[10];
+	/* instruction can access non-stack memory */
+	bool non_stack_access;
 };
 
 #define MAX_USED_MAPS 64 /* max number of maps accessed by one eBPF program */
diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c
index a656a8572bdb..393d9eacd215 100644
--- a/kernel/bpf/core.c
+++ b/kernel/bpf/core.c
@@ -1583,6 +1583,22 @@ bool bpf_insn_is_indirect_target(const struct bpf_verifier_env *env, const struc
 	return env->insn_aux_data[insn_idx].indirect_target;
 }
 
+bool bpf_insn_accesses_stack_only(const struct bpf_verifier_env *env,
+				  const struct bpf_prog *prog, int insn_idx)
+{
+	struct bpf_insn *insn;
+
+	/* cBPF: we have no verifier state, do a best-effort check based on
+	 * dst/src reg
+	 */
+	insn_idx += prog->aux->subprog_start;
+	insn = (struct bpf_insn *)prog->insnsi + insn_idx;
+	if (!env)
+		return insn->dst_reg == BPF_REG_FP ||
+		       insn->src_reg == BPF_REG_FP;
+	return !env->insn_aux_data[insn_idx].non_stack_access;
+}
+
 u16 bpf_out_stack_arg_cnt(const struct bpf_verifier_env *env, const struct bpf_prog *prog)
 {
 	const struct bpf_subprog_info *sub;
@@ -1592,6 +1608,7 @@ u16 bpf_out_stack_arg_cnt(const struct bpf_verifier_env *env, const struct bpf_p
 	sub = &env->subprog_info[prog->aux->func_idx];
 	return sub->stack_arg_cnt - bpf_in_stack_arg_cnt(sub);
 }
+
 #endif /* CONFIG_BPF_JIT */
 
 /* Base function for offset calculation. Needs to go into .text section,
diff --git a/kernel/bpf/fixups.c b/kernel/bpf/fixups.c
index 5aa3f7d99ac9..5228c910fbf5 100644
--- a/kernel/bpf/fixups.c
+++ b/kernel/bpf/fixups.c
@@ -185,16 +185,22 @@ static void adjust_insn_aux_data(struct bpf_verifier_env *env,
 	}
 
 	/*
-	 * The indirect_target flag of the original instruction was moved to the last of the
-	 * new instructions by the above memmove and memset, but the indirect jump target is
-	 * actually the first instruction, so move it back. This also matches with the behavior
-	 * of bpf_insn_array_adjust(), which preserves xlated_off to point to the first new
-	 * instruction.
+	 * The indirect_target and non_stack_access flags of the original
+	 * instruction were moved to the last of the new instructions by the
+	 * above memmove and memset, but those actually match the first
+	 * instruction, so move them back. This also matches with the behavior
+	 * of bpf_insn_array_adjust(), which preserves xlated_off to point to
+	 * the first new instruction.
 	 */
 	if (data[off + cnt - 1].indirect_target) {
 		data[off].indirect_target = 1;
 		data[off + cnt - 1].indirect_target = 0;
 	}
+
+	if (data[off + cnt - 1].non_stack_access) {
+		data[off].non_stack_access = 1;
+		data[off + cnt - 1].non_stack_access = 0;
+	}
 }
 
 static void adjust_subprog_starts(struct bpf_verifier_env *env, u32 off, u32 len)
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 8ed484cb1a8a..b3f0f430ad6a 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -3143,6 +3143,11 @@ static void mark_indirect_target(struct bpf_verifier_env *env, int idx)
 	env->insn_aux_data[idx].indirect_target = true;
 }
 
+static void mark_non_stack_access(struct bpf_verifier_env *env, int idx)
+{
+	env->insn_aux_data[idx].non_stack_access = true;
+}
+
 #define LR_FRAMENO_BITS	3
 #define LR_SPI_BITS	6
 #define LR_ENTRY_BITS	(LR_SPI_BITS + LR_FRAMENO_BITS + 1)
@@ -6300,6 +6305,10 @@ static int check_mem_access(struct bpf_verifier_env *env, int insn_idx, struct b
 		else
 			coerce_reg_to_size_sx(&regs[value_regno], size);
 	}
+
+	if (!err && reg->type != PTR_TO_STACK)
+		mark_non_stack_access(env, insn_idx);
+
 	return err;
 }
 

-- 
2.54.0



^ permalink raw reply related

* [PATCH v4 2/2] kconfig: Remove the architecture specific config for Propeller
From: xur @ 2026-06-04 19:56 UTC (permalink / raw)
  To: Yabin Cui, Will Deacon, Han Shen, Thomas Gleixner, Ingo Molnar,
	Borislav Petkov, Dave Hansen, H. Peter Anvin, Kees Cook,
	Nathan Chancellor, Nicolas Schier, Linus Walleij, Arnd Bergmann,
	Mathieu Desnoyers, Rong Xu, Miguel Ojeda, Peter Zijlstra,
	Jinjie Ruan, Lukas Bulwahn, linux-kernel, Juergen Gross,
	Helge Deller, Ryan Roberts, Marc Zyngier, Ard Biesheuvel,
	Vincent Donnefort, Alice Ryhl
  Cc: x86, linux-arm-kernel
In-Reply-To: <20260604195612.3757860-1-xur@google.com>

From: Rong Xu <xur@google.com>

The CONFIG_PROPELLER_CLANG option currently depends on
ARCH_SUPPORTS_PROPELLER_CLANG, but this dependency seems unnecessary.

Remove ARCH_SUPPORTS_PROPELLER_CLANG and allow users to control
Propeller builds solely through CONFIG_PROPELLER_CLANG. This simplifies
the kconfig and avoids potential confusion.

Move the .llvm_bb_addr_map sections grouping to
include/asm-generic/vmlinux.lds.h.

The Propeller documentation has been updated to reflect the most
recent tool location and now includes instructions for arm64.

Contributor Acknowledgments:
  * SPE instructions: Daniel Hoekwater <hoekwater@google.com>

Signed-off-by: Rong Xu <xur@google.com>
Suggested-by: Will Deacon <will@kernel.org>
Suggested-by: Nathan Chancellor <nathan@kernel.org>
Tested-by: Yabin Cui <yabinc@google.com>
Reviewed-by: Kees Cook <kees@kernel.org>
---
 Documentation/dev-tools/propeller.rst | 49 ++++++++++++++++++++-------
 arch/Kconfig                          |  5 +--
 arch/arm64/kernel/vmlinux.lds.S       |  1 +
 arch/x86/Kconfig                      |  1 -
 arch/x86/kernel/vmlinux.lds.S         |  5 +--
 include/asm-generic/vmlinux.lds.h     |  6 ++++
 6 files changed, 46 insertions(+), 21 deletions(-)

diff --git a/Documentation/dev-tools/propeller.rst b/Documentation/dev-tools/propeller.rst
index 92195958e3db..e927319941c9 100644
--- a/Documentation/dev-tools/propeller.rst
+++ b/Documentation/dev-tools/propeller.rst
@@ -28,8 +28,10 @@ A few important notes about adopting Propeller optimization:
    and the linker(ld.lld).
 
 #. In addition to LLVM toolchain, Propeller requires a profiling
-   conversion tool: https://github.com/google/autofdo with a release
-   after v0.30.1: https://github.com/google/autofdo/releases/tag/v0.30.1.
+   conversion tool: https://github.com/google/llvm-propeller.
+
+Current supported architectures include x86/X86_64 (via LBR),
+and arm64 (via SPE).
 
 The Propeller optimization process involves the following steps:
 
@@ -124,17 +126,30 @@ Here is an example workflow for building an AutoFDO+Propeller kernel:
 
       $ perf record --pfm-event RETIRED_TAKEN_BRANCH_INSTRUCTIONS:k -a -N -b -c <count> -o <perf_file> -- <loadtest>
 
-   Note you can repeat the above steps to collect multiple <perf_file>s.
+   - For arm64 with SPE::
+     There are a few kernel features that must be enabled to collect SPE profiles on Arm.
+     Below is a list of the required features:
+
+      - CONFIG_ARM_SPE_PMU=y
+      - CONFIG_PID_IN_CONTEXTIDR=y
+      - kpti=off
+
+     Use the following command to generate SPE perf data file::
+
+      $ perf record -e 'arm_spe_0/branch_filter=1,load_filter=0,store_filter=0/' -a -N -c <count> --no-switch-events -o <perf_file> -- <loadtest>
+
+     Note you can repeat the above steps to collect multiple <perf_file>s.
 
 4) (Optional) Download the raw perf file(s) to the host machine.
 
-5) Use the create_llvm_prof tool (https://github.com/google/autofdo) to
+5) Use the generate_propeller_profiles tool (https://github.com/google/llvm-propeller) to
    generate Propeller profile. ::
 
-      $ create_llvm_prof --binary=<vmlinux> --profile=<perf_file>
-                         --format=propeller --propeller_output_module_name
-                         --out=<propeller_profile_prefix>_cc_profile.txt
-                         --propeller_symorder=<propeller_profile_prefix>_ld_profile.txt
+      $ generate_propeller_profiles \
+             --binary=<vmlinux> --profile=<perf_file> \
+             --format=propeller --propeller_output_module_name \
+             --out=<propeller_profile_prefix>_cc_profile.txt \
+             --propeller_symorder=<propeller_profile_prefix>_ld_profile.txt
 
    "<propeller_profile_prefix>" can be something like "/home/user/dir/any_string".
 
@@ -146,10 +161,20 @@ Here is an example workflow for building an AutoFDO+Propeller kernel:
    you can create a temp list file "<perf_file_list>" with each line
    containing one perf file name and run::
 
-      $ create_llvm_prof --binary=<vmlinux> --profile=@<perf_file_list>
-                         --format=propeller --propeller_output_module_name
-                         --out=<propeller_profile_prefix>_cc_profile.txt
-                         --propeller_symorder=<propeller_profile_prefix>_ld_profile.txt
+      $ generate_propeller_profiles \
+             --binary=<vmlinux> --profile=@<perf_file_list> \
+             --format=propeller --propeller_output_module_name \
+             --out=<propeller_profile_prefix>_cc_profile.txt \
+             --propeller_symorder=<propeller_profile_prefix>_ld_profile.txt
+
+   For arm64 SPE, add the option '--profiler=perf_spe', like::
+
+      $ generate_propeller_profiles  \
+             --binary=<vmlinux> --profile=<perf_file> \
+             --profiler=perf_spe \
+             --format=propeller --propeller_output_module_name \
+             --out=<propeller_profile_prefix>_cc_profile.txt \
+             --propeller_symorder=<propeller_profile_prefix>_ld_profile.txt
 
 6) Rebuild the kernel using the AutoFDO and Propeller
    profiles. ::
diff --git a/arch/Kconfig b/arch/Kconfig
index 5e878924939a..99c2017eb515 100644
--- a/arch/Kconfig
+++ b/arch/Kconfig
@@ -895,13 +895,10 @@ config AUTOFDO_CLANG
 
 	  If unsure, say N.
 
-config ARCH_SUPPORTS_PROPELLER_CLANG
-	bool
-
 config PROPELLER_CLANG
 	bool "Enable Clang's Propeller build"
-	depends on ARCH_SUPPORTS_PROPELLER_CLANG
 	depends on CC_IS_CLANG && CLANG_VERSION >= 190000
+	depends on $(cc-option,-fbasic-block-sections=list=/dev/null)
 	help
 	  This option enables Clang’s Propeller build. When the Propeller
 	  profiles is specified in variable CLANG_PROPELLER_PROFILE_PREFIX
diff --git a/arch/arm64/kernel/vmlinux.lds.S b/arch/arm64/kernel/vmlinux.lds.S
index e1ac876200a3..8aaf404980a7 100644
--- a/arch/arm64/kernel/vmlinux.lds.S
+++ b/arch/arm64/kernel/vmlinux.lds.S
@@ -368,6 +368,7 @@ SECTIONS
 
 	STABS_DEBUG
 	DWARF_DEBUG
+	PROPELLER_DATA
 	MODINFO
 	ELF_DETAILS
 
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index 10bf3984102e..b875d2f27e48 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -130,7 +130,6 @@ config X86
 	select ARCH_SUPPORTS_LTO_CLANG
 	select ARCH_SUPPORTS_LTO_CLANG_THIN
 	select ARCH_SUPPORTS_RT
-	select ARCH_SUPPORTS_PROPELLER_CLANG    if X86_64
 	select ARCH_USE_BUILTIN_BSWAP
 	select ARCH_USE_CMPXCHG_LOCKREF		if X86_CX8
 	select ARCH_USE_MEMTEST
diff --git a/arch/x86/kernel/vmlinux.lds.S b/arch/x86/kernel/vmlinux.lds.S
index 4711a35e706c..74e336d7f9dd 100644
--- a/arch/x86/kernel/vmlinux.lds.S
+++ b/arch/x86/kernel/vmlinux.lds.S
@@ -423,10 +423,7 @@ SECTIONS
 
 	STABS_DEBUG
 	DWARF_DEBUG
-#ifdef CONFIG_PROPELLER_CLANG
-	.llvm_bb_addr_map : { *(.llvm_bb_addr_map) }
-#endif
-
+	PROPELLER_DATA
 	MODINFO
 	ELF_DETAILS
 
diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h
index 60c8c22fd3e4..5659f4b5a125 100644
--- a/include/asm-generic/vmlinux.lds.h
+++ b/include/asm-generic/vmlinux.lds.h
@@ -1011,6 +1011,12 @@
 #define PERCPU_DECRYPTED_SECTION
 #endif
 
+#ifdef CONFIG_PROPELLER_CLANG
+#define PROPELLER_DATA                                                     \
+	.llvm_bb_addr_map : { *(.llvm_bb_addr_map) }
+#else
+#define PROPELLER_DATA
+#endif
 
 /*
  * Default discarded sections.
-- 
2.54.0.1032.g2f8565e1d1-goog



^ permalink raw reply related

* [PATCH v4 1/2] kconfig: Remove the architecture specific config for AutoFDO
From: xur @ 2026-06-04 19:56 UTC (permalink / raw)
  To: Yabin Cui, Will Deacon, Han Shen, Thomas Gleixner, Ingo Molnar,
	Borislav Petkov, Dave Hansen, H. Peter Anvin, Kees Cook,
	Nathan Chancellor, Nicolas Schier, Linus Walleij, Arnd Bergmann,
	Mathieu Desnoyers, Rong Xu, Miguel Ojeda, Peter Zijlstra,
	Jinjie Ruan, Lukas Bulwahn, linux-kernel, Juergen Gross,
	Helge Deller, Ryan Roberts, Marc Zyngier, Ard Biesheuvel,
	Vincent Donnefort, Alice Ryhl
  Cc: x86, linux-arm-kernel
In-Reply-To: <20260604195612.3757860-1-xur@google.com>

From: Rong Xu <xur@google.com>

The CONFIG_AUTOFDO_CLANG option currently depends on
ARCH_SUPPORTS_AUTOFDO_CLANG, but this dependency seems unnecessary.

Remove ARCH_SUPPORTS_AUTOFDO_CLANG and allow users to control AutoFDO
builds solely through CONFIG_AUTOFDO_CLANG. This simplifies the kconfig
and avoids potential confusion.

Expand the AutoFDO documentation to include instructions for arm64.

Contributor acknowledgments:
  * SPE instructions: Daniel Hoekwater <hoekwater@google.com>
  * ETM instructions: Yabin Cui <yabinc@google.com>

Signed-off-by: Rong Xu <xur@google.com>
Suggested-by: Will Deacon <will@kernel.org>
Tested-by: Yabin Cui <yabinc@google.com>
Reviewed-by: Kees Cook <kees@kernel.org>
---
 Documentation/dev-tools/autofdo.rst | 41 +++++++++++++++++++++++++++++
 arch/Kconfig                        |  4 ---
 arch/x86/Kconfig                    |  1 -
 3 files changed, 41 insertions(+), 5 deletions(-)

diff --git a/Documentation/dev-tools/autofdo.rst b/Documentation/dev-tools/autofdo.rst
index bcf06e7d6ffa..ae03c4dfedc1 100644
--- a/Documentation/dev-tools/autofdo.rst
+++ b/Documentation/dev-tools/autofdo.rst
@@ -61,6 +61,9 @@ process consists of the following steps:
    the AutoFDO profile via offline tools.
 
 The support requires a Clang compiler LLVM 17 or later.
+Current supported architectures include x86/x86_64 (via LBR) and
+arm64 (via SPE or ETM).
+
 
 Preparation
 ===========
@@ -141,6 +144,35 @@ Here is an example workflow for AutoFDO kernel:
 
       $ perf record --pfm-events RETIRED_TAKEN_BRANCH_INSTRUCTIONS:k -a -N -b -c <count> -o <perf_file> -- <loadtest>
 
+   - For arm64 with SPE:
+
+     There are a few kernel features that must be enabled to collect SPE profiles on Arm.
+     Below is a list of the required features:
+
+      - CONFIG_ARM_SPE_PMU=y
+      - CONFIG_PID_IN_CONTEXTIDR=y
+      - kpti=off
+
+     Use the following command to generate SPE perf data file::
+
+      $ perf record -e ' arm_spe_0/branch_filter=1,load_filter=0,store_filter=0/'  -a -c <count> -N --no-switch-events -o <perf_file> -- <loadtest>
+
+   - For arm64 with ETM trace:
+
+     Follow the instructions in `Linaro OpenCSD document
+     <https://github.com/Linaro/OpenCSD/blob/master/decoder/tests/auto-fdo/autofdo.md>`_
+     to record ETM traces for AutoFDO::
+
+      $ perf record -e cs_etm/@tmc_etr0/k -a -o <etm_perf_file> -- <loadtest>
+      $ perf inject -i <etm_perf_file> -o <perf_file> --itrace=i500009il
+
+     For ARM platforms running Android, follow the instructions in `Android simpleperf
+     document <https://android.googlesource.com/kernel/common/+/refs/heads/android-mainline/gki/aarch64/afdo>`_
+     to record ETM traces for AutoFDO::
+
+      $ simpleperf record -e cs-etm:k -a -o <etm_perf_file> -- <loadtest>
+      $ simpleperf inject -i <etm_perf_file> -o <text_perf_file> --symdir <vmlinux_dir>
+
 4) (Optional) Download the raw perf file to the host machine.
 
 5) To generate an AutoFDO profile, two offline tools are available:
@@ -162,6 +194,15 @@ Here is an example workflow for AutoFDO kernel:
 
       $ llvm-profdata merge -o <profile_file> <profile_1> <profile_2> ... <profile_n>
 
+   For arm64 SPE, use the following command::
+
+      $ create_llvm_prof --binary=<vmlinux> --profile=<perf_file> --profiler=perf_spe --format=extbinary --out=<profile_file>
+
+   For arm64 ETM, use the following command::
+
+      $ create_llvm_prof --binary=<vmlinux> --profile=<text_perf_file> --profiler=text -format=extbinary -out=<profile_file>
+
+
 6) Rebuild the kernel using the AutoFDO profile file with the same config as step 1,
    (Note CONFIG_AUTOFDO_CLANG needs to be enabled)::
 
diff --git a/arch/Kconfig b/arch/Kconfig
index 0848932d1c8e..5e878924939a 100644
--- a/arch/Kconfig
+++ b/arch/Kconfig
@@ -879,12 +879,8 @@ config LTO_CLANG_THIN_DIST
 	  module-specific compiler options, and simplifies debugging.
 endchoice
 
-config ARCH_SUPPORTS_AUTOFDO_CLANG
-	bool
-
 config AUTOFDO_CLANG
 	bool "Enable Clang's AutoFDO build (EXPERIMENTAL)"
-	depends on ARCH_SUPPORTS_AUTOFDO_CLANG
 	depends on CC_IS_CLANG
 	help
 	  This option enables Clang’s AutoFDO build. When
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index f3f7cb01d69d..10bf3984102e 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -130,7 +130,6 @@ config X86
 	select ARCH_SUPPORTS_LTO_CLANG
 	select ARCH_SUPPORTS_LTO_CLANG_THIN
 	select ARCH_SUPPORTS_RT
-	select ARCH_SUPPORTS_AUTOFDO_CLANG
 	select ARCH_SUPPORTS_PROPELLER_CLANG    if X86_64
 	select ARCH_USE_BUILTIN_BSWAP
 	select ARCH_USE_CMPXCHG_LOCKREF		if X86_CX8
-- 
2.54.0.1032.g2f8565e1d1-goog



^ permalink raw reply related

* [PATCH v4 0/2] kconfig: Remove the architecture specific config for AutoFDO and Propeller
From: xur @ 2026-06-04 19:56 UTC (permalink / raw)
  To: Yabin Cui, Will Deacon, Han Shen, Thomas Gleixner, Ingo Molnar,
	Borislav Petkov, Dave Hansen, H. Peter Anvin, Kees Cook,
	Nathan Chancellor, Nicolas Schier, Linus Walleij, Arnd Bergmann,
	Mathieu Desnoyers, Rong Xu, Miguel Ojeda, Peter Zijlstra,
	Jinjie Ruan, Lukas Bulwahn, linux-kernel, Juergen Gross,
	Helge Deller, Ryan Roberts, Marc Zyngier, Ard Biesheuvel,
	Vincent Donnefort, Alice Ryhl
  Cc: x86, linux-arm-kernel

From: Rong Xu <xur@google.com>

ChangeLog:
  V4: (1) updated docs for AutoFDO and Propeller
      (2) moved .llvm_bb_addr_map sections grouping to the header file.
      (3) use $(cc-option,...) for supported archs.
  V3: (1) updated the base;
      (2) changed vmlinux.lds.S for arm64 to avoid the warnings
          from .llvm_bb_addr_map. 

Rong Xu (2):
  kconfig: Remove the architecture specific config for AutoFDO
  kconfig: Remove the architecture specific config for Propeller

 Documentation/dev-tools/autofdo.rst   | 41 ++++++++++++++++++++++
 Documentation/dev-tools/propeller.rst | 49 ++++++++++++++++++++-------
 arch/Kconfig                          |  9 +----
 arch/arm64/kernel/vmlinux.lds.S       |  1 +
 arch/x86/Kconfig                      |  2 --
 arch/x86/kernel/vmlinux.lds.S         |  5 +--
 include/asm-generic/vmlinux.lds.h     |  6 ++++
 7 files changed, 87 insertions(+), 26 deletions(-)


base-commit: f58316a441b4626324993db585fa4b7b7c780fac
-- 
2.54.0.1032.g2f8565e1d1-goog



^ permalink raw reply

* Re: [PATCH v2 2/2] PCI: imx6: Assert ref_clk_en after reference clock stabilizes on i.MX95
From: Frank Li @ 2026-06-04 19:34 UTC (permalink / raw)
  To: Richard Zhu
  Cc: l.stach, lpieralisi, kwilczynski, mani, robh, bhelgaas, s.hauer,
	kernel, festevam, linux-pci, linux-arm-kernel, imx, linux-kernel,
	stable
In-Reply-To: <20260518072715.3166514-3-hongxing.zhu@nxp.com>

On Mon, May 18, 2026 at 03:27:15PM +0800, Richard Zhu wrote:
> According to the PHY Databook Common Block Signals section, the
> ref_clk_en signal must remain de-asserted until the reference clock is
> running at the appropriate frequency. Once the clock is stable,
> ref_clk_en can be asserted. For lower power states where the reference
> clock to the PHY is disabled, ref_clk_en should also be de-asserted.
>
> Move the ref_clk_en bit manipulation into imx95_pcie_enable_ref_clk()
> to ensure the reference clock stabilizes before ref_clk_en is asserted
> and before the PHY reset is de-asserted. This aligns with the timing
> requirements specified in the PHY documentation.
>
> Fixes: d8574ce57d76 ("PCI: imx6: Add external reference clock input mode support")
> Cc: <stable@vger.kernel.org>
> Signed-off-by: Richard Zhu <hongxing.zhu@nxp.com>
> ---

Reviewed-by: Frank Li <Frank.Li@nxp.com>

>  drivers/pci/controller/dwc/pci-imx6.c | 28 +++++++++++++++++++++------
>  1 file changed, 22 insertions(+), 6 deletions(-)
>
> diff --git a/drivers/pci/controller/dwc/pci-imx6.c b/drivers/pci/controller/dwc/pci-imx6.c
> index 66e760015c92..c4b079c93648 100644
> --- a/drivers/pci/controller/dwc/pci-imx6.c
> +++ b/drivers/pci/controller/dwc/pci-imx6.c
> @@ -270,8 +270,6 @@ static int imx95_pcie_init_pre_reset(struct imx_pcie *imx_pcie)
>
>  static int imx95_pcie_init_phy(struct imx_pcie *imx_pcie)
>  {
> -	bool ext = imx_pcie->enable_ext_refclk;
> -
>  	/*
>  	 * ERR051624: The Controller Without Vaux Cannot Exit L23 Ready
>  	 * Through Beacon or PERST# De-assertion
> @@ -290,10 +288,6 @@ static int imx95_pcie_init_phy(struct imx_pcie *imx_pcie)
>  			IMX95_PCIE_PHY_CR_PARA_SEL,
>  			IMX95_PCIE_PHY_CR_PARA_SEL);
>
> -	regmap_update_bits(imx_pcie->iomuxc_gpr, IMX95_PCIE_SS_RW_REG_0,
> -			   IMX95_PCIE_REF_CLKEN,
> -			   ext ? 0 : IMX95_PCIE_REF_CLKEN);
> -
>  	return 0;
>  }
>
> @@ -742,7 +736,29 @@ static void imx95_pcie_clkreq_override(struct imx_pcie *imx_pcie, bool enable)
>
>  static int imx95_pcie_enable_ref_clk(struct imx_pcie *imx_pcie, bool enable)
>  {
> +	bool ext = imx_pcie->enable_ext_refclk;
> +
>  	imx95_pcie_clkreq_override(imx_pcie, enable);
> +	/*
> +	 * The ref_clk_en signal must remain de-asserted until the
> +	 * reference clock is running at appropriate frequency, at which
> +	 * point this bit can be asserted. For lower power states where
> +	 * the reference clock to the PHY is disabled, it may also be
> +	 * de-asserted.
> +	 * +------------------- -+--------+----------------+
> +	 * | External clock mode | Enable | PCIE_REF_CLKEN |
> +	 * +---------------------+--------+----------------+
> +	 * | TRUE                | X      | 1b'0           |
> +	 * +---------------------+--------+----------------+
> +	 * | FALSE               | TRUE   | 1b'1           |
> +	 * +---------------------+--------+----------------+
> +	 * | FALSE               | FALSE  | 1b'0           |
> +	 * +---------------------+--------+----------------+
> +	 */
> +	regmap_update_bits(imx_pcie->iomuxc_gpr, IMX95_PCIE_SS_RW_REG_0,
> +			   IMX95_PCIE_REF_CLKEN,
> +			   ext || !enable ? 0 : IMX95_PCIE_REF_CLKEN);
> +
>  	return 0;
>  }
>
> --
> 2.37.1
>


^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox