summaryrefslogtreecommitdiff
AgeCommit message (Collapse)Author
2026-10-09cxl/acpi: Convert QTG _DSM latencies to picosecondsKoichiro Den
The QTG _DSM expects read and write latencies in picoseconds, as specified in CXL 3.0, Table 9-30. The code base originally kept the latencies passed to the QTG _DSM in picoseconds in effect. Commit 51293c565cf4 ("cxl: Fix incorrect region perf data calculation") normalized CDAT and host bridge latencies to nanoseconds, but did not add a conversion back to picoseconds when constructing the _DSM input. Firmware can therefore select QTG IDs using understated latencies. Convert the latencies to picoseconds when constructing the _DSM input package. Multiply in 64 bits and reject latencies that cannot fit in the DWORD inputs defined by the specification. Fixes: 51293c565cf4 ("cxl: Fix incorrect region perf data calculation") Cc: stable@vger.kernel.org Signed-off-by: Koichiro Den <den@valinux.co.jp> Reviewed-by: Alison Schofield <alison.schofield@intel.com> Reviewed-by: Dave Jiang <dave.jiang@intel.com> Link: https://patch.msgid.link/20261009045452.1107921-4-den@valinux.co.jp Signed-off-by: Dave Jiang <dave.jiang@intel.com>
2026-10-09cxl: Account for link width in latency calculationKoichiro Den
cxl_pci_get_latency() derives bandwidth from the per-lane speed returned by pcie_link_speed_mbps(), ignoring the negotiated link width. This overestimates the FlitLatency contribution for multi-lane links. Sections 2.11.3 and 2.11.4 of the Intel CXL Memory Device Software Guide describe the link bandwidth as the product of the negotiated speed and width. Replace pcie_link_speed_mbps() with pcie_link_bandwidth_mbps() and migrate both CXL callers (the only users of the API). The new helper obtains speed and width from a single Link Status read. The CXL bandwidth calculation no longer needs a separate width read and multiplication. While at it, make error handling more robust by converting PCI config read errors to negative errno values and checking for zero bandwidth before calculating latency. Fixes: 4d07a05397c8 ("cxl: Calculate and store PCI link latency for the downstream ports") Cc: stable@vger.kernel.org Signed-off-by: Koichiro Den <den@valinux.co.jp> Reviewed-by: Alison Schofield <alison.schofield@intel.com> Acked-by: Bjorn Helgaas <bhelgaas@google.com> # pci/pci.c Reviewed-by: Dave Jiang <dave.jiang@intel.com> Link: https://patch.msgid.link/20261009045452.1107921-3-den@valinux.co.jp Signed-off-by: Dave Jiang <dave.jiang@intel.com>
2026-10-09cxl/port: Convert PCIe link latency to nanosecondsKoichiro Den
Commit 51293c565cf4 ("cxl: Fix incorrect region perf data calculation") converted CDAT latencies to nanoseconds and removed the conversion in cxl_region_perf_data_calculate(), but left PCIe link latencies in picoseconds. This inflates the FlitLatency contribution to region and node latencies by a factor of 1000, exposing incorrect values in sysfs. The resulting abstract distances can incorrectly reverse the memory tier ordering of nodes. Convert the FlitLatency contribution to nanoseconds before adding it to the access coordinates, rounding up as cdat_normalize() does. Fixes: 51293c565cf4 ("cxl: Fix incorrect region perf data calculation") Cc: stable@vger.kernel.org Signed-off-by: Koichiro Den <den@valinux.co.jp> Reviewed-by: Alison Schofield <alison.schofield@intel.com> Reviewed-by: Dave Jiang <dave.jiang@intel.com> Link: https://patch.msgid.link/20261009045452.1107921-2-den@valinux.co.jp Signed-off-by: Dave Jiang <dave.jiang@intel.com>
2026-10-09cxl/pci: Update only the Port GPF timeout fieldsGuixin Liu
update_gpf_port_dvsec() rebuilds the phase control register from just the timeout base and scale, so the rest of the register is cleared by the write. The other bits are reserved today and nothing is lost, but the full-register rewrite would clobber any field the spec defines there later. Read-modify-write the register and change only the two timeout fields. Fixes: a52b6a2c1c99 ("cxl/pci: Support Global Persistent Flush (GPF)") Assisted-by: LLM Signed-off-by: Guixin Liu <kanie@linux.alibaba.com> Reviewed-by: Dave Jiang <dave.jiang@intel.com> Reviewed-by: Alison Schofield <alison.schofield@intel.com> Link: https://patch.msgid.link/20261008034619.28059-4-kanie@linux.alibaba.com Signed-off-by: Dave Jiang <dave.jiang@intel.com>
2026-10-09cxl/pci: Program the Port GPF timeouts before caching the DVSECGuixin Liu
cxl_gpf_port_setup() caches the Port GPF DVSEC offset in the dport before programming the phase timeouts, and ignores the return value of both update_gpf_port_dvsec() calls: the function always reports success. A failing config write is therefore not only silent, it is also permanent: the timeouts stay at the hardware defaults instead of the maximum flush window the kernel is asking for, and the cached offset keeps any later endpoint attach from retrying the update. Propagate the errors and cache the offset only after both phases are programmed, so the next endpoint attach retries the setup. Fixes: 6af941db6a60 ("cxl/pci: Update Port GPF timeout only when the first EP attaching") Cc: stable@vger.kernel.org Assisted-by: LLM Signed-off-by: Guixin Liu <kanie@linux.alibaba.com> Reviewed-by: Jonathan Cameron <jonathan.cameron@oss.qualcomm.com> Reviewed-by: Dave Jiang <dave.jiang@intel.com> Reviewed-by: Alison Schofield <alison.schofield@intel.com> Link: https://patch.msgid.link/20261008034619.28059-3-kanie@linux.alibaba.com Signed-off-by: Dave Jiang <dave.jiang@intel.com>
2026-10-09cxl/pci: Use the Device GPF DVSEC for restricted endpointsGuixin Liu
cxl_gpf_get_dvsec() picks between the Port and the Device GPF DVSEC from the PCIe type of the given device, and only a plain endpoint counts as a device. A restricted CXL device is an endpoint too, but it falls into the port case and is probed for the Port GPF DVSEC instead of the Device GPF DVSEC. On an RCD, dirty shutdown tracking therefore never arms: the DVSEC lookup in cxl_nvdimm_arm_dirty_shutdown_tracking() fails, the dirty shutdown count stays invalid, and the warning complains about a missing Port GPF DVSEC on a device. Treat restricted endpoints as devices. Fixes: 36aace15d9bd ("cxl/pci: Drop the parameter is_port of cxl_gpf_get_dvsec()") Cc: stable@vger.kernel.org Assisted-by: LLM Signed-off-by: Guixin Liu <kanie@linux.alibaba.com> Reviewed-by: Jonathan Cameron <jonathan.cameron@oss.qualcomm.com> Reviewed-by: Dave Jiang <dave.jiang@intel.com> Reviewed-by: Alison Schofield <alison.schofield@intel.com> Link: https://patch.msgid.link/20261008034619.28059-2-kanie@linux.alibaba.com Signed-off-by: Dave Jiang <dave.jiang@intel.com>
2026-10-09cxl/port: Attach endpoints to a dport under the lock that pins itDave Jiang
devm_cxl_enumerate_ports() and add_port_attach_ep() look up or create a dport under its port's device lock, drop the lock, then pass the dport to cxl_add_ep(). add_ep() reads dport->port before taking the lock, and cxl_gpf_port_setup() reads and writes the dport with no lock held. A concurrent cxl_detach_ep() for another memdev below the same port can free the dport in that window: it calls del_dports() under the port lock when the last endpoint leaves. The enumerating memdev then dereferences freed memory. Attach the endpoint and set up GPF while the port lock from the lookup is still held. Make add_ep() assert that lock instead of taking it. Under the lock, a dport found in port->dports is still live, since removal erases it from the xarray before freeing it. [ dj: Added kdoc update from Alison. ] Fixes: de516b40116e ("cxl/port: Record dport in endpoint references") Reported-by: Li Ming <ming.li@zohomail.com> Closes: https://lore.kernel.org/linux-cxl/0c68324c-cfc7-46b2-9344-24ae40e88923@zohomail.com/ Assisted-by: LLM Reviewed-by: Li Ming <ming.li@zohomail.com> Reviewed-by: Alison Schofield <alison.schofield@intel.com> Link: https://patch.msgid.link/20261001154744.1095902-4-dave.jiang@intel.com Signed-off-by: Dave Jiang <dave.jiang@intel.com>
2026-10-09cxl/core: Hold the dport host lock across dport lookup and useDave Jiang
Callers of cxl_pci_find_port() and cxl_mem_find_port() can be handed a 'struct cxl_dport' that is already freed, and all four dereference it without the lock that would prevent that. A dport is kzalloc()'d into a devres group on its host device, so it dies when that host unbinds. The host is &port->dev for a switch port and port->uport_dev for the root port. Both helpers return the dport through an output parameter. The caller's port reference pins struct cxl_port, not the dport allocation, and the lookup itself is a bare xa_load() under no lock. Add cxl_port_dport_host() to name the device that pins a dport, and cxl_pci_find_dport() / cxl_mem_find_dport() to look one up with that device's lock held and asserted. Convert the four callers to use them and bail if the dport is gone. cxl_port_add_dport() already follows this rule. cxl_mem_probe() chose its endpoint devm host from dport->rch, which meant dereferencing the dport before taking any lock. Use cxl_port_dport_host(): rch dports only ever land on the root port, so it selects the same device without needing the dport. With every caller converted, the dport output parameter of cxl_pci_find_port() and cxl_mem_find_port() is unused. Drop it. Fixes: 733b57f262b0 ("cxl/pci: Early setup RCH dport component registers from RCRB") Assisted-by: LLM Reviewed-by: Li Ming <ming.li@zohomail.com> Reviewed-by: Alison Schofield <alison.schofield@intel.com> Link: https://patch.msgid.link/20261001154744.1095902-3-dave.jiang@intel.com Signed-off-by: Dave Jiang <dave.jiang@intel.com>
2026-10-09cxl/port: Clear cached dport pointers when a dport is removedDave Jiang
Switch decoders cache dport pointers in cxlsd->target[], and nothing clears them when a dport is freed. Readers then dereference freed memory. KASAN caught it under a cxl_test load/unload loop with concurrent sysfs reads (abbreviated). Clear the matching slots from cxl_dport_remove(), walking the port's decoders the way cxl_port_update_decoder_targets() does on the add side. Scan all nr_targets slots, the target[] allocation size, and clear every hit. Fixes: 8330671c57c7 ("cxl: Add helper to delete dport") Cc: stable@vger.kernel.org Assisted-by: LLM Reviewed-by: Jonathan Cameron <jonathan.cameron@oss.qualcomm.com> Reviewed-by: Li Ming <ming.li@zohomail.com> Reviewed-by: Gregory Price (Meta) <gourry@gourry.net> Reviewed-by: Alison Schofield <alison.schofield@intel.com> Link: https://patch.msgid.link/20261001154744.1095902-2-dave.jiang@intel.com Signed-off-by: Dave Jiang <dave.jiang@intel.com>
2026-10-09drm/panthor: Fix TOCTOU race between GROUP_REGISTERED check and eraseOsama Abdelkader
panthor_group_destroy() checks the GROUP_REGISTERED mark and then calls xa_erase() without holding the xarray lock across both. If the handle is destroyed by another thread in between, and a concurrent GROUP_CREATE reuses the freed ID, the first destroy erases and releases the new, still-initializing group, leading to a use-after-free in panthor_group_create(). Do the mark check and erase atomically under xa_lock(). Fixes: eec7e23d848d ("drm/panthor: Prevent potential UAF in group creation") Assisted-by: Claude:claude-opus-5.5 Reviewed-by: Boris Brezillon <boris.brezillon@collabora.com> Reviewed-by: Liviu Dudau <liviu.dudau@arm.com> Cc: stable@vger.kernel.org Signed-off-by: Osama Abdelkader <osama.abdelkader@gmail.com> Reviewed-by: Steven Price <steven.price@arm.com> Signed-off-by: Liviu Dudau <liviu.dudau@arm.com> Link: https://patch.msgid.link/20261008100935.3059587-1-osama.abdelkader@gmail.com
2026-10-09scripts/make_fit: Ignore schema checks when decomposing DTBsTing-Han Hou
With CHECK_DTBS enabled, Kbuild appends a schema validation command to the fdtoverlay command saved in a composite DTB's .cmd file. When DTB decomposition is requested, process_dtb() treats every word after -i as an input file, including the semicolon and the validator arguments. FIT generation then fails with FileNotFoundError while opening or checking the size of those words as files. Limit the input list to the fdtoverlay command before extracting its arguments. This preserves the base DTB and overlay order and ignores the subsequent schema validation command. Fixes: 17c31aded9a1 ("scripts/make_fit: Support decomposing DTBs") Assisted-by: LLM Signed-off-by: Ting-Han Hou <ue081723@gmail.com> Reviewed-by: Simon Glass <sjg@chromium.org> Link: https://patch.msgid.link/CA+NtrhQBK8uW32qO3dXxVxO1GSN-0EJfyJ16tnyx0o52T17WtQ@mail.gmail.com Signed-off-by: Nathan Chancellor <nathan@kernel.org>
2026-10-09clang-tools: Import os for broken pipe handlingTing-Han Hou
The BrokenPipeError handler in run-clang-tools.py calls os.open() and os.dup2() without importing os. If this handler is reached, it raises NameError instead of redirecting stdout and exiting with status 1. Import os so the handler can work as intended. Fixes: 87c7ee67deb7 ("scripts: handle BrokenPipeError for python scripts") Assisted-by: LLM Signed-off-by: Ting-Han Hou <ue081723@gmail.com> Link: https://patch.msgid.link/20261009175836.2277-1-ue081723@gmail.com Signed-off-by: Nathan Chancellor <nathan@kernel.org>
2026-10-09kconfig: use the value argument in sym_get_near_range_bound()Leizhen Zhang
sym_get_near_range_bound() is meant to check whether the given value is outside of the active range of an int/hex symbol, and is called with the symbol's current value from sym_calc_value() and with the default value from sym_get_string_default(). Since commit 9b8719973ee2 ("kconfig: use unsigned integers for hex range checks") it ignores its value argument and uses the current value of the symbol (via sym_get_range_val()) instead. As a result, an out-of-range default is no longer clamped in sym_get_string_default(), and savedefconfig writes out values that are equal to the effective default again, for example CONFIG_A=4 for: config A int "a" range 1 4 default 8 The existing savedefconfig_range test did not catch this because it only covers symbols without a prompt. Parse the value argument again, and add a test case for a symbol with a prompt. Found while fuzzing Kconfig input with ASan/UBSan. Fixes: 9b8719973ee2 ("kconfig: use unsigned integers for hex range checks") Assisted-by: Claude:claude-opus-5-5 Signed-off-by: Leizhen Zhang <lzsx618@gmail.com> Tested-by: Julian Braha <julianbraha@gmail.com> Reviewed-by: Julian Braha <julianbraha@gmail.com> Link: https://patch.msgid.link/20261008164540.3686383-1-lzsx618@gmail.com Signed-off-by: Nathan Chancellor <nathan@kernel.org>
2026-10-09kconfig: fix infinite loop in conf_choice() when no choice value is visibleLeizhen Zhang
If a choice is visible but none of its choice values is, conf_choice() counts cnt == 0 visible values and finds no default. When the choice has to be asked, e.g. because a new symbol was added to the same menu (oldconfig, syncconfig) or because stdin is at EOF (oldaskconfig), the loop that maps the answer to a menu entry never finds one and the while (1) loop repeats forever, printing "choice[1-0?]: 0" each time. This happens in the tree for s390 when the compiler supports none of the -march=z* options (e.g. a non-s390 host compiler), so that all values of the "Processor type" choice are invisible: $ make ARCH=s390 defconfig $ sed -i '/^CONFIG_NR_CPUS=/d' .config $ make ARCH=s390 oldconfig # hangs Before the removal of tristate choice support, conf_choice() returned early in this case. Do the same when there is no visible choice value. Found by fuzzing Kconfig input with ASan/UBSan. Fixes: fde192511bdb ("kconfig: remove tristate choice support") Assisted-by: Claude:claude-opus-5-5 libFuzzer Signed-off-by: Leizhen Zhang <lzsx618@gmail.com> Reviewed-by: Julian Braha <julianbraha@gmail.com> Tested-by: Julian Braha <julianbraha@gmail.com> Link: https://patch.msgid.link/20261008164521.3684735-1-lzsx618@gmail.com Signed-off-by: Nathan Chancellor <nathan@kernel.org>
2026-10-09kconfig: cleanup stale comment about 'keys' arrayJulian Braha
'keys' was removed from 'dialog_textbox' in: commit e14f1242a8be ("kconfig: menuconfig: simplify global jump key assignment") but the comment referencing it was never removed. Let's cleanup this stale comment. Signed-off-by: Julian Braha <julianbraha@gmail.com> Reviewed-by: Nicolas Schier <n.schier@fritz.com> Link: https://patch.msgid.link/20261009094617.17322-1-julianbraha@gmail.com Signed-off-by: Nathan Chancellor <nathan@kernel.org>
2026-10-09Merge branch 'bpf-use-scalar-evolution-to-widen-bounded-loops'Alexei Starovoitov
Eduard Zingerman says: ==================== bpf: use scalar evolution to widen bounded loops This series implements the scalar evolution (SCEV) technique for verification of loops. Scalar evolution is a static analysis technique that infers algebraic expressions describing how variables change within a loop body. These expressions can then be used to estimate the number of loop body executions. This estimate can be used to represent induction variables as ranges instead of enumerating each possible value. For example, the following loop: for (r0 = 0; r0 < 10; r0++) { ... } would now be verified with the assumption that r0 is a scalar value in the range [0..9] within its body. See the following patches for a more technical overview: - "bpf: compute scalar evolution expressions for loops" - "bpf: use SCEV to widen bounded loops" The series can be viewed as consisting of the following parts: - Preparatory patches fixing and extending liveness analysis and adding utility functions. The liveness changes collect stack access effects and register dependencies, including: - "bpf: fix arg_track_xfer() for atomic stack spills and fills" - "bpf: derive global subprog stack liveness from BTF signatures" - "bpf: compute parameters liveness in bpf_compute_live_registers()" - Patches relaxing the verifier's current restrictions on accessing memory via varying offset pointers, specifically: - "bpf: allow subrange relations for PTR_TO_STACK in regsafe()" - "bpf: representation for intervals with steps" - "bpf: varying offset access support for PTR_TO_BTF_ID pointers" - Patches to compute the immediate dominator tree and loop hierarchy. - Patches implementing scalar evolution and integrating it with the main verification pass: - "bpf: allow precision backtracking between overlapping checkpoints" - "bpf: compute scalar evolution expressions for loops" - "bpf: use SCEV to widen bounded loops" - "bpf: avoid widening registers that hinder exact stack-slot tracking" - Tests and test harness updates. Literature ========== - "Symbolic Evaluation of Chains of Recurrences for Loop Optimization" Robert A. van Engelen, 2000 - "The CR# Algebra and its Application in Loop Analysis and Optimization" Robert A. van Engelen, 2004 Supported patterns ================== As scalar evolution analysis attempts to infer recurring relationships from algebraic expressions describing loop variables, there are some limitations on what can be expressed. Below is a list of patterns that are expected to work with the current implementation. The examples are pseudo-code for the indicated BPF instruction shapes. Loop widening currently requires allow_uninit_stack, the verifier mode that permits reading uninitialized stack bytes. This requirement applies even to loops that use only registers. Without it, the verifier uses its existing iteration-by-iteration checking. 1. Counted loops, including decreasing counters: u64 i = 0; do { i += 1; } while (i < 10); u64 j = 3; do { j += -1; } while (j != 0); Header ranges are i in [0,9] and j in [1,3]. Pre-condition loops, such as while (i < 10) { i += 1; }, are also supported. The counter can reside in a register or a fixed 8-byte frame-pointer spill. 2. Strided accesses into a map value: /* bytes is a map-value array with at least 16 bytes. */ u64 i = 0, off = 0; do { bytes[off] = 1; i += 1; off += 2; } while (i < 8); At the header, off is in [0,14] with step 2. The byte store stays within the map value even though i and off are tracked separately. 3. A nonconstant entry value with a separate counted induction variable: u64 x = bpf_get_prandom_u32() & 6; // {0,2,4,6} u64 i = 0; do { x += 6; i += 1; } while (i < 3); The counter i supplies the bound. At the header, x widens to [0,18] with step 2, retaining the alignment common to its entry values and its slope; x itself need not start at a constant. 4. Conditional assignment from an invariant value: u64 x = 5; for (u64 i = 0; i < 3; i += 1) if (i == 1) x = 10; return x; Widening joins the pre-loop value 5 with the assigned value 10, giving x in [5,10] both inside and after the loop. 5. Early exits in addition to the counted backedge: u64 i = 0; do { if (bpf_get_prandom_u32() == 0) break; i += 1; } while (i != 3); The counter still bounds the loop. An additional exit reduces the available lower-bound information; it does not by itself prevent widening. 6. Array accesses through different memory types: Bounded varying offsets are supported for arrays in BTF-typed objects, stack memory, map values and keys, packet data and metadata, and memory buffers. The usual bounds, alignment and access restrictions still apply. For stack arrays indexed by a widened loop counter, only byte and half-word accesses are supported; see limitation 5 below. For an array in a BTF-typed object: struct inner { int a, b; }; struct object { struct inner arr[8]; }; /* o points to a live, non-NULL BTF-typed struct object. */ u64 i = bpf_get_prandom_u32() & 7; value = o->arr[i].b; The 4-byte load has offsets 4 + 8*i, i in [0,7]. Bounds keep accesses within arr; base and step 8 ensure that each possible offset selects member b. Such accesses also work without a loop. 7. Nested loops with separate counters: u64 i = 0; do { u64 j = 0; do { /* body */ j += 1; } while (j < 3); i += 1; } while (i < 4); Both loops can be widened: i is in [0,3] at the outer header, and j is in [0,2] at the inner header. The inner counter is reset on each outer iteration; i is unchanged by the inner loop. 8. Incrementing and comparing a pointer directly: /* bytes is a map-value array with at least 8 bytes. */ u8 *p = bytes, *end = bytes + 8; do { *p = 1; p++; } while (p != end); The initial pointer and fixed end pointer give eight iterations, without a separate integer counter. At the loop header, p ranges from bytes to bytes + 7, so each byte store stays within the array. Deriving the iteration count requires the compared pointers to have a provable common memory origin. 9. A loop bounded by an exit other than the latch: u64 i = 0, len = 14; do { if (i == len) break; use(i); i += 1; } while (i != 64); Every exit condition dominating the latch bounds the iteration count; the smallest bound wins. Here i is in [0,14] at the header, not [0,63], so use(i) sees the range established by the break. Limitations =========== Patterns that lose precision after widening ------------------------------------------- Widening can lose information that the verifier gets by checking each iteration separately. For example: r7 = 5; for (r6 = 0; r6 < 3; r6++) if (r6 == 1) r7 = 10; if (r7 != 10) invalid_stack_read(); R7 is always 10 at the real exit, but is represented as [5, 10] at loop entry and after the loop. The verifier therefore cannot exclude the invalid read and rejects this otherwise safe program. It would be hard to avoid this limitation. The same issue affects correlated linear counters at loop exit: u64 i = 0, off = 0; while (i < 8) { i++; off += 2; } if (off != 16) invalid_stack_read(); Enumeration establishes off == 16. Widening tracks i and off independently, and the exit check on i does not reconstruct off's exact final value. This can be lifted after analysis adjustments. Patterns not widened yet ------------------------ If one of the variables modified in the loop cannot be widened, the verifier checks each iteration instead. Examples include: 1. 32-bit recurrences and comparisons: for (u32 i = 0; i < 8; i++) // ALU32 / JMP32 use(i); Only 64-bit arithmetic and comparisons are supported. This limitation will be lifted. 2. Non-linear updates: for (u64 i = 1; i < 16; i *= 2) // non-linear recurrence use(i); Recognition is currently limited to simple additive recurrences. Literature describes a way to represent this, but it is not considered a priority at the moment. 3. A controlling counter or bound that is not constant at loop entry: u64 limit = unknown_in_range(1, 8); for (u64 i = 0; i < limit; i++) use(i); u64 i = unknown_in_range(0, 7); while (i < 8) i++; Both loops have finite bounds, but iteration-count evaluation currently requires concrete entry values. Other induction variables may have nonconstant entry ranges once a separate counter establishes the iteration bound. This limitation can be lifted eventually. 4. Alternative values referring to another evolving register: r7 = 0; for (r6 = 0; r6 < 4; r6++) if (condition) r7 = r6; use(r7); R6 changes on each iteration. Widening currently supports conditional assignments only from constants or values that do not change in the loop. This limitation can be lifted. 5. Stack addresses whose widening would lose precise slot tracking: u64 slots[4]; for (u64 i = 0; i < 4; i++) slots[i] = i; struct bpf_dynptr dptrs[4]; for (u64 i = 0; i < 4; i++) bpf_dynptr_from_xdp(ctx, 0, &dptrs[i]); Keep such indices concrete for 4- and 8-byte stack loads/stores and calls requiring fixed-offset stack objects. Dependencies inside nested loops also constrain the outer loop. It would be hard to lift this limitation. 6. Values modified by a nested loop do not get a closed-form summary for the enclosing loop: u64 sum = 0; for (u64 i = 0; i < 4; i++) for (u64 j = 0; j < 4; j++) sum++; use(sum); The inner loop may widen, but the outer analysis forgets values modified by it rather than deriving the combined recurrence. This limitation can be lifted. 7. Multiple backedges and irreducible control flow: i = 0; H: if (i >= 8) goto done; if (condition) { i++; goto H; } // first backedge i++; goto H; // second backedge done: i = 0; if (condition) goto body; // second entry into the loop H: i++; body: if (i < 8) goto H; Widening requires a reducible loop with one backedge and a supported dominating exit condition. Multiple exits are supported, but their early-exit paths reduce what can be inferred about iteration counts. It is unclear whether this can be lifted at the moment. 8. Large slopes and unsupported iteration bounds: for (u64 i = 0, x = 0; i < 4; i++) { use(x); x += 65536; // slope outside signed 16 bits } Slopes used for linear widening must be nonzero signed-16-bit constants. The inferred number of header executions must be finite and less than U32_MAX. Counter wraparound must be ruled out while deriving the bound. 9. Incompatible pointer alternatives or comparison operands: p = &map_a[0]; q = &map_b[0]; for (u64 i = 0; i < 10; i++) { if (i == 5) p = q; use(*p); } At the loop header, p has SCEV ANY(p_entry, q). The alternatives point into different maps, so their offsets cannot be joined while retaining one map's metadata. Joining invariant pointer alternatives requires compatible pointer metadata, including type, origin IDs, frame number. 10. Some hard-coded constant limits: - Programs with more than 16 nested loops in one function are rejected. - Loops with more than 256 exits exceed the metadata limits and are not analyzed for widening. - At most 4 exit conditions dominating the latch are consulted for the iteration count of a loop. - Expression traversal is limited to depth 8. Only a small fixed number of alternative values can be joined. Veristat changes ================ The main impact of the series is a 23% reduction in number of processed instructions on a subset of Meta BPF programs. Verdict changes --------------- Two programs flip succes->failure, both selftests whose expectations are updated by the series: - verifier_gotox/gotox_edges_at_limit: test's CFG is a nest of ~1000 irreducible loops, deeper than the per-function loop stack (16), the program is rejected with "Too many nested loops". - verifier_scev/conditional_assignment_on_second_iteration: a value assigned on the second iteration is checked after the loop with its widened range, so a stack read guarded by that check is rejected. No program from the production corpora changes verdict. Regressions ----------- 20 programs regress by 30-33% and 6 more by 10-21%, all from an appliction objects sharing one unwinder library. All programs share a common reason for regression: - a big outer loop contains several smaller nested loops; - nested loops are now widened and coverge, their convergence adds backedges to the outer loop's SCC, thus flipping checkpoint comparisons with the outer loop to RANGE_WITHIN mode. Support for 32-bit recurrences and comparisons widens these loops too and removes the regression. This would be handled in a followup. Statistics ---------- A: master; B: scev-minimal-v3. Corpora: cilium, meta, scx, selftests. Insns counts verifier-processed instructions, not program size. Object exclusions: verifier_scev.bpf.o. Histogram: selected programs, without the table filters. Total progs: 7952 Old success: 5637 New success: 5646 total_insns diff min: -99.84% total_insns diff max: 61.90% 0 -> value: 0 value -> 0: 0 total_insns abs max old: 837,487 total_insns abs max new: 837,487 -100 .. -95 %: 15 -95 .. -90 %: 12 -90 .. -85 %: 10 -85 .. -75 %: 8 -75 .. -70 %: 6 -70 .. -65 %: 14 -65 .. -60 %: 38 -60 .. -55 %: 10 -55 .. -50 %: 17 -50 .. -45 %: 36 -45 .. -40 %: 9 -40 .. -35 %: 25 -35 .. -25 %: 10 -25 .. -15 %: 11 -15 .. -10 %: 48 -10 .. -5 %: 47 -5 .. 0 %: 111 0 .. 5 %: 7488 5 .. 15 %: 3 15 .. 25 %: 5 30 .. 35 %: 20 35 .. 40 %: 7 40 .. 50 %: 1 60 .. 65 %: 1 Tables: success in both runs; baseline Insns >= 5,000. Ranked by percentage change, then instruction-count change, file, program. Meta names are anonymized; numbered objects are distinct files. Top wins (50) ------------- File Program Insns (A) Insns (B) Insns (DIFF) ---------------------------------- --------------------------------- --------- --------- ----------------- verifier_bounds.bpf.o crossing_64_bit_signed_boundary_2 12330 20 -12310 (-99.84%) verifier_iterating_callbacks.bpf.o test1 7004 12 -6992 (-99.83%) bpf_iter_tasks.bpf.o dump_task_sleepable 85522 556 -84966 (-99.35%) bpf_iter_task_stack.bpf.o dump_task_stack 7209 65 -7144 (-99.10%) profiler-10.bpf.o hhvm_stack 23828 347 -23481 (-98.54%) bpf_iter_unix.bpf.o dump_unix 9026 226 -8800 (-97.50%) meta-app-22.bpf.o program-03 8705 319 -8386 (-96.34%) meta-app-22.bpf.o program-06 8948 328 -8620 (-96.33%) security-sandbox-10.bpf.o fork 30760 1249 -29511 (-95.94%) profiler-02.bpf.o on_py_event 79064 3864 -75200 (-95.11%) profiler-03.bpf.o on_py_event 79064 3864 -75200 (-95.11%) profiler-05.bpf.o on_py_event 79064 3864 -75200 (-95.11%) security-monitor-66.bpf.o mac_sysctl_write 7333 448 -6885 (-93.89%) profiler-04.bpf.o get_task_stacks 73176 6050 -67126 (-91.73%) security-monitor-65.bpf.o lsm_bprm_creds_for_exec 17538 1701 -15837 (-90.30%) security-monitor-65.bpf.o lsm_task_alloc 5674 565 -5109 (-90.04%) security-monitor-65.bpf.o lsm_fo_task_update 11399 1181 -10218 (-89.64%) security-sandbox-29.bpf.o test_glob_match 47257 5477 -41780 (-88.41%) load-balancer-17.bpf.o edgeloom_tc 24854 2984 -21870 (-87.99%) profiler-13.bpf.o handle_aten_op_call_start 7183 976 -6207 (-86.41%) security-monitor-65.bpf.o lsm_file_open 154412 21168 -133244 (-86.29%) security-monitor-17.bpf.o test_execve 293300 46071 -247229 (-84.29%) security-sandbox-13.bpf.o kvm_die 36514 5807 -30707 (-84.10%) security-monitor-97.bpf.o child_iter 41266 8182 -33084 (-80.17%) scheduler-30.bpf.o lavd_dispatch 290915 66359 -224556 (-77.19%) security-monitor-25.bpf.o syscalls_kill 98038 27912 -70126 (-71.53%) security-sandbox-14.bpf.o kernel_module_request 7880 2357 -5523 (-70.09%) security-sandbox-14.bpf.o kernel_load_data 7881 2358 -5523 (-70.08%) security-sandbox-14.bpf.o kernel_read_file 7882 2359 -5523 (-70.07%) xdp_lb_bench.bpf.o xdp_lb_bench 27126 9208 -17918 (-66.05%) firewall-05.bpf.o ingress 54034 18584 -35450 (-65.61%) firewall-05.bpf.o tc_in 54034 18584 -35450 (-65.61%) firewall-07.bpf.o ingress 54034 18584 -35450 (-65.61%) firewall-07.bpf.o tc_in 54034 18584 -35450 (-65.61%) security-sandbox-01.bpf.o bpf 30692 10586 -20106 (-65.51%) security-monitor-34.bpf.o syscall_setresuid 122200 43305 -78895 (-64.56%) security-monitor-34.bpf.o syscalls_setfsgid 122200 43305 -78895 (-64.56%) security-monitor-34.bpf.o syscalls_setfsuid 122200 43305 -78895 (-64.56%) security-monitor-34.bpf.o syscalls_setgid 122200 43305 -78895 (-64.56%) security-monitor-34.bpf.o syscalls_setregid 122200 43305 -78895 (-64.56%) security-monitor-34.bpf.o syscalls_setreuid 122200 43305 -78895 (-64.56%) security-monitor-34.bpf.o syscalls_setuid 122200 43305 -78895 (-64.56%) security-monitor-15.bpf.o cve_2021_4034 122481 43629 -78852 (-64.38%) security-monitor-16.bpf.o test 122517 43665 -78852 (-64.36%) security-sandbox-10.bpf.o exec 12908 4626 -8282 (-64.16%) security-monitor-12.bpf.o connect_security_socket_connect 216252 78013 -138239 (-63.92%) security-monitor-19.bpf.o kernel_modules_do_init 92980 33776 -59204 (-63.67%) security-monitor-23.bpf.o enter_pivot_root 93518 34311 -59207 (-63.31%) security-monitor-08.bpf.o bpf_prog_detect 93384 34494 -58890 (-63.06%) firewall-05.bpf.o egress 58436 21951 -36485 (-62.44%) Top losses (38) --------------- File Program Insns (A) Insns (B) Insns (DIFF) -------------------------------- ----------------------------- --------- --------- ---------------- profiler-26.bpf.o handle_cu_mem_address_reserve 34326 45724 +11398 (+33.21%) profiler-26.bpf.o handle_cu_mem_create 34326 45724 +11398 (+33.21%) profiler-26.bpf.o handle_malloc 34326 45724 +11398 (+33.21%) profiler-26.bpf.o handle_cu_mem_map 34524 45922 +11398 (+33.01%) profiler-19.bpf.o sched_waking 17454 23153 +5699 (+32.65%) profiler-27.bpf.o handle_cuda_launch_exc 17477 23176 +5699 (+32.61%) profiler-27.bpf.o handle_cuda_launch 17490 23189 +5699 (+32.58%) profiler-12.bpf.o on_custom_event_begin 17697 23396 +5699 (+32.20%) profiler-12.bpf.o on_custom_event_end 17701 23400 +5699 (+32.20%) profiler-26.bpf.o handle_mtia_cached_alloc 35494 46892 +11398 (+32.11%) profiler-26.bpf.o handle_mtia_allocate_enter 35555 46953 +11398 (+32.06%) profiler-26.bpf.o handle_pytorch_malloc 35847 47245 +11398 (+31.80%) profiler-19.bpf.o oncpu 17990 23689 +5699 (+31.68%) profiler-12.bpf.o on_task_enqueued 18035 23734 +5699 (+31.60%) profiler-12.bpf.o sched_waking 18070 23769 +5699 (+31.54%) profiler-12.bpf.o on_task_dequeued 18210 23909 +5699 (+31.30%) profiler-12.bpf.o on_request_context_event 18301 24000 +5699 (+31.14%) profiler-12.bpf.o on_pytorch_event 18302 24001 +5699 (+31.14%) profiler-12.bpf.o on_task_stats 18384 24083 +5699 (+31.00%) profiler-12.bpf.o oncpu 18488 24187 +5699 (+30.83%) profiler-19.bpf.o sched_switch_offcpu 64726 78210 +13484 (+20.83%) profiler-12.bpf.o sched_switch 33515 40257 +6742 (+20.12%) profiler-09.bpf.o on_custom_event_start 38281 45973 +7692 (+20.09%) profiler-09.bpf.o trace_func_entry 38281 45973 +7692 (+20.09%) profiler-12.bpf.o get_crochet_walltime_stacks 43473 50215 +6742 (+15.51%) profiler-24.bpf.o get_task_stacks 94740 103742 +9002 (+9.50%) test_cls_redirect_subprogs.bpf.o cls_redirect 72052 73682 +1630 (+2.26%) security-sandbox-20.bpf.o redirect_socket_connect 72423 73913 +1490 (+2.06%) scheduler-28.bpf.o arena_alloc_mask 27159 27175 +16 (+0.06%) scheduler-30.bpf.o arena_alloc_mask 27159 27175 +16 (+0.06%) scx_cake.bpf.o arena_alloc_mask 28721 28737 +16 (+0.06%) scx_arena.bpf.o arena_alloc_mask 28734 28750 +16 (+0.06%) scx_arena_selftests.bpf.o arena_alloc_mask 28734 28750 +16 (+0.06%) scx_chaos.bpf.o arena_alloc_mask 28734 28750 +16 (+0.06%) scx_p2dq.bpf.o arena_alloc_mask 28734 28750 +16 (+0.06%) scx_cake.bpf.o cake_init_task 28758 28774 +16 (+0.06%) scheduler-01.bpf.o arena_alloc_mask 29197 29208 +11 (+0.04%) scheduler-03.bpf.o arena_alloc_mask 29197 29208 +11 (+0.04%) Changelog ========= v2 -> v3: TLDR: another bunch of major correctness fixes, plus several improvements to avoid veristat-meta failure identified for v2. - Widen only when allow_uninit_stack is set. - Mark scalar registers and stack spills used by the selected bounding condition's base, step and bound expressions precise before widening. - Join alternative values only for registers with compatible pointer metadata. Include the loop-entry value when a register is assigned an invariant in the body, including unconditional assignments. - Consider up to four exit conditions dominating the backedge and use the tightest available iteration bound. Match the induction variable in either operand of the comparison. - Require full spills for linear stack recurrences. Treat partially spilled and unallocated stack slots as unknown scalars and all-zero slots as zero. Materialize writable stack values safely, growing the stack when needed. - Keep base/step in sync with the bounds as the last step of reg_bounds_sync(). Preserve the register state when sign extension leaves all possible values unchanged. Check that step is nonzero and base is smaller than step. - Handle latch comparison polarity correctly when the continuing branch enters a nested loop. Compute SCEVs only for exits owned by the loop, fixing a verifier bug for irreducible inner loops. - Exclude BPF_F_INNER_MAP single-element arrays from the separate-lookup shortcut in bpf_same_memory_origin(). - Fix stack-pointer tracking through load-acquire/store-release and atomic exchange/fetch/cmpxchg operations, so accesses through recovered stack pointers are included in liveness analysis. - Derive global subprogram stack effects from BTF argument types, so freplace cannot invalidate a loop termination proof based on the original callee body. - Compute argument liveness from callee reads for static subprograms and from BTF-declared arguments for globals. Prepare argument metadata before liveness analysis, avoiding unnecessarily live registers that prevented widening. - Treat already-unbounded scalars as already widened, allowing unknown SCEVs such as stack outputs of probe-read helpers called through a subprogram. - Widen to an unbounded range when computing an induction variable's range overflows signed 64-bit arithmetic, retaining the power-of-two alignment common to the entry value and slope. - Fix dominator computation for subprograms whose entry is a loop header: initialize the entry as its own dominator during the fixed-point computation and skip updates to it. Use overflow-checked predecessor allocations. - Replace the private worklist heap with the kernel min-heap and limit expression rewriting to 32 passes before falling back to unknown. - Add regression coverage for the liveness, stack, pointer, iteration bound and widening changes. Fix __msg_next validation when an expected message is missing. v1 -> v2: TLDR: a bunch of major correctness fixes. - Introduce arg_access_info to distinguish read, may-write and must-write effects of helper/kfunc stack accesses. Handle map-specific argument semantics and writes through unknown callbacks. - Fix base/step tracking across signed overflow, truncation, sign/zero extension and linked-register updates. - Rework iteration-count calculation for signed/unsigned increasing and decreasing counters, including counter overflow checks. Evaluate the full latch base expression and consistently count header executions. - Substitute all registers in instantiate_header_scevs(). - Track pointer origins when evaluating latch expressions. Require compatible operands before deriving iteration bounds. - Preserve the correct congruence base when widening nonconstant entry values, assign fresh IDs to widened packet pointers, and exclude nullable pointers from widening. - Account for values modified on nested-loop exit paths when computing invariants. Do not use an inner loop's latch to bound an outer loop. - Restrict pruning against in-progress SCEV states to terminating innermost loops sharing the same entry checkpoint, preventing inner-loop pruning from hiding a nonterminating outer loop. - Rework loop entry/exit handling on the main verification pass. - Extend fixed-stack-offset widening suppression to ANY expressions and all dynptr arguments described by helper/kfunc prototypes. - Fix BTF array handling: retain bounds checks for fixed arrays inside flexible-array elements and preserve array type IDs when element types contain typedefs. - Fix SCEV handling of endian conversions and narrow fills on big-endian systems. - Grow expression storage geometrically and scale the expression hash table with program size. Add cancellation/rescheduling checks to dominator computation and fix the postorder-number allocation leak. - Pack bpf_reg_state/bpf_func_state for latter to fit back into the 1 KiB allocation bucket, measured SCEV memory consumption overhead is 3% now. - Major additions in the selftests. v1: https://lore.kernel.org/bpf/20260926-scev-minimal-rebase-v1-0-c8e5ab5ba79f@gmail.com/ v2: https://lore.kernel.org/bpf/20261004-scev-minimal-rebase-v2-0-f6254e29fdf2@gmail.com/ --- ==================== Link: https://patch.msgid.link/20261009-scev-minimal-rebase-v3-0-bcba9bc53ac9@gmail.com Signed-off-by: Alexei Starovoitov <ast@kernel.org>
2026-10-09selftests/bpf: Tests for SCEV analysis and loop wideningEduard Zingerman
- Computed expression shapes: register and spilled-counter recurrences, substitution at latches, agreeing and conflicting backedge updates, and ALU, extension, byte-swap, load/store and atomic expressions. - Iteration bounds and header-visit counts for: - pre-condition loops using JEQ, JLT, JLE, JGE and JGT; - post-condition loops using JLT, JLE, JGE, JGT and JNE; - signed pre- and post-conditions using JSLT, JSLE, JSGT and JSGE; - increasing and decreasing counters, nonzero initial values, immediate and invariant-register bounds, and immediate exits; - additional exits that reduce the minimum iteration count or tighten the upper bound; - rounded bounds, counter wraparound, count overflow, extreme steps and JNE divisibility. - Widening, backedge clamping and state-pruning convergence, including multiple induction variables used for map-value accesses and nonconstant entry values requiring a common alignment. Check precision propagation to initial values and bounds. - Nested and sibling loops, shared headers, exits to another loop header, exits across multiple nesting levels, and side entries into loop nests. Check conservative summaries for irreducible children while still widening supported reducible descendants, value preservation on inner-loop exits, and detection of nonterminating outer loops. - Invalidation of stack-slot expressions after indirect stack writes and global calls. Widening across bpf_probe_read_kernel() calls in static subprograms. - Stack spill widths and slot states, including narrow spills, zero, unallocated and STACK_MISC slots, and counters spilled and filled using store-release/load-acquire. - Avoid widening registers used to address stack spills, fills and dynptr constructor arguments, including dependencies through nested loops. Conversely, allow widening addresses of byte-sized stores. - Conditional assignments joining scalar values and stack pointers, including preservation of a common base and gcd-derived step. - Pointer widening and comparisons with compatible memory origins, fallback for incompatible origins and nullable pointers, and invalidation of stale packet pointer relationships. - Conservative fallback for unsupported recurrences, unavailable counter spills and zero computed header counts. Signed-off-by: Eduard Zingerman <eddyz87@gmail.com> Signed-off-by: Alexei Starovoitov <ast@kernel.org> Link: https://patch.msgid.link/20261009-scev-minimal-rebase-v3-51-bcba9bc53ac9@gmail.com
2026-10-09selftests/bpf: Tests for immediate dominator computationEduard Zingerman
Coverage: - straight-line code with an ldimm64 in the middle; - an asymmetric if-then-else diamond (unequal-depth idoms_intersect()); - a simple loop, a loop with an if-else body, and nested loops; - a loop header with two back edges (three predecessors); - an irreducible CFG (fixpoint convergence); - immediate dominators across subprogram boundaries, including a callee whose entry instruction is itself a loop header; - a self-loop; - an indirect jump (gotox) with a two-entry jump table. Signed-off-by: Eduard Zingerman <eddyz87@gmail.com> Signed-off-by: Alexei Starovoitov <ast@kernel.org> Link: https://patch.msgid.link/20261009-scev-minimal-rebase-v3-50-bcba9bc53ac9@gmail.com
2026-10-09selftests/bpf: Tests for loop hierarchy computationEduard Zingerman
Test cases covering the following branches in bpf_compute_loops(): - Case B: simple backedge creating a loop (loop_single) - Case B: two independent loops (loop_two_independent) - Case B + D: nested loops where the inner header's loop_header points to the outer header (loop_nested) - Case C: diamond CFG with no loops (fwd_edges_no_loop) - Case D: sibling inner loops within one outer loop (loop_nested_siblings) - Three levels of loop nesting (loop_three_levels) - Loop with an if-else body containing forward branches (loop_with_if_else) - Case E: An irreducible loop (loop_irreducible) - A self-loop (loop_self) - A test with sibling loops nested in outer loops (loop_nested_siblings_common_ancestors) - A test case with outer loop's backedge originating from an inner loop (loop_outer_backedge_from_inner). Cases B, C, D and E are described in loops.c:compute_loops_in_subprog(). Signed-off-by: Eduard Zingerman <eddyz87@gmail.com> Signed-off-by: Alexei Starovoitov <ast@kernel.org> Reviewed-by: Emil Tsalapatis <emil@etsalapatis.com> Link: https://patch.msgid.link/20261009-scev-minimal-rebase-v3-49-bcba9bc53ac9@gmail.com
2026-10-09selftests/bpf: Tests for varying offset access to PTR_TO_BTF_IDEduard Zingerman
Verifier tests for accessing arrays at a varying offset through a PTR_TO_BTF_ID pointer, e.g. a[i].b where 'i' is a register with a varying value: - bpf_obj_new() is used to obtain a PTR_TO_BTF_ID to a program-defined struct - bpf_get_current_task_btf() is used to obtain a PTR_TO_BTF_ID from the kernel BTF. The cases cover: - in-bounds access to a field of an array of structs; - out-of-bounds access where the maximal offset runs past the array; - a scalar (long) array with a matching stride; - a byte array (step 1); - a 4-byte read whose maximal offset spills past the array end; - an unaligned step that is not a whole number of elements; - both dimensions of a 2D array; - an array of pointers within a program-allocated object, where reading a pointer member yields a SCALAR_VALUE but the varying offset is still validated against the array bounds (in-bounds and out-of-bounds); - a varying access into an array embedded in a kernel BTF type (task_struct's comm[]), covering the non-allocated, trusted-pointer path (in-bounds and out-of-bounds); - an array nested below a struct member at a non-zero offset, exercising the offset telescoping done while walking across a struct boundary (in-bounds and out-of-bounds); - a trailing flexible array member, whose unbounded tail makes a varying access have no upper bound to exceed. A program-defined struct cannot be used here because bpf_obj_new() forbids flexible array members, so a kernel type (struct vring_used, with its 'ring[]' tail) is used (in-bounds, and an unaligned step that is not a whole number of elements). Signed-off-by: Eduard Zingerman <eddyz87@gmail.com> Signed-off-by: Alexei Starovoitov <ast@kernel.org> Link: https://patch.msgid.link/20261009-scev-minimal-rebase-v3-48-bcba9bc53ac9@gmail.com
2026-10-09selftests/bpf: Tests for register base/step state pruningEduard Zingerman
Tests for the base/step reasoning in states.c:range_within(): - step_prune_hit_multiple: prune a step-4 range contained in a cached step-2 range when the scalar is used to address the stack. - step_prune_miss_non_multiple: do not let a cached step-2 range hide a step-3 path reaching division by zero. - step_prune_hit_const_on_line: prune constant 6 against a cached base-0, step-3 range. - step_prune_miss_const_off_line: do not prune constant 7 against that range even though its bounds and tnum admit the value. Signed-off-by: Eduard Zingerman <eddyz87@gmail.com> Signed-off-by: Alexei Starovoitov <ast@kernel.org> Link: https://patch.msgid.link/20261009-scev-minimal-rebase-v3-47-bcba9bc53ac9@gmail.com
2026-10-09selftests/bpf: Tests for register base/step arithmeticEduard Zingerman
- step_arith{32,64}_no_overflow: non-overflowing +, *, << operations maintain step and base. - step_neg_value_range: Preserve the signed range of {-3, 0, 3, 6} when intersecting with a non-power-of-two step across zero. - step_arith{32,64}_overflow: Reset the stride and preserve valid bounds when a {32,64}-bit operation wraps. - step_mov32_truncate: Reset the stride when MOV32 discards nonzero upper bits from positive values. - step_mov32_negative: Reset the stride when MOV32 zero-extends the low 32 bits of negative signed values. - step_movsx: Reset the stride when sign extension changes the register value. - step_movsx_bounds_on_line: Keep bounds and stride when sign extension does not change the register value. - step_bounds_resync_after_arith: Snap bounds refined from var_off back onto the line. - step_linked_regs_add64: Translate both bounds and base when propagating a comparison's refinement through a scalar-ID delta. Signed-off-by: Eduard Zingerman <eddyz87@gmail.com> Signed-off-by: Alexei Starovoitov <ast@kernel.org> Link: https://patch.msgid.link/20261009-scev-minimal-rebase-v3-46-bcba9bc53ac9@gmail.com
2026-10-09selftests/bpf: Test for register liveness of subprogram argumentsEduard Zingerman
A static callee contributes the registers its code reads at the entry, a global callee its declared arguments. Signed-off-by: Eduard Zingerman <eddyz87@gmail.com> Signed-off-by: Alexei Starovoitov <ast@kernel.org> Link: https://patch.msgid.link/20261009-scev-minimal-rebase-v3-45-bcba9bc53ac9@gmail.com
2026-10-09selftests/bpf: Tests for BTF-based global subprog stack livenessEduard Zingerman
Check that passing a stack pointer to a no-op global callee produces: - use, def and may_def marks for a fixed-size memory argument. - No stack effects for an untrusted BTF pointer argument. This verifies that call effects follow the declared argument types rather than the callee's body. Signed-off-by: Eduard Zingerman <eddyz87@gmail.com> Signed-off-by: Alexei Starovoitov <ast@kernel.org> Link: https://patch.msgid.link/20261009-scev-minimal-rebase-v3-44-bcba9bc53ac9@gmail.com
2026-10-09selftests/bpf: Tests for map special cases in bpf_helper_stack_access_bytes()Eduard Zingerman
Tests for helper arguments whose access size or read/write effects depend on the map: - helper_map_output_known_size checks that for a known map access MEM_WRITE | MEM_UNINIT produces a must_write mark. - helper_map_output_merged_sizes checks that when maps with different value sizes merge, MEM_WRITE | MEM_UNINIT produces use and may_def marks over the maximum value size, without a def mark. - helper_bloom_peek_input checks that for bpf_map_peek_elem() on a bloom filter the buffer is treated as an input. Signed-off-by: Eduard Zingerman <eddyz87@gmail.com> Signed-off-by: Alexei Starovoitov <ast@kernel.org> Link: https://patch.msgid.link/20261009-scev-minimal-rebase-v3-43-bcba9bc53ac9@gmail.com
2026-10-09selftests/bpf: Tests for stack pointer spills/fills through atomicsEduard Zingerman
Check stack write tracking for atomic operations: - Restored by 64-bit load-acquire, xchg, fetch_add and cmpxchg. - Spilled by store-release and restored by a plain load. - Restored from a caller's stack slot by xchg in a callee. Signed-off-by: Eduard Zingerman <eddyz87@gmail.com> Signed-off-by: Alexei Starovoitov <ast@kernel.org> Link: https://patch.msgid.link/20261009-scev-minimal-rebase-v3-42-bcba9bc53ac9@gmail.com
2026-10-09selftests/bpf: Tests for may_def marks of atomic RMW operationsEduard Zingerman
Liveness analysis should produce may_def marks for read-modify-write atomic operations: - lock *(u64 *)(r10 -8) += r1 - r1 = atomic64_fetch_add((u64 *)(r10 -8), r1) - r1 = atomic64_xchg((u64 *)(r10 -8), r1) - r0 = atomic64_cmpxchg((u64 *)(r10 -8), r0, r1) Signed-off-by: Eduard Zingerman <eddyz87@gmail.com> Signed-off-by: Alexei Starovoitov <ast@kernel.org> Link: https://patch.msgid.link/20261009-scev-minimal-rebase-v3-41-bcba9bc53ac9@gmail.com
2026-10-09selftests/bpf: Tests for may_write stack-liveness trackingEduard Zingerman
Branches to cover: - per-insn writes (record_stack_access_off): * off_cnt==1, fully covered slots -> def + may_def (equal) * partial coverage -> may_def only (no def) * off_cnt 2-4, precise offsets -> may_def only, per offset - whole-frame writes: * off_cnt==0 (offset lost) -> may_def SPIS_ALL * ARG_IMPRECISE (frame lost) -> may_def SPIS_ALL per frame - merge_instances(): * both passes touch a frame -> may_def union * one pass leaves a frame untouched -> may_def preserved - merge_may_write(): a callee's writes into ancestor frames are summarized as may_def at the caller's call instruction - analyze_subprog() -> may_def stack slot range when callback is unknown. Tests exercising them: - may_write_two_precise_offsets - off_cnt 2-4 precise multi-offset - imprecise_frame_write - cross-frame ARG_IMPRECISE write, plus its caller-side summarization - merge_preserves_may_write - map-value vs stack pointer at two callsites: union is preserved when one pass does not touch the frame - must_write_not_same_slot - off_cnt==0 whole-frame may_def - two_byte_write_no_kill - partial write, may_def without def - kfunc_iter_stack_liveness - full-slot def == may_def - helper_output_unknown_size - output size unknown to liveness, may_def without use or def - caller_stack_write, conditional_stx_in_subprog, shared_instance_must_write_overwrite - callee writes summarized as may_def at the call site - bpf_loop_two_callbacks - call to unknown callback marks full stack range as may_def. Signed-off-by: Eduard Zingerman <eddyz87@gmail.com> Signed-off-by: Alexei Starovoitov <ast@kernel.org> Link: https://patch.msgid.link/20261009-scev-minimal-rebase-v3-40-bcba9bc53ac9@gmail.com
2026-10-09selftests/bpf: Test for stack-pointer subrange pruningEduard Zingerman
Validate changes from: "bpf: allow subrange relations for PTR_TO_STACK in regsafe()". Add a test case that checks that a widened stack pointer with range [-72, -16] is considered a superset of a stack pointer with range [-64, -16]. Signed-off-by: Eduard Zingerman <eddyz87@gmail.com> Signed-off-by: Alexei Starovoitov <ast@kernel.org> Link: https://patch.msgid.link/20261009-scev-minimal-rebase-v3-39-bcba9bc53ac9@gmail.com
2026-10-09selftests/bpf: Bound UNIX socket path loops by sun_path sizeEduard Zingerman
The bpf_iter/unix and skc_to_unix_sock tests index sun_path but bound that index by sizeof(struct sockaddr_un). This allows indices 108 and 109 into the 108-byte array. With SCEV loop widening, the variable-offset BTF access check rejects the resulting range. Bound the index by sizeof(sun_path) in both tests. Runtime behavior for valid UNIX addresses is unchanged. Signed-off-by: Eduard Zingerman <eddyz87@gmail.com> Signed-off-by: Alexei Starovoitov <ast@kernel.org> Link: https://patch.msgid.link/20261009-scev-minimal-rebase-v3-38-bcba9bc53ac9@gmail.com
2026-10-09selftests/bpf: __msg_next tag for matching messages on consecutive linesEduard Zingerman
Same as __msg, but expects the match on the line immediately after the last match, not just any later line. E.g.: __msg_next("a") __msg_next("c") This would match consecutive output "a\n" "c\n", but would not match output "a\n" "b\n" "c\n". Signed-off-by: Eduard Zingerman <eddyz87@gmail.com> Signed-off-by: Alexei Starovoitov <ast@kernel.org> Link: https://patch.msgid.link/20261009-scev-minimal-rebase-v3-37-bcba9bc53ac9@gmail.com
2026-10-09bpf: Bpf_func_state size optimizationEduard Zingerman
Pack bpf_reg_state to bring it's size from 88 bytes to 80. Consequently, bpf_func_state changes from 1048 to 960. Thus bpf_func_state moves from 2K to 1K allocator bucket. 1K bucket is the one it fit before the SCEV series. With this optimization SCEV increases memory consumption on a subset of Meta internal programs by 3%, without this optimization the increase is 48%. Signed-off-by: Eduard Zingerman <eddyz87@gmail.com> Signed-off-by: Alexei Starovoitov <ast@kernel.org> Link: https://patch.msgid.link/20261009-scev-minimal-rebase-v3-36-bcba9bc53ac9@gmail.com
2026-10-09bpf: Avoid widening registers that hinder exact stack-slot trackingEduard Zingerman
For loops like this: int arr[10]; for (int i = 0; i < 10; i++) arr[i] = i; use(arr[i]); SCEV-based loop widening converts `i` from being enumerated 10 times as a constant value 0, 1, ..., 9 to being enumerated once as a range [0..9]. From the point of view of the main verification pass, this replaces constant-offset stack access with varying-offset stack access. The verifier tracks stack values accessed through varying offsets much less precisely than those accessed via constant offsets. For example, the read at use(arr[i]) would yield STACK_MISC. Precision might be lost for the following operations: - spills (BPF_ST/STX to the stack): stored value can no longer be recovered when read back, e.g. after the loop; - fills (BPF_LDX from the stack): a varying-offset read of a spilled pointer yields a SCALAR_VALUE, so the pointer is lost. - calls that construct objects on the stack (dynptr, iter, irq_flag, res_spin_lock): the stack argument is required to have a constant offset. This commit adds logic to avoid widening registers used by such instructions: - During the main SCEV computation stage, collect_store_base_regs(): - inspects instructions accessing the stack (spill, fill, call to a function listed in bpf_needs_fixed_stack_off), and - if there is a SCEV expression for a stack base address register, records which registers the expression depends on in the bpf_loop->store_base_reg field. - `store_base_reg`s accumulated for nested loops are propagated to outer loops as well. - During the loop-widening stage, bpf_compute_loop_iters() refuses to widen a loop when some of the registers the loop changes appear in store_base_regs. Signed-off-by: Eduard Zingerman <eddyz87@gmail.com> Signed-off-by: Alexei Starovoitov <ast@kernel.org> Link: https://patch.msgid.link/20261009-scev-minimal-rebase-v3-35-bcba9bc53ac9@gmail.com
2026-10-09bpf: Use SCEV to widen bounded loopsEduard Zingerman
Loop-bound computation and register widening =========================================== During the main verification pass, use SCEV expressions and register values at the entry to the loop to estimate the number of iterations the loop might execute. Use this number to widen loop induction variables. For example: 0: r7 = 5; 1: r6 = 0; 2: while (r6 < 3) { // latch 3: if (r6 == 1) 4: r7 = 10; 5: r6 += 1; 6: } 8: use(r7); - Scalar evolution expressions (SCEVs) at the latch are: - r6 = (linear r6 1) - r7 = (any r7 10) - For this pre-condition loop, this corresponds to the linear inequality `r6_entry + n < 3`. With r6_entry = 0, the inequality is `n < 3`. - Hence infer that r6's range at line 2 is [0,3], including the final failed test. The body starts with r6 in [0,2]. - r7's (any r7 10) expression means that its initial value 5 from line 0 is joined with 10 from line 4: it is widened to [5,10] within and after the loop. Currently such inference is supported for: - Reducible loops with one backedge and complete backedge/exit lists. - A linear latch dominating the backedge, with one successor exiting the loop. Additional exits are allowed; they can shorten execution. - 64-bit arithmetic and comparisons whose continuation condition normalizes to signed/unsigned < or <=, or !=. - Initial counter, latch offset, slope and bound evaluable as constants at loop entry, yielding a finite, representable iteration bound. - Values with an eligible type at loop entry, e.g. SCALAR_VALUE or PTR_TO_STACK, but not PTR_TO_CTX. In order for widening to proceed, every register live at the loop entry has to have a SCEV that can be widened. Currently, the following expressions are supported: - (linear <self> <step>): entry value + iteration# * constant step. - (reg <self>): loop invariant; keep its entry value unchanged. - (any ...): join the entry value with immediates or invariant registers; types must match, and immediates require SCALAR_VALUE. Main verification loop integration ================================== Before proceeding with the usual instruction processing, do_check() takes the following steps at loop entries (H): - Checks whether the loop can be widened and obtains its iteration bound (scev.c:bpf_compute_loop_iters()). - If it can, saves an unwidened checkpoint E0, which will be used as a base for deriving widened values throughout this invocation of the loop. - Widens registers in the current state using E0 and SCEV expressions computed for H (scev.c:bpf_widen_scev_regs()). - Pushes a per-frame loop-stack record to the current verifier state. For terminating loops, the record stores E0 and the iteration bounds. - Lets is_state_visited() create a new checkpoint W at the same header, now containing the widened values, and proceeds with normal checking. - On an exit edge, pops the loop record and verifies the exit path normally. is_state_visited() treats a loop proven to terminate like an iterator-based loop: RANGE_WITHIN comparison attempts to establish convergence against a checkpoint whose exploration is still in progress. For example: r0 = 0; H: r0 += 1; if (r0 != 3) goto H; exit; first H: prove H_count = 3; save E0(r0 = 0) widen r0 to [0,2]; push the loop record save W(r0 = [0,2]) body: r0 becomes [1,3]; fork at the conditional exit: r0 = 3; pop the loop record and check exit backedge: r0 = [1,2]; clamp, then match with W and prune Clamping -------- Independent register ranges lose correlations between induction variables. For example: r6 = 0; r7 = 0; do { r6 += 1; r7 += 2; } while (r6 < 3); use(r7); header: r6 in [0,2], r7 in {0,2,4} body: r6 in [1,3], r7 in {2,4,6} backedge: r6 in [1,2], r7 still in {2,4,6} The latch narrows r6 but does not propagate this refinement to r7. To account for this, before comparing states on a backedge, scev.c:bpf_clamp_scev_regs() recomputes r7's range from E0 and the saved iteration bound, then intersects the current range {2,4,6} with the recomputed {0,2,4}, arriving at {2,4}. Signed-off-by: Eduard Zingerman <eddyz87@gmail.com> Signed-off-by: Alexei Starovoitov <ast@kernel.org> Link: https://patch.msgid.link/20261009-scev-minimal-rebase-v3-34-bcba9bc53ac9@gmail.com
2026-10-09bpf: Compute scalar evolution expressions for loopsEduard Zingerman
Assign algebraic expressions to loop variables (registers and stack spills), describing how their values evolve across iterations. These summaries provide symbolic input for the subsequent widening patch. The algorithm draws on ideas from the following papers: - "Symbolic Evaluation of Chains of Recurrences for Loop Optimization" Robert A. van Engelen, 2000 - "The CR# Algebra and its Application in Loop Analysis and Optimization" Robert A. van Engelen, 2004 The analysis proceeds in two phases: - Compute expressions for one symbolic iteration: start each variable with a reference to its input value, apply instruction effects and join expressions where control-flow paths merge. - Convert the resulting backedge updates to recurrences at the header, then substitute these recurrences into expressions at the latch. Analyze loops innermost first. Within each loop, visit blocks in topological order (reverse postorder, with backedges ignored and nested loops collapsed). Treat each nested loop as an opaque operation: preserve its invariant values, forget the others and continue at its exits, without expanding its iterations. For example, using 64-bit arithmetic: 0: r7 = 5; 1: r6 = 0; 2: do { // header 3: if (r6 == 1) 4: r7 = 10; 5: r6 += 1; 6: } while (r6 < 3); // latch Expressions use symbolic inputs r6 and r7; the concrete values from lines 0-1 are supplied later by the main verifier. First phase: ------------ At each instruction, an environment maps register numbers to their current expressions. Transfer functions update this mapping, while joins merge the environments arriving along different paths. For illustration, follow each branch separately to the backedge: - At 3, take the path skipping 4: r6 = r6, r7 = r7. - At 5, transfer(r6 += 1): r6 = r6 -> r6 = (+ r6 1). - At 2, the first path contributes r6 = (+ r6 1), r7 = r7. - At 4, on the other path from 3, transfer(r7 = 10): r7 = r7 -> r7 = 10. - At 5, transfer(r6 += 1): r6 = r6 -> r6 = (+ r6 1). - At 2, join the two paths' contributions: join((+ r6 1), (+ r6 1)) = (+ r6 1) for r6; join(r7, 10) = (any r7 10) for r7. Equal expressions stay unchanged; different expressions become ANY alternatives. These summarize one iteration, without unrolling the loop. Second phase: ------------- The update r6 = (+ r6 1) says that each iteration adds 1 to r6. Repeating this update n times gives r6_entry + n, a linear recurrence. - At header 2, r6 = (+ r6 1) becomes (linear r6 1), meaning r6_entry + n, where n counts iterations from zero. - At latch 6, substitute this recurrence into (+ r6 1): (+ (linear r6 1) 1) simplifies to (linear (+ r6 1) 1), meaning r6_entry + 1 + n. - At both points, r7 retains (any r7 10): either its entry value or 10. ANY records alternatives, not numeric ranges or the path conditions selecting them. Unchanged variables retain their inputs. Signed-off-by: Eduard Zingerman <eddyz87@gmail.com> Signed-off-by: Alexei Starovoitov <ast@kernel.org> Link: https://patch.msgid.link/20261009-scev-minimal-rebase-v3-33-bcba9bc53ac9@gmail.com
2026-10-09bpf: Allow precision backtracking between overlapping checkpointsEduard Zingerman
SCEV integration logic might create two consecutive checkpoints at the same instruction w/o processing any instructions in between. This happens for loop-entry checkpoint -> regular checkpoint sequences, where the loop-entry checkpoint has to remain unchanged but the regular checkpoint is used for widening. This commit adapts bpf_mark_chain_precision() to support this. Signed-off-by: Eduard Zingerman <eddyz87@gmail.com> Signed-off-by: Alexei Starovoitov <ast@kernel.org> Link: https://patch.msgid.link/20261009-scev-minimal-rebase-v3-32-bcba9bc53ac9@gmail.com
2026-10-09bpf: Record basic-block ends in insn_aux_dataEduard Zingerman
SCEV needs information about the program's basic-block structure. Piggyback on compute_predecessors() and set insn_aux_data->bb_end for an instruction if: - it has multiple successors, or - its sole successor has multiple predecessors, or - it is a jump instruction. Signed-off-by: Eduard Zingerman <eddyz87@gmail.com> Signed-off-by: Alexei Starovoitov <ast@kernel.org> Link: https://patch.msgid.link/20261009-scev-minimal-rebase-v3-31-bcba9bc53ac9@gmail.com
2026-10-09bpf: Add bpf_spill_size()Eduard Zingerman
A utility function counting the number of STACK_SPILL slots in a bpf_stat_state instance. For use by SCEV widening logic. Signed-off-by: Eduard Zingerman <eddyz87@gmail.com> Signed-off-by: Alexei Starovoitov <ast@kernel.org> Link: https://patch.msgid.link/20261009-scev-minimal-rebase-v3-30-bcba9bc53ac9@gmail.com
2026-10-09bpf: Add bpf_same_memory_origin()Eduard Zingerman
When dealing with loops like: for (ptr = ...; ptr < end_ptr; ptr += 8) ... Scalar evolution needs to know if ptr and end_ptr identify a same memory location (possibly with different offsets). If they do, it is sound to attempt to infer iteration bounds for such a loop. Signed-off-by: Eduard Zingerman <eddyz87@gmail.com> Signed-off-by: Alexei Starovoitov <ast@kernel.org> Link: https://patch.msgid.link/20261009-scev-minimal-rebase-v3-29-bcba9bc53ac9@gmail.com
2026-10-09bpf: Add bpf_split_cur_state()Eduard Zingerman
SCEV widening needs an explicit checkpoint preserving the original loop-entry state, independently of ordinary state-cache heuristics. This commit extracts checkpoint creation logic from bpf_is_state_visited() into a separate utility function. Signed-off-by: Eduard Zingerman <eddyz87@gmail.com> Signed-off-by: Alexei Starovoitov <ast@kernel.org> Link: https://patch.msgid.link/20261009-scev-minimal-rebase-v3-28-bcba9bc53ac9@gmail.com
2026-10-09bpf: Add bpf_reg_union() and bpf_reg_union_compatible()Eduard Zingerman
Utility functions to take a union of two registers' scalar values: merge the circular 32-bit and 64-bit bounds, tnums and base/step of two registers, then synchronize and validate the result. Leave id invalidation and type validation to the caller. Caller should use bpf_reg_union_compatible() to ensure that final union result is sensible. Signed-off-by: Eduard Zingerman <eddyz87@gmail.com> Signed-off-by: Alexei Starovoitov <ast@kernel.org> Link: https://patch.msgid.link/20261009-scev-minimal-rebase-v3-27-bcba9bc53ac9@gmail.com
2026-10-09bpf: Add bpf_mark_reg_known_scalar()Eduard Zingerman
Provide a helper to set a register to a known scalar value for use outside verifier.c. To be used by SCEV widening logic. Signed-off-by: Eduard Zingerman <eddyz87@gmail.com> Signed-off-by: Alexei Starovoitov <ast@kernel.org> Link: https://patch.msgid.link/20261009-scev-minimal-rebase-v3-26-bcba9bc53ac9@gmail.com
2026-10-09bpf: Add bpf_set_reg_range()Eduard Zingerman
A utility function for setting a register's range and step information. Used by SCEV widening and clamping logic further in the series. Signed-off-by: Eduard Zingerman <eddyz87@gmail.com> Signed-off-by: Alexei Starovoitov <ast@kernel.org> Link: https://patch.msgid.link/20261009-scev-minimal-rebase-v3-25-bcba9bc53ac9@gmail.com
2026-10-09bpf: Compute loop hierarchyEduard Zingerman
SCEV needs to process loops from innermost to outermost, summarizing results for nested loops. Add a pass to compute the loop structure. Use a non-recursive adaptation of the algorithm from: "A New Algorithm for Identifying Loops in Decompilation" by Wei et al. Record the loop hierarchy per instruction: - The field insn_aux_data->loop_header records the innermost loop header containing it. - The field insn_aux_data->loop records additional information about the loop if this instruction is a loop header (exits, backedges, irreducibility). Signed-off-by: Eduard Zingerman <eddyz87@gmail.com> Signed-off-by: Alexei Starovoitov <ast@kernel.org> Link: https://patch.msgid.link/20261009-scev-minimal-rebase-v3-24-bcba9bc53ac9@gmail.com
2026-10-09bpf: Compute immediate dominatorsEduard Zingerman
SCEV needs to find an exit condition that dominates a loop's backedge; such conditions bound every loop iteration. Compute the immediate dominator tree as a prerequisite for identifying such conditions. Use the iterative reverse-postorder algorithm from: "A Simple, Fast Dominance Algorithm" by Cooper et al. Signed-off-by: Eduard Zingerman <eddyz87@gmail.com> Signed-off-by: Alexei Starovoitov <ast@kernel.org> Link: https://patch.msgid.link/20261009-scev-minimal-rebase-v3-23-bcba9bc53ac9@gmail.com
2026-10-09bpf: Move the live-register and SCC printout to a standalone functionEduard Zingerman
Tests for multiple analyses performed by the verifier need the verifier log to contain analysis results alongside the program disassembly. This patch moves the log-level-2 program dump from bpf_compute_live_registers() to a standalone function called from bpf_check(), in order to provide a common logging function for such analyses (and thus avoid printing program disassembly multiple times). Rename the dump header from "Live regs before insn:" to "Program dump (scc? insn#: live_regs_before):" and print available source-line information before the corresponding instructions. Signed-off-by: Eduard Zingerman <eddyz87@gmail.com> Signed-off-by: Alexei Starovoitov <ast@kernel.org> Reviewed-by: Emil Tsalapatis <emil@etsalapatis.com> Link: https://patch.msgid.link/20261009-scev-minimal-rebase-v3-22-bcba9bc53ac9@gmail.com
2026-10-09bpf: Save DFS postorder numbers for program instructionsEduard Zingerman
Dominator intersections and SCEV worklist scheduling need to compare instructions by their DFS postorder rank. Save per-instruction postorder numbers alongside the existing postorder sequence. This commit modifies bpf_compute_postorder() to actually do the DFS traversal, instead of scheduling traversal of all siblings at once. Consider a graph: A -> B A -> C C -> B Old result: C, B, A DFS result: B, C, A Fixes: efcda22aa541 ("bpf: compute instructions postorder per subprogram") Signed-off-by: Eduard Zingerman <eddyz87@gmail.com> Signed-off-by: Alexei Starovoitov <ast@kernel.org> Link: https://patch.msgid.link/20261009-scev-minimal-rebase-v3-21-bcba9bc53ac9@gmail.com
2026-10-09bpf: Varying offset access support for PTR_TO_BTF_ID pointersEduard Zingerman
Relax the requirements for reads like `p->arr[i].field`, where `p` is a PTR_TO_BTF_ID, by allowing `i` to be a non-constant value. This is useful for loops over arrays accessed from a PTR_TO_BTF_ID when the verifier widens the loop induction variable. Check that the access stays within array bounds based on the access register's min and max bounds. Check that the access is aligned with array elements using the register's `base` and `step`. Technically, modify btf_struct_access() such that: - it identifies `field` by walking BTF from the minimum offset (the register's reg_smin + static instruction offset); - it remembers which arrays contain `field`; - it checks that offsets `k * step` stay within the array and point to the same `field`; - it does not check upper bounds for flexible arrays (as is already the case for constant-offset access). Support for flexible arrays nested inside trailing struct members (e.g. p->nested.data[i], with data declared as int data[]) is left for future work. Signed-off-by: Eduard Zingerman <eddyz87@gmail.com> Signed-off-by: Alexei Starovoitov <ast@kernel.org> Link: https://patch.msgid.link/20261009-scev-minimal-rebase-v3-20-bcba9bc53ac9@gmail.com
2026-10-09bpf: Representation for intervals with stepsEduard Zingerman
Extend scalar register tracking with a linear "base + step * k" description: each scalar carries two u16 fields, base and step (invariant: base < step), recording that its value is some point on the line base + step * k. A fresh scalar starts as base=0, step=1, i.e. any integer. This lets the verifier reason about strided values, e.g. an index multiplied by an element size, which neither the min/max bounds nor the tnum can capture for a non-power-of-2 stride. Such reasoning is necessary to allow array access with a variable offset through PTR_TO_BTF_ID, e.g. for expressions like p->arr[i].field. reg_bounds_sync() uses the description in two ways: - __reg_bound_offset() clears the low bits of var_off implied by the number of trailing zeros shared by base and step. - __update_bounds_from_step() intersects the 64-bit range with the line via cnum64_intersect_linear(), snapping smin/smax to actual points on it. This is the last step of the sync, as refining the range from var_off can move the bounds off the line. range_within() is extended to check whether the cur register's equation describes a subset of the points described by the old register's equation. Scalar ALU operations update base/step as follows: - ADD of a constant (including pointer + scalar): keep step, shift base; - MUL by a constant: scale step and base by the constant; - LSH by a constant: scale step and base by the corresponding power of two; - sign extension: keep the register unchanged if all values fit the target type, reset to base=0, step=1 otherwise; - All other operations reset to base=0, step=1. Branch (eq/neq) checks do not take into account base/step yet. Signed-off-by: Eduard Zingerman <eddyz87@gmail.com> Signed-off-by: Alexei Starovoitov <ast@kernel.org> Link: https://patch.msgid.link/20261009-scev-minimal-rebase-v3-19-bcba9bc53ac9@gmail.com
2026-10-09bpf: Allow subrange relations for PTR_TO_STACK in regsafe()Eduard Zingerman
regsafe() currently requires an exact match for PTR_TO_STACK registers. This prevents an explored state from pruning a current state whose variable-offset range is a subset of the explored range. This change allows a checkpoint containing a widened stack pointer to cover narrower offsets reached on subsequent loop iterations, for example [-56, -8] within [-64, -8]. Merge the PTR_TO_STACK case with PTR_TO_MEM and others, as the set of necessary checks is almost identical. The only difference is a map_uid check, which is not needed for PTR_TO_STACK. All locations that produce PTR_TO_STACK reset map_uid to zero. Signed-off-by: Eduard Zingerman <eddyz87@gmail.com> Signed-off-by: Alexei Starovoitov <ast@kernel.org> Link: https://patch.msgid.link/20261009-scev-minimal-rebase-v3-18-bcba9bc53ac9@gmail.com