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

prop_array_add_uint{8 16 32 64} stores unsigned values via signed creator breaking roundtrip semantics

Summary

TEMPLATE(size) macro generates prop_array_add_uint{8 16 32 64} using prop_number_create_integer((int64_t)val) at line 187 copy-paste from add_int sibling at line 171. Parallel prop_array_set_uint{8 16 32 64} at line 155 correctly uses prop_number_create_unsigned_integer((uint64_t)val) and so does prop_dictionary_set_uint in sibling prop_dictionary_util.c:167. Any unsigned value whose high bit relative to width is set stored as SIGNED prop_number and matching prop_array_get_uint##size rejects it due to prop_number_unsigned(num)==false && prop_number_integer_value(num)<0 guard. Pure logic/correctness bug no demonstrated security impact. TEMPLATE instantiated 4 times (lines 196-199) all four widths affected. Only in-tree kernel callers dm_ioctl.c:658 (add_uint64 with 32-bit udev_t high bit never set) dm_target.c:246 (add_uint32 small version always <INT32_MAX). Latent risk future caller storing attacker-influenced unsigned via add_uintN reading back via get_uintN gets false return or uses uninitialized stack variable. [VERIFIED pass2: runtime reproduced - top-bit-set values stored by signed creator unreadable by get_uintN at all four widths; unsigned-creator fix]

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-2240 Β· 10 files
FileTypeDescriptionSize
VERDICT.md β€” 1.7 KB ↓ raw
README.md β€” 258 B ↓ raw
build.sh β€” 112 B view raw
run.sh β€” 83 B view raw
env.txt β€” 314 B view raw
manifest.json β€” 647 B view raw
verdict.json β€” 2.0 KB view raw
fix.diff β€” 361 B view raw
prop_uint_roundtrip.c β€” 1.3 KB view raw
run.log β€” 403 B view raw

DF-2240 evidence pack

  • finding: sys/libprop/prop_array_util.c (186-188)
  • verdict: reproduced (certain) - see VERDICT.md
  • runtime PoC: prop_uint_roundtrip.c
  • reproduce: build.sh / run.sh ; expected output in run.log
  • fix.diff: present, git-apply-able
VERDICT.md
↓ download raw

DF-2240 - Verification Verdict

Status: reproduced Impact: none (hardening) Confidence: certain

Mechanism

The prop_array_add_uint{8,16,32,64} template instantiates prop_number_create_integer((int64_t)val) - the SIGNED creator - while prop_array_set_uint* correctly uses prop_number_create_unsigned_integer. A value with its top data bit set is stored as a signed prop_number one width class larger (prop_number_size returns 64 for 2^31, 32 for 2^15, 16 for 2^7), so prop_array_get_uintN (which rejects size>N) cannot read it back.

Verdict

REPRODUCED on the stock guest with the userland libprop (same sources as sys/libprop): add_uint8(0xff), add_uint16(0xffff), add_uint32(0xffffffff), add_uint64(0xffffffffffffffff) all store, but every matching get_uintN returns false; control values with the top bit clear round-trip fine at all four widths. Signed round-trip semantics broken for exactly the values the unsigned API exists for.

Threat / reachability

API correctness: kernel/userland consumers using add_uintN + get_uintN silently lose values >= 2^(N-1); no direct memory-safety impact.

Privilege escalation assessment

n/a (API semantics)

Fix

Use prop_number_create_unsigned_integer((uint64_t)val) in the add_uint template, matching set_uint.

Kernel references

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

fix.diff compile-validated in the batch combined kernel build (all 37 diffs of this batch applied to in-guest /usr/src; make -j6 nativekernel KERNCONF=X86_64_GENERIC, see batch build log referenced in VERDICT.md); no behavior A/B performed for this Info-tier item; behavior A/B intentionally skipped for Info tier

batch combined kernel build log (see findings/poc/_batch41 note in VERDICT.md)
↓ fix.diffper-fix-DF-2240

Confirmed kernel references

Detail

Exploit chain

no escalation path: API roundtrip semantics only

Evidence (decisive lines)

run.log: control 0x7f/0x7fff/0x7fffffff/0x7fffffffffffffff -> get true; boundary 0xff/0xffff/0xffffffff/0xffffffffffffffff -> get FALSE at all four widths

PoC changes

Added control values (top bit clear) so the boundary breakage is contrasted in the same table.

Verified recommended fix

Use prop_number_create_unsigned_integer((uint64_t)val) in the add_uint template, matching set_uint.

Verdict

REPRODUCED on the stock guest with the userland libprop (same sources as sys/libprop): add_uint8(0xff), add_uint16(0xffff), add_uint32(0xffffffff), add_uint64(0xffffffffffffffff) all store, but every matching get_uintN returns false; control values with the top bit clear round-trip fine at all four widths. Signed round-trip semantics broken for exactly the values the unsigned API exists for.