From d2a915ba32289e6c18f877957fe38f5af8e798e5 Mon Sep 17 00:00:00 2001 From: Kazuki Nakashima <65545348+lynnswap@users.noreply.github.com> Date: Wed, 26 Aug 2026 07:38:32 +0900 Subject: [PATCH 01/12] chore: track issue 83 remediation --- Docs/issue-remediation-progress.md | 39 ++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 Docs/issue-remediation-progress.md diff --git a/Docs/issue-remediation-progress.md b/Docs/issue-remediation-progress.md new file mode 100644 index 0000000..fac2b29 --- /dev/null +++ b/Docs/issue-remediation-progress.md @@ -0,0 +1,39 @@ +# Issue remediation progress + +Base: `main` at `101748f62fa27d6851b4284df7cafc721081fa5c` + +## Current issue: #83 + +Branch: `codex/issue-83-bounded-objc-metadata-reads` + +Goal: + +- Make variable-length file-backed Objective-C tables and loaded-image root/list + reads reject malformed external ranges without arithmetic overflow, unchecked + dereference, or raw-helper termination. +- Preserve readable sibling metadata and discovery order while emitting bounded + typed degradation through the existing diagnostic owner. + +Confirmed scope from the issue: + +- File-backed method, property, and ivar table reads. +- Loaded-image class, category, and protocol roots and member-list headers. +- Direct relative method/property list queries that bypass checked traversal. +- Deterministic 32/64-bit file and loaded-image boundary fixtures. + +Design gate pending: + +- Identify one file-table owner for offset conversion, count/stride arithmetic, + entry/byte budgets, complete-range validation, and decoding. +- Identify one loaded-image range owner that can prove a full layout before any + dereference without inventing process memory readability. +- Map legal empty, malformed table, unreadable entry, and programmer misuse to + distinct observable semantics. +- Confirm the additive dependency API needed by PrivateHeaderKit diagnostics and + direct-query parity before implementation begins. + +Required regression coverage: + +- Existing protocol-list behavior from #60. +- Existing relative-member behavior from #65. +- Existing fixed-field behavior from #79. From 1f5b8b6ff6304e5ed799e9b07a7a9fa07cba3dd7 Mon Sep 17 00:00:00 2001 From: Kazuki Nakashima <65545348+lynnswap@users.noreply.github.com> Date: Wed, 26 Aug 2026 07:54:10 +0900 Subject: [PATCH 02/12] docs: approve issue 83 design gate --- Docs/issue-remediation-progress.md | 106 ++++++++++++++++++++++++++--- 1 file changed, 97 insertions(+), 9 deletions(-) diff --git a/Docs/issue-remediation-progress.md b/Docs/issue-remediation-progress.md index fac2b29..0dc9459 100644 --- a/Docs/issue-remediation-progress.md +++ b/Docs/issue-remediation-progress.md @@ -21,19 +21,107 @@ Confirmed scope from the issue: - Direct relative method/property list queries that bypass checked traversal. - Deterministic 32/64-bit file and loaded-image boundary fixtures. -Design gate pending: +Dependency bases: -- Identify one file-table owner for offset conversion, count/stride arithmetic, - entry/byte budgets, complete-range validation, and decoding. -- Identify one loaded-image range owner that can prove a full layout before any - dereference without inventing process memory readability. -- Map legal empty, malformed table, unreadable entry, and programmer misuse to - distinct observable semantics. -- Confirm the additive dependency API needed by PrivateHeaderKit diagnostics and - direct-query parity before implementation begins. +- MachOObjCSection: + `0d17e3d77556991dc128aa92547ea1b1ea8f9e2e` +- MachOSwiftSection: + `a7e5982ed7de5dab5dec76036682ea55825b77a8` + +Verified evidence: + +- Nine production file-backed method/property/ivar branches use the same + `readDataSequence` helper, which performs unchecked count/stride arithmetic, + narrowing, `try!` I/O, and aligned loads. +- Regular class, protocol, and category member-list headers collapse unreadable + non-null storage into absence; their loaded decoders do not prove the full + table range. +- All twelve loaded class/protocol/category root properties read section pointer + tables and referenced layouts without a complete image-range proof. A + good/bad/good root sequence terminates at the bad pointer. +- Singular method/property relative-list queries bypass the checked plural + resolver added for #65. +- File/image `__objc_methlist` iterators repeat unchecked header, list-size, + alignment, and entry-size operations on the same public table surface. +- Valid method table ranges can still contain arithmetic-poisoned entries: + cache IMP subtraction and relative entry offsets can underflow/overflow or + manufacture zero-offset fallback metadata. +- The existing protocol reader already owns the correct 65,536-entry / 512-KiB + budget, checked arithmetic, full file/image range proof, and unaligned decode. + +Design gate approved: + +- Rename/generalize the protocol-only table reader into one neutral + `ObjCMetadataTableReader`. It owns exact offset/count/stride conversion, + checked multiplication/addition, the existing entry/byte budgets, complete + file/image range proof, fallible I/O/probing, unaligned decode, and checked + logical entry offsets. +- Delete all four `readDataSequence` overloads once their nine production call + sites migrate. Method/property/ivar direct APIs become thin compatibility + projections over a typed table outcome; a legal zero-count table is success + with `[]`, while malformed/unreadable storage is failure. +- Nonempty entry-size lists validate the ABI stride before decoding. A zero-count + list does not validate unused stride/alignment, preserving #65 semantics. +- Method entry decoding uses checked cache/displacement/entry-offset arithmetic. + A malformed entry is skipped with an indexed failure; no `?? 0` fallback is + used, and later siblings retain discovery order. +- Add an independent Diagnostics SPI `ObjCMetadataTableDiagnostic` and additive + `ObjCMetadataReadResult.tableDiagnostics`. It represents class/protocol/ + category member tables without changing the exhaustive #60 protocol, #65 + relative-member, or #79 fixed-field diagnostic enums. +- Add a concrete Diagnostics SPI root read on `MachOImage.ObjectiveC` covering + the twelve existing root properties plus ordered root-table diagnostics. + Existing properties keep their signatures and project the same per-section + checked owner; `ObjCSectionRepresentable` gains no requirement. +- The loaded root owner reads raw 32/64-bit section fields with exact conversion, + checks slide/address/range arithmetic, rejects pointer-size remainders, applies + the common budget, probes the complete pointer table, and reads each referenced + layout independently so good/bad/good roots survive in order. +- Loaded metaclass/superclass/category-class layouts use the same checked layout + primitive because they are reachable from retained roots and share the same + full-range invariant. +- Relative plural and singular method/property queries share one per-entry + resolver. Unloaded entries are normal omissions; unavailable or malformed + entries remain typed in the plural path and project to `nil` in the singular + compatibility API. +- File/image method-section iterators use checked header/list size/alignment and + stop safely on structural failure instead of trapping. +- PrivateHeaderKit consumes root and per-subject table diagnostics through the + existing `RawDumpObjCDiagnosticsAccumulator` member channel. It adds no report, + set, cap, omission counter, sorting owner, persistence path, or DB migration. + +Observable semantics: + +- absent section / wrong bitness: `nil`, no diagnostic; +- present empty root section: `[]`, no diagnostic; +- malformed whole root/member table: no values from that table plus one table + diagnostic; +- malformed root/member entry: omit that entry, append an indexed diagnostic, + and continue later siblings in source order; +- zero member pointer: absent, no diagnostic; +- nonzero unreadable header/table: failure, never absence or regular fallback; +- malformed singular relative query: `nil`; +- public compatibility APIs discard typed diagnostics but use the same checked + outcome and therefore cannot re-enter an unsafe decoder. + +Explicit exclusions: + +- Hostile C-string pointer hardening remains outside this table/layout change, + matching MachOObjCSection Evolution 0007. No claim is made that arbitrary + loaded strings are fully hardened. +- Runtime RW-extension array-of-lists has a distinct tagged array ABI and is not + used by PrivateHeaderKit's current metadata traversal. Its unchecked public + queries require a separately tracked design rather than a guessed #83 patch. +- No current iOS 27 crash is attributed to #83; deterministic malformed fixtures + are the correctness gate rather than a claimed crash recovery. Required regression coverage: - Existing protocol-list behavior from #60. - Existing relative-member behavior from #65. - Existing fixed-field behavior from #79. +- Diagnostics SPI compile coverage and legacy direct-query parity. +- 32/64-bit file and loaded-image exact-boundary, truncation, excessive-count, + byte-budget, arithmetic-overflow, and good/bad/good fixtures. +- Exact dependency coherence: MachOObjCSection, MachOSwiftSection, then + PrivateHeaderKit pins and pin-contract tests. From 56f70e3952abc16c0b1475e051c4637c88046502 Mon Sep 17 00:00:00 2001 From: Kazuki Nakashima <65545348+lynnswap@users.noreply.github.com> Date: Wed, 26 Aug 2026 07:55:26 +0900 Subject: [PATCH 03/12] docs: track loaded RW array follow-up --- Docs/issue-remediation-progress.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Docs/issue-remediation-progress.md b/Docs/issue-remediation-progress.md index 0dc9459..ae213d6 100644 --- a/Docs/issue-remediation-progress.md +++ b/Docs/issue-remediation-progress.md @@ -2,6 +2,11 @@ Base: `main` at `101748f62fa27d6851b4284df7cafc721081fa5c` +Delivery order: + +1. #83 bounded regular Objective-C tables and loaded roots +2. #87 bounded loaded Objective-C RW extension arrays + ## Current issue: #83 Branch: `codex/issue-83-bounded-objc-metadata-reads` @@ -111,7 +116,7 @@ Explicit exclusions: loaded strings are fully hardened. - Runtime RW-extension array-of-lists has a distinct tagged array ABI and is not used by PrivateHeaderKit's current metadata traversal. Its unchecked public - queries require a separately tracked design rather than a guessed #83 patch. + queries are tracked by #87 rather than receiving a guessed #83 patch. - No current iOS 27 crash is attributed to #83; deterministic malformed fixtures are the correctness gate rather than a claimed crash recovery. From 68427481028e153f41f1f23ed0004921e1e96877 Mon Sep 17 00:00:00 2001 From: Kazuki Nakashima <65545348+lynnswap@users.noreply.github.com> Date: Wed, 26 Aug 2026 08:09:16 +0900 Subject: [PATCH 04/12] docs: record issue 83 table reader checkpoint --- Docs/issue-remediation-progress.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/Docs/issue-remediation-progress.md b/Docs/issue-remediation-progress.md index ae213d6..fe29f1e 100644 --- a/Docs/issue-remediation-progress.md +++ b/Docs/issue-remediation-progress.md @@ -130,3 +130,16 @@ Required regression coverage: byte-budget, arithmetic-overflow, and good/bad/good fixtures. - Exact dependency coherence: MachOObjCSection, MachOSwiftSection, then PrivateHeaderKit pins and pin-contract tests. + +Dependency progress: + +- MachOObjCSection core checkpoint + `748070697d20cace20618ef8bc9f6b4d10949c69` centralizes the neutral file/image + table reader, preserves #60 failure precedence, migrates the nine file table + paths plus image member tables, makes method arithmetic fallible, and removes + the unsafe `readDataSequence` owner. +- At that checkpoint `swift build`, 57 protocol safety tests, and 17 combined + relative-member/fixed-field regression tests pass. +- Phase A continues checked direct projections, method iterators, and table/file + fixtures. Phase B is stacked from the core checkpoint for loaded roots, + relationships, regular headers, relative direct parity, and loaded fixtures. From b0b1a3ba2af81da83dcbc7ec2f49d3e5a4361883 Mon Sep 17 00:00:00 2001 From: Kazuki Nakashima <65545348+lynnswap@users.noreply.github.com> Date: Wed, 26 Aug 2026 08:28:30 +0900 Subject: [PATCH 05/12] docs: record issue 83 phase A validation --- Docs/issue-remediation-progress.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/Docs/issue-remediation-progress.md b/Docs/issue-remediation-progress.md index fe29f1e..c994e64 100644 --- a/Docs/issue-remediation-progress.md +++ b/Docs/issue-remediation-progress.md @@ -143,3 +143,16 @@ Dependency progress: - Phase A continues checked direct projections, method iterators, and table/file fixtures. Phase B is stacked from the core checkpoint for loaded roots, relationships, regular headers, relative direct parity, and loaded fixtures. +- MachOObjCSection Phase A checkpoint + `c1a78f8ed6f6aaa592317bdd553129b10abb29af` adds no-trap file/image method + iterators, checked `EntrySizeList` projections, empty-before-stride semantics, + indexed property-coordinate failures, section read cleanup, and 18 direct + reader/member/iterator tests. +- Phase A validation: debug/release builds and 90 focused new + #60/#65/#79 + tests pass; unsafe-pattern and diff checks are clean. The full suite reaches + an unchanged absolute `/Users/JH/Downloads/iOS18.5-SwiftUI` fixture dependency + and then traps in the identical base test force unwrap, so that environment + fixture is not changed by #83. +- A follow-up checkpoint is restoring the public `EntrySizeList.size` `Int` + signature while retaining the internal fallible size owner; malformed legacy + projections return zero instead of changing public source compatibility. From 0e1b45f4f89d14a553519f212e10a12748f900ca Mon Sep 17 00:00:00 2001 From: Kazuki Nakashima <65545348+lynnswap@users.noreply.github.com> Date: Wed, 26 Aug 2026 08:30:53 +0900 Subject: [PATCH 06/12] docs: finalize issue 83 phase A checkpoint --- Docs/issue-remediation-progress.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Docs/issue-remediation-progress.md b/Docs/issue-remediation-progress.md index c994e64..a0a47d3 100644 --- a/Docs/issue-remediation-progress.md +++ b/Docs/issue-remediation-progress.md @@ -156,3 +156,8 @@ Dependency progress: - A follow-up checkpoint is restoring the public `EntrySizeList.size` `Int` signature while retaining the internal fallible size owner; malformed legacy projections return zero instead of changing public source compatibility. +- MachOObjCSection Phase A final + `309091379d653f4cbf7d07aab7faa1269736b61c` preserves the public `Int` + signatures for entry size, count, and size while keeping iterators on the + internal fallible size API. Malformed projections return zero; legal empty + lists return the header size. The focused regression set passes 92 tests. From 5861cdac4dc8439ccf810b69e84fa21108ce823b Mon Sep 17 00:00:00 2001 From: Kazuki Nakashima <65545348+lynnswap@users.noreply.github.com> Date: Wed, 26 Aug 2026 09:03:36 +0900 Subject: [PATCH 07/12] docs: record issue 83 loaded phase --- Docs/issue-remediation-progress.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Docs/issue-remediation-progress.md b/Docs/issue-remediation-progress.md index a0a47d3..f878117 100644 --- a/Docs/issue-remediation-progress.md +++ b/Docs/issue-remediation-progress.md @@ -161,3 +161,13 @@ Dependency progress: signatures for entry size, count, and size while keeping iterators on the internal fallible size API. Malformed projections return zero; legal empty lists return the header size. The focused regression set passes 92 tests. +- MachOObjCSection Phase B final + `4382ab91edce1027bb5498f6c5cd7f5388de0a98` adds the loaded root aggregate + Diagnostics SPI, exact 32/64-bit section decoding, per-entry layout probing, + checked class/category/protocol member headers, checked relationship layouts, + and singular/plural relative-list parity. +- Phase B validation: 87 focused tests, debug/release builds, iOS/watchOS + cross-builds, diff checks, and branch-wide codex-review are clean. Its only + full-suite failure is the same base absolute-fixture force unwrap. Phase A/B + have two semantic merge conflicts to resolve in the shared relative-list + owners before consumer integration. From 321e4e704efbf02622683df4bdc28bd85808a0f4 Mon Sep 17 00:00:00 2001 From: Kazuki Nakashima <65545348+lynnswap@users.noreply.github.com> Date: Wed, 26 Aug 2026 09:09:48 +0900 Subject: [PATCH 08/12] docs: record issue 83 dependency integration --- Docs/issue-remediation-progress.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Docs/issue-remediation-progress.md b/Docs/issue-remediation-progress.md index f878117..332f989 100644 --- a/Docs/issue-remediation-progress.md +++ b/Docs/issue-remediation-progress.md @@ -161,6 +161,12 @@ Dependency progress: signatures for entry size, count, and size while keeping iterators on the internal fallible size API. Malformed projections return zero; legal empty lists return the header size. The focused regression set passes 92 tests. +- MachOObjCSection Phase A completed at + `98c4605fc18054c53f49cabf89929c5012073191`. Later checkpoints separate + legacy size arithmetic from parser budgets and route 32/64-bit raw method + sections through exact section/segment coordinates without using unsafe + existential getters or `MachOImage.vmaddrSlide`. Focused coverage is 97 tests; + debug/release builds pass. - MachOObjCSection Phase B final `4382ab91edce1027bb5498f6c5cd7f5388de0a98` adds the loaded root aggregate Diagnostics SPI, exact 32/64-bit section decoding, per-entry layout probing, @@ -171,3 +177,7 @@ Dependency progress: full-suite failure is the same base absolute-fixture force unwrap. Phase A/B have two semantic merge conflicts to resolve in the shared relative-list owners before consumer integration. +- Integration branch `codex/issue-83-integrated-objc-metadata` starts from the + Phase A final SHA and is applying both Phase B commits. Loaded roots must use + the Phase A section/segment coordinates owner; no duplicate slide calculation + is permitted. From 1a2a1f94268f5677532ab7b8a24552a6744b2743 Mon Sep 17 00:00:00 2001 From: Kazuki Nakashima <65545348+lynnswap@users.noreply.github.com> Date: Wed, 26 Aug 2026 09:44:31 +0900 Subject: [PATCH 09/12] docs: track file root hardening follow-up --- Docs/issue-remediation-progress.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Docs/issue-remediation-progress.md b/Docs/issue-remediation-progress.md index 332f989..90cfdfb 100644 --- a/Docs/issue-remediation-progress.md +++ b/Docs/issue-remediation-progress.md @@ -5,7 +5,8 @@ Base: `main` at `101748f62fa27d6851b4284df7cafc721081fa5c` Delivery order: 1. #83 bounded regular Objective-C tables and loaded roots -2. #87 bounded loaded Objective-C RW extension arrays +2. #88 bounded file-backed Objective-C root sections +3. #87 bounded loaded Objective-C RW extension arrays ## Current issue: #83 @@ -117,6 +118,9 @@ Explicit exclusions: - Runtime RW-extension array-of-lists has a distinct tagged array ABI and is not used by PrivateHeaderKit's current metadata traversal. Its unchecked public queries are tracked by #87 rather than receiving a guessed #83 patch. +- File-backed class/category/protocol root sections retain a separate unchecked + coordinate/`try!` owner and are tracked by #88. #83 keeps its stated loaded-root + scope; the shared owners introduced here are prerequisites for that follow-up. - No current iOS 27 crash is attributed to #83; deterministic malformed fixtures are the correctness gate rather than a claimed crash recovery. From 24a082a74dffe39204e1733c93e393459f0a40f1 Mon Sep 17 00:00:00 2001 From: Kazuki Nakashima <65545348+lynnswap@users.noreply.github.com> Date: Wed, 26 Aug 2026 11:26:26 +0900 Subject: [PATCH 10/12] fix: bound Objective-C metadata table reads --- Docs/generation.md | 24 +- Docs/issue-remediation-progress.md | 28 +++ Package.resolved | 6 +- Package.swift | 4 +- .../PrivateHeaderKitRawDumpMain.swift | 31 ++- .../RawDumpObjCDiagnostics.swift | 220 ++++++++++++++++++ .../PrivateHeaderKitHelperProtocolTests.swift | 4 +- .../ObjCMemberListDiagnosticsTests.swift | 38 ++- 8 files changed, 332 insertions(+), 23 deletions(-) diff --git a/Docs/generation.md b/Docs/generation.md index 6d1a1b1..6449df9 100644 --- a/Docs/generation.md +++ b/Docs/generation.md @@ -143,14 +143,22 @@ Loaded-image reads of relative method/property list-of-lists consult each entry's runtime loaded state, while file-backed reads inspect every structurally valid entry. Both preserve outer-table order and validate the outer table and each nonempty inner member table before decoding; an empty inner list does not -require an otherwise unused entry size. One malformed loaded list preserves its -valid siblings and produces a typed member-list degradation; unloaded lists are -skipped without warning. Once that target is published, PrivateHeaderKit reports -the precise owner and degradation as an `objc-metadata-warning` and persists the -warning in `generation.sqlite`. A bounded diagnostics report records when -additional warnings were omitted, so malformed metadata cannot grow process -output without limit. Live warning presentation is also capped across the run; -one aggregate warning points to the retained per-target details in the database. +require an otherwise unused entry size. Regular method, property, and ivar +tables use the same finite count and byte budgets, checked arithmetic, and +complete-range validation. Loaded class, protocol, and category root tables +validate both the complete pointer table and each referenced layout before +decoding. One malformed relative list, regular member-table entry, loaded-root +entry, or loaded class/category relationship preserves its readable siblings +and produces a typed degradation; unloaded relative lists are skipped without +warning. These guarantees cover structural table, list, and layout ranges; they +do not validate referenced C strings, loaded class RW-extension arrays, or +file-backed root-section tables. Once that target is published, +PrivateHeaderKit reports the precise owner and degradation as an +`objc-metadata-warning` and persists the warning in `generation.sqlite`. A +bounded diagnostics report records when additional warnings were omitted, so +malformed metadata cannot grow process output without limit. Live warning +presentation is also capped across the run; one aggregate warning points to the +retained per-target details in the database. State, attempts, publication intent, and run diagnostics are stored in `generation.sqlite`, outside the published header tree. The `.privateheaderkit` diff --git a/Docs/issue-remediation-progress.md b/Docs/issue-remediation-progress.md index 90cfdfb..aa38fcf 100644 --- a/Docs/issue-remediation-progress.md +++ b/Docs/issue-remediation-progress.md @@ -185,3 +185,31 @@ Dependency progress: Phase A final SHA and is applying both Phase B commits. Loaded roots must use the Phase A section/segment coordinates owner; no duplicate slide calculation is permitted. +- MachOObjCSection Phase A/B integration completed at + `e1972524f608fbbf1d0551186168a117f8ec4dd7`. The integration keeps raw + section/segment coordinates as the loaded-root owner, bounds loaded method + headers to their section, and decodes 32-bit relative selector references at + their actual pointer width. Its focused union, debug/release builds, iOS and + watchOS cross-builds, unsafe audit, and branch-wide codex-review are clean. +- Diagnostic propagation completed at + `41a1652d95c34bbb2fdf32ba4b7f5cd9d3ba20ba`. All six class/protocol/category + file/image `readInfo` paths retain regular table failures and loaded + relationships in `tableDiagnostics`; existing protocol, relative-outer, and + fixed-field diagnostic arrays remain independent. Relative inner tables use + the new channel without duplicating outer-list diagnostics. +- Final MachOObjCSection validation: 119 focused Phase A/B/C and #60/#65/#79 + tests pass; debug/release, iOS arm64, watchOS arm64/arm64_32, Diagnostics SPI + compile, diff checks, independent audit, and codex-review are clean. The SHA + is published on `codex/issue-83-diagnostic-propagation`. +- MachOSwiftSection cohort pin `3344bd04e6c0819b17df126bea57bae1bbed3022` + resolves the exact Objective-C reader SHA. Debug build, six ObjC-ancestor + tests after the required fixture build, the release MachOSwiftSection product, + diff checks, and codex-review are clean. The all-product release build still + reaches its pre-existing `MachOFixtureSupport` compatible-module failure; the + library product itself compiles successfully. +- PrivateHeaderKit now uses the loaded root aggregate, routes root and subject + table diagnostics through the existing bounded member channel, and pins both + dependency SHAs. Full tests, release build, iOS/watchOS Core and test compile, + both simulator-helper release builds, release-script tests, and pin-contract + tests pass. Exact iOS 27 build `24A5390f` runtime validation remains before + the final review and Ready PR. diff --git a/Package.resolved b/Package.resolved index cc80741..a5f3afe 100644 --- a/Package.resolved +++ b/Package.resolved @@ -1,5 +1,5 @@ { - "originHash" : "afe6393989064d60f345d3b63e7668e4541bfdcee8911add15f52d9c8bb39673", + "originHash" : "bf9bc82de8b74cbd9b5723f3edfea31631e1790a92b45368c79b146d9e3fe49b", "pins" : [ { "identity" : "associatedobject", @@ -59,7 +59,7 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/lynnswap/MachOObjCSection.git", "state" : { - "revision" : "0d17e3d77556991dc128aa92547ea1b1ea8f9e2e" + "revision" : "41a1652d95c34bbb2fdf32ba4b7f5cd9d3ba20ba" } }, { @@ -67,7 +67,7 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/lynnswap/MachOSwiftSection.git", "state" : { - "revision" : "a7e5982ed7de5dab5dec76036682ea55825b77a8" + "revision" : "3344bd04e6c0819b17df126bea57bae1bbed3022" } }, { diff --git a/Package.swift b/Package.swift index 4785758..7ff494b 100644 --- a/Package.swift +++ b/Package.swift @@ -37,7 +37,7 @@ let package = Package( ), .package( url: "https://github.com/lynnswap/MachOObjCSection.git", - revision: "0d17e3d77556991dc128aa92547ea1b1ea8f9e2e" + revision: "41a1652d95c34bbb2fdf32ba4b7f5cd9d3ba20ba" ), .package( url: "https://github.com/MxIris-Reverse-Engineering/swift-objc-dump.git", @@ -45,7 +45,7 @@ let package = Package( ), .package( url: "https://github.com/lynnswap/MachOSwiftSection.git", - revision: "a7e5982ed7de5dab5dec76036682ea55825b77a8" + revision: "3344bd04e6c0819b17df126bea57bae1bbed3022" ), .package( url: "https://github.com/MxIris-Reverse-Engineering/swift-demangling", diff --git a/Sources/PrivateHeaderKitRawDumpCore/PrivateHeaderKitRawDumpMain.swift b/Sources/PrivateHeaderKitRawDumpCore/PrivateHeaderKitRawDumpMain.swift index cc6b80d..a78de1d 100644 --- a/Sources/PrivateHeaderKitRawDumpCore/PrivateHeaderKitRawDumpMain.swift +++ b/Sources/PrivateHeaderKitRawDumpCore/PrivateHeaderKitRawDumpMain.swift @@ -1156,11 +1156,14 @@ private func dumpObjC( options: DumpOptions, fileManager: FileManager ) async throws { - var metadata = switch machO { + var metadata: CollectedObjCMetadata + switch machO { case .file(let file): - collectObjCMetadata(from: file.objc, in: machO, options: options) + metadata = collectObjCMetadata(from: file.objc, in: machO, options: options) case .loaded(let image): - collectObjCMetadata(from: image.objc, in: machO, options: options) + let roots = image.objc.readRoots() + options.objcDiagnostics.append(contentsOf: roots.tableDiagnostics) + metadata = collectObjCMetadata(from: roots, in: machO, options: options) } #if canImport(ObjectiveC) @@ -1198,6 +1201,24 @@ private struct CollectedObjCMetadata { var runtimeOriginClassNames: Set = [] } +private protocol RawDumpObjCRoots { + var classes64: [ObjCClass64]? { get } + var classes32: [ObjCClass32]? { get } + var nonLazyClasses64: [ObjCClass64]? { get } + var nonLazyClasses32: [ObjCClass32]? { get } + var protocols64: [ObjCProtocol64]? { get } + var protocols32: [ObjCProtocol32]? { get } + var categories64: [ObjCCategory64]? { get } + var categories32: [ObjCCategory32]? { get } + var nonLazyCategories64: [ObjCCategory64]? { get } + var nonLazyCategories32: [ObjCCategory32]? { get } + var categories2_64: [ObjCCategory64]? { get } + var categories2_32: [ObjCCategory32]? { get } +} + +extension MachOFile.ObjectiveC: RawDumpObjCRoots {} +extension ObjCImageRootReadResult: RawDumpObjCRoots {} + @discardableResult func supplementMissingRuntimeClassInfos( _ runtimeInfos: [ObjCClassInfo], @@ -1242,8 +1263,8 @@ private func runtimeClassSortKey(_ name: String) -> String { return "\(rank):\(name)" } -private func collectObjCMetadata( - from objc: Section, +private func collectObjCMetadata( + from objc: Roots, in machO: RawMachO, options: DumpOptions ) -> CollectedObjCMetadata { diff --git a/Sources/PrivateHeaderKitRawDumpCore/RawDumpObjCDiagnostics.swift b/Sources/PrivateHeaderKitRawDumpCore/RawDumpObjCDiagnostics.swift index d04e0ec..2b917b7 100644 --- a/Sources/PrivateHeaderKitRawDumpCore/RawDumpObjCDiagnostics.swift +++ b/Sources/PrivateHeaderKitRawDumpCore/RawDumpObjCDiagnostics.swift @@ -75,6 +75,7 @@ final class RawDumpObjCDiagnosticsAccumulator { append(contentsOf: result.fieldDiagnostics) append(contentsOf: result.diagnostics) append(contentsOf: result.memberListDiagnostics) + append(contentsOf: result.tableDiagnostics) } func append(contentsOf diagnostics: [ObjCMetadataFieldDiagnostic]) { @@ -95,6 +96,12 @@ final class RawDumpObjCDiagnosticsAccumulator { } } + func append(contentsOf diagnostics: [ObjCMetadataTableDiagnostic]) { + for diagnostic in diagnostics { + memberDiagnostics.append(privateHeaderKitDiagnostic(from: diagnostic)) + } + } + var report: PrivateHeaderKitRawDumpDiagnosticsReport { var selected: [PrivateHeaderKitRawDumpDiagnostic] = [] selected.reserveCapacity(PrivateHeaderKitRawDumpDiagnosticsReport.maximumDiagnosticCount) @@ -251,6 +258,45 @@ private func privateHeaderKitDiagnostic( ) } +private func privateHeaderKitDiagnostic( + from diagnostic: ObjCMetadataTableDiagnostic +) -> PrivateHeaderKitRawDumpDiagnostic { + rawDumpMetadataTableDiagnostic( + owner: diagnostic.owner, + site: diagnostic.site, + failure: diagnostic.failure + ) +} + +private func rawDumpMetadataTableDiagnostic( + owner: ObjCMetadataTableDiagnostic.Owner, + site: ObjCMetadataTableDiagnostic.Site, + failure: ObjCMetadataTableDiagnostic.Failure +) -> PrivateHeaderKitRawDumpDiagnostic { + let ownerDescription: String + let metadataDescription: String + switch owner { + case let .member(subject, kind): + ownerDescription = subjectDescription(subject) + metadataDescription = "\(metadataTableMemberKindDescription(kind)) metadata table" + case let .loadedImageRoot(section, pointerWidth): + ownerDescription = "Objective-C loaded-image roots" + metadataDescription = + "\(pointerWidthDescription(pointerWidth))" + + " \(rootSectionDescription(section)) root table" + case let .loadedRelationship(subject, role): + ownerDescription = subjectDescription(subject) + metadataDescription = "\(loadedRelationshipDescription(role)) relationship" + } + + return PrivateHeaderKitRawDumpDiagnostic( + owner: ownerDescription, + degradation: + "\(metadataDescription)\(metadataTableSiteDescription(site))" + + " could not be fully read: \(failureDescription(failure))" + ) +} + func rawDumpMemberListDiagnostic( className: String, kind: ObjCMemberListDiagnostic.Kind, @@ -276,6 +322,81 @@ private func memberKindDescription(_ kind: ObjCMemberListDiagnostic.Kind) -> Str } } +private func metadataTableMemberKindDescription( + _ kind: ObjCMetadataTableDiagnostic.MemberKind +) -> String { + switch kind { + case .ivar: "ivar" + case .instanceMethod: "instance-method" + case .classMethod: "class-method" + case .optionalInstanceMethod: "optional-instance-method" + case .optionalClassMethod: "optional-class-method" + case .instanceProperty: "instance-property" + case .classProperty: "class-property" + } +} + +private func rootSectionDescription( + _ section: ObjCMetadataTableDiagnostic.LoadedImageRootSection +) -> String { + switch section { + case .classList: "class-list" + case .nonLazyClassList: "non-lazy-class-list" + case .protocolList: "protocol-list" + case .categoryList: "category-list" + case .nonLazyCategoryList: "non-lazy-category-list" + case .categoryList2: "category-list-2" + } +} + +private func pointerWidthDescription( + _ pointerWidth: ObjCMetadataTableDiagnostic.PointerWidth +) -> String { + switch pointerWidth { + case .bits32: "32-bit" + case .bits64: "64-bit" + } +} + +private func loadedRelationshipDescription( + _ role: ObjCMetadataTableDiagnostic.LoadedRelationshipRole +) -> String { + switch role { + case .metaclass: "metaclass" + case .superclass: "superclass" + case .categoryClass: "category-class" + case .categoryStubClass: "category-stub-class" + } +} + +private func metadataTableSiteDescription( + _ site: ObjCMetadataTableDiagnostic.Site +) -> String { + switch site { + case .table(let provenance), .relationship(let provenance): + provenanceDescription(provenance) + case let .entry(index, provenance): + " entry \(index)\(provenanceDescription(provenance))" + } +} + +private func provenanceDescription( + _ provenance: ObjCMetadataTableDiagnostic.Provenance +) -> String { + var coordinates: [String] = [] + if let logicalOffset = provenance.logicalOffset { + coordinates.append("logical offset \(logicalOffset)") + } + if let fileOffset = provenance.fileOffset { + coordinates.append("file offset \(fileOffset)") + } + if let imageAddress = provenance.imageAddress { + coordinates.append("image address \(imageAddress)") + } + guard !coordinates.isEmpty else { return "" } + return " (\(coordinates.joined(separator: ", ")))" +} + private func memberLocationDescription( _ location: ObjCMemberListDiagnostic.Location ) -> String { @@ -308,6 +429,20 @@ private func subjectDescription(_ subject: ObjCMetadataFieldDiagnostic.Subject) } } +private func subjectDescription( + _ subject: ObjCMetadataTableDiagnostic.MetadataSubject +) -> String { + switch subject { + case .class(let name): + "Objective-C class \(boundedMetadataString(name))" + case .protocol(let name): + "Objective-C protocol \(boundedMetadataString(name))" + case let .category(className, name): + "Objective-C category \(boundedMetadataString(className))" + + "(\(boundedMetadataString(name)))" + } +} + private func classRoleDescription( _ role: ObjCMetadataFieldDiagnostic.ClassRole ) -> String { @@ -456,3 +591,88 @@ private func failureDescription( "image range at address \(address) is not readable for \(byteCount) bytes" } } + +private func failureDescription( + _ failure: ObjCMetadataTableDiagnostic.Failure +) -> String { + switch failure { + case .unsupportedListEncoding: + "list encoding is unsupported by this reader" + case .invalidListOffset(let offset): + "list offset \(offset) is not a readable address" + case .invalidElementCount(let count): + "element count \(count) cannot be represented" + case .invalidSignedElementCount(let count): + "signed element count \(count) is negative" + case .invalidElementStride(let stride): + "element stride \(stride) cannot be represented" + case let .elementStrideTooSmall(advertised, minimum): + "element stride \(advertised) is smaller than \(minimum)" + case let .unexpectedElementStride(advertised, expected): + "element stride \(advertised) does not match expected size \(expected)" + case let .misalignedTableOffset(offset, requiredAlignment): + "table offset \(offset) is not aligned to \(requiredAlignment) bytes" + case let .misalignedTableAddress(address, requiredAlignment): + "table address \(address) is not aligned to \(requiredAlignment) bytes" + case let .excessiveElementCount(actual, maximum): + "element count \(actual) exceeds the safety limit \(maximum)" + case let .excessiveByteCount(actual, maximum): + "table size \(actual) bytes exceeds the safety limit \(maximum)" + case let .byteCountOverflow(elementCount, elementSize): + "byte count overflowed for \(elementCount) elements of size \(elementSize)" + case let .rangeOverflow(startOffset, byteCount): + "range overflowed from offset \(startOffset) for \(byteCount) bytes" + case let .unreadableFileRange(offset, byteCount): + "file range at offset \(offset) is not readable for \(byteCount) bytes" + case let .unreadableImageRange(address, byteCount): + "image range at address \(address) is not readable for \(byteCount) bytes" + case .invalidFileListOffset(let offset): + "file list offset \(offset) cannot be represented" + case .unresolvedListPointer: + "list pointer could not be rebased" + case .missingListBackingData: + "list pointer has no readable backing data" + case let .unreadableFileHeader(offset, byteCount): + "file header at offset \(offset) is not readable for \(byteCount) bytes" + case .invalidEntryLogicalOffset: + "entry logical offset overflowed" + case .invalidMethodImplementationOffset: + "method implementation offset overflowed" + case .invalidRelativeDisplacement: + "relative field displacement overflowed" + case let .invalidSectionByteCount(byteCount, pointerSize): + "section size \(byteCount) is not a multiple of pointer size \(pointerSize)" + case let .invalidSectionCoordinates( + sectionAddress, + sectionSize, + sectionFileOffset, + segmentAddress, + segmentSize, + segmentFileOffset, + segmentFileSize + ): + "section coordinates (address \(sectionAddress), size \(sectionSize)," + + " file offset \(sectionFileOffset)) are outside segment coordinates" + + " (address \(segmentAddress), size \(segmentSize)," + + " file offset \(segmentFileOffset), file size \(segmentFileSize))" + case .missingImageBaseSegment: + "the loaded image has no __TEXT base segment" + case let .invalidLoadedSectionAddress( + imageBase, + imageVirtualMemoryAddress, + sectionAddress + ): + "section address \(sectionAddress) cannot be mapped from image base" + + " \(imageBase) and image virtual address \(imageVirtualMemoryAddress)" + case .invalidPointer(let rawValue): + "pointer value \(rawValue) is not a readable address" + case .missingReferencedImage(let address): + "address \(address) does not belong to an available loaded image" + case let .unreadableReferencedLayout(address, byteCount): + "referenced layout at image address \(address)" + + " is not readable for \(byteCount) bytes" + case let .invalidEntryArithmetic(baseAddress, targetAddress): + "target address \(targetAddress) cannot be represented relative" + + " to base address \(baseAddress)" + } +} diff --git a/Tests/PrivateHeaderKitHelperProtocolTests/PrivateHeaderKitHelperProtocolTests.swift b/Tests/PrivateHeaderKitHelperProtocolTests/PrivateHeaderKitHelperProtocolTests.swift index b22c9de..5013fcc 100644 --- a/Tests/PrivateHeaderKitHelperProtocolTests/PrivateHeaderKitHelperProtocolTests.swift +++ b/Tests/PrivateHeaderKitHelperProtocolTests/PrivateHeaderKitHelperProtocolTests.swift @@ -292,7 +292,7 @@ struct PrivateHeaderKitHelperProtocolTests { let state = try #require(pin["state"] as? [String: Any]) #expect(pin["location"] as? String == "https://github.com/lynnswap/MachOObjCSection.git") - #expect(state["revision"] as? String == "0d17e3d77556991dc128aa92547ea1b1ea8f9e2e") + #expect(state["revision"] as? String == "41a1652d95c34bbb2fdf32ba4b7f5cd9d3ba20ba") #expect(state["version"] == nil) let swiftSectionPin = try #require( @@ -307,7 +307,7 @@ struct PrivateHeaderKitHelperProtocolTests { ) #expect( swiftSectionState["revision"] as? String - == "a7e5982ed7de5dab5dec76036682ea55825b77a8" + == "3344bd04e6c0819b17df126bea57bae1bbed3022" ) #expect(swiftSectionState["version"] == nil) diff --git a/Tests/PrivateHeaderKitRawDumpTests/ObjCMemberListDiagnosticsTests.swift b/Tests/PrivateHeaderKitRawDumpTests/ObjCMemberListDiagnosticsTests.swift index c4812df..c649bc6 100644 --- a/Tests/PrivateHeaderKitRawDumpTests/ObjCMemberListDiagnosticsTests.swift +++ b/Tests/PrivateHeaderKitRawDumpTests/ObjCMemberListDiagnosticsTests.swift @@ -6,6 +6,35 @@ import Testing @testable import PrivateHeaderKitRawDumpCore struct ObjCMemberListDiagnosticsTests { + @Test func regularTableDiagnosticsUseTheExistingBoundedMemberChannel() throws { + let fixture = try InvalidMemberListFixture( + memberDiagnosticCount: 0, + includesFieldDiagnostic: false, + includesProtocolDiagnostic: false, + includesTableDiagnostic: true + ) + let result = fixture.objcClass.readInfo(in: fixture.machO) + + #expect(result.fieldDiagnostics.isEmpty) + #expect(result.diagnostics.isEmpty) + #expect(result.memberListDiagnostics.isEmpty) + #expect(result.tableDiagnostics.count == 1) + + let accumulator = RawDumpObjCDiagnosticsAccumulator() + accumulator.append(contentsOf: result) + + let report = accumulator.report + #expect(report.diagnostics.count == 1) + #expect(report.omittedDiagnosticCount == 0) + #expect(report.diagnostics.first?.owner == "Objective-C class MemberOwner") + #expect( + report.diagnostics.first?.degradation + == "ivar metadata table (logical offset 5120, file offset 5120)" + + " could not be fully read: element count 65537" + + " exceeds the safety limit 65536" + ) + } + @Test func wholeTableFailureKeepsMemberKindAndOuterOffset() { let record = rawDumpMemberListDiagnostic( className: "Owner", @@ -202,7 +231,8 @@ private final class InvalidMemberListFixture { init( memberDiagnosticCount: Int, includesFieldDiagnostic: Bool = true, - includesProtocolDiagnostic: Bool = true + includesProtocolDiagnostic: Bool = true, + includesTableDiagnostic: Bool = false ) throws { let fileSize = 0x4000 let vmAddress: UInt64 = 0x1_0000_0000 @@ -266,7 +296,7 @@ private final class InvalidMemberListFixture { data.storeValue( RawEntrySizeListHeader( entsizeAndFlags: UInt32(MemoryLayout.size), - count: 1 + count: includesTableDiagnostic ? 65_537 : 1 ), at: ivarListOffset ) @@ -291,7 +321,9 @@ private final class InvalidMemberListFixture { name: address(classNameOffset), baseMethods: address(memberListOffset) | 1, baseProtocols: includesProtocolDiagnostic ? address(protocolListOffset) : 0, - ivars: includesFieldDiagnostic ? address(ivarListOffset) : 0, + ivars: includesFieldDiagnostic || includesTableDiagnostic + ? address(ivarListOffset) + : 0, weakIvarLayout: 0, baseProperties: 0 ), From 86ca183ac5ab5e40215147fd022e845093bca4f1 Mon Sep 17 00:00:00 2001 From: Kazuki Nakashima <65545348+lynnswap@users.noreply.github.com> Date: Wed, 26 Aug 2026 11:43:17 +0900 Subject: [PATCH 11/12] docs: record issue 83 runtime validation --- Docs/issue-remediation-progress.md | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/Docs/issue-remediation-progress.md b/Docs/issue-remediation-progress.md index aa38fcf..7502ca1 100644 --- a/Docs/issue-remediation-progress.md +++ b/Docs/issue-remediation-progress.md @@ -211,5 +211,16 @@ Dependency progress: table diagnostics through the existing bounded member channel, and pins both dependency SHAs. Full tests, release build, iOS/watchOS Core and test compile, both simulator-helper release builds, release-script tests, and pin-contract - tests pass. Exact iOS 27 build `24A5390f` runtime validation remains before - the final review and Ready PR. + tests pass. +- Exact iOS 27 beta build `24A5390f` runtime validation is complete. The three + installed iOS 27 builds share one CoreSimulator runtime identifier, so the + documented runtime-match override temporarily selected `24A5390f`; the + previous `24A5423a` choice was restored afterward and no test device remains. + `Foundation` completed with 859 generated files, no metadata warnings, and an + `ok` database integrity check. +- `AXSpringBoardServerInstance` traversed the new loaded root/member readers, + then reproduced its known unrelated runtime-fallback abort while loading + `SpringBoardUI.framework`. The failure capsule and Crash Reporter agree on PID + 43633, helper LC_UUID `b511c0cf-6944-3e14-958d-b320a3e13a71`, and SIGABRT; + the backtrace reaches `dumpObjC`'s post-collection runtime fallback before + `+[SpringBoardUI load]` throws its intentional process-use exception. From e42fd1c982f39c83beb23cd1cc56758cca75541f Mon Sep 17 00:00:00 2001 From: Kazuki Nakashima <65545348+lynnswap@users.noreply.github.com> Date: Wed, 26 Aug 2026 11:43:43 +0900 Subject: [PATCH 12/12] chore: remove issue remediation ledger --- Docs/issue-remediation-progress.md | 226 ----------------------------- 1 file changed, 226 deletions(-) delete mode 100644 Docs/issue-remediation-progress.md diff --git a/Docs/issue-remediation-progress.md b/Docs/issue-remediation-progress.md deleted file mode 100644 index 7502ca1..0000000 --- a/Docs/issue-remediation-progress.md +++ /dev/null @@ -1,226 +0,0 @@ -# Issue remediation progress - -Base: `main` at `101748f62fa27d6851b4284df7cafc721081fa5c` - -Delivery order: - -1. #83 bounded regular Objective-C tables and loaded roots -2. #88 bounded file-backed Objective-C root sections -3. #87 bounded loaded Objective-C RW extension arrays - -## Current issue: #83 - -Branch: `codex/issue-83-bounded-objc-metadata-reads` - -Goal: - -- Make variable-length file-backed Objective-C tables and loaded-image root/list - reads reject malformed external ranges without arithmetic overflow, unchecked - dereference, or raw-helper termination. -- Preserve readable sibling metadata and discovery order while emitting bounded - typed degradation through the existing diagnostic owner. - -Confirmed scope from the issue: - -- File-backed method, property, and ivar table reads. -- Loaded-image class, category, and protocol roots and member-list headers. -- Direct relative method/property list queries that bypass checked traversal. -- Deterministic 32/64-bit file and loaded-image boundary fixtures. - -Dependency bases: - -- MachOObjCSection: - `0d17e3d77556991dc128aa92547ea1b1ea8f9e2e` -- MachOSwiftSection: - `a7e5982ed7de5dab5dec76036682ea55825b77a8` - -Verified evidence: - -- Nine production file-backed method/property/ivar branches use the same - `readDataSequence` helper, which performs unchecked count/stride arithmetic, - narrowing, `try!` I/O, and aligned loads. -- Regular class, protocol, and category member-list headers collapse unreadable - non-null storage into absence; their loaded decoders do not prove the full - table range. -- All twelve loaded class/protocol/category root properties read section pointer - tables and referenced layouts without a complete image-range proof. A - good/bad/good root sequence terminates at the bad pointer. -- Singular method/property relative-list queries bypass the checked plural - resolver added for #65. -- File/image `__objc_methlist` iterators repeat unchecked header, list-size, - alignment, and entry-size operations on the same public table surface. -- Valid method table ranges can still contain arithmetic-poisoned entries: - cache IMP subtraction and relative entry offsets can underflow/overflow or - manufacture zero-offset fallback metadata. -- The existing protocol reader already owns the correct 65,536-entry / 512-KiB - budget, checked arithmetic, full file/image range proof, and unaligned decode. - -Design gate approved: - -- Rename/generalize the protocol-only table reader into one neutral - `ObjCMetadataTableReader`. It owns exact offset/count/stride conversion, - checked multiplication/addition, the existing entry/byte budgets, complete - file/image range proof, fallible I/O/probing, unaligned decode, and checked - logical entry offsets. -- Delete all four `readDataSequence` overloads once their nine production call - sites migrate. Method/property/ivar direct APIs become thin compatibility - projections over a typed table outcome; a legal zero-count table is success - with `[]`, while malformed/unreadable storage is failure. -- Nonempty entry-size lists validate the ABI stride before decoding. A zero-count - list does not validate unused stride/alignment, preserving #65 semantics. -- Method entry decoding uses checked cache/displacement/entry-offset arithmetic. - A malformed entry is skipped with an indexed failure; no `?? 0` fallback is - used, and later siblings retain discovery order. -- Add an independent Diagnostics SPI `ObjCMetadataTableDiagnostic` and additive - `ObjCMetadataReadResult.tableDiagnostics`. It represents class/protocol/ - category member tables without changing the exhaustive #60 protocol, #65 - relative-member, or #79 fixed-field diagnostic enums. -- Add a concrete Diagnostics SPI root read on `MachOImage.ObjectiveC` covering - the twelve existing root properties plus ordered root-table diagnostics. - Existing properties keep their signatures and project the same per-section - checked owner; `ObjCSectionRepresentable` gains no requirement. -- The loaded root owner reads raw 32/64-bit section fields with exact conversion, - checks slide/address/range arithmetic, rejects pointer-size remainders, applies - the common budget, probes the complete pointer table, and reads each referenced - layout independently so good/bad/good roots survive in order. -- Loaded metaclass/superclass/category-class layouts use the same checked layout - primitive because they are reachable from retained roots and share the same - full-range invariant. -- Relative plural and singular method/property queries share one per-entry - resolver. Unloaded entries are normal omissions; unavailable or malformed - entries remain typed in the plural path and project to `nil` in the singular - compatibility API. -- File/image method-section iterators use checked header/list size/alignment and - stop safely on structural failure instead of trapping. -- PrivateHeaderKit consumes root and per-subject table diagnostics through the - existing `RawDumpObjCDiagnosticsAccumulator` member channel. It adds no report, - set, cap, omission counter, sorting owner, persistence path, or DB migration. - -Observable semantics: - -- absent section / wrong bitness: `nil`, no diagnostic; -- present empty root section: `[]`, no diagnostic; -- malformed whole root/member table: no values from that table plus one table - diagnostic; -- malformed root/member entry: omit that entry, append an indexed diagnostic, - and continue later siblings in source order; -- zero member pointer: absent, no diagnostic; -- nonzero unreadable header/table: failure, never absence or regular fallback; -- malformed singular relative query: `nil`; -- public compatibility APIs discard typed diagnostics but use the same checked - outcome and therefore cannot re-enter an unsafe decoder. - -Explicit exclusions: - -- Hostile C-string pointer hardening remains outside this table/layout change, - matching MachOObjCSection Evolution 0007. No claim is made that arbitrary - loaded strings are fully hardened. -- Runtime RW-extension array-of-lists has a distinct tagged array ABI and is not - used by PrivateHeaderKit's current metadata traversal. Its unchecked public - queries are tracked by #87 rather than receiving a guessed #83 patch. -- File-backed class/category/protocol root sections retain a separate unchecked - coordinate/`try!` owner and are tracked by #88. #83 keeps its stated loaded-root - scope; the shared owners introduced here are prerequisites for that follow-up. -- No current iOS 27 crash is attributed to #83; deterministic malformed fixtures - are the correctness gate rather than a claimed crash recovery. - -Required regression coverage: - -- Existing protocol-list behavior from #60. -- Existing relative-member behavior from #65. -- Existing fixed-field behavior from #79. -- Diagnostics SPI compile coverage and legacy direct-query parity. -- 32/64-bit file and loaded-image exact-boundary, truncation, excessive-count, - byte-budget, arithmetic-overflow, and good/bad/good fixtures. -- Exact dependency coherence: MachOObjCSection, MachOSwiftSection, then - PrivateHeaderKit pins and pin-contract tests. - -Dependency progress: - -- MachOObjCSection core checkpoint - `748070697d20cace20618ef8bc9f6b4d10949c69` centralizes the neutral file/image - table reader, preserves #60 failure precedence, migrates the nine file table - paths plus image member tables, makes method arithmetic fallible, and removes - the unsafe `readDataSequence` owner. -- At that checkpoint `swift build`, 57 protocol safety tests, and 17 combined - relative-member/fixed-field regression tests pass. -- Phase A continues checked direct projections, method iterators, and table/file - fixtures. Phase B is stacked from the core checkpoint for loaded roots, - relationships, regular headers, relative direct parity, and loaded fixtures. -- MachOObjCSection Phase A checkpoint - `c1a78f8ed6f6aaa592317bdd553129b10abb29af` adds no-trap file/image method - iterators, checked `EntrySizeList` projections, empty-before-stride semantics, - indexed property-coordinate failures, section read cleanup, and 18 direct - reader/member/iterator tests. -- Phase A validation: debug/release builds and 90 focused new + #60/#65/#79 - tests pass; unsafe-pattern and diff checks are clean. The full suite reaches - an unchanged absolute `/Users/JH/Downloads/iOS18.5-SwiftUI` fixture dependency - and then traps in the identical base test force unwrap, so that environment - fixture is not changed by #83. -- A follow-up checkpoint is restoring the public `EntrySizeList.size` `Int` - signature while retaining the internal fallible size owner; malformed legacy - projections return zero instead of changing public source compatibility. -- MachOObjCSection Phase A final - `309091379d653f4cbf7d07aab7faa1269736b61c` preserves the public `Int` - signatures for entry size, count, and size while keeping iterators on the - internal fallible size API. Malformed projections return zero; legal empty - lists return the header size. The focused regression set passes 92 tests. -- MachOObjCSection Phase A completed at - `98c4605fc18054c53f49cabf89929c5012073191`. Later checkpoints separate - legacy size arithmetic from parser budgets and route 32/64-bit raw method - sections through exact section/segment coordinates without using unsafe - existential getters or `MachOImage.vmaddrSlide`. Focused coverage is 97 tests; - debug/release builds pass. -- MachOObjCSection Phase B final - `4382ab91edce1027bb5498f6c5cd7f5388de0a98` adds the loaded root aggregate - Diagnostics SPI, exact 32/64-bit section decoding, per-entry layout probing, - checked class/category/protocol member headers, checked relationship layouts, - and singular/plural relative-list parity. -- Phase B validation: 87 focused tests, debug/release builds, iOS/watchOS - cross-builds, diff checks, and branch-wide codex-review are clean. Its only - full-suite failure is the same base absolute-fixture force unwrap. Phase A/B - have two semantic merge conflicts to resolve in the shared relative-list - owners before consumer integration. -- Integration branch `codex/issue-83-integrated-objc-metadata` starts from the - Phase A final SHA and is applying both Phase B commits. Loaded roots must use - the Phase A section/segment coordinates owner; no duplicate slide calculation - is permitted. -- MachOObjCSection Phase A/B integration completed at - `e1972524f608fbbf1d0551186168a117f8ec4dd7`. The integration keeps raw - section/segment coordinates as the loaded-root owner, bounds loaded method - headers to their section, and decodes 32-bit relative selector references at - their actual pointer width. Its focused union, debug/release builds, iOS and - watchOS cross-builds, unsafe audit, and branch-wide codex-review are clean. -- Diagnostic propagation completed at - `41a1652d95c34bbb2fdf32ba4b7f5cd9d3ba20ba`. All six class/protocol/category - file/image `readInfo` paths retain regular table failures and loaded - relationships in `tableDiagnostics`; existing protocol, relative-outer, and - fixed-field diagnostic arrays remain independent. Relative inner tables use - the new channel without duplicating outer-list diagnostics. -- Final MachOObjCSection validation: 119 focused Phase A/B/C and #60/#65/#79 - tests pass; debug/release, iOS arm64, watchOS arm64/arm64_32, Diagnostics SPI - compile, diff checks, independent audit, and codex-review are clean. The SHA - is published on `codex/issue-83-diagnostic-propagation`. -- MachOSwiftSection cohort pin `3344bd04e6c0819b17df126bea57bae1bbed3022` - resolves the exact Objective-C reader SHA. Debug build, six ObjC-ancestor - tests after the required fixture build, the release MachOSwiftSection product, - diff checks, and codex-review are clean. The all-product release build still - reaches its pre-existing `MachOFixtureSupport` compatible-module failure; the - library product itself compiles successfully. -- PrivateHeaderKit now uses the loaded root aggregate, routes root and subject - table diagnostics through the existing bounded member channel, and pins both - dependency SHAs. Full tests, release build, iOS/watchOS Core and test compile, - both simulator-helper release builds, release-script tests, and pin-contract - tests pass. -- Exact iOS 27 beta build `24A5390f` runtime validation is complete. The three - installed iOS 27 builds share one CoreSimulator runtime identifier, so the - documented runtime-match override temporarily selected `24A5390f`; the - previous `24A5423a` choice was restored afterward and no test device remains. - `Foundation` completed with 859 generated files, no metadata warnings, and an - `ok` database integrity check. -- `AXSpringBoardServerInstance` traversed the new loaded root/member readers, - then reproduced its known unrelated runtime-fallback abort while loading - `SpringBoardUI.framework`. The failure capsule and Crash Reporter agree on PID - 43633, helper LC_UUID `b511c0cf-6944-3e14-958d-b320a3e13a71`, and SIGABRT; - the backtrace reaches `dumpObjC`'s post-collection runtime fallback before - `+[SpringBoardUI load]` throws its intentional process-use exception.