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)
PoC verification
Evidence pack
findings/poc/DF-2240 Β· 10 files| File | Type | Description | Size | |
|---|---|---|---|---|
| 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
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_testablefix.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)
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.
No comments yet.