https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=297439
Bug ID: 297439
Summary: netmap: pipe peer teardown leaves a NULL kring array
in netmap_monitor_stop()
Product: Base System
Version: 14.4-STABLE
Hardware: amd64
OS: Any
Status: New
Severity: Affects Some People
Priority: ---
Component: kern
Assignee: [email protected]
Reporter: [email protected]
Created attachment 273631
--> https://bugs.freebsd.org/bugzilla/attachment.cgi?id=273631&action=edit
Minimized four-call pipe master/slave reproducer
Overview
========
A netmap pipe master/slave registration sequence can leave an adapter with a
NULL TX/RX kring-array pointer while its advertised ring count remains
nonzero.
During descriptor teardown, netmap_do_unregif() reaches
netmap_monitor_stop(). The latter iterates over the advertised rings and
dereferences NMR(na, t)[i], even though the selected ring-array base is NULL.
This produces a supervisor read from address 0 and a kernel page fault.
The crash has been reduced to a four-call modern NIOCCTRL registration
program containing one pipe master registration and one corresponding pipe
slave registration.
Tested environment
==================
Operating system: FreeBSD 14.4-STABLE
Architecture: amd64
Kernel ident: SYZKALLER
Base image source revision: cc921088d9ee
Base image date: 2026-05-14
Virtualization: QEMU/KVM
The kernel is based on the FreeBSD 14.4-STABLE amd64 image identified by
revision cc921088d9ee, with the SYZKALLER kernel
configuration/instrumentation.
The reported reproductions were performed as root in clean snapshot VMs.
Unprivileged-user and jail reachability have not been tested.
Reproducer
==========
The attached minimized .syz program contains four system calls:
1. open /dev/netmap
2. register a netmap pipe master named "vale0:pipebase{p0"
3. open /dev/netmap
4. register the corresponding pipe slave named "vale0:pipebase}p0"
The program does not contain explicit close() calls. Descriptor teardown
occurs when the executor process exits.
The reproducer uses a local syzkaller syscall-description suffix named
NIOCCTRL_SAFE. This suffix invokes the normal NIOCCTRL ioctl with a regular
NETMAP_REQ_REGISTER request. The suffix only constrains mutation of the
request fields; it does not refer to a different kernel ioctl.
The crash reproduced using the following syzkaller execution settings:
Threaded: true
Repeat: true
Procs: 6
Sandbox: none
A single one-shot execution is not claimed to reproduce deterministically.
The four-call input reproduced the exact crash site in a clean VM under the
above repeated threaded settings.
A subsequent focused pipe-lifecycle campaign used the minimized program as a
seed. It reached the same direct instruction pointer eight times across
consecutive VM boots, including hits after only 25, 33, 47, and 65 cumulative
executions.
Observed result
===============
The original and reproduced failures reported:
Fatal trap 12: page fault while in kernel mode
fault virtual address = 0x0
instruction pointer = 0xffffffff80a1ff80
panic: page fault
Relevant stack:
panic
trap_pfault
netmap_do_unregif+0x1c7
netmap_dtor+0xae
devfs_destroy_cdevpriv
devfs_close_f
_fdrop
closef
fdescfree / closefp_impl
exit1 / amd64_syscall
Exact-image disassembly resolves direct instruction pointer
0xffffffff80a1ff80 to netmap_monitor_stop(), inlined or called from the
netmap_do_unregif path.
At the faulting instruction, the selected TX/RX kring-array base is NULL.
The adapter still has a nonzero ring count, so the loop executes and attempts
to evaluate:
NMR(na, t)[i]
This results in a read from address 0.
Expected result
===============
Closing or destroying either endpoint of a netmap pipe must not leave the
peer or a related adapter with NULL ring arrays and stale nonzero ring
counts.
Teardown should either preserve the arrays until the final user is gone or
atomically clear/update all state used by later teardown and monitor cleanup.
Confirmed invariant violation
=============================
The directly confirmed state at the crash is:
1. The TX/RX kring-array pointer selected by NMR(na, t) is NULL.
2. netmap_all_rings(na, t), or the corresponding ring-count state, still
causes netmap_monitor_stop() to enter the ring loop.
3. netmap_monitor_stop() indexes the NULL array.
4. The crash occurs during descriptor teardown through netmap_do_unregif().
Possible underlying cause
=========================
The current leading hypothesis involves pipe-peer teardown.
netmap_pipe_krings_delete_both() can delete or clear the kring arrays for
both pipe endpoints. A later teardown of the other still-live endpoint may
then enter netmap_monitor_stop() using stale/nonzero ring-count metadata
after the peer arrays have already been cleared.
This hypothesis is consistent with:
- the minimized master/slave registration sequence;
- the fault occurring during process-exit descriptor teardown;
- related crashes observed in netmap_pipe_krings_delete_both();
- exact register and disassembly evidence showing a NULL array with a
nonempty ring iteration range.
However, the precise reference-count or ownership transition responsible for
the stale state has not yet been proven. This report therefore distinguishes
the confirmed NULL-array invariant violation from the proposed pipe-peer
teardown explanation.
Impact
======
The demonstrated impact is a local kernel panic caused by a NULL pointer
dereference during netmap descriptor teardown.
The reproductions reported here were performed as root and require access to
/dev/netmap. Unprivileged-user and jail reachability have not been tested.
Exploitability has not been assessed.
Affected branches
=================
The issue was reproduced on stable/14.
Source review found the same relevant monitor-stop ring iteration and
two-endpoint pipe teardown structure in FreeBSD main, but the crash has not
yet been reproduced on main.
Suggested investigation
=======================
No tested patch is currently provided.
Areas that may need review include:
1. Ownership and reference transitions between pipe master and slave
adapters.
2. Whether netmap_pipe_krings_delete_both() can clear both endpoints while
one endpoint remains reachable.
3. Whether ring counts and kring-array pointers are updated atomically and
consistently.
4. Whether netmap_monitor_stop() should defensively reject a NULL ring array
even when a nonzero ring count is present.
5. Whether teardown of the second endpoint can enter monitor cleanup after
the peer has already removed the shared ring state.
Reproduction history
====================
The original fuzzing campaign saved five reports at the same direct crash
site.
Automatic reduction then produced the following accepted exact-title
reproducers:
- 9-call modern-only program;
- 6-call master/slave pipe program;
- final 4-call master/slave pipe program.
Removing either complete open/register pair prevented reproduction during
minimization. Both successful master and slave registrations are therefore
required at the syscall level in the current minimized input.
The focused follow-up campaign subsequently produced eight additional exact
direct-PC hits across consecutive VM boots.
Tools used
==========
The issue was discovered and reproduced using syzkaller with locally
developed FreeBSD netmap syscall descriptions and structured seed programs.
AI-assisted tools were used to organize experiment artifacts, inspect crash
logs, group related crash sites, and help draft this report. The minimized
input, direct fault location, register state, and reproduction results were
manually checked against the saved kernel image and source tree.
--
You are receiving this mail because:
You are the assignee for the bug.
lmpx.com only provides a reader for public news (NNTP) servers. It is not
affiliated with the servers or forums shown here and is not responsible for
the content of articles, which is written by their respective authors.