[GH-ISSUE #3345] [Bug] Incomplete Objective-C property attributes produce established atomic and read-write flags #1213

Closed
opened 2026-10-16 21:02:53 +00:00 by bsakaguchi · 1 comment
Owner

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

Before submitting

  • I searched the open and closed issues for a related report.
  • I removed credentials, tokens, private paths, and sensitive target data.

Area

Native analysis or decompilation

Observed behavior

inspect-native-dispatch-metadata reports atomicity: "atomic" and is_readonly: false for a property whose encoded type is an unfinished quoted class name. The raw attribute is retained as T@"NSString, but the decoder treats it as complete enough to infer flags from the absence of N and R. This contradicts the documented unknown semantics for incomplete attribute metadata.

The CLI exits 0. Both property records retain their names and share the incomplete attribute string. The objc_properties_categories coverage facet is complete, with no diagnostic about the unfinished quoted type. The inline CLI reproduction was executed locally with the existing compiled runtime and installed locked dependencies.

Reproduction

  1. Use a clean checkout at 8dde6370d0809ad04cda3342d4e6f290f48aa063 on macOS and prepare the runtime from the repository root:
npm ci
npm run build:cached
  1. From that checkout root, create the following local files in a disposable directory, keeping client configuration isolated:
rea_entry="$PWD/scripts/rea.mjs"
work=$(mktemp -d)
mkdir "$work/home"
cd "$work"
node --input-type=module <<'JS'
import { writeFileSync } from 'node:fs';
const b = Buffer.alloc(4096), base = 0x100000000n;
const u32 = (at, n) => b.writeUInt32LE(n, at);
const ptr = (at, n) => b.writeBigUInt64LE(base + BigInt(n), at);
u32(0, 0xfeedfacf); u32(4, 0x0100000c); u32(12, 2); u32(16, 1); u32(20, 152);
u32(32, 0x19); u32(36, 152); b.write('__DATA', 40); ptr(56, 0);
b.writeBigUInt64LE(4096n, 64); b.writeBigUInt64LE(4096n, 80);
u32(88, 5); u32(92, 5); u32(96, 1);
b.write('__objc_classlist', 104); b.write('__DATA', 120); ptr(136, 0x180);
b.writeBigUInt64LE(16n, 144); u32(152, 0x180);
for (const [slot, cls, ro, name] of [[0x180, 0x200, 0x300, 0x400], [0x188, 0x700, 0x800, 0x900]]) {
  ptr(slot, cls); ptr(cls + 32, ro); u32(ro, 2); u32(ro + 8, 16); ptr(ro + 24, name);
}
b.write('Fixture\0', 0x400); b.write('Sibling\0', 0x900); ptr(0x340, 0x500);
u32(0x500, 16); u32(0x504, 2);
ptr(0x508, 0x600); ptr(0x510, 0x620); ptr(0x518, 0x640); ptr(0x520, 0x620);
b.write('first\0', 0x600); b.write('Tq,N\0', 0x620); b.write('second\0', 0x640);
writeFileSync('valid.bin', b);
ptr(0x518, 0x2000); writeFileSync('unmapped.bin', b);
ptr(0x518, 0x640); b[0x600] = 0; writeFileSync('empty.bin', b);
JS
  1. Create a variation containing a NUL-terminated attribute string whose quoted Objective-C class name has no closing quote, then inspect it:
node --input-type=module <<'JS'
import { readFileSync, writeFileSync } from 'node:fs';
const bytes = readFileSync('valid.bin');
bytes.fill(0, 0x620, 0x640);
bytes.write('T@"NSString\0', 0x620);
writeFileSync('incomplete-attributes.bin', bytes);
JS
HOME="$work/home" node "$rea_entry" inspect-native-dispatch-metadata "$work/incomplete-attributes.bin" --format json

Both property records point to the same attribute string. Only that string changes; the class records and property names remain valid. The string has a valid file mapping and terminator, so this exercises incomplete type metadata rather than an unreadable pointer. The fixture generator also creates the unmapped-name and empty-name variants, but the incomplete-attribute command above selects only incomplete-attributes.bin.

Expected behavior

Preserve the raw encoded attribute and the decoded property names. The incomplete type declaration must leave atomicity and unestablished is_readonly null. Absence of a flag in incomplete metadata does not establish the Objective-C default. A complete typed string without N remains atomic, and an explicit N still establishes nonatomic behavior.

docs/native-investigation.md:376-381 states that only a complete typed attribute string without N establishes atomicity, and that missing or incomplete metadata leaves atomicity and unestablished read-only values null.

Impact and scope

Impact level: P2, supported. Incomplete property metadata becomes established analysis facts that can mislead ABI or behavior reconstruction. The defect is bounded to incomplete attributes and does not change the selected executable.

A workaround is available. The executed output retains the raw T attribute and unfinished quoted type, so callers can inspect those bytes and decline to rely on the inferred flags. This preserves the source evidence, though the normalized flags remain incorrect.

REA version

6.4.0 (main at 8dde6370d0)

Runtime and platform

Node.js v26.10.0; macOS 27.0.1 arm64. The reproduction used the existing compiled runtime and installed locked dependencies.

Target and artifact context

A synthetic 4096-byte, 64-bit little-endian arm64 Mach-O executable containing valid Fixture and Sibling class records. Fixture has named properties first and second, both pointing to the same attribute string. The unchanged valid.bin contains complete Tq,N attributes; incomplete-attributes.bin replaces only that string with the NUL-terminated T@"NSString encoding, whose class-name quote never closes.

Interface

CLI

Earliest failure stage

Native analysis or reconstruction

Relevant output or evidence

Each property contains the following fields, and the command exits 0:

{
  "type_encoding": "@\"NSString",
  "attributes": [{"name":"T","value":"@\"NSString"}],
  "atomicity": "atomic",
  "is_readonly": false
}

The objc_properties_categories coverage facet is complete, with no diagnostic about the unfinished quoted type. The raw type shows that its quoted class-name declaration is incomplete, while the normalized flags assert atomic and read-write behavior.

Typed outcome or recovery expected

Inspect the incomplete variation and require null atomicity and null unestablished read-only status while preserving its raw attribute and valid class/property identities. Compare with the unchanged control, whose complete Tq,N attributes must still report nonatomic and read-write properties:

HOME="$work/home" node "$rea_entry" inspect-native-dispatch-metadata "$work/valid.bin" --format json

Also retain the documented atomic default for a complete typed attribute string without N. An explicit N must still establish nonatomic behavior.

Analysis or suspected cause

Source-derived reasoning: src/native/AppleObjcProperties.ts:23-30 accepts every nonempty single T token as a type encoding. src/native/AppleObjcProperties.ts:40-49 derives atomicity and read-only flags from that value. The quoted-class-name splitter at src/native/AppleObjcProperties.ts:55-68 does not report an unfinished quote state. These paths explain how a readable, NUL-terminated but incomplete type declaration can produce established defaults.

Additional context

  • Related #484 defines provider-neutral dispatch metadata with unknown and unavailable outcomes; it does not report incomplete property attributes producing established flags.
  • Related #2610 concerns an unmapped protocol-list slot discarding valid class metadata; this issue concerns incorrect flags on retained property records.
  • Related #2611 concerns empty class names rejecting sibling metadata; this issue uses valid class and property names and an incomplete attribute type.
  • Related #3186 concerns an ivar alignment sentinel returning null; this issue concerns unknown property flags becoming established values.
Originally created by @coygeek on GitHub (Oct 14, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/3345 ### Before submitting - [x] I searched the open and closed issues for a related report. - [x] I removed credentials, tokens, private paths, and sensitive target data. ### Area Native analysis or decompilation ### Observed behavior `inspect-native-dispatch-metadata` reports `atomicity: "atomic"` and `is_readonly: false` for a property whose encoded type is an unfinished quoted class name. The raw attribute is retained as `T@"NSString`, but the decoder treats it as complete enough to infer flags from the absence of `N` and `R`. This contradicts the documented unknown semantics for incomplete attribute metadata. The CLI exits 0. Both property records retain their names and share the incomplete attribute string. The `objc_properties_categories` coverage facet is `complete`, with no diagnostic about the unfinished quoted type. The inline CLI reproduction was executed locally with the existing compiled runtime and installed locked dependencies. ### Reproduction 1. Use a clean checkout at `8dde6370d0809ad04cda3342d4e6f290f48aa063` on macOS and prepare the runtime from the repository root: ```sh npm ci npm run build:cached ``` 2. From that checkout root, create the following local files in a disposable directory, keeping client configuration isolated: ```sh rea_entry="$PWD/scripts/rea.mjs" work=$(mktemp -d) mkdir "$work/home" cd "$work" node --input-type=module <<'JS' import { writeFileSync } from 'node:fs'; const b = Buffer.alloc(4096), base = 0x100000000n; const u32 = (at, n) => b.writeUInt32LE(n, at); const ptr = (at, n) => b.writeBigUInt64LE(base + BigInt(n), at); u32(0, 0xfeedfacf); u32(4, 0x0100000c); u32(12, 2); u32(16, 1); u32(20, 152); u32(32, 0x19); u32(36, 152); b.write('__DATA', 40); ptr(56, 0); b.writeBigUInt64LE(4096n, 64); b.writeBigUInt64LE(4096n, 80); u32(88, 5); u32(92, 5); u32(96, 1); b.write('__objc_classlist', 104); b.write('__DATA', 120); ptr(136, 0x180); b.writeBigUInt64LE(16n, 144); u32(152, 0x180); for (const [slot, cls, ro, name] of [[0x180, 0x200, 0x300, 0x400], [0x188, 0x700, 0x800, 0x900]]) { ptr(slot, cls); ptr(cls + 32, ro); u32(ro, 2); u32(ro + 8, 16); ptr(ro + 24, name); } b.write('Fixture\0', 0x400); b.write('Sibling\0', 0x900); ptr(0x340, 0x500); u32(0x500, 16); u32(0x504, 2); ptr(0x508, 0x600); ptr(0x510, 0x620); ptr(0x518, 0x640); ptr(0x520, 0x620); b.write('first\0', 0x600); b.write('Tq,N\0', 0x620); b.write('second\0', 0x640); writeFileSync('valid.bin', b); ptr(0x518, 0x2000); writeFileSync('unmapped.bin', b); ptr(0x518, 0x640); b[0x600] = 0; writeFileSync('empty.bin', b); JS ``` 3. Create a variation containing a NUL-terminated attribute string whose quoted Objective-C class name has no closing quote, then inspect it: ```sh node --input-type=module <<'JS' import { readFileSync, writeFileSync } from 'node:fs'; const bytes = readFileSync('valid.bin'); bytes.fill(0, 0x620, 0x640); bytes.write('T@"NSString\0', 0x620); writeFileSync('incomplete-attributes.bin', bytes); JS HOME="$work/home" node "$rea_entry" inspect-native-dispatch-metadata "$work/incomplete-attributes.bin" --format json ``` Both property records point to the same attribute string. Only that string changes; the class records and property names remain valid. The string has a valid file mapping and terminator, so this exercises incomplete type metadata rather than an unreadable pointer. The fixture generator also creates the unmapped-name and empty-name variants, but the incomplete-attribute command above selects only `incomplete-attributes.bin`. ### Expected behavior Preserve the raw encoded attribute and the decoded property names. The incomplete type declaration must leave `atomicity` and unestablished `is_readonly` null. Absence of a flag in incomplete metadata does not establish the Objective-C default. A complete typed string without `N` remains atomic, and an explicit `N` still establishes nonatomic behavior. `docs/native-investigation.md:376-381` states that only a complete typed attribute string without `N` establishes atomicity, and that missing or incomplete metadata leaves atomicity and unestablished read-only values null. ### Impact and scope Impact level: P2, supported. Incomplete property metadata becomes established analysis facts that can mislead ABI or behavior reconstruction. The defect is bounded to incomplete attributes and does not change the selected executable. A workaround is available. The executed output retains the raw `T` attribute and unfinished quoted type, so callers can inspect those bytes and decline to rely on the inferred flags. This preserves the source evidence, though the normalized flags remain incorrect. ### REA version 6.4.0 (main at 8dde6370d0809ad04cda3342d4e6f290f48aa063) ### Runtime and platform Node.js v26.10.0; macOS 27.0.1 arm64. The reproduction used the existing compiled runtime and installed locked dependencies. ### Target and artifact context A synthetic 4096-byte, 64-bit little-endian arm64 Mach-O executable containing valid `Fixture` and `Sibling` class records. `Fixture` has named properties `first` and `second`, both pointing to the same attribute string. The unchanged `valid.bin` contains complete `Tq,N` attributes; `incomplete-attributes.bin` replaces only that string with the NUL-terminated `T@"NSString` encoding, whose class-name quote never closes. ### Interface CLI ### Earliest failure stage Native analysis or reconstruction ### Relevant output or evidence Each property contains the following fields, and the command exits 0: ```text { "type_encoding": "@\"NSString", "attributes": [{"name":"T","value":"@\"NSString"}], "atomicity": "atomic", "is_readonly": false } ``` The `objc_properties_categories` coverage facet is `complete`, with no diagnostic about the unfinished quoted type. The raw type shows that its quoted class-name declaration is incomplete, while the normalized flags assert atomic and read-write behavior. ### Typed outcome or recovery expected Inspect the incomplete variation and require null atomicity and null unestablished read-only status while preserving its raw attribute and valid class/property identities. Compare with the unchanged control, whose complete `Tq,N` attributes must still report nonatomic and read-write properties: ```sh HOME="$work/home" node "$rea_entry" inspect-native-dispatch-metadata "$work/valid.bin" --format json ``` Also retain the documented atomic default for a complete typed attribute string without `N`. An explicit `N` must still establish nonatomic behavior. ### Analysis or suspected cause Source-derived reasoning: `src/native/AppleObjcProperties.ts:23-30` accepts every nonempty single `T` token as a type encoding. `src/native/AppleObjcProperties.ts:40-49` derives atomicity and read-only flags from that value. The quoted-class-name splitter at `src/native/AppleObjcProperties.ts:55-68` does not report an unfinished quote state. These paths explain how a readable, NUL-terminated but incomplete type declaration can produce established defaults. ### Additional context - Related [#484](https://github.com/morluto/rea/issues/484) defines provider-neutral dispatch metadata with unknown and unavailable outcomes; it does not report incomplete property attributes producing established flags. - Related [#2610](https://github.com/morluto/rea/issues/2610) concerns an unmapped protocol-list slot discarding valid class metadata; this issue concerns incorrect flags on retained property records. - Related [#2611](https://github.com/morluto/rea/issues/2611) concerns empty class names rejecting sibling metadata; this issue uses valid class and property names and an incomplete attribute type. - Related [#3186](https://github.com/morluto/rea/issues/3186) concerns an ivar alignment sentinel returning null; this issue concerns unknown property flags becoming established values.
Author
Owner

@z2Ace0107 commented on GitHub (Oct 15, 2026):

I'd like to work on this issue. I reproduced the incomplete quoted-type case by running the production attribute parser in isolation on Windows with Node 24.18.0; the complete-type and explicit-attribute controls behave as expected.

I'll preserve raw attributes and property names, keep unestablished flags unknown, and add regression coverage. Public CLI/MCP verification is still pending.

<!-- gh-comment-id:6181775767 --> @z2Ace0107 commented on GitHub (Oct 15, 2026): I'd like to work on this issue. I reproduced the incomplete quoted-type case by running the production attribute parser in isolation on Windows with Node 24.18.0; the complete-type and explicit-attribute controls behave as expected. I'll preserve raw attributes and property names, keep unestablished flags unknown, and add regression coverage. Public CLI/MCP verification is still pending.
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#1213
No description provided.