* Re: [PATCH v4 2/3] dt-bindings: mfd: syscon: add aspeed,ast2600-i3c-global compatible
From: Andrew Jeffery @ 2026-06-05 0:43 UTC (permalink / raw)
To: Lee Jones
Cc: Krzysztof Kozlowski, Dawid Glazik, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Joel Stanley, linux-aspeed,
devicetree, linux-arm-kernel, linux-kernel, maciej.lawniczak
In-Reply-To: <20260425-poised-accomplished-hyena-d2c1a0@quoll>
Hi Lee,
On Sat, 2026-04-25 at 11:40 +0200, Krzysztof Kozlowski wrote:
> On Fri, Apr 24, 2026 at 10:21:00PM +0200, Dawid Glazik wrote:
> > Add aspeed,ast2600-i3c-global to the syscon binding compatible
> > lists to document the AST2600 I3C global register syscon node.
> >
> > Signed-off-by: Dawid Glazik <dawid.glazik@linux.intel.com>
> > ---
> > Documentation/devicetree/bindings/mfd/syscon.yaml | 2 ++
> > 1 file changed, 2 insertions(+)
>
> Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>
Thanks for applying the AST2700 SoC1 pinctrl binding[1].
Can you pick this one up too? I've applied the corresponding dts patch
to the BMC tree, it'd be great to address the warning that's currently
produced.
Cheers,
Andrew
[1]: https://lore.kernel.org/all/20260521-pinctrl-single-bit-v5-1-308be2c160fc@aspeedtech.com/
^ permalink raw reply
* [PATCH v1] arm64: errata: Workaround NVIDIA Olympus device store/load ordering erratum
From: Shanker Donthineni @ 2026-06-04 23:12 UTC (permalink / raw)
To: Catalin Marinas, Will Deacon, linux-arm-kernel
Cc: Mark Rutland, linux-kernel, linux-doc, Shanker Donthineni,
Vikram Sethi, Jason Sequeira
On systems with NVIDIA Olympus cores, a Device-nGnR* load can be
observed by a peripheral before an older, non-overlapping Device-nGnR*
store to the same peripheral. This breaks the program-order guarantee
that software expects for Device-nGnR* accesses and can leave a
peripheral in an incorrect state, as a load is observed before an
earlier store takes effect.
The erratum can occur only when all of the following apply:
- A PE executes a Device-nGnR* store followed by a younger
Device-nGnR* load.
- The store is not a store-release.
- The accesses target the same peripheral and do not overlap in bytes.
- There is at most one intervening Device-nGnR* store in program
order, and there are no intervening Device-nGnR* loads.
- There is no DSB, and no DMB that orders loads, between the store and
the load.
- Specific micro-architectural and timing conditions occur.
Two ways to restore ordering: insert a barrier (any DSB, or a DMB that
orders loads) between the store and the load, or make the store a
store-release. A load-acquire on the load side would not help, because
acquire semantics do not prevent a load from being observed ahead of an
older store; only the store side (release or a barrier) closes the
window.
Promote the raw MMIO store helpers (__raw_writeb/w/l/q) from plain str*
to stlr* (Store-Release), which removes the "store is not a
store-release" condition for every device write the kernel issues.
Because writel() and writel_relaxed() are both built on __raw_writel()
in asm-generic/io.h, patching the raw variants covers both the
non-relaxed and relaxed APIs without touching the higher layers. Note
that writel()'s own barrier sits before the store, so it does not order
the store against a subsequent readl(); the store-release promotion is
what provides that ordering.
Like ARM64_ERRATUM_832075 on the load side, the change is gated on a new
ARM64_WORKAROUND_DEVICE_STORE_RELEASE capability and only activated on
parts that match MIDR_NVIDIA_OLYMPUS, so unaffected CPUs continue to use
the plain str* sequence.
Co-developed-by: Vikram Sethi <vsethi@nvidia.com>
Signed-off-by: Vikram Sethi <vsethi@nvidia.com>
Signed-off-by: Shanker Donthineni <sdonthineni@nvidia.com>
---
Documentation/arch/arm64/silicon-errata.rst | 2 ++
arch/arm64/Kconfig | 23 ++++++++++++++++++++
arch/arm64/include/asm/io.h | 24 ++++++++++++++-------
arch/arm64/kernel/cpu_errata.c | 8 +++++++
arch/arm64/tools/cpucaps | 1 +
5 files changed, 50 insertions(+), 8 deletions(-)
diff --git a/Documentation/arch/arm64/silicon-errata.rst b/Documentation/arch/arm64/silicon-errata.rst
index 211119ce7adc..899bed3908bb 100644
--- a/Documentation/arch/arm64/silicon-errata.rst
+++ b/Documentation/arch/arm64/silicon-errata.rst
@@ -256,6 +256,8 @@ stable kernels.
+----------------+-----------------+-----------------+-----------------------------+
| NVIDIA | Carmel Core | N/A | NVIDIA_CARMEL_CNP_ERRATUM |
+----------------+-----------------+-----------------+-----------------------------+
+| NVIDIA | Olympus core | T410-OLY-1027 | NVIDIA_OLYMPUS_1027_ERRATUM |
++----------------+-----------------+-----------------+-----------------------------+
| NVIDIA | T241 GICv3/4.x | T241-FABRIC-4 | N/A |
+----------------+-----------------+-----------------+-----------------------------+
| NVIDIA | T241 MPAM | T241-MPAM-1 | N/A |
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
index fe60738e5943..a6bac84b05a1 100644
--- a/arch/arm64/Kconfig
+++ b/arch/arm64/Kconfig
@@ -564,6 +564,29 @@ config ARM64_ERRATUM_832075
If unsure, say Y.
+config NVIDIA_OLYMPUS_1027_ERRATUM
+ bool "NVIDIA Olympus: device store/load ordering erratum"
+ default y
+ help
+ This option adds an alternative code sequence to work around an
+ NVIDIA Olympus core erratum where a Device-nGnR* store can be
+ observed by a peripheral after a younger Device-nGnR* load to the
+ same peripheral. This breaks the program order that drivers rely
+ on for MMIO and can leave a device in an incorrect state.
+
+ The workaround promotes the raw MMIO store helpers
+ (__raw_writeb/w/l/q) to Store-Release (STLR), which restores the
+ required ordering. Because writel() and writel_relaxed() are built
+ on __raw_writel(), both are covered without changes to the higher
+ layers.
+
+ The fix is applied through the alternatives framework, so enabling
+ this option does not by itself activate the workaround: it is
+ patched in only when an affected CPU is detected, and is a no-op on
+ unaffected CPUs.
+
+ If unsure, say Y.
+
config ARM64_ERRATUM_834220
bool "Cortex-A57: 834220: Stage 2 translation fault might be incorrectly reported in presence of a Stage 1 fault (rare)"
depends on KVM
diff --git a/arch/arm64/include/asm/io.h b/arch/arm64/include/asm/io.h
index 8cbd1e96fd50..b6d7966e9c19 100644
--- a/arch/arm64/include/asm/io.h
+++ b/arch/arm64/include/asm/io.h
@@ -25,29 +25,37 @@
#define __raw_writeb __raw_writeb
static __always_inline void __raw_writeb(u8 val, volatile void __iomem *addr)
{
- volatile u8 __iomem *ptr = addr;
- asm volatile("strb %w0, %1" : : "rZ" (val), "Qo" (*ptr));
+ asm volatile(ALTERNATIVE("strb %w0, [%1]",
+ "stlrb %w0, [%1]",
+ ARM64_WORKAROUND_DEVICE_STORE_RELEASE)
+ : : "rZ" (val), "r" (addr));
}
#define __raw_writew __raw_writew
static __always_inline void __raw_writew(u16 val, volatile void __iomem *addr)
{
- volatile u16 __iomem *ptr = addr;
- asm volatile("strh %w0, %1" : : "rZ" (val), "Qo" (*ptr));
+ asm volatile(ALTERNATIVE("strh %w0, [%1]",
+ "stlrh %w0, [%1]",
+ ARM64_WORKAROUND_DEVICE_STORE_RELEASE)
+ : : "rZ" (val), "r" (addr));
}
#define __raw_writel __raw_writel
static __always_inline void __raw_writel(u32 val, volatile void __iomem *addr)
{
- volatile u32 __iomem *ptr = addr;
- asm volatile("str %w0, %1" : : "rZ" (val), "Qo" (*ptr));
+ asm volatile(ALTERNATIVE("str %w0, [%1]",
+ "stlr %w0, [%1]",
+ ARM64_WORKAROUND_DEVICE_STORE_RELEASE)
+ : : "rZ" (val), "r" (addr));
}
#define __raw_writeq __raw_writeq
static __always_inline void __raw_writeq(u64 val, volatile void __iomem *addr)
{
- volatile u64 __iomem *ptr = addr;
- asm volatile("str %x0, %1" : : "rZ" (val), "Qo" (*ptr));
+ asm volatile(ALTERNATIVE("str %x0, [%1]",
+ "stlr %x0, [%1]",
+ ARM64_WORKAROUND_DEVICE_STORE_RELEASE)
+ : : "rZ" (val), "r" (addr));
}
#define __raw_readb __raw_readb
diff --git a/arch/arm64/kernel/cpu_errata.c b/arch/arm64/kernel/cpu_errata.c
index 5377e4c2eba2..958d7f16bfeb 100644
--- a/arch/arm64/kernel/cpu_errata.c
+++ b/arch/arm64/kernel/cpu_errata.c
@@ -809,6 +809,14 @@ const struct arm64_cpu_capabilities arm64_errata[] = {
ERRATA_MIDR_ALL_VERSIONS(MIDR_NVIDIA_CARMEL),
},
#endif
+#ifdef CONFIG_NVIDIA_OLYMPUS_1027_ERRATUM
+ {
+ /* NVIDIA Olympus core */
+ .desc = "NVIDIA Olympus device load/store ordering erratum",
+ .capability = ARM64_WORKAROUND_DEVICE_STORE_RELEASE,
+ ERRATA_MIDR_ALL_VERSIONS(MIDR_NVIDIA_OLYMPUS),
+ },
+#endif
#ifdef CONFIG_ARM64_WORKAROUND_TRBE_OVERWRITE_FILL_MODE
{
/*
diff --git a/arch/arm64/tools/cpucaps b/arch/arm64/tools/cpucaps
index 811c2479e82d..d367257bf770 100644
--- a/arch/arm64/tools/cpucaps
+++ b/arch/arm64/tools/cpucaps
@@ -120,6 +120,7 @@ WORKAROUND_CAVIUM_TX2_219_PRFM
WORKAROUND_CAVIUM_TX2_219_TVM
WORKAROUND_CLEAN_CACHE
WORKAROUND_DEVICE_LOAD_ACQUIRE
+WORKAROUND_DEVICE_STORE_RELEASE
WORKAROUND_NVIDIA_CARMEL_CNP
WORKAROUND_PMUV3_IMPDEF_TRAPS
WORKAROUND_QCOM_FALKOR_E1003
--
2.43.0
^ permalink raw reply related
* Re: [PATCH] KVM: arm64: Reallocate the nested_mmus array under the mmu_lock
From: Sean Christopherson @ 2026-06-04 22:58 UTC (permalink / raw)
To: Oliver Upton
Cc: Hyunwoo Kim, maz, joey.gouly, seiden, suzuki.poulose, yuzenghui,
catalin.marinas, will, christoffer.dall, linux-arm-kernel, kvmarm
In-Reply-To: <aiH7xKnprU9bUap5@kernel.org>
On Thu, Jun 04, 2026, Oliver Upton wrote:
> The shortlog is very confusing, since "allocate behind $LOCK" is usually
> something alarming. Maybe instead:
>
> KVM: arm64: Reassign nested_mmus array behind mmu_lock
+1 from the peanut gallery. After reading the shortlog, I was about to grab my
pitchfork :-)
^ permalink raw reply
* Re: [PATCH v2 3/3] arm64: dts: allwinner: A133: add support for Baijie Helper A133 board
From: Alexander Sverdlin @ 2026-06-04 22:37 UTC (permalink / raw)
To: Andre Przywara
Cc: Paul Kocialkowski, linux-sunxi, devicetree, linux-arm-kernel,
linux-kernel
In-Reply-To: <20260518235432.07537260@ryzen.lan>
Hi Andre,
On Mon, 2026-05-18 at 23:54 +0200, Andre Przywara wrote:
> > > > +®_dcdc2 {
> > > > + regulator-always-on;
> > > > + regulator-min-microvolt = <500000>;
> > > > + regulator-max-microvolt = <1300000>;
> > >
> > > Should be:
> > > regulator-min-microvolt = <900000>;
> > > regulator-max-microvolt = <1300000>;
> >
> > 0.81..1.2v according to A133 Datasheet Revision 1.1 Jul.14, 2020?
>
> Do you have the CPU OPPs for this board? Do they slightly
> overclock/over-volt the core? We have seen this for some other boards.
> But you could go with the safer 810mV...1200mV range, and we adjust
> this when needed.
I'm not sure how to interpret this, if it helps, vendor BSP has:
root@HelperA133:~# hexdump -Cv /proc/device-tree/opp_l_table/opp@1512000000/
clock-latency-ns name opp-hz opp-microvolt-b0 opp-microvolt-b1 opp-microvolt-b2 opp-microvolt-b3
root@HelperA133:~# hexdump -Cv /proc/device-tree/opp_l_table/opp@1512000000/opp-microvolt-b0
00000000 00 12 01 60 |...`|
root@HelperA133:~# hexdump -Cv /proc/device-tree/opp_l_table/opp@1512000000/opp-microvolt-b1
00000000 00 11 3e 10 00 11 3e 10 00 11 65 20 |..>...>...e |
root@HelperA133:~# hexdump -Cv /proc/device-tree/opp_l_table/opp@1512000000/opp-microvolt-b2
00000000 00 10 c8 e0 |....|
root@HelperA133:~# hexdump -Cv /proc/device-tree/opp_l_table/opp@1512000000/opp-microvolt-b3
00000000 00 10 7a c0 |..z.|
root@HelperA133:~# hexdump -Cv /proc/device-tree/opp_l_table/compatible
00000000 61 6c 6c 77 69 6e 6e 65 72 2c 73 75 6e 35 30 69 |allwinner,sun50i|
00000010 2d 6f 70 65 72 61 74 69 6e 67 2d 70 6f 69 6e 74 |-operating-point|
00000020 73 00 |s.|
At least 1512MHz is possible (but datasheet says 1.6GHz, so probably no overclocking yet).
--
Alexander Sverdlin.
^ permalink raw reply
* Re: [PATCH] KVM: arm64: Reallocate the nested_mmus array under the mmu_lock
From: Oliver Upton @ 2026-06-04 22:27 UTC (permalink / raw)
To: Hyunwoo Kim
Cc: maz, joey.gouly, seiden, suzuki.poulose, yuzenghui,
catalin.marinas, will, christoffer.dall, linux-arm-kernel, kvmarm
In-Reply-To: <aiHEKOeZMVwsRlvP@v4bel>
Hi,
The shortlog is very confusing, since "allocate behind $LOCK" is usually
something alarming. Maybe instead:
KVM: arm64: Reassign nested_mmus array behind mmu_lock
On Fri, Jun 05, 2026 at 03:30:00AM +0900, Hyunwoo Kim wrote:
> Code that walks kvm->arch.nested_mmus[] holds kvm->mmu_lock. By contrast,
> kvm_vcpu_init_nested() reallocates the array and frees the old buffer while
> holding only kvm->arch.config_lock, so a walker can reference the freed
> array.
It wouldn't hurt to share slightly more information here. Are you
dealing with a concurrent MMU notifier?
> Allocate the new array outside the lock, as the allocation can sleep, and
> do only the copy and the pointer swap under the mmu_lock. After the swap no
> walker can reach the old buffer, so free it once the lock has been
> released.
>
> Fixes: 4f128f8e1aaac ("KVM: arm64: nv: Support multiple nested Stage-2 mmu structures")
> Signed-off-by: Hyunwoo Kim <imv4bel@gmail.com>
The diff itself LGTM
Reviewed-by: Oliver Upton <oupton@kernel.org>
Thanks,
Oliver
> ---
> arch/arm64/kvm/nested.c | 33 ++++++++++++++++++++-------------
> 1 file changed, 20 insertions(+), 13 deletions(-)
>
> diff --git a/arch/arm64/kvm/nested.c b/arch/arm64/kvm/nested.c
> index 38f672e940878..6f7bc9a9992e0 100644
> --- a/arch/arm64/kvm/nested.c
> +++ b/arch/arm64/kvm/nested.c
> @@ -89,21 +89,28 @@ int kvm_vcpu_init_nested(struct kvm_vcpu *vcpu)
> * again, and there is no reason to affect the whole VM for this.
> */
> num_mmus = atomic_read(&kvm->online_vcpus) * S2_MMU_PER_VCPU;
> - tmp = kvrealloc(kvm->arch.nested_mmus,
> - size_mul(sizeof(*kvm->arch.nested_mmus), num_mmus),
> - GFP_KERNEL_ACCOUNT | __GFP_ZERO);
> - if (!tmp)
> - return -ENOMEM;
>
> - swap(kvm->arch.nested_mmus, tmp);
> + if (num_mmus > kvm->arch.nested_mmus_size) {
> + tmp = kvcalloc(num_mmus, sizeof(*tmp), GFP_KERNEL_ACCOUNT);
> + if (!tmp)
> + return -ENOMEM;
>
> - /*
> - * If we went through a realocation, adjust the MMU back-pointers in
> - * the previously initialised kvm_pgtable structures.
> - */
> - if (kvm->arch.nested_mmus != tmp)
> - for (int i = 0; i < kvm->arch.nested_mmus_size; i++)
> - kvm->arch.nested_mmus[i].pgt->mmu = &kvm->arch.nested_mmus[i];
> + write_lock(&kvm->mmu_lock);
> +
> + if (kvm->arch.nested_mmus_size) {
> + memcpy(tmp, kvm->arch.nested_mmus,
> + size_mul(sizeof(*tmp), kvm->arch.nested_mmus_size));
> +
> + for (int i = 0; i < kvm->arch.nested_mmus_size; i++)
> + tmp[i].pgt->mmu = &tmp[i];
> + }
> +
> + swap(kvm->arch.nested_mmus, tmp);
> +
> + write_unlock(&kvm->mmu_lock);
> +
> + kvfree(tmp);
> + }
>
> for (int i = kvm->arch.nested_mmus_size; !ret && i < num_mmus; i++)
> ret = init_nested_s2_mmu(kvm, &kvm->arch.nested_mmus[i]);
> --
> 2.43.0
>
^ permalink raw reply
* Re: [PATCH net 1/2] net: airoha: Fix use-after-free in metadata dst teardown
From: Jacob Keller @ 2026-06-04 21:53 UTC (permalink / raw)
To: Lorenzo Bianconi
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: <aiHsxFrleJfpLeuA@lore-rh-laptop>
On 6/4/2026 2:23 PM, Lorenzo Bianconi wrote:
>> 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.
>
Ok. So when would any flow need to call metadata_dst_free()? I guess
some path might happen to know that its already past RCU grace period
for all accesses or something...
I guess mostly my brain would be less confused if we had
metadata_dst_free() call dst_destroy or have a metadata_dst_release() or
something to make the two sides of the naming match... but I guess thats
really just noise on top of the API, since it doesn't really add any value.
Thanks for the explanation and helping un-confuse me at least a little!
>>
>> 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.
>
Ok. So basically its correct, it just looks odd because the code is
re-used by several other flows.
>>
>> 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.
>
I might have been mistaken. I did a search for metadata_dst_free() in
the driver and I think I confused the airoha_metadata_dst_free() call
site while scanning the code.
All of that said, thanks for a quick response and now that I understand
it makes sense:
Reviewed-by: Jacob Keller <jacob.e.keller@intel.com>
Regards,
Jake
^ permalink raw reply
* Re: [PATCH bpf-next v2 8/8] selftests/bpf: add tests to validate KASAN on JIT programs
From: bot+bpf-ci @ 2026-06-04 21:45 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-8-c066e627fda8@bootlin.com>
[-- Attachment #1: Type: text/plain, Size: 3984 bytes --]
> 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..0000000000000
> --- /dev/null
> +++ b/tools/testing/selftests/bpf/prog_tests/kasan.c
> @@ -0,0 +1,356 @@
[ ... ]
> + ctx->klog_fd = open_kernel_logs();
> + if (!ASSERT_OK_FD(ctx->klog_fd, "open kernel logs"))
> + goto destroy;
[ ... ]
> + for (i = 0; i < ARRAY_SIZE(tests); i++) {
> + test = &tests[i];
> + run_subtest(ctx, test);
> + }
The Sashiko AI review on the RFC v1 8/8 posting raised a concern here that
does not seem to have been addressed in v2:
https://lore.kernel.org/bpf/20260425065049.BEC63C2BCB0@smtp.kernel.org
The original question was:
KASAN operates in single-shot mode by default. After the first subtest
triggers a report, will subsequent reports be suppressed, causing the
remaining subtests to fail? Also, since BPF CI runs with panic_on_warn=1,
will the first KASAN splat invoke check_panic_on_warn() and immediately
panic the CI runner?
KASAN runs in single-shot mode by default. mm/kasan/report.c suppresses
reports after the first one unless kasan_multi_shot is set.
This loop runs roughly 50 subtests, and each one expects a fresh KASAN
report emitted via the JIT-inserted __asan_load/__asan_store calls that go
through kasan_report().
Under default single-shot KASAN, would the subtests after the first one see
no report and fail the positive assertions?
Separately, BPF CI runs with panic_on_warn=1 (config.ppc64el still sets
panic_on_warn=1). Would the first KASAN splat reach check_panic_on_warn()
and panic the runner?
Nothing in v2 appears to enable kasan_multi_shot or guard against
panic_on_warn. Was this intended to be handled somewhere?
> 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 @@
[ ... ]
> +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));
This isn't a bug, but should this second call be
bpf_kfunc_kasan_unpoison() rather than bpf_kfunc_kasan_poison()?
Every other program in this file (st_on_stack, st_not_on_stack, ldx_*,
simple_atomic_*, load_acquire_*, store_release_*) calls
bpf_kfunc_kasan_unpoison() after the access, and stx_not_on_stack below
repeats the same poison-twice pattern.
> + return 0;
> +}
[ ... ]
> +SEC("tcx/ingress")
> +int stx_not_on_stack(struct __sk_buff *skb)
> +{
[ ... ]
> + bpf_kfunc_kasan_poison(val, sizeof(struct kasan_write_val));
> + switch (access_size) {
> + case 1:
> + val->data_1 = access_size;
> + break;
[ ... ]
> + }
> + bpf_kfunc_kasan_poison(val, sizeof(struct kasan_write_val));
Same question here as in stx_on_stack: the second call is
bpf_kfunc_kasan_poison() where the other programs use
bpf_kfunc_kasan_unpoison().
The intended store still happens while the memory is poisoned, so the
expected KASAN report is still generated and the positive assertion still
passes. The effect is that the leftover poison stays on the test_map slab
value (and on the stack region for stx_on_stack) until the next subtest
re-poisons and unpoisons the same region. Was poisoning a second time here
intentional?
> + return 0;
> +}
---
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 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
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox