ELF ABI-note descriptor read out of bounds (note_overflow ignores n_descsz)
| Field | Value |
|---|---|
| ID | DF-0020 |
| Status | new |
| Severity | Low |
| CVSS 3.1 | CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:L |
| CWE | CWE-125 Out-of-bounds Read |
| File | sys/kern/imgact_elf.c |
| Lines | 1700-1707, 1780, 1866-1874 |
| Area | kern |
| Confidence | likely |
| Discovered | 2026-06-29 |
| Reported | pending |
Summary
note_overflow() validates that a note's n_namesz fits in the remaining
PT_NOTE segment but never validates n_descsz. A crafted, truncated
.note.ABI-tag that matches the DragonFly brandnote (n_namesz=10,
n_descsz=4, n_type=1, vendor "DragonFly") β but whose segment is too
short to actually hold the 4-byte descriptor β passes note_overflow, matches,
and causes bsd_trans_osrel() to dereference the descriptor at
note + sizeof(Elf_Note) + roundup2(n_namesz, 4) = note + 24, 2β6 bytes
past the end of the segment buffer. This is a kernel out-of-bounds read on a
file supplied by any local user via execve(2).
Root cause
sys/kern/imgact_elf.c:1700-1707 β note_overflow:
static boolean_t
note_overflow(const Elf_Note *note, size_t maxsize)
{
if (sizeof(*note) > maxsize)
return TRUE;
if (note->n_namesz > maxsize - sizeof(*note)) /* checks namesz only */
return TRUE;
return FALSE; /* n_descsz never checked */
}
The note walk calls note_overflow(note, note_end - note) (:1780-1781). The
match (:1786-1790) requires n_descsz == checknote->hdr.n_descsz (4 for the
brandnote); on match, bsd_trans_osrel runs (:1791-1794):
static boolean_t
__elfN(bsd_trans_osrel)(const Elf_Note *note, int32_t *osrel)
{
uintptr_t p;
p = (uintptr_t)(note + 1); /* +12 (sizeof Elf_Note) */
p += roundup2(note->n_namesz, sizeof(Elf32_Addr)); /* +12 (roundup2(10,4)) */
*osrel = *(const int32_t *)(p); /* :1872 reads note+24 */
return (TRUE);
}
For a segment of p_filesz=22 (12-byte Elf_Note + 10-byte name, no
descriptor): note_overflow(note, 22) returns FALSE (12<=22, 10<=10), the
brandnote matches, and the descriptor read at note+24 lands past the 22-byte
segment buffer.
- In the
limited_to_first_pagepath, the buffer isimage_header(PAGE_SIZE); placing the note near the page end makes the read escape the page. - In the cross-page path, the buffer is
kmalloc(notesz)(the segment size); the read escapes the allocation.
Threat model & preconditions
- Attacker position: any local unprivileged user. The image activator runs
for every local
execve(2), including of attacker-owned files; no privilege required and the binary need not be loadable (the header/note check runs before vmspace setup). - Privileges gained or impact: kernel OOB read of β€4 bytes adjacent to the
note buffer. The read value is stored in
p_osrel, which is not exposed to userspace β so this is not a confirmed info leak. When the OOB read straddles an unmapped page boundary, the kernel faults β panic (local DoS). - Required config or capabilities: none; default kernel.
- Reachability:
execve(2)of a crafted ELF with a truncated PT_NOTE.note.ABI-tag.
Proof of concept
PoC source: findings/poc/DF-0020/elf_note_oob.py
Emits a minimal ELF with one PT_NOTE whose brandnote header claims a 4-byte descriptor but whose segment truncates it, placed near the first-page boundary.
Build & run (unprivileged)
python3 findings/poc/DF-0020/elf_note_oob.py -o /tmp/oob_elf chmod +x /tmp/oob_elf /tmp/oob_elf
Expected output
Either nothing visible (OOB bytes land in mapped adjacent memory, value to
p_osrel, not exposed) or β when the OOB read straddles an unmapped page β a
kernel page-fault panic (local DoS).
Impact
Kernel OOB read on attacker-controlled input; the value is not exposed, so the realistic impact is a local DoS (panic on a faulting read) rather than an info leak. Rated Low. Defense-in-depth: an attacker controlling adjacent kernel memory (via a separate primitive) could combine, but standalone this is a robustness/correctness fix.
Recommended fix
Validate n_descsz in note_overflow:
--- a/sys/kern/imgact_elf.c
+++ b/sys/kern/imgact_elf.c
@@ -1699,11 +1699,16 @@ static boolean_t
note_overflow(const Elf_Note *note, size_t maxsize)
{
+ size_t avail;
+
if (sizeof(*note) > maxsize)
return TRUE;
- if (note->n_namesz > maxsize - sizeof(*note))
+ avail = maxsize - sizeof(*note);
+ if (note->n_namesz > avail)
+ return TRUE;
+ /* Ensure the claimed descriptor also fits within the segment. */
+ avail -= roundup2(note->n_namesz, sizeof(Elf32_Addr));
+ if (note->n_descsz > avail)
return TRUE;
return FALSE;
}
References
sys/kern/imgact_elf.c:1700-1707βnote_overflow(ignoresn_descsz).sys/kern/imgact_elf.c:1780β caller withnote_end - note.sys/kern/imgact_elf.c:1866-1874βbsd_trans_osreldescriptor deref.- CWE-125 Out-of-bounds Read.
Timeline
- 2026-06-29 Discovered during automated file-by-file audit of
sys/kern/imgact_elf.c. - pending Reported to DragonFlyBSD security contact.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-0020 Β· 16 files| File | Type | Description | Size | |
|---|---|---|---|---|
| elf_note_oob.c | trigger-source | minimal C generator for the crafted ELF (rewritten from elf_note_oob.py because the guest has no python3) | 6.2 KB | view raw |
| elf_note_oob.py | trigger-source | original Python seed PoC (kept for reference) | 3.7 KB | view raw |
| build.sh | build-script | cc -O2 -Wall -o elf_note_oob elf_note_oob.c | 179 B | view raw |
| run.sh | run-script | craft ELF + chmod + execve | 999 B | view raw |
| VERDICT.md | verdict | full narrative: mechanism, why EI_OSABI=200, exploit chain (none), fix, fix validation | 11.0 KB | β raw |
| fix.diff | suggested-fix | git-apply-able fix: validate n_descsz in note_overflow via underflow-proof need=a+b check | 815 B | view raw |
| build.log | build-log | final successful build, full output | 22 B | view raw |
| run.log | run-log | baseline #0 decisive run, full output | 274 B | view raw |
| run.2.log | run-log | baseline 5-run stress (OOB silent each time) | 723 B | view raw |
| fix_build.log | build-log | single-fix kernel build output (make -j6 nativekernel) | 5.6 MB | β download |
| fix_run.log | run-log | patched #1 decisive run, full output | 290 B | view raw |
| env.txt | environment | uname, cc version, sysctl kern.elf64.fallback_brand | 1.5 KB | view raw |
| manifest.json | manifest | this file | 3.3 KB | view raw |
| README.md | readme | human reproduce doc | 4.1 KB | β raw |
| ../fix_build_combined.log | build-log | Combined 41-finding kernel build (rc=0, -Werror clean) | 5.6 MB | β download |
| ../fix_build_summary.txt | build-summary | Summary of the combined 41-finding kernel build | 826 B | view raw |
DF-0020 β PoC
elf_note_oob.c (C; the seed was elf_note_oob.py β rewritten in C
because the DragonFly guest has no python3) crafts a minimal ELF64
whose truncated .note.ABI-tag triggers a kernel OOB read in
bsd_trans_osrel().
The bug
note_overflow() (sys/kern/imgact_elf.c:1700-1707) validates that a
note's n_namesz fits in the remaining PT_NOTE segment but never
validates n_descsz. A crafted PT_NOTE that matches the DragonFly
brandnote (n_namesz=10, n_descsz=4, n_type=1, vendor "DragonFly")
but is truncated (p_filesz=22, no descriptor) passes note_overflow
(12<=22, 10<=10), matches, and bsd_trans_osrel() reads the 4-byte
descriptor at note + sizeof(Elf_Note) + roundup2(n_namesz, 4) == note+24
β past the 22-byte segment, and with noteloc=4072, past the 4096-byte
image_header page entirely. OOB read of adjacent kernel memory.
Impact: kernel OOB read of β€4 bytes; value goes to p_osrel (not
exposed to userspace) β no info leak, no escalation. Realistic ceiling
is a silent OOB read on every local execve (robustness /
defense-in-depth) or, if the OOB straddles an unmapped page, a local
DoS via page-fault panic.
Why EI_OSABI = 200
The PoC sets e_ident[EI_OSABI] = 200 so the brand can ONLY be
selected via the PT_NOTE match path (loop 1 in get_brandinfo,
sys/kern/imgact_elf.c:550-561). With EI_OSABI=0 (the seed value),
loop 2's hdr->e_ident[EI_OSABI] == bi->brand match would select the
DragonFly brand regardless of whether the note check passed, masking
the bug. With EI_OSABI=200 and kern.elf64.fallback_brand=-1, the
unfixed kernel accepts the binary (note match + OOB), while the fixed
kernel rejects it with ENOEXEC β a clean before/after contrast.
Why noteloc = 4072 (not 4074)
The seed Python PoC used noteloc = 4096 - 22 = 4074, but 4074 % 4 = 2,
and the note walk breaks immediately on !aligned(note, Elf32_Addr)
(sys/kern/imgact_elf.c:1778). With noteloc=4072 (4-aligned), the
walk proceeds past the alignment check, the truncated note matches, and
bsd_trans_osrel performs the OOB read.
Build (unprivileged)
cc -O2 -Wall -o elf_note_oob elf_note_oob.c
(or just ./build.sh)
Run (unprivileged)
./run.sh # equivalent to: ./elf_note_oob /tmp/df0020_oob_elf chmod +x /tmp/df0020_oob_elf /tmp/df0020_oob_elf
Expected output
Bug present (unpatched #0 kernel)
execve succeeds: the crafted binary is loaded via the OOB brand
match. Since the binary has no PT_LOAD, control jumps to e_entry=0
and the new process dies with SIGSEGV:
--- attempting execve --- Segmentation fault (core dumped) # execve succeeded
The shell does NOT print "Exec format error". (5/5 reproducible; OOB read happens silently each time β adjacent lwbuf-pool page is mapped.)
Fixed kernel (this fix.diff applied)
execve fails with ENOEXEC. The shell prints:
--- attempting execve --- ./run.sh: /tmp/df0020_oob_elf: Exec format error
No SIGSEGV, no OOB read. (3/3 reproducible.)
Files
| File | Purpose |
|---|---|
elf_note_oob.c |
minimal C generator for the crafted ELF |
elf_note_oob.py |
original Python seed (kept for reference) |
build.sh |
exact build command (cc -O2 -Wall ...) |
run.sh |
exact run invocation (craft + chmod + exec) |
VERDICT.md |
full narrative (mechanism, fix, validation) |
fix.diff |
git apply-able fix to sys/kern/imgact_elf.c |
build.log |
full final build output |
run.log |
baseline (#0) decisive run, full output |
run.2.log |
baseline 5-run stress (OOB silent each time) |
fix_build.log |
full single-fix kernel build output |
fix_run.log |
patched (#1) decisive run, full output |
env.txt |
guest environment (uname, cc, sysctl) |
manifest.json |
machine-readable catalog |
DF-0020 β ELF ABI-note descriptor read out of bounds (note_overflow ignores n_descsz)
Verdict
REPRODUCED. The bug is real and confirmed by both source-level tracing
and an empirical before/after contrast on the running kernel. The fix
(fix.diff) closes the bug deterministically (validated on a built-and-
booted single-fix kernel). Impact is a kernel OOB read of β€4 bytes
adjacent to a per-exec scratch buffer; the read value goes to p_osrel
and is not exposed to userspace, so the realistic impact ceiling is a
silent OOB read on every local execve(2) of a crafted binary β i.e.
defense-in-depth / robustness β with a possible local DoS (page-fault
panic) if the OOB read straddles an unmapped page boundary. No
privilege escalation is derivable (no write primitive, value not
exposed).
Mechanism (trigger β primitive β effect)
The image activator walks an ELF's PT_NOTE program header looking for
the DragonFly ABI brandnote (sys/kern/imgact_elf.c:1670-1693,
check_note β check_PT_NOTE). For each candidate note it calls
note_overflow(note, maxsize) (:1700-1707) to validate that the note
header + name fit in the remaining segment:
static boolean_t
note_overflow(const Elf_Note *note, size_t maxsize)
{
if (sizeof(*note) > maxsize)
return TRUE;
if (note->n_namesz > maxsize - sizeof(*note)) /* checks namesz only */
return TRUE;
return FALSE; /* n_descsz never checked */
}
n_descsz is never validated. The note-walk match (:1786-1790)
requires note->n_descsz == checknote->hdr.n_descsz (4 for the
DragonFly brandnote, :131), so a truncated note whose 12-byte header
claims n_descsz=4 but whose segment truncates the descriptor
passes note_overflow, matches, and reaches the
BN_TRANSLATE_OSREL callback bsd_trans_osrel (:1791-1794):
static boolean_t
__elfN(bsd_trans_osrel)(const Elf_Note *note, int32_t *osrel)
{
uintptr_t p;
p = (uintptr_t)(note + 1); /* +12 (sizeof Elf_Note) */
p += roundup2(note->n_namesz, sizeof(Elf32_Addr)); /* +12 (roundup2(10,4)) */
*osrel = *(const int32_t *)(p); /* :1872 reads note+24 */
return (TRUE);
}
The dereference at note + 24 reads 4 bytes (note+24..note+28) β but
the segment buffer ends at note + 22 (12-byte header + 10-byte name,
no descriptor), so the read lands 2β6 bytes past note_end and, in
this PoC's placement, past the entire 4096-byte mapped lwbuf page.
Buffer placement (this PoC)
noteloc = 4072, p_filesz = 22 β endbyte = 4094 < PAGE_SIZE, so
limited_to_first_page is TRUE and the note pointer resolves into
imgp->image_header (a single 4096-byte page mapped by
exec_map_first_page β exec_map_page β lwbuf,
sys/kern/kern_exec.c:850-867). With note = image_header + 4072:
| Address (image_header + X) | Contents / Region |
|---|---|
| 4072..4083 | Elf_Note header (12 bytes) β in buffer |
| 4084..4093 | "DragonFly\0" name (10 bytes) β in buffer |
| 4094..4095 | (only 2 bytes left before page end) |
| 4096..4099 | OOB read β bsd_trans_osrel reads 4 bytes |
| straddling the page boundary into adjacent KVA |
Why the OOB is silent on this guest
image_header is mapped by lwbuf, which on DragonFly uses a per-CPU
sf_buf-style KVA window. The virtual page adjacent to the lwbuf mapping
is usually another mapped page, so the 4-byte OOB read silently returns
adjacent kernel memory rather than faulting. The read value lands in
p_osrel (sys/kern/imgact_elf.c:867) and is never copied to userspace
via auxargs, so there is no info leak as a direct consequence.
Depending on guest memory layout / system load, the OOB read may instead
fault on an unmapped page β kernel page-fault panic (local DoS).
Why the PoC uses EI_OSABI = 200
get_brandinfo (sys/kern/imgact_elf.c:534-599) tries four selection
paths in order: (1) PT_NOTE brand match, (2) EI_OSABI /
OLD_EI_BRAND match, (3) interpreter-path match, (4) default
fallback brand. The DragonFly brand has .brand = ELFOSABI_NONE (0)
and .flags = BI_CAN_EXEC_DYN | BI_BRAND_NOTE (not MANDATORY)
(sys/cpu/x86_64/misc/elf_machdep.c:58-68).
If the PoC set EI_OSABI = 0 (the original elf_note_oob.py value),
loop (2) would also match the DragonFly brand regardless of whether
the PT_NOTE check passed β masking the bug behaviorally. With
EI_OSABI = 200 (an obscure value no registered brand matches) and no
PT_INTERP, the only path that can select a brand is loop (1) β
the PT_NOTE match. Combined with kern.elf64.fallback_brand = -1
(default, verified), this yields a clean, deterministic contrast:
| Kernel state | Brand loop (1) result | execve result |
|---|---|---|
Unfixed (#0) |
match (OOB read happens) | succeeds β child SIGSEGV at e_entry=0 (no PT_LOAD) |
Fixed (#1, this fix) |
note_overflow returns TRUE |
fails with ENOEXEC β shell prints "Exec format error" |
The "execve succeeds on unfixed, fails on fixed" difference is the
decisive before/after evidence: the only thing that changed is whether
the truncated PT_NOTE passes note_overflow β i.e. whether the OOB
descriptor read happens.
Exploit chain
None β this is a read-only primitive. The leaked bytes go to
p_osrel, which is not exposed to userspace through auxargs or any
other copyout path. No write primitive is derivable; no privilege
escalation is possible from this bug alone. The realistic impact
ceiling is:
- Silent kernel OOB read (4 bytes) on every local
execveof a crafted binary β defense-in-depth / robustness concern; the OOB value is attacker-influenced only via the position of the note within the segment, not its content (the descriptor bytes are read from adjacent kernel memory, which the attacker does not control). - Local DoS via page-fault panic if the OOB read straddles an unmapped kernel VA page. Reproducibility of this depends on guest memory layout (lwbuf-pool adjacency) and is not deterministic.
A secondary combinator: if an attacker already has a separate primitive that places attacker-controlled bytes adjacent to the lwbuf pool, this read could be used as a controlled info-leak side channel. Standalone, no escalation.
PoC changes (vs. the seed elf_note_oob.py)
- Rewrote the generator in C (
elf_note_oob.c) because the guest has nopython3. The C version emits byte-identical bytes to the Python PoC for the same parameters and is self-contained. - Changed
EI_OSABIfrom 0 to 200 so brand selection can ONLY happen via the PT_NOTE path (see "Why the PoC uses EI_OSABI = 200" above). This turns an ambiguous silent OOB into a clean before/after contrast (execve succeeds vs ENOEXEC). - Changed
notelocfrom 4074 to 4072 (4-byte aligned) so thealigned(note, Elf32_Addr)check atimgact_elf.c:1778does not immediately break the note walk before the match. (4074 % 4 = 2, which would silently break the walk and make the PoC ineffective.) - Added
build.sh/run.shrepro scripts and full untrimmed logs (build.log,run.log,run.2.log,fix_build.log,fix_run.log,env.txt).
Fix
fix.diff (this folder) β sys/kern/imgact_elf.c:1700 note_overflow.
Add an n_descsz-aware bounds check that avoids underflow:
static boolean_t
note_overflow(const Elf_Note *note, size_t maxsize)
{
size_t avail, need;
if (sizeof(*note) > maxsize)
return TRUE;
avail = maxsize - sizeof(*note);
if (note->n_namesz > avail)
return TRUE;
/*
* The descriptor (and rounded name) must also fit inside the
* remaining segment, otherwise a caller that dereferences the
* descriptor (e.g. bsd_trans_osrel()) would read past the end of
* the validated note buffer.
*/
need = roundup2(note->n_namesz, sizeof(Elf32_Addr)) +
roundup2(note->n_descsz, sizeof(Elf32_Addr));
if (need > avail)
return TRUE;
return FALSE;
}
need is computed by addition (no underflow) and compared against
avail, which is the post-header bytes remaining. For the PoC's
truncated note (n_namesz=10, n_descsz=4, maxsize=22):
need = roundup2(10,4) + roundup2(4,4) = 12 + 4 = 16 > avail=10 β
returns TRUE, breaking the walk before the match β no OOB.
This supersedes the finding markdown's proposed fix. The markdown's
version used avail -= roundup2(note->n_namesz, sizeof(Elf32_Addr))
which, when roundup2(n_namesz,4) > avail (true for n_namesz=10,
avail=10: rounded is 12), underflows the size_t to a huge value and
the subsequent n_descsz > avail check silently passes β i.e. the
markdown's proposed fix would NOT close the bug for this PoC. The
first single-fix kernel I built with that shape confirmed this: the
PoC still triggered the OOB (Segmentation fault, execve succeeded).
The corrected need = a + b; need > avail form is underflow-proof and
was re-validated from scratch on a freshly-built #1 kernel.
Fix validation (Phase 8)
Built a single-fix kernel with make -j6 nativekernel
KERNCONF=X86_64_GENERIC from the with-src snapshot + this fix.diff
applied. Booted it as /boot/kernel/kernel (the bare name the loader
boots). Confirmed kern.version = DragonFly 6.5-DEVELOPMENT #1:
Sun Jul 12 16:48:55 UTC 2026 (build timestamp today, #1 vs the
unpatched #0).
Ran the same PoC on both kernels:
| Kernel | PoC execve result | OOB? |
|---|---|---|
#0 unpatched baseline |
succeeds; child SIGSEGV at e_entry=0 |
YES |
#1 patched (this fix) |
fails: sh: /tmp/df0020_oob_elf: Exec format error (ENOEXEC) |
NO |
5/5 baseline runs succeeded (OOB silent each time, no panic); 3/3 patched runs failed cleanly with ENOEXEC. Deterministic before/after.
Kernel references (confirmed during verification)
sys/kern/imgact_elf.c:1700-1707βnote_overflow(does not checkn_descsz).sys/kern/imgact_elf.c:127-136β DragonFly brandnote definition (n_descsz = sizeof(int32_t) = 4,BN_TRANSLATE_OSREL).sys/kern/imgact_elf.c:1780-1781β caller (note_overflow(note, note_end - note)).sys/kern/imgact_elf.c:1786-1794β match +bsd_trans_osrelinvocation.sys/kern/imgact_elf.c:1866-1875βbsd_trans_osrelderef atnote + sizeof(Elf_Note) + roundup2(n_namesz, 4).sys/kern/imgact_elf.c:534-599βget_brandinfo's 4 selection loops (why EI_OSABI=200 is needed).sys/cpu/x86_64/misc/elf_machdep.c:58-68β DragonFly brand.brand = ELFOSABI_NONE,BI_BRAND_NOTE.sys/kern/kern_exec.c:850-867βexec_map_first_page(image_header = single lwbuf page).
Fix verification
fixedVALIDATED. PoC on unpatched #0: execve SUCCEEDS (OOB brand match), 5/5 runs. On single-fix #1: execve REJECTED with ENOEXEC, 3/3 runs. The finding markdown's proposed fix shape (avail -= roundup2) was tested first and did NOT close the bug (size_t underflow); the corrected need=a+b form is validated.
baseline #0: Segmentation fault (execve succeeded via OOB), x5 runs. patched #1: Exec format error (ENOEXEC), x3 runs.
Confirmed kernel references
- sys/kern/imgact_elf.c:1700
- sys/kern/imgact_elf.c:1704
- sys/kern/imgact_elf.c:1707
- sys/kern/imgact_elf.c:127
- sys/kern/imgact_elf.c:131
- sys/kern/imgact_elf.c:1780
- sys/kern/imgact_elf.c:1786
- sys/kern/imgact_elf.c:1791
- sys/kern/imgact_elf.c:1866
- sys/kern/imgact_elf.c:1872
- sys/kern/imgact_elf.c:550
- sys/cpu/x86_64/misc/elf_machdep.c:58
- sys/kern/kern_exec.c:850
Detail
Exploit chain
none -- read-only primitive. The 4-byte OOB read returns into kernel-local p_osrel (never copied to userspace). No info leak, no write, no escalation.
Evidence (decisive lines)
baseline #0: execve SUCCEEDS via OOB brand match (child SIGSEGV at entry=0), 5/5 runs, no panic (OOB silent). patched #1: execve REJECTED with ENOEXEC, 3/3 runs, no OOB.
PoC changes
Rewrote generator in C (guest has no python3). Changed EI_OSABI from 0 to 200 so brand can ONLY be selected via PT_NOTE path. Changed noteloc to 4072 (4-aligned). Added build.sh/run.sh, VERDICT.md, fix.diff, full logs.
Verified recommended fix
Add n_descsz-aware bounds check to note_overflow at sys/kern/imgact_elf.c:1700: compute need = roundup2(n_namesz,4) + roundup2(n_descsz,4) by ADDITION and return TRUE if need > avail. SUPERSIDES finding markdown's proposed fix (avail -= roundup2 form underflows size_t for the PoC's exact parameters; empirically verified insufficient). Full git-apply-able diff in findings/poc/DF-0020/fix.diff.
Verdict
REPRODUCED. note_overflow() at sys/kern/imgact_elf.c:1700-1707 validates n_namesz against the remaining PT_NOTE segment but never n_descsz; when a truncated .note.ABI-tag matches the DragonFly brandnote, bsd_trans_osrel() dereferences (int32_t)(note + sizeof(Elf_Note) + roundup2(n_namesz,4)) == note+24 -- 2..6 bytes past note_end. Proof: with EI_OSABI=200 (brand can ONLY be selected via PT_NOTE path), the unpatched #0 kernel ACCEPTS the crafted binary (execve succeeds), 5/5 runs; the same PoC on single-fix #1 kernel is REJECTED with ENOEXEC, 3/3 runs. OOB read is silent on this guest (adjacent lwbuf-pool page mapped); panic is the realistic ceiling.
No comments yet.