mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [RESEND][REGRESSION][SECURITY] bpf, s390: verifier/JIT mismatch allows unprivileged kernel memory access
@ 2026-07-23  8:16 Min-gyu Kim
  2026-07-23  9:05 ` Eduard Zingerman
  0 siblings, 1 reply; 3+ messages in thread
From: Min-gyu Kim @ 2026-07-23  8:16 UTC (permalink / raw)
  To: Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko,
	Eduard Zingerman, Kumar Kartikeya Dwivedi, Ilya Leoshkevich,
	Heiko Carstens, Vasily Gorbik
  Cc: bpf, linux-s390, linux-kernel, regressions, security

Hello,

I am reporting a potential security regression involving eBPF verifier state
pruning and the s390x JIT. I reproduced arbitrary kernel read and write access
and local privilege escalation as an unprivileged user.

AI assistance was used during discovery and analysis. In accordance with the
kernel security-bug reporting guidance, I am not requesting an embargo. The
reproducer and exploit are not attached or published; I can provide them
privately to maintainers on request.

Summary and affected versions

A verifier regression can cause the verifier and the s390x JIT to disagree
about the upper 32 bits of scalar registers. A crafted socket-filter program
can consequently form an unchecked 64-bit offset from an ARRAY map-value
pointer and access attacker-selected kernel addresses.

The regression was introduced by:

  107e169799057bc6a379ddb625cbe1e51cfc7d72
  ("bpf: disable and remove registers chain based liveness")

The commit first appears in v6.18-rc1 and is not present in v6.17. The cause
was still present in the v6.18, v6.19, v7.0, and v7.1 stable branches and in
v7.2-rc4, bpf, and bpf-next when checked by source comparison on 2026-07-22.
I also confirmed the relevant logic in mainline commit
4539944e515183668109bdf4d0c3d7d228383d88 on 2026-07-23. Dynamic
reproduction was performed on v6.18.38.

Affected code:

  kernel/bpf/verifier.c
  kernel/bpf/states.c in current development trees
  arch/s390/net/bpf_jit_comp.c

Required conditions:

  Architecture:                        64-bit s390x
  Program type:                        BPF_PROG_TYPE_SOCKET_FILTER
  Map type:                            BPF_MAP_TYPE_ARRAY
  CONFIG_BPF_SYSCALL:                  enabled
  BPF JIT:                             enabled and active
  kernel.unprivileged_bpf_disabled:    0
  Required capabilities:               none
  User namespace:                      not required

Root cause

The verifier records an ALU32 definition in subreg_def. A later 64-bit read
causes mark_insn_zext() to mark that instruction for explicit zero extension.
Before 107e16979905, a cache hit in is_state_visited() propagated the 64-bit
read to the alternate path's ALU32 definition.

Commit 107e16979905 removed that propagation when register-chain liveness was
replaced with static liveness. The static live-register mask does not track
read width or path-specific subreg_def information. In current states.c,
regs_exact() compares only fields before id, while subreg_def follows id in
struct bpf_reg_state. The SCALAR_VALUE case in regsafe() also does not compare
subreg_def. States with equal tracked scalar values but different ALU32
definitions can therefore compare as equal, leaving the pruned path's ALU32
instruction without a zero-extension mark.

On s390, ALU32 ADD updates the low 32 bits while preserving the register's
high half. EMIT_ZERO() emits llgfr only when the program-wide verifier_zext
flag is clear. If another ALU32 definition is marked, verifier_zext is set and
the JIT relies on each instruction's zext_dst flag. The pruned instruction
lacks that flag and can execute with attacker-controlled high bits intact.

Reproduction and impact

I ran the test as uid 1000 with all capability sets empty:

  Linux syzkaller 6.18.38 s390x
  kernel.unprivileged_bpf_disabled = 0
  net.core.bpf_jit_enable = 1

The exploit disclosed array_map_ops through the existing map->ops pointer,
derived the KASLR slide, and obtained arbitrary kernel read and write
primitives. It used those primitives to locate and modify the calling
process's credentials. map->ops was read only and was not overwritten.

The resulting shell reported:

  uid=0(root) gid=0(root) groups=0(root),27(sudo),100(users),1000(poc)
  context=system_u:system_r:kernel_t:s0

A standalone C reproducer and exploit, verifier log, kernel configuration,
and build details are available privately on request.

Mitigation and fix direction

Setting kernel.unprivileged_bpf_disabled to a non-zero value prevents this
attack by unprivileged users. Disabling the s390 BPF JIT also prevents the
demonstrated mismatch.

I have not yet tested a patch. A conservative verifier fix may be to avoid
pruning live scalar states whose relevant subreg_def values differ, or to
preserve the read-width information needed to mark the current path's ALU32
instruction. As defense in depth, the s390 JIT could avoid relying only on
the program-wide verifier_zext flag. I can prepare a regression test and test
proposed changes in the s390x QEMU environment used for reproduction.

References

  Regression-introducing commit:
  https://github.com/torvalds/linux/commit/107e169799057bc6a379ddb625cbe1e51cfc7d72

  Public review discussion related to read width and zero extension:
  https://www.spinics.net/lists/bpf/msg139280.html

  Merged patch discussion:
  https://www.spinics.net/lists/bpf/msg152234.html

If appropriate, I would appreciate a Reported-by tag as:

  Min-gyu Kim <gimm78064@gmail.com>

Please let me know if you need additional information or would like the
private reproducer and exploit.

Thank you for your time.

Best regards,
Min-gyu Kim

^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2026-07-24  1:03 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-07-23  8:16 [RESEND][REGRESSION][SECURITY] bpf, s390: verifier/JIT mismatch allows unprivileged kernel memory access Min-gyu Kim
2026-07-23  9:05 ` Eduard Zingerman
2026-07-24  1:03   ` Eduard Zingerman

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

all inboxes | Powered by JetHome®