[GH-ISSUE #4919] [Bug] Direct binary plist projection discards known infinity signs and NaN identity #3052

Closed
opened 2026-10-31 00:35:35 +00:00 by bsakaguchi · 1 comment
Owner

Originally created by @morluto on GitHub (Oct 17, 2026).
Original GitHub issue: https://github.com/morluto/rea/issues/4919

Before submitting

  • Searched open and closed reports for binary infinity/non-finite values; #4906 explicitly excludes direct binary doubles. #4295 fixed node classification, not value identity.
  • Only self-authored inert data is used.

Area

Evidence, artifacts, or reconstruction

Observed behavior

A 217-byte binary NSKeyedArchiver archive with three scalar objects, positive infinity, negative infinity and NaN, returns three observed scalars with identical { "$plist_type": "real", "value": null }. The producer already distinguishes all three; the shared projection discards that identity.

Reproduction

Executed once through the built public CLI and once directly through the decoder on macOS 26.3 arm64 / Node 24.18.0, REA6.5, working source based on d205c528e with the reviewed #4906 XML fix.

Build inert bytes with locked plist@5.0.0:

const bytes = buildBinary({
 $archiver: "NSKeyedArchiver", $version: 100000,
 $objects: ["$null", Infinity, -Infinity, NaN],
 $top: { positive: { "CF$UID": 1 }, negative: { "CF$UID": 2 }, nan: { "CF$UID": 3 } }
});

Write the buffer and run node scripts/rea.mjs inspect-keyed-archive <path> --json. Exit0; objects1–3 are scalar/observed with value:null; original bytes unchanged. Direct plist.parseBinary(bytes) returns respectively Infinity,-Infinity,NaN.

Expected behavior

Preserve observed IEEE real kind/sign in JSON-safe typed values. Do not invent original XML spelling for binary data. Retain malformed XML/unreadable-number uncertainty separately. NaN payload-bit preservation is a separate question, not required to distinguish NaN from signed infinity.

Impact and scope

Deterministic on the authored fixture. Affects directly decoded binary keyed archives; Interface Builder shares the projector (source-derived scope). Real-world frequency: NOT_ENOUGH_INFO, not measured. Native inspect_plist using plutil XML conversion can recover canonical values after #4906 and does not establish direct binary decoding correctness.

Relevant output or evidence

parse-binary.js in locked plist uses DataView.getFloat32/getFloat64. src/domain/apple/plistValue.ts replaces every raw non-finite Number with value:null. The fixture producer and boundary observation establish distinct values before this projection.

Typed outcome or recovery expected

Distinct JSON-safe positive/negative infinity and NaN scalar values, with source provenance and honest limitations.

Analysis

Correct the producing representation so binary real identity is separate from XML parsing uncertainty; adding a spelling field to a binary observation would be misleading. Preserve scalar-vs-dictionary distinction and explicit unknowns from malformed XML. XML strict-grammar policy remains #4852. No target execution, injection or capture acquisition involved.

Originally created by @morluto on GitHub (Oct 17, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/4919 ### Before submitting - [x] Searched open and closed reports for binary infinity/non-finite values; #4906 explicitly excludes direct binary doubles. #4295 fixed node classification, not value identity. - [x] Only self-authored inert data is used. ### Area Evidence, artifacts, or reconstruction ### Observed behavior A 217-byte binary NSKeyedArchiver archive with three scalar objects, positive infinity, negative infinity and NaN, returns three observed scalars with identical `{ "$plist_type": "real", "value": null }`. The producer already distinguishes all three; the shared projection discards that identity. ### Reproduction Executed once through the built public CLI and once directly through the decoder on macOS 26.3 arm64 / Node 24.18.0, REA6.5, working source based on d205c528e with the reviewed #4906 XML fix. Build inert bytes with locked `plist@5.0.0`: ```js const bytes = buildBinary({ $archiver: "NSKeyedArchiver", $version: 100000, $objects: ["$null", Infinity, -Infinity, NaN], $top: { positive: { "CF$UID": 1 }, negative: { "CF$UID": 2 }, nan: { "CF$UID": 3 } } }); ``` Write the buffer and run `node scripts/rea.mjs inspect-keyed-archive <path> --json`. Exit0; objects1–3 are scalar/observed with value:null; original bytes unchanged. Direct `plist.parseBinary(bytes)` returns respectively Infinity,-Infinity,NaN. ### Expected behavior Preserve observed IEEE real kind/sign in JSON-safe typed values. Do not invent original XML spelling for binary data. Retain malformed XML/unreadable-number uncertainty separately. NaN payload-bit preservation is a separate question, not required to distinguish NaN from signed infinity. ### Impact and scope Deterministic on the authored fixture. Affects directly decoded binary keyed archives; Interface Builder shares the projector (source-derived scope). Real-world frequency: NOT_ENOUGH_INFO, not measured. Native inspect_plist using plutil XML conversion can recover canonical values after #4906 and does not establish direct binary decoding correctness. ### Relevant output or evidence `parse-binary.js` in locked plist uses DataView.getFloat32/getFloat64. `src/domain/apple/plistValue.ts` replaces every raw non-finite Number with value:null. The fixture producer and boundary observation establish distinct values before this projection. ### Typed outcome or recovery expected Distinct JSON-safe positive/negative infinity and NaN scalar values, with source provenance and honest limitations. ### Analysis Correct the producing representation so binary real identity is separate from XML parsing uncertainty; adding a spelling field to a binary observation would be misleading. Preserve scalar-vs-dictionary distinction and explicit unknowns from malformed XML. XML strict-grammar policy remains #4852. No target execution, injection or capture acquisition involved.
bsakaguchi 2026-10-31 00:35:35 +00:00
  • closed this issue
  • added the
    triage
    bug
    labels
Author
Owner

@morluto commented on GitHub (Oct 17, 2026):

Fixed on main in f2b7bbb31.

Root cause: shared projection treated all raw non-finite Numbers as unresolved, because XML parsing also used NaN/infinity for failed decoding and overflow. The binary decoder already retained distinct IEEE values.

Structural fix: XML numeric decoding now produces an explicit owner-defined UnknownPlistReal for unresolved numeric fallbacks, including integer parse failure/overflow. Recognized CFPropertyList literals become actual numeric values. The shared projection preserves known NaN versus signed infinity using canonical JSON labels; XML unknowns retain value:null and an honest numeric-identity limitation. The superseded recognized-value class and spelling alias were removed. No extra binary walk or mode flag was added. Key filtering and generated-wrapper identity preserve scalar-versus-dictionary distinctions.

Executed on macOS26.3 arm64 / Node24.18:

  • Public CLI/MCP regression baseline: 2 failures,5 controls passed.
  • Final stable focused run: 439 passed across25files, including public binary CLI/MCP, XML uncertainty, numeric precision, provider/lifecycle and native plutil/signature controls.
  • Independently authored binary float32/float64 bit patterns test positive/negative infinity, positive/negative NaN payloads, signed zero, zero and finite control, without a plist-builder oracle. XML malformed/overflow controls remain unknown. Input bytes remain unchanged.
  • check:fast, docs:check5, discovery1563 files exactly once, normal commit/pre-push hooks passed. Hook simplified one test array expression;55 unit checks passed again on the committed head. Two read-only source reviews found no blocker.
  • One intermediate validation run loaded mismatched exports during owner relocation; its9 failures were retained separately, and the complete stable run above passed after relocation.

Research used DeepWiki for source navigation, checked primary CFBinaryPList decoding and the locked plist decoder, and validated authored IEEE payloads. Source: https://github.com/swiftlang/swift-corelibs-foundation/blob/main/Sources/CoreFoundation/CFBinaryPList.c#L1184

Limits: JSON labels do not claim original XML spelling, NaN sign or payload-bit preservation; original archive bytes remain authoritative for those details. No Linux/Windows or real Interface Builder engine qualification. #4852 malformed XML compatibility policy remains open; this change preserves existing explicit-unknown outcomes rather than deciding that policy. No performance/full-codebase completion claim. Heavy CPU test/profiling remain stopped.

<!-- gh-comment-id:6243420576 --> @morluto commented on GitHub (Oct 17, 2026): Fixed on main in f2b7bbb31. Root cause: shared projection treated all raw non-finite Numbers as unresolved, because XML parsing also used NaN/infinity for failed decoding and overflow. The binary decoder already retained distinct IEEE values. Structural fix: XML numeric decoding now produces an explicit owner-defined UnknownPlistReal for unresolved numeric fallbacks, including integer parse failure/overflow. Recognized CFPropertyList literals become actual numeric values. The shared projection preserves known NaN versus signed infinity using canonical JSON labels; XML unknowns retain value:null and an honest numeric-identity limitation. The superseded recognized-value class and spelling alias were removed. No extra binary walk or mode flag was added. Key filtering and generated-wrapper identity preserve scalar-versus-dictionary distinctions. Executed on macOS26.3 arm64 / Node24.18: - Public CLI/MCP regression baseline: 2 failures,5 controls passed. - Final stable focused run: 439 passed across25files, including public binary CLI/MCP, XML uncertainty, numeric precision, provider/lifecycle and native plutil/signature controls. - Independently authored binary float32/float64 bit patterns test positive/negative infinity, positive/negative NaN payloads, signed zero, zero and finite control, without a plist-builder oracle. XML malformed/overflow controls remain unknown. Input bytes remain unchanged. - check:fast, docs:check5, discovery1563 files exactly once, normal commit/pre-push hooks passed. Hook simplified one test array expression;55 unit checks passed again on the committed head. Two read-only source reviews found no blocker. - One intermediate validation run loaded mismatched exports during owner relocation; its9 failures were retained separately, and the complete stable run above passed after relocation. Research used DeepWiki for source navigation, checked primary CFBinaryPList decoding and the locked plist decoder, and validated authored IEEE payloads. Source: https://github.com/swiftlang/swift-corelibs-foundation/blob/main/Sources/CoreFoundation/CFBinaryPList.c#L1184 Limits: JSON labels do not claim original XML spelling, NaN sign or payload-bit preservation; original archive bytes remain authoritative for those details. No Linux/Windows or real Interface Builder engine qualification. #4852 malformed XML compatibility policy remains open; this change preserves existing explicit-unknown outcomes rather than deciding that policy. No performance/full-codebase completion claim. Heavy CPU test/profiling remain stopped.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
morluto/rea#3052
No description provided.