From 6668ed271eaefaa63e686bdfbedaeb7b8e492722 Mon Sep 17 00:00:00 2001 From: Kumar Kartikeya Dwivedi Date: Fri, 4 Sep 2026 10:43:19 +0200 Subject: selftests/bpf: Reject graph kptr use after RCU unlock Add a sleepable verifier test that loads a graph-node local kptr in an explicit RCU read-side critical section, then passes its node to bpf_rbtree_remove() after the section ends. Before the verifier fix, the stale NON_OWN_REF flag makes the node look like a live borrowed reference and the program is accepted. After the fix, the pointer is demoted without NON_OWN_REF and the graph kfunc argument is rejected. Also exercise a graph kptr loaded while a spin lock provides implicit RCU protection. The pointer must be invalidated when the lock is released, which guards the required ordering between non-owning-reference invalidation and RCU demotion. Update the existing fault-protected load test state description. The post-unlock pointer no longer carries NON_OWN_REF, but remains readable because the load is rewritten to use BPF_PROBE_MEM. Signed-off-by: Kumar Kartikeya Dwivedi Link: https://lore.kernel.org/r/20260904084325.52250-7-memxor@gmail.com Signed-off-by: Alexei Starovoitov --- arch/arm/mach-rpc/time.c | 97 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100644 arch/arm/mach-rpc/time.c (limited to 'arch/arm/mach-rpc/time.c') diff --git a/arch/arm/mach-rpc/time.c b/arch/arm/mach-rpc/time.c new file mode 100644 index 000000000..9f8edcfe9 --- /dev/null +++ b/arch/arm/mach-rpc/time.c @@ -0,0 +1,97 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * linux/arch/arm/common/time-acorn.c + * + * Copyright (c) 1996-2000 Russell King. + * + * Changelog: + * 24-Sep-1996 RMK Created + * 10-Oct-1996 RMK Brought up to date with arch-sa110eval + * 04-Dec-1997 RMK Updated for new arch/arm/time.c + * 13=Jun-2004 DS Moved to arch/arm/common b/c shared w/CLPS7500 + */ +#include +#include +#include +#include +#include + +#include +#include + +#include + +#define RPC_CLOCK_FREQ 2000000 +#define RPC_LATCH DIV_ROUND_CLOSEST(RPC_CLOCK_FREQ, HZ) + +static u32 ioc_time; + +static u64 ioc_timer_read(struct clocksource *cs) +{ + unsigned int count1, count2, status; + unsigned long flags; + u32 ticks; + + local_irq_save(flags); + ioc_writeb (0, IOC_T0LATCH); + barrier (); + count1 = ioc_readb(IOC_T0CNTL) | (ioc_readb(IOC_T0CNTH) << 8); + barrier (); + status = ioc_readb(IOC_IRQREQA); + barrier (); + ioc_writeb (0, IOC_T0LATCH); + barrier (); + count2 = ioc_readb(IOC_T0CNTL) | (ioc_readb(IOC_T0CNTH) << 8); + ticks = ioc_time + RPC_LATCH - count2; + local_irq_restore(flags); + + if (count2 < count1) { + /* + * The timer has not reloaded between reading count1 and + * count2, check whether an interrupt was actually pending. + */ + if (status & (1 << 5)) + ticks += RPC_LATCH; + } else if (count2 > count1) { + /* + * The timer has reloaded, so count2 indicates the new + * count since the wrap. The interrupt would not have + * been processed, so add the missed ticks. + */ + ticks += RPC_LATCH; + } + + return ticks; +} + +static struct clocksource ioctime_clocksource = { + .read = ioc_timer_read, + .mask = CLOCKSOURCE_MASK(32), + .rating = 100, +}; + +void __init ioctime_init(void) +{ + ioc_writeb(RPC_LATCH & 255, IOC_T0LTCHL); + ioc_writeb(RPC_LATCH >> 8, IOC_T0LTCHH); + ioc_writeb(0, IOC_T0GO); +} + +static irqreturn_t +ioc_timer_interrupt(int irq, void *dev_id) +{ + ioc_time += RPC_LATCH; + legacy_timer_tick(1); + return IRQ_HANDLED; +} + +/* + * Set up timer interrupt. + */ +void __init ioc_timer_init(void) +{ + WARN_ON(clocksource_register_hz(&ioctime_clocksource, RPC_CLOCK_FREQ)); + ioctime_init(); + if (request_irq(IRQ_TIMER0, ioc_timer_interrupt, 0, "timer", NULL)) + pr_err("Failed to request irq %d (timer)\n", IRQ_TIMER0); +} -- cgit v1.3.1