Skip to content

iOS: stop importing private Apple symbols, and gate every build on it - #5923

Merged
shai-almog merged 14 commits into
masterfrom
ios-no-private-api
Sep 30, 2026
Merged

shai-almog merged 14 commits into
masterfrom
ios-no-private-api

Conversation

@shai-almog

Copy link
Copy Markdown
Collaborator

App Store Connect rejected uploads with Validation failed (409) The app references non-public symbols: _CCCryptorGCMAddAAD, _CCCryptorGCMAddIV, _CCCryptorGCMFinal while every check was green.

Fixes

  • Executor.replaceMarker: a #define toggle matches the whole name. Enabling CN1_INCLUDE_CRYPTO no longer also enables CN1_INCLUDE_CRYPTO_GCM, so crypto users stop compiling the GCM path unless they use the vault or set ios.crypto.gcm.
  • AES-GCM rewritten on public CommonCrypto (CTR + ECB + C GHASH). Spec vectors pass; byte-identical to the old output on 3000 random cases; authenticates before decrypting.
  • IOSNative.m no longer imports sqlite3_key/sqlite3_rekey (exported by Apple's libsqlite3, undeclared in sqlite3.h) unless the bundled cipher engine defines them. Found by the new gate.

Gates

  • scripts/check-ios-private-api.py: fails on any SDK symbol no public header declares. Source mode (all feature gates on, every optional-engine variant) in scripts-ios.yml; binary mode on the Release device app in ios-packaging.yml.
  • scripts/check-builder-define-toggles.py (pr.yml) + ExecutorDefineMarkerTest.

The rationale for each is in the code comments. Companion BuildDaemon PR carries the same builder fix.

🤖 Generated with Claude Code

shai-almog and others added 2 commits September 29, 2026 16:07
App Store Connect rejected customer uploads with "Validation failed (409) The
app references non-public symbols: _CCCryptorGCMAddAAD, _CCCryptorGCMAddIV,
_CCCryptorGCMFinal", and every check here was green. A private symbol compiles
and links like any other -- "private" only means no public header declares it --
and the code sat behind a feature gate the sample app never turns on.

The fixes:

- Executor.replaceMarker switches a #define by its WHOLE name. replaceInFile was a
  plain String.replace, so enabling //#define CN1_INCLUDE_CRYPTO also uncommented
  //#define CN1_INCLUDE_CRYPTO_GCM on the next line: every app that used any
  crypto API compiled the GCM path, whatever ios.crypto.gcm said. replaceInFile
  and the five raw str.replace toggles in IPhoneBuilder now go through it.
- AES-GCM is rebuilt on public CommonCrypto only: AES-CTR from inc32(J0), H and
  the tag mask as single ECB blocks, and GHASH in portable, branch-free C. It
  passes the McGrew-Viega vectors for 128/192/256-bit keys, matches the old SPI
  output byte for byte on 3000 random cases, and now authenticates BEFORE it
  decrypts, so a forged message never writes plaintext into the caller's buffer.
- IOSNative.m no longer imports sqlite3_key / sqlite3_rekey from Apple's
  libsqlite3, which exports them but declares neither in sqlite3.h. They are
  referenced only when the bundled cipher engine defines them inside the app;
  otherwise stand-ins answer SQLITE_MISUSE, which is what Apple's copy returned.
  The new gate found this one; it shipped in 7.0.267.

The gates:

- scripts/check-ios-private-api.py fails on any SDK symbol that no public SDK
  header declares. Source mode compiles every port native with every feature
  gate on, and again with each optional engine the translator can omit hidden,
  so the branches customers compile are covered and not just the sample's. Binary
  mode reads the linked Release app and every bundle nested in it. Both self-test
  against a probe importing the three GCM symbols, before trusting a clean
  result. Wired into scripts-ios.yml (source) and ios-packaging.yml (binary).
- scripts/check-builder-define-toggles.py (pr.yml) fails on a raw .replace of a
  #define literal in the builders, and ExecutorDefineMarkerTest pins the helper.

Verified locally: reverting CN1Crypto.m/.h and IOSNative.m to master makes the
source gate report exactly the three GCM symbols plus sqlite3_key/sqlite3_rekey;
reverting replaceMarker fails 3 of the 6 unit tests. The unsigned Release device
build of hellocodenameone passes the binary gate (5 binaries, ~3750 imports).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@shai-almog

Copy link
Copy Markdown
Collaborator Author

@codex review

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 29, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-09-30T03:17:36.674470Z de57852 Manual request
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ee184f5c74

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread scripts/check-ios-private-api.py Outdated
if not imports:
# Every executable imports at least dyld's and libSystem's entry points.
fail("read no imports from %s -- nm output did not parse" % binary)
symbols = {s for s, lib in imports if not SWIFT_LIBRARY.match(lib)}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Do not discard unmangled imports from Swift libraries

When a linked dependency imports a private C or Objective-C symbol from a library whose name starts with libswift, Swift, or _Swift (including mixed frameworks such as SwiftUI), this filter removes the import solely because of its source library. classify() already skips genuinely Swift-mangled names via MANGLED_PREFIXES, so the broader library filter also hides unmangled private symbols; because third-party dependencies are covered only by binary mode, the gate can then pass an app that App Store validation rejects.

Useful? React with 👍 / 👎.

Comment on lines +471 to +474
if name.endswith((".app", ".appex", ".framework", ".xpc")):
path = executable(os.path.join(base, name))
if path:
found.append(path)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Discover executable bundles instead of filtering suffixes

When an app embeds a code-bearing .bundle or another package with CFBundleExecutable outside these four suffixes, its extensionless executable is never added to the scan; the file loop only recovers files ending in .dylib. Binary mode is the only coverage for code brought in by dependencies, so a private Apple import in such a nested executable can pass this gate even though the script claims to inspect every Mach-O in the app. Discover nested bundles from their Info.plist/CFBundleExecutable metadata rather than this suffix allowlist.

Useful? React with 👍 / 👎.

Comment thread scripts/check-ios-private-api.py Outdated
Comment on lines +546 to +552
cmd = [clang, "-c", "-arch", "arm64", "-target", "arm64-apple-ios" + target,
"-isysroot", sdk, "-fmodules", "-fmodules-cache-path=" + cache,
"-fobjc-arc" if name in arc else "-fno-objc-arc", "-w",
"-Wno-error=implicit-function-declaration", "-Wno-error=int-conversion",
"-Wno-error=incompatible-pointer-types",
"-I", NATIVE_SOURCES, "-I", tree, "-I", stub_dir,
"-include", os.path.join(tree, os.path.basename(prefix_header))]

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Exercise dependency-backed native branches in source mode

Turning on harvested #define gates does not enable branches guarded by optional headers: this compile command only adds the native, generated-project, and stub directories, so checks such as __has_include(<MLKitVision/MLKitVision.h>) in CN1Vision.m and __has_include(<TFLTensorFlowLite/TFLTensorFlowLite.h>) in CN1Inference.m remain false in the workflow's sample project. Those feature implementations are therefore absent from both the object sweep and the sample binary, allowing a private Apple call added inside an optional MLKit/LiteRT path to reach customer builds without this gate seeing it.

Useful? React with 👍 / 👎.

Comment thread scripts/check-ios-private-api.py Outdated
Comment on lines +673 to +685
sdk = sdk_root(args.sdk, args.developer_dir)
clang = find_clang(args.developer_dir)
target = deltas.CN1_DEFAULT_DEPLOYMENT_TARGET
floor = deltas.sdk_minimum_deployment_target(sdk)
if floor and deltas.sdk_version_macro(floor) > deltas.sdk_version_macro(target):
target = floor
triple = "arm64-apple-ios" + target
print("sdk : %s" % sdk)
print("clang : %s" % clang)
print("deployment target : %s" % target)

exported = exported_names(sdk)
headers = public_header_names(sdk)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Use the matching platform SDK for nested binaries

The script builds one export/header index from the default iphoneos SDK and reuses it for every executable returned from the app, including the companion watch app and watch extensions that this workflow explicitly ships. For a watchOS-only private symbol, classify() sees that the name is absent from the iPhoneOS exports and silently skips it, while differences between the public watchOS and iOS headers are likewise lost. Select the watchOS SDK index for watch binaries, rather than validating every nested platform against iPhoneOS.

Useful? React with 👍 / 👎.

@shai-almog

shai-almog commented Sep 29, 2026 •

Copy link
Copy Markdown
Collaborator Author

Compared 157 screenshots: 157 matched.

Native Android coverage

  • 📊 Line coverage: 9.37% (9335/99611 lines covered) [HTML preview] (artifact android-coverage-report, jacocoAndroidReport/html/index.html)
    • Other counters: instruction 9.10% (47873/525841), branch 3.61% (1797/49819), complexity 3.58% (1902/53096), method 5.53% (1544/27918), class 11.06% (414/3742)
    • Lowest covered classes
      • kotlin.collections.kotlin.collections.ArraysKt___ArraysKt – 0.00% (0/6367 lines covered)
      • kotlin.collections.unsigned.kotlin.collections.unsigned.UArraysKt___UArraysKt – 0.00% (0/2384 lines covered)
      • org.jacoco.agent.rt.internal_0e20598.asm.org.jacoco.agent.rt.internal_0e20598.asm.ClassReader – 0.00% (0/1524 lines covered)
      • kotlin.collections.kotlin.collections.CollectionsKt___CollectionsKt – 0.00% (0/1187 lines covered)
      • org.jacoco.agent.rt.internal_0e20598.asm.org.jacoco.agent.rt.internal_0e20598.asm.MethodWriter – 0.00% (0/922 lines covered)
      • kotlin.sequences.kotlin.sequences.SequencesKt___SequencesKt – 0.00% (0/736 lines covered)
      • com.google.common.cache.com.google.common.cache.LocalCache$Segment – 0.00% (0/726 lines covered)
      • okio.okio.Buffer – 0.00% (0/687 lines covered)
      • kotlin.text.kotlin.text.StringsKt___StringsKt – 0.00% (0/625 lines covered)
      • org.jacoco.agent.rt.internal_0e20598.asm.org.jacoco.agent.rt.internal_0e20598.asm.Frame – 0.00% (0/570 lines covered)

✅ Native Android screenshot tests passed.

Native Android coverage

  • 📊 Line coverage: 9.37% (9335/99611 lines covered) [HTML preview] (artifact android-coverage-report, jacocoAndroidReport/html/index.html)
    • Other counters: instruction 9.10% (47873/525841), branch 3.61% (1797/49819), complexity 3.58% (1902/53096), method 5.53% (1544/27918), class 11.06% (414/3742)
    • Lowest covered classes
      • kotlin.collections.kotlin.collections.ArraysKt___ArraysKt – 0.00% (0/6367 lines covered)
      • kotlin.collections.unsigned.kotlin.collections.unsigned.UArraysKt___UArraysKt – 0.00% (0/2384 lines covered)
      • org.jacoco.agent.rt.internal_0e20598.asm.org.jacoco.agent.rt.internal_0e20598.asm.ClassReader – 0.00% (0/1524 lines covered)
      • kotlin.collections.kotlin.collections.CollectionsKt___CollectionsKt – 0.00% (0/1187 lines covered)
      • org.jacoco.agent.rt.internal_0e20598.asm.org.jacoco.agent.rt.internal_0e20598.asm.MethodWriter – 0.00% (0/922 lines covered)
      • kotlin.sequences.kotlin.sequences.SequencesKt___SequencesKt – 0.00% (0/736 lines covered)
      • com.google.common.cache.com.google.common.cache.LocalCache$Segment – 0.00% (0/726 lines covered)
      • okio.okio.Buffer – 0.00% (0/687 lines covered)
      • kotlin.text.kotlin.text.StringsKt___StringsKt – 0.00% (0/625 lines covered)
      • org.jacoco.agent.rt.internal_0e20598.asm.org.jacoco.agent.rt.internal_0e20598.asm.Frame – 0.00% (0/570 lines covered)

Benchmark Results

Detailed Performance Metrics

Metric Duration
SIMD kernel backend scalar fallback (no native SIMD)
SIMD int-add (64K x300) java 119ms / native 215ms = 0.5x speedup
SIMD float-mul (64K x300) java 91ms / native 88ms = 1.0x speedup
SIMD kernel correctness PASS (native result == scalar reference)
Base64 payload size 8192 bytes
Base64 benchmark iterations 6000
Base64 SIMD byte path gated to scalar (CPU autovectorizes scalar; explicit SIMD not beneficial here)
Base64 CN1 encode 77.000 ms
Base64 CN1 decode 70.000 ms
Base64 native encode 506.000 ms
Base64 encode ratio (CN1/native) 0.152x (84.8% faster)
Base64 native decode 328.000 ms
Base64 decode ratio (CN1/native) 0.213x (78.7% faster)
Image encode benchmark status skipped (SIMD unsupported)

@shai-almog

shai-almog commented Sep 29, 2026 •

Copy link
Copy Markdown
Collaborator Author

Compared 193 screenshots: 193 matched.
✅ JavaScript-port screenshot tests passed.

@github-actions

github-actions Bot commented Sep 29, 2026 •

Copy link
Copy Markdown
Contributor

✅ Continuous Quality Report

Test & Coverage

Static Analysis

  • SpotBugs [Report archive]
    • ✅ ByteCodeTranslator: 0 findings (no issues)
    • ✅ android: 0 findings (no issues)
    • ✅ backend: 0 findings (no issues)
    • ✅ build-hint-catalog: 0 findings (no issues)
    • ✅ build-hint-tools: 0 findings (no issues)
    • ✅ codenameone-maven-plugin: 0 findings (no issues)
    • ✅ core-unittests: 0 findings (no issues)
    • ✅ ios: 0 findings (no issues)
  • ✅ PMD: 0 findings (no issues) [Report archive]
  • ✅ Checkstyle: 0 findings (no issues) [Report archive]

Generated automatically by the PR CI workflow.

…hing by library

Review findings on the binary mode, which is the only coverage dependencies get:

- Imports from a Swift library were dropped wholesale, which would also hide an
  unmangled private C symbol a Swift library re-exports. Mangled names were
  already skipped by name; now only the swift_* runtime entry points, and only
  when they bind to a libswift library, are credited. The one name this surfaced
  in the sample, _TtCs12_SwiftObject, is a mangled Swift class name and is
  skipped under the same rule.
- Nested executables were found by bundle suffix; a code-bearing .bundle or an
  extensionless executable anywhere else was missed. Every Mach-O is now found
  by its magic.
- Every binary was judged against the iPhoneOS SDK, so a watchOS-only private
  symbol in the companion watch app was absent from the exports and passed. The
  platform is read from each binary's LC_BUILD_VERSION and indexed per SDK.

Source mode now also names the __has_include(<...>) branches it cannot compile
(MLKit and LiteRT pod headers, and SDK headers newer than the one in use), so
that gap is printed on every run instead of passing silently.

Sample Release device app: clean, 3 iOS and 2 watchOS binaries.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@shai-almog

Copy link
Copy Markdown
Collaborator Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Another round soon, please!

Reviewed commit: 1bab8ca349

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

The native contract stages the builder's own INCLUDE_CN1_PUSH2 disabling
replacement by matching IPhoneBuilder's source, and it looked for the literal
str.replace(...) call this branch moved onto Executor.replaceMarker. It now
matches the replaceMarker call; the pair it extracts is unchanged, and both the
push-enabled and push-disabled configurations pass locally.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@shai-almog

shai-almog commented Sep 29, 2026 •

Copy link
Copy Markdown
Collaborator Author

Compared 150 screenshots: 150 matched.
✅ Native Apple TV (tvOS, Metal) screenshot tests passed.

@github-actions

github-actions Bot commented Sep 29, 2026 •

Copy link
Copy Markdown
Contributor

✅ ByteCodeTranslator Quality Report

Test & Coverage

  • ✅ Tests: 740 total, 0 failed, 57 skipped

Benchmark Results

  • Execution Time: 20975 ms

  • Hotspots (Top 20 sampled methods):

    • 9.91% com.codename1.tools.translator.BytecodeMethod.equals (197 samples)
    • 6.94% com.codename1.tools.translator.IteratorEscape.ctorOnlyStoresParamsIntoThis (138 samples)
    • 4.43% java.lang.StringBuilder.append (88 samples)
    • 3.77% org.objectweb.asm.tree.analysis.Frame.merge (75 samples)
    • 3.07% java.util.ArrayList.indexOf (61 samples)
    • 2.21% com.codename1.tools.translator.JavascriptReachability.enqueueResolved (44 samples)
    • 2.01% com.codename1.tools.translator.IteratorEscape.mangle (40 samples)
    • 1.91% com.codename1.tools.translator.BytecodeMethod.optimize (38 samples)
    • 1.86% java.lang.System.identityHashCode (37 samples)
    • 1.86% java.util.HashMap.putVal (37 samples)
    • 1.86% com.codename1.tools.translator.ByteCodeClass.hasDeclaredMethod (37 samples)
    • 1.71% com.codename1.tools.translator.bytecodes.Invoke.resolveDirectTarget (34 samples)
    • 1.51% java.lang.String.equals (30 samples)
    • 1.36% java.io.FileOutputStream.writeBytes (27 samples)
    • 1.31% java.io.FileOutputStream.open0 (26 samples)
    • 1.26% java.lang.Object.hashCode (25 samples)
    • 1.21% com.codename1.tools.translator.IteratorEscape.walk (24 samples)
    • 1.16% com.codename1.tools.translator.ByteCodeClass.generateCCode (23 samples)
    • 1.11% java.lang.StringCoding.encode (22 samples)
    • 1.06% com.codename1.tools.translator.NativeSymbolIndex.<init> (21 samples)
  • ⚠️ Coverage report not generated.

Static Analysis

  • ✅ SpotBugs: no findings (report was not generated by the build).
  • ⚠️ PMD report not generated.
  • ⚠️ Checkstyle report not generated.

Generated automatically by the PR CI workflow.

@shai-almog

shai-almog commented Sep 29, 2026 •

Copy link
Copy Markdown
Collaborator Author

Compared 223 screenshots: 223 matched.
✅ Native Apple Watch (watchOS, Core Graphics) screenshot tests passed.

The screenshot suites failed on iOS, tvOS, watchOS and Mac Catalyst with one
test: "Crypto API test failed: crypto operation failed with code -5", which is
CN1_CRYPTO_E_UNSUPPORTED from an AES-GCM round trip.

GCM was meant to be opt-in (vault or ios.crypto.gcm=true) because it called
CommonCrypto's private SPI. But the prefix-matching replace this branch fixed
enabled it for every crypto user anyway, so AES-GCM through
com.codename1.security has always worked on iOS -- CryptoApiTest depends on it,
and MacOSBuildHints defaults macOS to on to match. Fixing the replace turned it
off for those applications.

With GCM now built from public calls, the reason for opt-in is gone:

- CN1Crypto.h carries CN1_CRYPTO_GCM_PUBLIC_API. For such a port GCM is on
  whenever the crypto API is, unless ios.crypto.gcm=false trims it, and the
  "cannot tell whether it uses the vault" build stop no longer applies.
- A port without the marker -- anything released before it, which a pinned
  build.cn1Version can stage -- keeps the opt-in rule, since there enabling GCM
  links the private SPI.
- resolveCryptoGcm / cryptoGcmUndecidable hold the rule, pinned by
  CryptoGcmDecisionTest; the catalog now documents ios.crypto.gcm as default true.

The regenerated sample reports "Crypto API enabled, AES-GCM enabled", and the
private-API gate is still clean.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@shai-almog

shai-almog commented Sep 30, 2026 •

Copy link
Copy Markdown
Collaborator Author

Compared 166 screenshots: 166 matched.
✅ Native Mac screenshot tests passed.

Benchmark Results

  • VM Translation Time: 0 seconds
  • Compilation Time: 331 seconds

Detailed Performance Metrics

Metric Duration
SIMD kernel backend SSE2 (x64) / NEON (arm64) native kernels
SIMD int-add (64K x300) java 75ms / native 3ms = 25.0x speedup
SIMD float-mul (64K x300) java 61ms / native 3ms = 20.3x speedup
SIMD kernel correctness PASS (native result == scalar reference)
Base64 native bridge unavailable (CN1 + SIMD + image benchmarks only)
Base64 payload size 8192 bytes
Base64 benchmark iterations 6000
Base64 SIMD byte path active (NEON-accelerated)
Base64 CN1 encode 48.000 ms
Base64 CN1 decode 57.000 ms
Image encode benchmark iterations 100
Image createMask (SIMD off) 11.000 ms
Image createMask (SIMD on) 5.000 ms
Image createMask ratio (SIMD on/off) 0.455x (54.5% faster)
Image applyMask (SIMD off) 60.000 ms
Image applyMask (SIMD on) 60.000 ms
Image applyMask ratio (SIMD on/off) 1.000x (0.0% slower)
Image modifyAlpha (SIMD off) 52.000 ms
Image modifyAlpha (SIMD on) 46.000 ms
Image modifyAlpha ratio (SIMD on/off) 0.885x (11.5% faster)
Image modifyAlpha removeColor (SIMD off) 66.000 ms
Image modifyAlpha removeColor (SIMD on) 46.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off) 0.697x (30.3% faster)

