Zero-offset data_off on data-requiring bref types silently yields chain->data==NULL with error==0 β NULL panic or unkillable while(1) tsleep hang
| Field | Value |
|---|---|
| ID | DF-2617 |
| Status | new |
| Severity | Medium |
| CVSS 3.1 | CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:H |
| CWE | CWE-476 NULL Pointer Dereference / CWE-835 Loop with Unreachable Exit Condition |
| File | sys/vfs/hammer2/hammer2_chain.c |
| Lines | 938-939 |
| Area | vfs |
| Confidence | certain |
| Discovered | 2026-08-28 |
| Pass | 2 (GLM 5.3 second pass) |
| Bucket | hammer2 |
| Reported | pending |
| Known CVE | none |
| CVE match | novel |
Summary
hammer2_chain_load_data() treats any data_off whose non-radix bits are
zero as "no media reference" and returns silently β even when the bref type
(INODE, INDIRECT, FREEMAP_NODE/LEAF, DATA) absolutely requires a data block.
The chain is left with chain->data == NULL and chain->error == 0,
defeating every downstream error check. A crafted INDIRECT parent then hits
the debugging placeholder while (1) tsleep(parent, 0, "xxx", 0) in
hammer2_chain_lookup β a permanent, non-interruptible hang of the kernel
thread while it holds the chain exclusively locked plus the whole topology
lock stack, wedging the mount; an INODE parent is NULL-dereferenced at
chain.c:2496.
Root cause
chain.c:938-939: if ((chain->bref.data_off & ~HAMMER2_OFF_MASK_RADIX) == 0)
return; β no error set, no type check (legitimate only for embedded DIRENTs
with radix 0). Reachable sinks with data==NULL/error==0: (a)
hammer2_chain_lookup INODE case derefs parent->data->ipdata.meta.op_flags
at chain.c:2496 after the parent->error check at 2473 passes β NULL-page
read panic; (b) INDIRECT case (parent installed at chain.c:2682-2688)
reaches chain.c:2524-2529: kprintf + while (1) tsleep(parent, 0, "xxx", 0)
β flags 0 means no PCATCH, timeout 0 means forever; the xop backend thread is
unkillable and holds the chain lock; repeatable triggers strand one backend
thread each until the xop pool is exhausted; (c) hammer2_chain_create
(chain.c:3243-3247), hammer2_chain_base_and_count (chain.c:1244-1247)
and hammer2_chain_indirect_maintenance (chain.c:4084-4088, 4177) compute
base = &parent->data->npdata[0] (small offset from NULL) and
hammer2_chain_countbrefs dereferences base[count-1].type at
chain.c:1297 β wild near-NULL read panic. Freemap-allocated offsets are
never 0 on legitimate images.
Threat model & preconditions
- Attacker position: crafted filesystem image, mounted; unprivileged user
triggers via ordinary path resolution (
ls,stat, readdir) descending through the crafted indirect block. - Privileges gained or impact: unkillable mount-wide deadlock (all filesystem operations block forever in D-state; repeated triggers exhaust the xop worker pool) or kernel NULL-page panic.
- Required config or capabilities: mount of crafted image (root or
vfs.usermount=1+ owned device). - Reachability: single path-resolution syscall.
Proof of concept
Build & run
craft image: INDIRECT bref with data_off = 0x0000000000000010 (radix 16, offset 0), methods = CHECK_NONE, in a directory inode's blockset; recompute parent check; mount; ls /mnt/crafted_dir
Expected output
ls never returns; kill -9 fails; ps shows the hammer2 xop thread asleep on channel 'xxx'. INODE variant (data_off=0x0A): stat /mnt/x -> NULL deref at chain.c:2496 -> kernel panic.
Impact
Permanent unkillable hang (mount-wide wedge, repeatable to strand the xop pool) or deterministic kernel panic from one unprivileged syscall after a crafted-image mount.
Recommended fix
Only the embedded-DIRENT case may return without error; every other type
with a zero-offset data_off must be errored so the existing
parent->error checks (chain.c:2473, 2876) terminate lookups cleanly.
Additionally guard the dereferences that run before any error check
(chain_create at 3226-3238, indirect_maintenance at 4084).
--- a/sys/vfs/hammer2/hammer2_chain.c
+++ b/sys/vfs/hammer2/hammer2_chain.c
@@ -936,7 +936,23 @@ hammer2_chain_load_data(hammer2_chain_t *chain)
if ((chain->bref.data_off & ~HAMMER2_OFF_MASK_RADIX) == 0) {
- return;
+ /*
+ * No media reference. Only legal for embedded dirents
+ * (bytes == 0). Anything else read from media is
+ * corrupt; error the chain so lookups terminate instead
+ * of dereferencing NULL data or stalling forever.
+ */
+ if (chain->bref.type == HAMMER2_BREF_TYPE_DIRENT &&
+ chain->bytes == 0) {
+ return;
+ }
+ chain->error = HAMMER2_ERROR_CHECK;
+ krateprintf(&krate_h2chk,
+ "chain %016jx.%02x zero data_off\n",
+ chain->bref.data_off, chain->bref.type);
+ return;
}
hmp = chain->hmp;
References
- chain.c:2524-2529 (the placeholder
while(1) tsleepsink)
Timeline
- 2026-08-28 Discovered during automated audit (pass 2, GLM 5.3).
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-2617 Β· 22 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 3.2 KB | β raw | |
| VERDICT.md | β | 7.6 KB | β raw | |
| forge_df2617.py | β | 8.8 KB | view raw | |
| mkbase2617.sh | β | 648 B | view raw | |
| trigger_P1.sh | β | 478 B | view raw | |
| trigger_P2.sh | β | 474 B | view raw | |
| trigger_H.sh | β | 1.3 KB | view raw | |
| build.sh | β | 155 B | view raw | |
| run.sh | β | 227 B | view raw | |
| run_H.log | β | 3.2 KB | view raw | |
| dmesg_H.txt | β | 1.3 KB | view raw | |
| panic.txt | β | 750 B | view raw | |
| panic2.txt | β | 743 B | view raw | |
| fix.diff | β | 2.9 KB | view raw | |
| fix_build.log | β | 5.7 MB | β download | |
| fix_run.log | β | 3.6 KB | view raw | |
| env.txt | β | 1.3 KB | view raw | |
| code_hashes.txt | β | 476 B | view raw | |
| base2617.img | β | 64.0 MB | β download | |
| P1.img | β | 64.0 MB | β download | |
| P2.img | β | 64.0 MB | β download | |
| H.img | β | 64.0 MB | β download |
DF-2617 β PoC evidence pack
hammer2_chain_load_data() (sys/vfs/hammer2/hammer2_chain.c:938-939) treats
bref.data_off == 0 as an early SUCCESS return for every blockref type, so
data-requiring types (INODE, INDIRECT, DATA, media DIRENT, FREEMAP_*) end up
with chain->data == NULL and chain->error == 0. Every consumer that
trusts the (absent) error then dereferences NULL or wedges forever in the
debugging loop at chain.c:2524-2529.
Verified sinks (all from mount/ls of a crafted image, root):
| Variant | Corrupted bref | Observed (stock INVARIANTS kernel #0) |
|---|---|---|
| P1 | volhdr sroot INODE data_off=0 |
Fatal trap 12 at hammer2_vfs_mount+0x1118: movq 0x90(%r8),%rax, fault VA 0x90 (vfsops.c:1309/1311 super-root ipdata.meta read, right after the useless schain->error check at vfsops.c:1285) β panic.txt |
| P2 | PFS "testvol" INODE data_off=0 (in sroot's blockset) |
Fatal trap 12 at hammer2_pfsalloc+0x5ef: movzbl 0x87(%r13),%eax, fault VA 0x87 = ripdata->meta.pfs_type (vfsops.c:1560-1561 PFS-label scan, guarded by a chain->error check that load_data never arms) β panic2.txt |
| H | first INDIRECT under PFS root data_off=0 |
mount succeeds; ls wedges in the while (1) tsleep(parent, 0, "xxx", 0) loop at chain.c:2524-2529: serial shows hammer2: unexpected NULL data on 0xfffff80119504500; ls PID in state D5, wchan h2coll, kill -9 has no effect; xop backend thread h2xop-testvol.23 permanently asleep on wchan xxx β run_H.log, dmesg_H.txt |
Reproduce
# guest (root): build the base image (newfs_hammer2 -L testvol + 33 files)
sh mkbase2617.sh # -> base2617.img
# host: forge the three malformed images
python3 forge_df2617.py base2617.img P1 P1.img # sroot data_off = 0
python3 forge_df2617.py base2617.img P2 P2.img # PFS inode data_off = 0
python3 forge_df2617.py base2617.img H H.img # INDIRECT data_off = 0
# (push images + trigger_*.sh to guest:/root/poc/df2617/)
# guest (root), stock kernel:
sh trigger_P1.sh # expect Fatal trap 12 in hammer2_vfs_mount (panic.txt)
sh trigger_P2.sh # expect Fatal trap 12 in hammer2_pfsalloc (panic2.txt)
sh trigger_H.sh # expect MOUNT_RC=0, then unkillable ls + kthread wchan 'xxx'
Success criteria (stock INVARIANTS kernel #0, uname in env.txt):
* P1/P2: Fatal trap 12: page fault while in kernel mode on the serial
console with the addresses above.
* H: hammer2: unexpected NULL data on <chain> on console; ls process
remains in D5/h2coll after kill -9; a h2xop-* kernel thread sits
on wchan xxx forever; the mount is unusable.
With fix.diff applied (kernel #1) all three triggers fail cleanly (kprintf + EINVAL mount failure / EIO readdir, no panic, no hang) and the uncorrupted base image still mounts and lists normally (fix_run.log).
- Full narrative: VERDICT.md
- Machine verdict: verdict.json (schema: audit/persist_poc.py)
- Verified fix: fix.diff (git-apply-able; validated by guest rebuild)
Forger technique (from DF-2616/DF-2620): CHECK_NONE (methods=0x00) every ancestor bref whose media block contains edited bytes, recompute the three volume-header CRC32Cs of every on-disk volhdr copy.
DF-2617 VERDICT β zero data_off silent NULL in hammer2_chain_load_data
Status: REPRODUCED (panic x2 + unkillable hang) Β· Impact: dos (kernel NULL
dereference panic from mount; permanent unkillable wedge from ls) Β·
Fix: validated on guest (kernel #1 v3) β all three triggers fail cleanly,
legit filesystem still mounts and lists.
Root cause (source trace)
hammer2_chain_load_data() (sys/vfs/hammer2/hammer2_chain.c:920) short-circuits
when the blockref has no media offset:
938: if ((chain->bref.data_off & ~HAMMER2_OFF_MASK_RADIX) == 0)
939: return;
For blockref types that REQUIRE a media data block β INODE, INDIRECT, DATA,
media DIRENT (>64-byte names), FREEMAP_NODE, FREEMAP_LEAF β this leaves
chain->data == NULL and chain->error == 0. hammer2_chain_lock()
returns 0 (chain.c:881), so every chain->error guard downstream of a
lock/lookup is disarmed. (Contrast hammer2_chain_alloc() chain.c:185-192,
which computes chain->bytes = 0 for radix 0 and documents the embedded
case β the only legitimate radix-0 users are embedded dirents, whose names
live in the bref itself (hammer2_chain_dirent_test() chain.c:5786-5795
reads bref.check.buf for names <= 64 bytes), and freshly allocated
(HAMMER2_CHAIN_INITIAL) chains whose data_off is 0 until
hammer2_chain_modify() allocates.)
Sinks verified on the guest (stock INVARIANTS kernel #0)
| # | Sink | Trigger | Observation |
|---|---|---|---|
| 1 | vfsops.c:1309-1324 β after if (schain->error) at 1285 passes silently: ripdata = &schain->data->ipdata; then ripdata->meta.pfs_clid (+0x90) / pfs_type |
P1: sroot INODE bref data_off=0 in volhdr, mount -o ro /dev/vn0@testvol |
Fatal trap 12, fault VA 0x90, Stopped at hammer2_vfs_mount+0x1118: movq 0x90(%r8),%rax (panic.txt) |
| 2 | vfsops.c:1555-1561 (hammer2_update_pmps PFS-label scan) β chain->error guard disarmed, ripdata = &chain->data->ipdata; β hammer2_pfsalloc() reads meta.pfs_type (+0x87) |
P2: PFS "testvol" INODE bref data_off=0 inside sroot's blockset, mount |
Fatal trap 12, fault VA 0x87, Stopped at hammer2_pfsalloc+0x5ef: movzbl 0x87(%r13),%eax (panic2.txt) |
| 3 | chain.c:2524-2529 β hammer2_chain_lookup() INDORECT-parent case: kprintf("hammer2: unexpected NULL data") then while (1) tsleep(parent, 0, "xxx", 0); (no PCATCH, no timeout) |
H: first INDIRECT under PFS root data_off=0, mount OK, then ls /mnt/h2 (readdir uses HAMMER2_LOOKUP_ALWAYS, descends into the indirect, re-enters lookup with it as parent) |
mount RC=0; console: hammer2: unexpected NULL data on 0xfffff80119504500; ls PID 864 state D5, wchan h2coll, kill -9 ineffective (UNKILLABLE=yes); xop backend thread h2xop-testvol.23 asleep forever on wchan xxx; mount unusable (run_H.log, dmesg_H.txt) |
The finding's cited sink chain.c:2496 (parent->data->ipdata.meta.op_flags
for INODE lookup parents) is the same deref family as sinks 1/2 β in the
mount flows it is shadowed by the vfsops derefs (which run first), and it is
reached generically whenever a corrupt INODE is a lookup parent (e.g. a
corrupted subdirectory). Both chain.c:2496 and 2524 are protected by the
fix because hammer2_chain_lookup() checks parent->error at chain.c:2473
before the blockref-array switch at 2482 β once load_data arms the error.
PoC
forge_df2617.py (host, python3) builds three malformed images from
base2617.img (guest mkbase2617.sh: newfs_hammer2 -L testvol, 33 files,
sync, umount β forces INDIRECTs in the PFS root blockset):
- P1 volhdr sroot INODE bref
data_off = 0 - P2 PFS "testvol" INODE bref
data_off = 0(CHECK_NONE the sroot bref) - H first INDIRECT bref under the PFS root
data_off = 0(CHECK_NONE sroot + PFS inode brefs)
Ancestor CHECK_NONE (methods=0x00) + volume-header CRC32C recompute per DF-2616/DF-2620 technique; only volhdr copy #0 carries the magic in these images (the forger patches every copy that does).
Threat model: mount of a crafted hammer2 image β root, or unprivileged with
vfs.usermount=1 and an owned device (per the finding).
Fix (fix.diff β 3 hunks, validated by in-guest rebuild)
- hammer2_chain.c load_data: in the zero-data_off early return, set
chain->error = HAMMER2_ERROR_EIO+ kprintf for every type except embedded DIRENT and CHAIN_INITIAL chains. This arms every downstreamchain->error/parent->errorguard (chain.c:2473, vfsops.c:1285/1427/1555). - hammer2_vfsops.c:1390 label scan: skip errored chains instead of
strcmp()-ingchain->data->ipdata.filename(caught as Fatal trap 12 at VA 0x100,strcmp+0x10, during fix-validation iteration v2). - hammer2_iocom.c:313 hammer2_update_spans: skip errored chains instead
of reading
ripdata->meta.pfs_clid(caught as Fatal trap 12 at VA 0x90,hammer2_autodmsg+0x273on the iocom thread, during fix-validation iteration v2).
Coordination with DF-2616: orthogonal, not subsuming. DF-2616's
hammer2_bad_data_off() treats (data_off & ~MASK_RADIX) == 0 as the legal
"embedded, no media block" case and returns 0; DF-2617 covers exactly that
case for data-requiring types. Both hunks can coexist: DF-2616 rejects bad
geometry when an offset IS present, DF-2617 rejects the absent offset for
types that require one.
Residual (documented, not fixed here): other chain->data->ipdata consumers
reachable only from other operation paths (e.g. xops.c:403/539 on unlink,
vfsops.c:2251 recovery scan) still deref without checking chain->error β
with hunk 1 they now fail only if individually guarded; upstream should
sweep all ->data-> derefs of lookup results for error checks.
Fix validation (guest, kernel #1 v3, uname in env.txt)
| Trigger | Stock kernel #0 | Patched kernel #1 |
|---|---|---|
P1 mount |
Fatal trap 12 @ vfs_mount+0x1118 (VA 0x90) | hammer2_chain_load_data: illegal zero data_off on bref type 1 β hammer2_mount: error I/O Error reading super-root β mount: Invalid argument (EINVAL), guest up (fix_run.log) |
P2 mount |
Fatal trap 12 @ pfsalloc+0x5ef (VA 0x87) | kprintf + I/O error scanning PFS labels + PFS label I/O error β EINVAL, guest up |
H mount+ls |
unkillable D5/h2coll ls, kthread wchan xxx |
mount OK, ls returns (empty/short listing), no hang, no xxx wchan, umount clean |
| control base2617.img | (baseline: mounts, 33 files) | mounts, 33 files listed, umount clean (fix_control.log) |
| root fs (vbd0s1d@ROOT) | boots | boots, hammer2 root fs normal across 3 rebuild/reboot cycles |
Honest classification notes
- The panic and the hang both require mounting a crafted image. On the
stock guest
vfs.usermount=0, so root is needed; the finding's threat model (vfs.usermount=1 + owned device) applies unchanged. - Sink 3 is a permanent, unkillable kernel-side wedge: the sleeping xop thread holds the chain topology lock, so every subsequent operation on the mount piles up; the mount can never be unmounted; the only recovery is a reboot. That is a full local DoS of the machine's storage subsystem, not just of one directory.
- Sink 1/2 are read-only NULL derefs at fixed near-NULL VAs (0x90/0x87) β not directly exploitable for code execution on this kernel config (no way to map page 0), hence DoS classification, matching the finding's Medium severity.
Fix status (batch validation)
Applied with the 36 sibling fix.diffs of this batch in findings/poc/_batch41/all_fixes.patch; single in-guest make -j6 nativekernel KERNCONF=X86_64_GENERIC completed RC=0 with zero errors under -Werror (full log: findings/poc/_batch41/fix_build8.log). Behavior A/B intentionally skipped for this Info-tier item.
Fix verification
fixedValidated end-to-end on the guest: baseline stock kernel #0 reproduces all three failure modes (2 panics + unkillable hang); with fix.diff (3 hunks: chain.c load_data error-arming, vfsops.c label-scan guard, iocom.c update_spans guard) built via make -j6 nativekernel + installkernel, P1 mount fails cleanly (EINVAL, 'illegal zero data_off on bref type 1' + 'error I/O Error reading super-root'), P2 mount fails cleanly (EINVAL, 'I/O error scanning PFS labels' + 'PFS label not found'), H ls returns with clean per-entry ENOENT, no 'xxx' wchan / no stuck D-state threads, umount clean, and the uncorrupted control image mounts and lists all 33 files with clean umount. Two intermediate iterations (v1, v2) each caught a further unguarded consumer (vfsops.c:1392 strcmp crash VA 0x100; iocom.c:313 autodmsg crash VA 0x90) and added the corresponding guard - both crashes documented in fix_run.log history/VERDICT.md.
['fix.diff', 'fix_build.log (37912 lines, full untrimmed nativekernel build, PATCH_RC=0 BUILD_RC=0 INSTALL_RC=0)', 'fix_run.log (P1/P2 EINVAL + kprintf; H no-hang ls output, NO_STUCK_THREADS, UMOUNT_OK, control 33 files)']
Confirmed kernel references
- sys/vfs/hammer2/hammer2_chain.c:938
- sys/vfs/hammer2/hammer2_chain.c:2496
- sys/vfs/hammer2/hammer2_chain.c:2524
- sys/vfs/hammer2/hammer2_chain.c:2473
- sys/vfs/hammer2/hammer2_vfsops.c:1285
- sys/vfs/hammer2/hammer2_vfsops.c:1309
- sys/vfs/hammer2/hammer2_vfsops.c:1392
- sys/vfs/hammer2/hammer2_vfsops.c:1555
- sys/vfs/hammer2/hammer2_iocom.c:313
Detail
Evidence (decisive lines)
['panic.txt: P1 Fatal trap 12, fault VA 0x90, Stopped at hammer2_vfs_mount+0x1118: movq 0x90(%r8),%rax', 'panic2.txt: P2 Fatal trap 12, fault VA 0x87, Stopped at hammer2_pfsalloc+0x5ef: movzbl 0x87(%r13),%eax', "run_H.log: MOUNT_RC=0; ls PID 864 state D5 wchan h2coll; UNKILLABLE=yes after kill -9; kthread h2xop-testvol.23 wchan xxx; dmesg_H.txt: 'hammer2: unexpected NULL data on 0xfffff80119504500'", "fix_run.log: patched kernel #1 - P1/P2 mount: Invalid argument with 'illegal zero data_off on bref type 1' + 'error I/O Error reading super-root'/'PFS label not found'; H ls returns (clean per-entry ENOENT), NO_STUCK_THREADS, UMOUNT_OK, control image lists 33 files", 'VERDICT.md: full narrative incl. two intermediate fix iterations (strcmp+0x10 VA 0x100 crash via vfsops.c:1392; hammer2_autodmsg+0x273 VA 0x90 crash via iocom.c:313) that motivated fix hunks 2 and 3']
PoC changes
Seed sketch had no runnable forger. Wrote forge_df2617.py (walks volhdr->sroot->PFS inode incl. INDIRECT arrays, sets data_off=0 on the chosen bref, CHECK_NONE ancestors per DF-2616/DF-2620 technique, recomputes all volhdr CRC32Cs - note: an early version wrote CRCs to a python slice copy and had to be fixed to patch the bytearray in place); wrote mkbase2617.sh (newfs + 33 files to force INDIRECTs in the PFS root blockset); triggers P1/P2/H capture panic/hang evidence in-guest. Fix evolved over 3 iterations: v1 (load_data error only) left vfsops.c:1392 strcmp crash (VA 0x100); v2 added label-scan guard but hit hammer2_autodmsg+0x273 (iocom.c:313 update_spans, VA 0x90); v3 added the iocom guard - all triggers then clean.
Verified recommended fix
In hammer2_chain_load_data's zero-data_off early return, set chain->error = HAMMER2_ERROR_EIO (+kprintf) for all bref types except embedded DIRENT and HAMMER2_CHAIN_INITIAL chains, and add chain->error guards at the consumers that deref chain->data immediately after lookup (vfsops.c:1390 label scan, iocom.c:313 update_spans); see fix.diff.
Verdict
CONFIRMED on the QEMU guest (stock INVARIANTS kernel #0). hammer2_chain_load_data (chain.c:938-939) early-returns SUCCESS when bref.data_off has no media offset, leaving chain->data==NULL and chain->error==0 for data-requiring bref types; every downstream chain->error/parent->error guard is disarmed. Three sinks demonstrated from mount/ls of crafted images: (P1) sroot INODE data_off=0 -> Fatal trap 12, fault VA 0x90, Stopped at hammer2_vfs_mount+0x1118 movq 0x90(%r8),%rax (vfsops.c:1309-1324 ipdata read after the useless schain->error check at 1285); (P2) PFS INODE data_off=0 -> Fatal trap 12, fault VA 0x87, Stopped at hammer2_pfsalloc+0x5ef movzbl 0x87(%r13),%eax (vfsops.c:1555-1561 PFS-label scan); (H) INDIRECT data_off=0 -> mount succeeds, ls descends into the indirect and wedges forever in the debugging loop at chain.c:2524-2529 while(1) tsleep(parent,0,"xxx",0): console 'hammer2: unexpected NULL data on 0xfffff80119504500', ls PID stuck D5/wchan h2coll, kill -9 ineffective, xop backend thread h2xop-testvol.23 asleep on wchan 'xxx' forever, mount permanently unusable. Both cited sink families in the finding are real; the chain.c:2496 parent->data->ipdata deref specifically is shadowed in the mount flows by the vfsops derefs (same silent-NULL class) and is protected by the fix via the parent->error check at chain.c:2473. Fix (3 hunks: load_data arms HAMMER2_ERROR_EIO for zero data_off on non-embedded/non-INITIAL types; vfsops label-scan skips errored chains before strcmp; iocom hammer2_update_spans skips errored chains) validated by in-guest nativekernel rebuild: P1/P2 mounts fail cleanly with EINVAL + kprintf, H ls returns with clean ENOENT/EIO errors, no panic, no hang, no 'xxx' wchan, umount clean, and the uncorrupted control image still mounts and lists all 33 files. Impact dos (not privesc): the NULL derefs read fixed near-NULL VAs (0x90/0x87) and the hang is a permanent unkillable wedge; all require mounting a crafted image (root, or unprivileged with vfs.usermount=1 + owned device).
No comments yet.