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

drm_gem_create_mmap_offset_size commits the offset-hash + unr publication before drm_vma_offset_add() is known to succeed β€” failure leaves on_map=true, a leaked idx/hash slot, and a retry that reports success with no vma offset

Summary

DF-specific publication state (unr idx + legacy hash item + on_map=true) is finalized before the Linux-side drm_vma_offset_add result is checked; on failure the publication stays committed (self-healing leak until object release) and any retry early-returns success at :573 while vma_node has no offset. No crash risk (remove-on-never-added is a documented no-op). Unwind-on-failure fix, compile-validated.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-4460 Β· 10 files
FileTypeDescriptionSize
README.md β€” 635 B ↓ raw
VERDICT.md β€” 2.9 KB ↓ raw
run.sh β€” 170 B view raw
build.sh β€” 143 B view raw
run.log β€” 1.3 KB view raw
build.log β€” 3.4 KB view raw
fix.diff β€” 737 B view raw
manifest.json β€” 1.1 KB view raw
verdict.json β€” 2.9 KB view raw
env.txt β€” 630 B view raw

DF-4460 β€” drm_gem_create_mmap_offset_size commits hash+unr publication before drm_vma_offset_add is known to succeed; failure leaves on_map=true and a retry returns success with no vma offset

Verify (static)

Build (fix compile-validation)

sh build.sh (inside guest, fixed drm_gem.c staged) β€” drm.ko rebuilds -Werror clean, RC=0 (build.log).

Files

  • VERDICT.md β€” ordering analysis, retry hole, impact ceiling
  • run.sh + run.log β€” static proof
  • build.sh + build.log β€” patched module build
  • fix.diff β€” unwind publication on drm_vma_offset_add failure
VERDICT.md
↓ download raw

DF-4460 β€” PoC Verification Verdict

File: sys/dev/drm/drm_gem.c (pass 2), drm_gem_create_mmap_offset_size() at drm_gem.c:566-588. Guest: DragonFly 6.5-DEVELOPMENT (X86_64_GENERIC), cc 8.3.

Verdict: INCONCLUSIVE at runtime (code defect Certain; hardening/Info)

Root cause

576:    obj->map_list.key = alloc_unr(mm->idxunr);
577:    ret = drm_ht_insert_item(&mm->offset_hash, &obj->map_list);
578:    if (ret != 0) { ... free_unr ... return ret; }   /* balanced */
583:    obj->on_map = true;
585:    return drm_vma_offset_add(dev->vma_offset_manager, &obj->vma_node,
586:                  size / PAGE_SIZE);

The DragonFly-side publication (unr idx + legacy offset hash) is committed at 576-583 before the Linux-side drm_vma_offset_add() result is known. When drm_vma_offset_add() fails (drm_mm insert failure β€” ENOMEM/sp exhausted range after DRM_FILE_PAGE_OFFSET_SIZE is carved out, drm_vma_manager.c:200-215), the function returns the error but leaves:

  • obj->on_map = true and the hash item + unr idx committed, and
  • the early-return retry hole: a subsequent drm_gem_create_mmap_offset[_size]() sees obj->on_map (573-574) and returns 0 β€” reporting success while obj->vma_node still has no offset. On the Linux ->mmap path drm_vma_node_offset_addr() would then return 0 and callers hand userspace a bogus 0 offset; on the DragonFly d_mmap_single path the object is still findable via the legacy hash (the DF offset scheme at i915_gem.c:2647-2649 uses map_list.key, not vma_node), so the user-visible effect is limited to the leaked idx+hash slot held until object release (drm_gem_free_mmap_offset, 529-546, self-heals via the same on_map flag) and the inconsistent state.

Severity: Info (resource lifetime inconsistency, self-healing leak of a 22-bit unr idx + hash bucket per failure; no demonstrated memory safety impact).

Reproduction

Trigger = drm_vma_offset_add() failure = drm_mm range exhaustion / ENOMEM inside GEM offset creation β€” not injectable on the QEMU guest (no drm hardware; no fault-injection knob). Static proof: run.sh / run.log prints the committed-before-checked ordering in-tree.

Fix validation

fix.diff unwinds the publication (drm_ht_remove_item + free_unr + on_map=false) when drm_vma_offset_add fails. Compile-validated on the guest: drm.ko rebuilt with the patch, -Werror, RC=0 (build.log; combined with sibling DF-4458/DF-4459 hunks). git apply --check passes against the pristine tree. fix_status=not_testable (trigger not injectable), compile-validated.

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

not_testable
baseline no→ patch + rebuild →patched clean

fix.diff (git apply --check OK) unwinds the publication on drm_vma_offset_add failure so retries start clean. Compile-validated on the guest: drm.ko rebuilt with the fix, -Werror, RC=0 (build.log). Behavioral runs impossible: the trigger (drm_mm insert failure) is not injectable on this guest (fix_status not_testable, compile-validated).

fix.diff; build.log (patched drm.ko full -Werror compile+link, DRM_BUILD_RC=0); VERDICT.md 'Fix validation'
↓ fix.diffper-fix-DF-4460

Confirmed kernel references

Detail

Evidence (decisive lines)

run.log (ordering: 583 before 585-586, retry early-return 573-574, release-only cleanup at 543); build.log (drm.ko rebuilt with fix, -Werror, DRM_BUILD_RC=0); fix.diff (git apply --check OK)

PoC changes

no seed; static-verification script authored for the pack

Verified recommended fix

Unwind the hash/unr publication (drm_ht_remove_item + free_unr + on_map=false) when drm_vma_offset_add() fails

Verdict

Code defect certain (Info-grade impact): drm_gem_create_mmap_offset_size (drm_gem.c:566-588) allocates the unr idx and inserts the legacy offset-hash item, then sets obj->on_map = true (583) BEFORE learning whether drm_vma_offset_add() (585, drm_vma_manager.c:200-215) succeeded. On failure the function returns the error but leaves the publication committed: the 22-bit idx and hash slot leak until object release (drm_gem_free_mmap_offset 529-546 self-heals via the same on_map flag), obj state is inconsistent, and any retry hits the early-return at 573-574 and reports success while obj->vma_node still has no offset (drm_vma_node_offset_addr would yield 0 for Linux-path callers). On DragonFly the user-visible offset scheme uses map_list.key (i915_gem.c:2647-2649), so the practical impact is the leaked publication + inconsistent state β€” no demonstrated memory-safety impact. Trigger (drm_mm insert failure / range exhaustion) is not injectable on the QEMU guest; defect proven statically (run.log).