ParparVM vs HotSpot (JDK 25): macOS arm64

Runner CPU: Apple M1 (Virtual) (baseline macos-arm64)

Ratios are ParparVM / JDK 25: below 1.00x ParparVM is faster (time) or smaller (RAM). Median of 5 interleaved, paired rounds; every run's output was verified. A regression is a ratio more than 15% (time) / 15% (RAM) above its baseline in vm/selfhost/perf-baseline.json (more, for a row whose calibration runs were noisier; the file records it), and for RAM also more than 0.05x above it in absolute terms. Both run unpinned on all of the runner's CPUs, with their own default thread counts.

Benchmark Cores Time RAM Status
hello (HelloCodenameOne) 3 0.77x (base 0.96x, -19.5%) 1.18x (base 1.34x, -12.2%) ok
translator (self) 3 0.52x (base 0.54x, -3.8%) 0.77x (base 0.74x, +4.4%) ok
intArithmetic 3 1.05x (base 1.03x, +1.7%) 0.12x (base 0.12x, -0.4%) ok
longArithmetic 3 1.02x (base 1.03x, -0.8%) 0.11x (base 0.11x, -2.2%) ok
mathTranscendental 3 0.98x (base 1.01x, -2.4%) 0.12x (base 0.12x, -0.4%) ok
arraySequential 3 0.45x (base 0.42x, +6.4%) 0.53x (base 0.53x, -0.2%) ok
arrayRandom 3 0.97x (base 0.99x, -1.5%) 0.32x (base 0.32x, -0.1%) ok
objectAllocation 3 3.89x (base 3.70x, +5.3%) 0.51x (base 0.48x, +5.4%) ok
valueEscape 3 0.53x (base 0.51x, +4.8%) 0.08x (base 0.09x, -3.9%) ok
hashMapChurn 3 0.99x (base 1.18x, -16.0%) 0.07x (base 0.07x, +3.3%) ok
stringBuilding 3 0.85x (base 0.84x, +1.1%) 0.47x (base 0.54x, -13.6%) ok
recursion 3 1.21x (base 1.24x, -1.8%) 0.12x (base 0.12x, -0.5%) ok
quicksort 3 0.99x (base 1.01x, -2.6%) 0.11x (base 0.11x, -1.2%) ok

