---
id: "CVE-2026-98647"
kind: "vulnerability"
aliases: ["CVE-2026-98647"]
title: "Linux kernel: BPF verifier divide-by-zero on empty flexible array struct"
severity: "low"
cvss: null
epss: null
exploited: false
status: "open"
category: "runtime"
products: []
ecosystems: []
tags: ["bpf", "linux-kernel", "linux-servers", "runtime", "self-hosted"]
not_affected_if: ["bpf-disabled", "unprivileged-bpf-disabled"]
published_at: "2026-10-18T16:17:17+00:00"
first_seen_at: "2026-10-23T21:57:16+00:00"
updated_at: "2026-10-25T05:20:50+00:00"
sources: [{"source": "nvd", "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-98647", "first_seen_at": "2026-10-23T21:57:16+00:00"}]
---

# Linux kernel: BPF verifier divide-by-zero on empty flexible array struct

The Linux kernel BPF verifier can divide by zero in btf_struct_walk() when a program accesses past a struct whose trailing flexible array has an empty element type. Loading a crafted BPF program can crash the kernel with an oops.

_Summary, check and fix written by a model from the sources; labelled as such._

## How to check

- Check the running kernel with `uname -r` and compare against the fixed kernel builds listed in the NVD or distribution advisory for CVE-2026-98647.
- Check whether unprivileged BPF is enabled with `sysctl kernel.unprivileged_bpf_disabled`; a value of `0` lets unprivileged users load programs.

## Fix

- Update the Linux kernel to a release that includes the upstream commit rejecting zero-sized element types in btf_struct_walk().
- Until patched, set `sysctl -w kernel.unprivileged_bpf_disabled=2` to limit BPF program loading.

## Timeline

- 2026-10-23T21:57:16+00:00 nvd https://nvd.nist.gov/vuln/detail/CVE-2026-98647

## From the sources

In the Linux kernel, the following vulnerability has been resolved:

bpf: Fix divide-by-zero in btf_struct_walk()

When an access goes past the struct and the last member is a flexible
array, btf_struct_walk() folds the offset back into a single element with
(off - moff) % t->size, but never checks that the element type has a size.

BTF takes an empty struct, so this in program BTF

	/* event could be empty */
	struct event {
 #ifdef HAVE_TIMESTAMP
		__u64 ts;
 #endif
	};

	struct batch {
		int nr;
		struct event events[];
	};

divides by zero at prog load time. Getting there needs a PTR_TO_BTF_ID that
is not MEM_ALLOC, e.g. a plain read of a local kptr stashed in a map from a
sleepable program.

Oops: divide error: 0000 [#1] SMP KASAN PTI
RIP: 0010:btf_struct_walk+0x53f/0x1570
Call Trace:
 <TASK>
 btf_struct_access+0x42a/0xcd0
 check_ptr_to_btf_access+0x4dc/0x1160
 check_mem_access+0x3a45/0x8740
 check_load_mem+0x36a/0xd10
 do_check_common+0x3ef0/0xb210
 bpf_check+0x6d3b/0x8580
 bpf_prog_load+0xf7c/0x2720
 __sys_bpf+0xa83/0x3690
 __x64_sys_bpf+0xc7/0x150
 x64_sys_call+0x1f3f/0x27e0
 do_syscall_64+0xe5/0x610
 entry_SYSCALL_64_after_hwframe+0x76/0x7e
 </TASK>

Reject a zero-sized element type. The fixed array path in the same function
already bails out on the same thing:

	btf_struct_walk()
	...
		/* skip empty array */
		if (moff == mtrue_end)
			continue;

		msize /= total_nelems;

---

_Free to read at a human pace. An API with keys and an MCP server are in preparation; early access: hello@infrastack.ai_
