β¬’ DragonFlyBSD Kernel Audit
← triage Β· dashboard
DF-2825

Negative l_len rejected with EINVAL instead of the POSIX [l_start+l_len, l_start-1] semantics

Field Value
ID DF-2825
Status new
Severity Info
CVSS 3.1 CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:L
CWE CWE-20 (standards-conformance deviation, fail-closed)
File sys/kern/kern_lockf.c
Lines 230-231
Area kern
Confidence certain
Discovered 2026-08-31
Pass 2 (GLM 5.3 second pass)
Bucket base:kern
Reported pending
Known CVE none
CVE match novel

Summary

lf_advlock() returns EINVAL for fl->l_len < 0, while POSIX.1-2008 (and FreeBSD, and Linux) interpret negative lengths as the range [l_start+l_len, l_start-1]. Portable applications using backward locks fail closed on DragonFly. Observed on the guest: F_SETLK(start=10, len=-5) β†’ EINVAL where POSIX expects the lock [5,9]. No memory-safety impact. Fix: convert instead of reject (row diff).

Timeline

  • 2026-08-31 Discovered during pass-2 audit of kern_lockf.c (GLM 5.3).

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-2825 Β· 10 files
FileTypeDescriptionSize
README.md β€” 672 B ↓ raw
VERDICT.md β€” 834 B ↓ raw
build.sh β€” 67 B view raw
env.txt β€” 509 B view raw
fix.diff β€” 500 B view raw
lockf_neg.c β€” 1.0 KB view raw
manifest.json β€” 491 B view raw
run.log β€” 127 B view raw
run.sh β€” 72 B view raw
verdict.json β€” 1.9 KB view raw

DF-2825 β€” negative l_len rejected (non-POSIX) (Info-tier hardening verification)

What

negative l_len rejected (non-POSIX)

Build

cc -Wall -O2 -o lockf_neg lockf_neg.c

Run

./lockf_neg

Expected

baseline (stock kernel): see verdict below fix kernel (all-41-fixes build): the bad behavior is gone

Files

README.md VERDICT.md build.sh env.txt fix.diff lockf_neg.c manifest.json run.log run.sh verdict.json

Shared harness: h41.c (combined KLD, sysctl debug.h41.cmd=N driven). Full batch context: the fix kernel contains all 41 hardening fixes of this batch compiled together (build RC=0) and passed the complete A/B suite plus a clean reboot.

VERDICT.md
↓ download raw

DF-2825 β€” VERDICT

status: reproduced | reproduced: 1 | impact: none | confidence: certain

Mechanically reproduced on the stock guest: fcntl(F_SETLK, l_start=10, l_len=-5) returns -1/EINVAL (kern_lockf.c:230-231 rejects negative lengths outright), while POSIX.1-2008 (and FreeBSD/Linux) interpret l_len<0 as the byte range [l_start+l_len, l_start-1] = [5,9]; portable backward-lock applications fail closed on DragonFly. Fail-closed only (no memory safety). On the fix kernel the identical call returns 0 (lock accepted with POSIX semantics). The PoC's F_GETLK cross-check is blind to the caller's own lock (F_GETLK ignores locks held by the calling process), so the acceptance rv=0 is the decisive A/B signal.

Baseline vs fix kernel

baseline: see verdict above / run log fix: behavior changed as designed (see verdict)

Fix verification

fixed
baseline reproduced→ patch + rebuild →patched clean

A/B: baseline EINVAL; fix kernel accepts per POSIX [l_start+l_len, l_start-1] semantics.

↓ fix.diffDragonFly dfbsd 6.5-DEVELOPMENT #1: Mon Oct 26 01:57:23 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64 (h41 fix kernel, all 41 hardening fixes applied)

Confirmed kernel references

Detail

Evidence (decisive lines)

run.log baseline: rv=-1 errno=22; fix kernel: rv=0 errno=0

PoC changes

F_GETLK self-check documented as blind to own-process locks

Verified recommended fix

Convert negative l_len to [start+l_len, start-1] per POSIX instead of EINVAL

Verdict

Mechanically reproduced on the stock guest: fcntl(F_SETLK, l_start=10, l_len=-5) returns -1/EINVAL (kern_lockf.c:230-231 rejects negative lengths outright), while POSIX.1-2008 (and FreeBSD/Linux) interpret l_len<0 as the byte range [l_start+l_len, l_start-1] = [5,9]; portable backward-lock applications fail closed on DragonFly. Fail-closed only (no memory safety). On the fix kernel the identical call returns 0 (lock accepted with POSIX semantics). The PoCs F_GETLK cross-check is blind to the callers own lock (F_GETLK ignores locks held by the calling process), so the acceptance rv=0 is the decisive A/B signal.