Result: no regression

@github-actions

github-actions Bot commented Sep 30, 2026 •

Copy link
Copy Markdown
Contributor

Developer Guide build artifacts are available for download from this workflow run:

Developer Guide quality checks:

  • AsciiDoc linter: No issues found (report)
  • Vale: No alerts found (report)
  • Paragraph capitalization: No paragraph capitalization issues (report)
  • LanguageTool: No grammar matches (report)
  • Image references: No unused images detected (report)

@github-actions

Copy link
Copy Markdown
Contributor

Cloudflare Preview

@shai-almog

shai-almog commented Sep 30, 2026 •

Copy link
Copy Markdown
Collaborator Author

Compared 154 screenshots: 154 matched.
✅ Native Mac screenshot tests passed.

Benchmark Results

  • VM Translation Time: 0 seconds
  • Compilation Time: 399 seconds

Detailed Performance Metrics

Metric Duration
SIMD kernel backend SSE2 (x64) / NEON (arm64) native kernels
SIMD int-add (64K x300) java 73ms / native 3ms = 24.3x speedup
SIMD float-mul (64K x300) java 72ms / native 3ms = 24.0x speedup
SIMD kernel correctness PASS (native result == scalar reference)
Base64 payload size 8192 bytes
Base64 benchmark iterations 6000
Base64 SIMD byte path active (NEON-accelerated)
Base64 CN1 encode 74.000 ms
Base64 CN1 decode 84.000 ms
Base64 native encode 1020.000 ms
Base64 encode ratio (CN1/native) 0.073x (92.7% faster)
Base64 native decode 396.000 ms
Base64 decode ratio (CN1/native) 0.212x (78.8% faster)
Base64 SIMD encode 59.000 ms
Base64 encode ratio (SIMD/CN1) 0.797x (20.3% faster)
Base64 SIMD decode 63.000 ms
Base64 decode ratio (SIMD/CN1) 0.750x (25.0% faster)
Base64 encode ratio (SIMD/native) 0.058x (94.2% faster)
Base64 decode ratio (SIMD/native) 0.159x (84.1% faster)
Image encode benchmark iterations 100
Image createMask (SIMD off) 11.000 ms
Image createMask (SIMD on) 5.000 ms
Image createMask ratio (SIMD on/off) 0.455x (54.5% faster)
Image applyMask (SIMD off) 79.000 ms
Image applyMask (SIMD on) 76.000 ms
Image applyMask ratio (SIMD on/off) 0.962x (3.8% faster)
Image modifyAlpha (SIMD off) 56.000 ms
Image modifyAlpha (SIMD on) 77.000 ms
Image modifyAlpha ratio (SIMD on/off) 1.375x (37.5% slower)
Image modifyAlpha removeColor (SIMD off) 57.000 ms
Image modifyAlpha removeColor (SIMD on) 78.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off) 1.368x (36.8% slower)

