120 lines
7.2 KiB
Markdown
120 lines
7.2 KiB
Markdown
# Installed native bridge import reproduces AppleDatabase retention
|
|
|
|
The corrected fixed-200 diagnostic reproduced the native heap inventory growth
|
|
**without a managed caller in the measured process**. With the exact installed
|
|
.NET Apple cryptography bridge still loaded, `Security::AppleDatabase` rose from
|
|
**1 to 201 objects** and stayed at 201 after five seconds idle. All observed
|
|
returned CF references balanced, native delete statuses were zero, and numeric
|
|
FD counts and owned keychain files/FDs stayed flat. The native bridge plus Apple
|
|
import/keychain path is sufficient in this control; managed .NET and the OPC UA
|
|
SDK are not required in the measured process.
|
|
|
|
This does not identify the retaining native owner or separate bridge-native
|
|
ownership from Apple caching. A classified heap object is not an open handle,
|
|
and its shallow bytes do not explain the full RSS change. The control provides
|
|
no production mitigation, budget waiver, full-soak/G4 pass or #799 closure.
|
|
|
|
## Controlled sequence and actual ownership
|
|
|
|
The measured arm ran once in a fresh private root on macOS **26.5.2** build
|
|
**25F84** arm64. Its C child hash-checked and loaded the exact installed native
|
|
bridge, verified its export image and rejected CLR/SDK images. The separate
|
|
.NET **10.0.14** SDK **2.0.1** seed made a normal RSA application fixture, verified
|
|
its private key against the public leaf, disposed the SDK manager, wrote private
|
|
mode-0600 inputs under a mode-0700 directory and exited before the measured child.
|
|
The seed's SDK PKI was removed before native measurement. The child consumed
|
|
public DER and PKCS1 private key bytes, not a PFX, and did not emulate managed
|
|
PFX parsing, matching or finalizer timing. The explicit legacy-key release was
|
|
after identity disposal. [The design](NATIVE_IMPORT_DESIGN.md) maps the actual
|
|
installed binary ABI, native exports and wrapper copy/release sequence.
|
|
|
|
The native child performed the pinned bridge's DER certificate import, modern
|
|
RSA key creation/public copy/external representation, ephemeral legacy private
|
|
key import, private keychain move/identity lookup and identity certificate copy.
|
|
Every operation exercised nine ordered wrapper keychain-copy calls. The logical
|
|
owner model counted only observed non-null copies whose type, pointer and
|
|
private generated path matched the initial native keychain. The collector
|
|
independently replayed those owner transitions and reconciled actual deletion
|
|
stage and returned CF output counts. No logical owner was invented for a null
|
|
copy. Any previously tracked item losing its association would fail the run.
|
|
|
|
All **200** moves and identity certificate copies succeeded. Native keychain
|
|
create/path/set-never-lock/delete and the reached native OSStatus outputs were
|
|
zero. The bridge operations that use return value one did return one. Both
|
|
first/last returned certificate identity checks matched the unchanged seed leaf.
|
|
All **1,800** wrapper copies had bridge-normalized status zero. Four calls per
|
|
operation returned an owned private keychain reference: post-move original cert
|
|
Track, copied cert Track, original cert Untrack and copied cert Untrack. The
|
|
other five calls returned null, including both identity calls. All **800**
|
|
returned keychain copies were released. Across all outputs the ledger was
|
|
**2,402 acquired / 2,402 released**, including **200/200** initial keychain refs.
|
|
There were no fallback cleanups or remaining logical owners.
|
|
|
|
In all 200 operations, the original cert was released, then the initial
|
|
import-state logical owner was relinquished. Deletion occurred inside the final
|
|
**copied-certificate Untrack**, while that keychain copy, copied certificate,
|
|
identity and legacy key references were still alive. The native delete returned
|
|
zero, then the initial native keychain owner, copied keychain ref and copied
|
|
certificate were released in order. The identity's subsequent Untrack returned
|
|
no keychain association. This observed order differs from the fixed identity
|
|
association wrongly assumed by [attempt A](NATIVE_IMPORT_ATTEMPT.md), which
|
|
stopped during its first operation and remains preserved separately.
|
|
|
|
## Native observations and cleanup
|
|
|
|
| Checkpoint | AppleDatabase count / shallow bytes | Numeric FDs | Owned keychain files / FDs | RSS bytes |
|
|
|---|---:|---:|---:|---:|
|
|
| Before | 1 / 768 | 3 | 0 / 0 | 7,749,632 |
|
|
| First | 2 / 1,536 | 3 | 0 / 0 | 10,862,592 |
|
|
| 200 | 201 / 154,368 | 3 | 0 / 0 | 14,598,144 |
|
|
| Five-second idle | 201 / 154,368 | 3 | 0 / 0 | 14,614,528 |
|
|
|
|
Default and search-list fingerprints were equal before and after, with all
|
|
query statuses zero and search-list count two. Four owned query copies were
|
|
released; no setter or restore API was called. All temporary private keychains
|
|
were generated with unrelated filenames, prompt disabled, and path-verified
|
|
before deletion. There were zero owned keychain files before harness cleanup.
|
|
Both builds, seed and measured process exited zero and were reaped. The remaining
|
|
private fixture files were overwritten and unlinked only after reaping; the task
|
|
root was removed. No physical secure-erasure guarantee is claimed.
|
|
|
|
Native child time was **16.772 seconds**, parent time including probes **17.022
|
|
seconds**. Heap probes were `heap --noContent -s`, numeric descriptors from
|
|
`lsof`, and RSS from Mach task info. Parent and child checkpoints use a
|
|
180-second limit, with separate bounded probe and process-reap overhead. The
|
|
C build succeeded with **six unsuppressed deprecation warnings** for the legacy
|
|
public keychain APIs it exercises. The seed build had zero warnings/errors.
|
|
|
|
The [empty native keychain control](PURE_APPLE.md) stayed at one database object
|
|
throughout its 200 operations, so empty create/delete/release alone was
|
|
insufficient. The [SDK configured-store A/B](README.md) reproduced inventory
|
|
growth despite its observed SDK and managed owner release. This native import
|
|
control narrows that observation to the installed native bridge plus
|
|
Security.framework path. It does not assign a particular .NET, SDK or Apple
|
|
defect, establish native delete status for the separate managed A/B, or show
|
|
that a public-Apple-only import sequence reproduces the result.
|
|
|
|
## Pinned evidence
|
|
|
|
The unchanged private raw result is `/tmp/opcua2-native-import-20261110b`.
|
|
[Selected receipts and hashes](evidence/2026-11-10-native-import-attempt-b/verification.json)
|
|
retain build logs, provenance, seed and cleanup receipts, and a subset of the
|
|
207 exact raw JSONL rows. Full raw heap/FD inventories and all 200 operation
|
|
rows remain local. There is no retained private fixture. Source, installed
|
|
bridge, native executable and seed DLL hashes are in the provenance.
|
|
|
|
- Result JSON SHA-256: `73771e5aea164d0207a1096d3a9ecb92603166a82a905dfc6860598b0b37f8b1`
|
|
- Full raw JSONL SHA-256: `350f35e46e159424c5e552b90e3b4a3a46d572d7ba034df274cd1646636e7498`
|
|
- Native executable SHA-256: `6a0d0d1980b505c20b5b270c8e3eceb6d0256b6b8953fbc2dad4b6f66db732cf`
|
|
- Seed DLL SHA-256: `bc9ea0f2ee2e6bd004bb2ecb801cbc4b1cbc250b1267bf31616d1f334a102893`
|
|
|
|
The fixed collector invocation requires the pinned binary set and a fresh
|
|
private output. Any further run needs its own scope and serialization review.
|
|
|
|
```sh
|
|
PYTHONDONTWRITEBYTECODE=1 python3 \
|
|
scripts/validation/opcua-lifecycle-memory/native-retention/native_import.py \
|
|
--references /tmp/opcua2-native-reload-20261110a/bin/Release/net10.0 \
|
|
--output /tmp/opcua2-native-import-FRESH
|
|
```
|