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

pfr_add_tables() allocates the ktable and bumps rs->tables BEFORE the duplicate check of a DIOCRADDTABLES batch: duplicate element leaks the table and permanently desynchronizes the ruleset count (DIOCRGETTABLES -> ENOTTY until reboot)

Summary

When one DIOCRADDTABLES batch contains the same table twice, pfr_create_ktable() (kmalloc ktable + 2 radix heads + rs->tables++) has already run for the second element when the addq duplicate scan hits 'goto _skip'; the fresh table is neither inserted nor freed - on both the live and DUMMY paths. rs->tables drifts +1 per leak while the RB tree does not, so every later pfr_get_tables() ends with n>0, prints 'pfr_get_tables: corruption detected (2).' and returns ENOTTY. VERIFIED: one ioctl pair leaks ~1.4-2.8KB (kernel complains 'malloc_uninit: 2816 bytes of pfrktable still allocated' at kldunload) and breaks 'pfctl -s Tables' for the ruleset until reboot - persistent admin-plane DoS plus repeatable unbounded leak. Root-only but 100% deterministic. Fix: duplicate scan before create (or unwind on skip).

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-3339 Β· 3 files
FileTypeDescriptionSize
duptable_leak.c trigger-source DIOCRADDTABLES with duplicate batch + DIOCRGETTABLES probe 2.8 KB view raw
run.log run-log full deterministic output incl. vmstat + dmesg 647 B view raw
verdict.json verdict machine verdict 2.3 KB view raw

Fix verification

not_testable
per-fix-DF-3339

Confirmed kernel references

Detail

Exploit chain

root sends one DIOCRADDTABLES containing [T,T] -> ktable leak + rs->tables drift -> all table listing ioctls for the ruleset return ENOTTY until reboot (admin-plane DoS) + unbounded repeatable leak.

Evidence (decisive lines)

['run.log: [1] nadd=1, [3] DIOCRGETTABLES rc=-1 errno=25, dmesg corruption line, vmstat 3 allocs', 'kernel source pf_table.c:1170-1182 (create-before-dup-check)']

PoC changes

added #include for pfvar.h's IFNAMSIZ; nothing else - first run reproduced.

Verified recommended fix

In pfr_add_tables(), scan addq for the duplicate before pfr_create_ktable() (or destroy the fresh table and decrement rs->tables on the _skip path).

Verdict

REPRODUCED (deterministic): pfr_add_tables() allocates the ktable and increments rs->tables BEFORE scanning addq for duplicates, then goto _skip discards the pointer on a duplicate batch element. Two identical pfr_table entries in one DIOCRADDTABLES leak ~1.4KB (vmstat: 3 pfrktable allocs for 1 usable table; kldunload: 'malloc_uninit: 2816 bytes of pfrktable still allocated') and permanently drift pf_main_ruleset.tables vs the RB tree, so every subsequent DIOCRGETTABLES prints 'pfr_get_tables: corruption detected (2).' and returns ENOTTY - pfctl -s Tables fails until reboot. Root-only, deterministic; Low severity.