@shai-almog

Copy link
Copy Markdown
Collaborator Author

@codex review

@shai-almog

shai-almog commented Sep 30, 2026 •

Copy link
Copy Markdown
Collaborator Author

Compared 155 screenshots: 155 matched.
✅ Native iOS Metal screenshot tests passed.

Benchmark Results

  • VM Translation Time: 0 seconds
  • Compilation Time: 1847 seconds

Build and Run Timing

Metric Duration
Simulator Boot 1000 ms
Simulator Boot (Run) 1000 ms
App Install 24000 ms
App Launch 10000 ms
Test Execution 453000 ms

Detailed Performance Metrics

Metric Duration
SIMD kernel backend SSE2 (x64) / NEON (arm64) native kernels
SIMD int-add (64K x300) java 121ms / native 4ms = 30.2x speedup
SIMD float-mul (64K x300) java 294ms / native 6ms = 49.0x speedup
SIMD kernel correctness PASS (native result == scalar reference)
Base64 payload size 8192 bytes
Base64 benchmark iterations 6000
Base64 SIMD byte path active (NEON-accelerated)
Base64 CN1 encode 88.000 ms
Base64 CN1 decode 92.000 ms
Base64 native encode 2919.000 ms
Base64 encode ratio (CN1/native) 0.030x (97.0% faster)
Base64 native decode 1924.000 ms
Base64 decode ratio (CN1/native) 0.048x (95.2% faster)
Base64 SIMD encode 116.000 ms
Base64 encode ratio (SIMD/CN1) 1.318x (31.8% slower)
Base64 SIMD decode 94.000 ms
Base64 decode ratio (SIMD/CN1) 1.022x (2.2% slower)
Base64 encode ratio (SIMD/native) 0.040x (96.0% faster)
Base64 decode ratio (SIMD/native) 0.049x (95.1% faster)
Image encode benchmark iterations 100
Image createMask (SIMD off) 10.000 ms
Image createMask (SIMD on) 5.000 ms
Image createMask ratio (SIMD on/off) 0.500x (50.0% faster)
Image applyMask (SIMD off) 522.000 ms
Image applyMask (SIMD on) 43.000 ms
Image applyMask ratio (SIMD on/off) 0.082x (91.8% faster)
Image modifyAlpha (SIMD off) 59.000 ms
Image modifyAlpha (SIMD on) 538.000 ms
Image modifyAlpha ratio (SIMD on/off) 9.119x (811.9% slower)
Image modifyAlpha removeColor (SIMD off) 39.000 ms
Image modifyAlpha removeColor (SIMD on) 45.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off) 1.154x (15.4% slower)

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c7cdc086ad

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread scripts/check-ios-private-api.py Outdated
def public_header_names(sdk):
"""Every identifier the SDK's headers spell outside a comment."""
names = set()
for header in walk(sdk, (".h",)):

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Exclude private SDK headers from the public-name index

When an SDK ships headers under PrivateHeaders or System/Library/PrivateFrameworks, this unrestricted recursive walk adds their identifiers to the supposedly public index. classify() will consequently accept an imported private symbol merely because its private header declares the name, so the gate can pass the same kind of binary that App Store validation rejects. Restrict this index to genuinely public header locations and explicitly exclude private-header/framework trees.

Useful? React with 👍 / 👎.

Comment thread scripts/check-ios-private-api.py Outdated
Comment on lines +592 to +593
cmd = [clang, "-c", "-arch", "arm64", "-target", "arm64-apple-ios" + target,
"-isysroot", sdk, "-fmodules", "-fmodules-cache-path=" + cache,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Compile and scan the tvOS-specific native branches

The source sweep always compiles with an iOS target, so code guarded by #if TARGET_OS_TV is preprocessed away. I also checked .github/workflows/scripts-ios.yml: the two new private-API steps run only in the iOS Metal jobs, while build-ios-tv builds the tvOS target without passing its product to this checker; the binary step in ios-packaging.yml scans an iOS app and cannot cover the separate tvOS product. A private Apple call added to one of the existing tvOS-only branches can therefore merge and ship without either mode examining it.

Useful? React with 👍 / 👎.

Comment thread scripts/check-ios-private-api.py Outdated

def imports_of_binary(path):
"""(symbol, library) for every import of one Mach-O, per `nm -m`."""
out = run(["nm", "-m", "-u", "-arch", "arm64", path])

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Scan every architecture in fat Mach-O binaries

When a shipped Mach-O contains an arm64 slice plus another device slice such as arm64e, this command succeeds after dumping only arm64, so the fallback that scans without -arch never runs. The tool's Mach-O help defines --arch as selecting the architecture(s) to dump; therefore an architecture-conditional private import present only in the other slice is invisible even though App Store validation examines that slice too. Enumerate and scan every slice, or omit the architecture filter for binary mode.

Useful? React with 👍 / 👎.

… keeps GCM

Review findings on #5923:

- tvOS was never checked: the source sweep compiled for iOS, so every
  TARGET_OS_TV branch was preprocessed away, and the tvOS app is a separate
  product no job scanned. The sweep now takes --sdk appletvsimulator (the only
  tvOS build any job makes; the device SDK cannot compile the port at all, having
  no LocalAuthentication, which IOSNative.m imports unconditionally). Gates whose
  code tvOS cannot compile are derived, round by round, from where clang's errors
  fall, and listed; an error inside no enabled gate stays fatal. 10 gates today
  (OIDC, HomeKit, NFC, StoreKit, contacts, ...). The tvOS job runs it, and
  run-tv-ui-tests.sh binary-checks the tvOS app right after building it.
- nm read only the arm64 slice of a universal binary (`-arch arm64` succeeds and
  skips arm64e); it now reads every slice.
- Headers under PrivateHeaders or PrivateFrameworks no longer count as public.
  No SDK in Xcode 26 or 27 ships either, but one that did would make a private
  import read as declared.
- A detected vault user keeps AES-GCM even with ios.crypto.gcm=false, as on the
  private-SPI path; otherwise moving to the public-GCM port would break an
  existing vault app at run time.

Verified: iOS and tvOS source sweeps clean, the Release app still clean, a tvOS
simulator binary importing CCCryptorGCMAddIV is reported, 400 plugin tests pass.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@shai-almog

Copy link
Copy Markdown
Collaborator Author

@codex review

@shai-almog

shai-almog commented Sep 30, 2026 •

Copy link
Copy Markdown
Collaborator Author

Compared 155 screenshots: 155 matched.
✅ Native iOS Metal screenshot tests passed.

Benchmark Results

  • VM Translation Time: 0 seconds
  • Compilation Time: 2573 seconds

Build and Run Timing

Metric Duration
Simulator Boot 89000 ms
Simulator Boot (Run) 1000 ms
App Install 15000 ms
App Launch 1000 ms
Test Execution 510000 ms

Detailed Performance Metrics

Metric Duration
SIMD kernel backend SSE2 (x64) / NEON (arm64) native kernels
SIMD int-add (64K x300) java 70ms / native 3ms = 23.3x speedup
SIMD float-mul (64K x300) java 127ms / native 3ms = 42.3x speedup
SIMD kernel correctness PASS (native result == scalar reference)
Base64 payload size 8192 bytes
Base64 benchmark iterations 6000
Base64 SIMD byte path active (NEON-accelerated)
Base64 CN1 encode 85.000 ms
Base64 CN1 decode 86.000 ms
Base64 native encode 421.000 ms
Base64 encode ratio (CN1/native) 0.202x (79.8% faster)
Base64 native decode 807.000 ms
Base64 decode ratio (CN1/native) 0.107x (89.3% faster)
Base64 SIMD encode 101.000 ms
Base64 encode ratio (SIMD/CN1) 1.188x (18.8% slower)
Base64 SIMD decode 46.000 ms
Base64 decode ratio (SIMD/CN1) 0.535x (46.5% faster)
Base64 encode ratio (SIMD/native) 0.240x (76.0% faster)
Base64 decode ratio (SIMD/native) 0.057x (94.3% faster)
Image encode benchmark iterations 100
Image createMask (SIMD off) 17.000 ms
Image createMask (SIMD on) 3.000 ms
Image createMask ratio (SIMD on/off) 0.176x (82.4% faster)
Image applyMask (SIMD off) 452.000 ms
Image applyMask (SIMD on) 36.000 ms
Image applyMask ratio (SIMD on/off) 0.080x (92.0% faster)
Image modifyAlpha (SIMD off) 558.000 ms
Image modifyAlpha (SIMD on) 36.000 ms
Image modifyAlpha ratio (SIMD on/off) 0.065x (93.5% faster)
Image modifyAlpha removeColor (SIMD off) 59.000 ms
Image modifyAlpha removeColor (SIMD on) 35.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off) 0.593x (40.7% faster)

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 31f1de327f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

# IOSNative.m imports unconditionally (the simulator SDK does ship it).
SDK_TRIPLES = {
"iphoneos": ("arm64-apple-ios", ""),
"appletvsimulator": ("arm64-apple-tvos", "-simulator"),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Cover device-only tvOS branches in the API sweep

The new tvOS source sweep targets only appletvsimulator, and run-tv-ui-tests.sh likewise checks only the simulator product. Fresh evidence beyond the earlier tvOS coverage issue is the existing device-only block in CN1JailbreakDetector.m under #if !(TARGET_IPHONE_SIMULATOR): that code is omitted from both checks, even though customers archive the separate appletvos target for App Store submission. A tvOS-private import in such a device-only branch can therefore pass this gate; add an appletvos source sweep (working around the unconditional unavailable imports as needed) or scan an archived device product.

Useful? React with 👍 / 👎.

/// asked through the hint. With public GCM, enabling it is harmless and nothing is asked.
static String cryptoGcmUndecidable(boolean publicGcm, boolean usesVault, boolean vaultUnknown,
String hint) {
if (publicGcm || !vaultUnknown || usesVault || "true".equals(hint) || "false".equals(hint)) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve crypto when vault scanning is inconclusive

When the database scan exhausts its budget on an archive under buildinRes, it sets vaultUnknown, but the separate permission scan only walks classesDir, so usesCryptoAPI can remain false. Treating every public-GCM port as immediately decidable then lets the build continue, and resolveCryptoGcm() returns false at its !usesCryptoAPI guard while the base CN1_INCLUDE_CRYPTO marker is also left disabled. If the unread portion actually uses Vault, the shipped app reaches the unsupported crypto stubs; even setting ios.crypto.gcm=true cannot repair this path. Public GCM removes the private-symbol risk, so an unknown vault result should conservatively enable the base crypto and GCM implementation or still stop for an explicit decision.

Useful? React with 👍 / 👎.

shai-almog and others added 2 commits September 30, 2026 05:17
…requires it

A tvOS slice compiles the port with the switches the iOS app turned on, and ten
of them could not compile for tvOS at all, so an app using any of them with a
TV slice failed its TV build; and no tvOS device build could succeed, because
IOSNative.m imported LocalAuthentication, which the tvOS device SDK lacks.

- LocalAuthentication is imported only where the SDK has it.
- Contacts (AddressBook), the photo picker, NFC, OIDC, hotspot and Wi-Fi info
  are switched off for TARGET_OS_TV. Their natives are defined outside the
  switches, so they answer "unsupported" as for an app without the feature.
- StoreKit on tvOS looks the product up and pays with paymentWithProduct:,
  since paymentWithProductIdentifier: does not exist there; a product the store
  does not return reports through itemPurchaseError.
- App review is a no-op on tvOS (no SKStoreReviewController).
- HomeKit scene create/delete answer NOT_SUPPORTED on tvOS, where HomeKit cannot
  build action sets; everything else in HomeKit is unchanged.
- HeadphonesDetector moves off the C AudioSession API (deprecated, absent on
  tvOS) to AVAudioSession route changes, and starts itself: nothing ever created
  it, so ios.headphoneCallback never fired on any device.

The tvOS sweep of check-ios-private-api.py is now strict -- every gate on,
against both appletvos and appletvsimulator, no gate dropped -- so a feature
that breaks tvOS fails CI instead of every customer's TV build. The module
cache check-ios-sdk-deltas.py compiled through was a fixed shared name under
the temp directory; it is per process now, and inside the caller's work
directory for the private-API gate.

Verified: all three sweeps (iphoneos, appletvos, appletvsimulator) compile 172
objects with 79 gates and are clean; the sample's TV target builds for the
tvOS DEVICE SDK (it could not before) and its app passes binary mode; the push
completion contract passes.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Review finding: when the vault scan runs out of budget on a library under
buildinRes, vaultUnknown is set but the permission scan, which walks only the
application's classes, can leave usesCryptoAPI false. With the old port the
build stopped and asked; with public GCM it went on, and resolveCryptoGcm's
!usesCryptoAPI guard left both CN1_INCLUDE_CRYPTO and GCM off -- a library
that did use the vault reached the unsupported stubs, and not even
ios.crypto.gcm=true could repair it here.

cryptoApiRequired now decides the base crypto flag: detected vault use turns
it on; an unknown answer does too with public GCM (it links nothing Apple
rejects) unless ios.crypto.gcm=false says there is no vault; with an older
port only an explicit true does, as BuildDaemon already had. CryptoGcmDecisionTest
covers it; 401 plugin tests pass.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@shai-almog

Copy link
Copy Markdown
Collaborator Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 00ef210945

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +516 to +520
PLATFORM_SDKS = {
"1": "macosx", "2": "iphoneos", "3": "appletvos", "4": "watchos", "6": "macosx",
"7": "iphonesimulator", "8": "appletvsimulator", "9": "watchsimulator",
"11": "xros", "12": "xrsimulator",
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Accept symbolic LC_BUILD_VERSION platform names

On otool versions that print symbolic platform values such as IOS, TVOSSIMULATOR, or WATCHOSSIMULATOR, this numeric-only map makes platform_sdk() abort before inspecting the first binary. The repository already accounts for this variation in WatchNativeBuilder.java:2503-2517, but both newly added binary gates depend on this parser, so affected Xcode runners will fail every packaging/tvOS run instead of checking imports. Normalize the captured value and map both numeric and symbolic spellings.

Useful? React with 👍 / 👎.

Comment thread scripts/check-ios-private-api.py Outdated
Comment on lines +801 to +804
target = deltas.CN1_DEFAULT_DEPLOYMENT_TARGET
floor = deltas.sdk_minimum_deployment_target(sdk)
if floor and deltas.sdk_version_macro(floor) > deltas.sdk_version_macro(target):
target = floor

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Read the tvOS deployment floor for tvOS sweeps

When the new --sdk appletvos or appletvsimulator sweeps run under Xcode 27, this calls sdk_minimum_deployment_target(), which extracts only SupportedTargets.iphoneos.MinimumDeploymentTarget and therefore returns no floor for a tvOS SDK. The sweep stays at 14.0 even though TvNativeBuilder.java:184-197 raises real tvOS builds to the SDK's 15.0 floor because lower targets fail; this can either stop the gate before it scans anything or preprocess different deployment-target branches than the shipped product. Select the SupportedTargets key corresponding to args.sdk.

Useful? React with 👍 / 👎.

…loor

Review findings:

- Some otool versions print LC_BUILD_VERSION platforms symbolically (IOS,
  TVOSSIMULATOR, WATCHOSSIMULATOR) rather than as numbers, and the parser knew
  only the numbers, so binary mode would abort on the first binary on such a
  runner. Both spellings map now, as WatchNativeBuilder already accepts;
  platform_of() is separate from the otool call so it can be exercised alone.
- sdk_minimum_deployment_target read only SupportedTargets.iphoneos, which a
  tvOS SDK does not have, so tvOS sweeps got no floor and stayed below it (under
  Xcode 27 the tvOS floor is 15.0). It takes the SDK's own key now, and a tvOS
  sweep starts from TvNativeBuilder's 13.0 default before raising, as the builder
  does.

Verified under Xcode 27 and 26.3: appletvos, appletvsimulator and iphoneos
sweeps are clean at 15.0/15.0/15.0 and 13.0/13.0/14.0; the iOS Release app and
the tvOS device app still pass binary mode.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@shai-almog

Copy link
Copy Markdown
Collaborator Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4eab570e04

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread scripts/check-ios-private-api.py Outdated
# file's, told apart by the word after it: an architecture count for Mach-O (a handful), the
# class-file version for Java (45 and up).
MACHO_THIN = {b"\xfe\xed\xfa\xce", b"\xce\xfa\xed\xfe", b"\xfe\xed\xfa\xcf", b"\xcf\xfa\xed\xfe"}
MACHO_FAT = {b"\xca\xfe\xba\xbe", b"\xca\xfe\xba\xbf"}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Recognize byte-swapped fat Mach-O files

When an embedded dependency uses the valid FAT_CIGAM or FAT_CIGAM_64 encoding, is_macho() returns false because this set contains only FAT_MAGIC/FAT_MAGIC_64; the architecture count is also decoded only as big-endian. Consequently binaries_in() silently omits that executable and binary mode can pass despite a private import inside it. Add the swapped magics and decode nfat_arch using the byte order selected by the magic.

Useful? React with 👍 / 👎.

Review finding: is_macho() knew only FAT_MAGIC / FAT_MAGIC_64, so an embedded
executable whose universal header is stored little-endian (FAT_CIGAM /
FAT_CIGAM_64, equally valid) was never found, and a private import in it
passed. Both byte orders are recognised now, and the architecture count is read
in the order the magic announces. Checked against synthetic headers in all four
encodings plus a Java class file and a thin Mach-O; the sample app still yields
its 5 binaries and passes.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@shai-almog

Copy link
Copy Markdown
Collaborator Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a0cea2c598

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread scripts/check-ios-private-api.py Outdated
Comment on lines +516 to +517
if os.path.islink(path) or APPLE_EMBEDDED.match(name):
continue

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Validate libswift dylibs instead of skipping by basename

When an app embeds a third-party Mach-O named libswiftFoo.dylib, this condition skips the entire binary solely because its basename resembles an Apple runtime library. Fresh evidence beyond the earlier import-filter issue is that the narrower _swift_ symbol exemption never runs for this skipped file. Because dependencies are covered only by binary mode, an unmangled private Apple import in such a dylib passes this gate even though App Store validation still scans it; restrict the whole-file exemption to verified Xcode runtime artifacts, or scan the binary and exempt only its known runtime symbols.

Useful? React with 👍 / 👎.

shai-almog and others added 4 commits September 30, 2026 06:03
…hem by name

Review finding: any Frameworks/libswift*.dylib was skipped whole because its
name looked like Apple's back-deployed Swift runtime, so a third-party
libswiftFoo.dylib -- and any private import in it -- passed unread. Nothing is
skipped by name now; Apple's runtime passes on its own because the few
unmangled internals it imports are already listed one by one in
COMPILER_EMITTED. The sample app's libswift_Concurrency.dylib is now scanned
(235 imports) and the app still passes.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…dening

The gate exists to keep OUR port and OUR sample application off private Apple
API. Binary mode reads one application we build, whose contents are a known,
closed set: the port, the translated code, the sample's Swift extensions and
watch app, the cn1libs it bundles, and Apple's embedded Swift runtime. Several
review rounds hardened it against inputs that set never contains, which added
code without adding coverage:

- byte-swapped universal headers and the libswift-by-name scan are reverted
  (the two preceding reverts);
- nested executables are found by bundle again -- .app/.appex/.framework/.xpc
  and each one's CFBundleExecutable -- instead of by sniffing every file's magic.

Kept: reading every architecture slice, per-platform SDKs for the watch and TV
binaries, symbolic otool platforms, and the tvOS sweeps, which found and now
guard real breakage. The docstring states the scope.

Verified: the iOS Release app (5 binaries) and the tvOS device app pass, and a
probe importing CCCryptorGCMAddIV is still reported.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@shai-almog

Copy link
Copy Markdown
Collaborator Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. More of your lovely PRs please.

Reviewed commit: de57852fd6

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@shai-almog
shai-almog merged commit 6454b64 into master Sep 30, 2026
50 checks passed
@shai-almog
shai-almog deleted the ios-no-private-api branch September 30, 2026 05:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant