diff --git a/scripts/validation/opcua-lifecycle-memory/native-retention/NATIVE_IMPORT_TIMING.md b/scripts/validation/opcua-lifecycle-memory/native-retention/NATIVE_IMPORT_TIMING.md new file mode 100644 index 00000000..59362034 --- /dev/null +++ b/scripts/validation/opcua-lifecycle-memory/native-retention/NATIVE_IMPORT_TIMING.md @@ -0,0 +1,120 @@ +# Deferring native keychain deletion did not prevent inventory growth + +The fixed paired diagnostic reproduced the same `Security::AppleDatabase` +inventory in both measured processes: **1 → 2 → 201 → 201**. Deferring native +`SecKeychainDelete` and the initial keychain `CFRelease` until after all observed +returned item, key, data, error and copy owners were released did not prevent the +increase on this pinned native bridge path. This rules out changing only that +observed deletion timing as a remedy in this control. It does not identify +bridge-internal or Apple ownership, establish whether any unobserved native +references remain, or assign a platform defect. + +The two arms used the same fresh private RSA seed, same measured C binary and +installed native bridge, and separate processes. The fixed normal arm ran first +and reproduced [attempt B](NATIVE_IMPORT.md), whose source and receipts remain +unchanged. The deferred arm ran next. There was one paired execution, 200 +operations per arm, with no retry, count change, reversed order or managed caller +in either measured child. These are bounded diagnostics on macOS **26.5.2** build +**25F84** arm64. No production mitigation, RSS budget decision, full-soak/G4 pass +or #799 closure follows. + +## What changed between the arms + +Both arms ran the same pinned native imports, nine ordered wrapper keychain +copies per operation, and item, copy and legacy-key release sequence. The logical +registry retired at the same point in all **400** operations: the final copied +certificate Untrack after original certificate disposal and initial import-state +logical release. At logical zero there were five observed returned references: +the initial keychain, current keychain copy, copied certificate, identity and +legacy key. The original certificate reference was already released. + +| Arm | Actual native delete and initial release | Observed returned CF owners at delete | +|---|---|---:| +| Normal | At logical zero, during copied certificate Untrack | 5: initial keychain and four other references | +| Deferred | After all item, copy and legacy-key releases | 1: initial keychain only | + +In the deferred arm the initial native owner stayed held after the logical +registry retired; its delete and `CFRelease` ran only after all other observed +returned owners were gone. The collector replayed every logical owner transition +and independently verified the physical keychain owner and actual deletion state. +This ledger covers references returned to the child. It does not enumerate +unobservable references within the installed bridge or Security.framework. + +All **200** keychain deletes in each arm returned native status zero. All bridge +operations that use one as success returned one; the reached OSStatus fields +were zero. Each arm observed **1,800** keychain copy calls with normalized status +zero and **800** returned private associations. The four associated copy stages +were identical: post-move original certificate Track, copied certificate Track, +original certificate Untrack and copied certificate Untrack. Both identity calls +returned null with success. Each arm balanced **2,402/2,402** returned CF owners, +including **200/200** initial keychain refs, with zero remaining logical owners +and no fallback cleanup. First and last certificate identity checks matched the +one unchanged shared seed in both arms. + +## Native measurements and cleanup + +| Arm | Checkpoint | AppleDatabase count / shallow bytes | Numeric FDs | Owned keychain files / FDs | RSS bytes | +|---|---|---:|---:|---:|---:| +| Normal | Before | 1 / 768 | 3 | 0 / 0 | 7,667,712 | +| Normal | First | 2 / 1,536 | 3 | 0 / 0 | 10,895,360 | +| Normal | 200 | 201 / 154,368 | 3 | 0 / 0 | 14,548,992 | +| Normal | Five-second idle | 201 / 154,368 | 3 | 0 / 0 | 14,581,760 | +| Deferred | Before | 1 / 768 | 3 | 0 / 0 | 7,536,640 | +| Deferred | First | 2 / 1,536 | 3 | 0 / 0 | 10,878,976 | +| Deferred | 200 | 201 / 154,368 | 3 | 0 / 0 | 14,123,008 | +| Deferred | Five-second idle | 201 / 154,368 | 3 | 0 / 0 | 14,139,392 | + +Native child times were **17.403** and **17.290** seconds, respectively. Parent +wall including the seed and both arms was **35.545 seconds**. Heap inventory +came from `heap --noContent -s`; RSS was Mach task resident size. Numeric FD +counts did not rise. A classified native heap object is not an open handle and +its shallow bytes do not explain the full RSS change. The one fixed-order pair +does not estimate a timing effect on RSS or generalize beyond this control. + +The single .NET **10.0.14** SDK **2.0.1** seed ran outside both measured children. +Its private DER/PKCS1 fixture files were mode 0600 in a private root, and its +SDK PKI was removed after seed exit. Both measured children read the same seed +files; the public leaf hash and private file identity metadata remained stable. +The native children checked the pinned bridge image and rejected CLR/SDK images. +No PFX parser or managed finalizer behavior was emulated. The explicit legacy +key release order was unchanged between the arms. + +All four read-only default/search-list fingerprints matched; all query statuses +were zero and the search-list count was two. No setter or restore API ran. +Both builds, seed and both measured children exited zero and were reaped. Both +owned keychain directories already had zero keychain files before harness +cleanup. The remaining private fixture files were overwritten and unlinked +only after all reaping, and the private root was removed. No physical secure +erasure guarantee is claimed. Native compilation emitted **six unsuppressed +deprecation warnings** for the exercised public keychain APIs; the seed build +reported zero warnings and errors. + +The result narrows the remaining question: the installed native bridge plus +Security.framework import path retained the database inventory with both tested +release orders, while the [empty keychain control](PURE_APPLE.md) did not. It +remains unresolved which native component owns the retained objects. There is no +supported application-only correction demonstrated by this experiment. + +## Pinned evidence + +Raw private output is `/tmp/opcua2-native-import-timing-20261110a`. +[Selected receipts](evidence/2026-11-10-native-import-timing/verification.json) +retain the source/runtime/binary hashes, build logs, shared seed and cleanup +receipts, both arm summaries and nine exact raw JSONL rows per arm. The 207 raw +rows per arm and all native heap/FD snapshots stay local and are hash-bound. + +- Pair result SHA-256: `f0b4195fe445259934b91e9c6743437c8427844b8fe5e3a9aafe28847f495986` +- Normal raw JSONL SHA-256: `73288755c768065ad0a8b2dad6964696d707f1c6be5a6b8bd531902cb3b4bf51` +- Deferred raw JSONL SHA-256: `61691ff4c500d2696f6998cd225d19677a0cb8eb47ed8f7545b01f76e4acc456` +- Measured binary SHA-256: `cec035355aea998d6dd9311561b23e1617a1096191847cf99f1a0a90a7b6d112` +- Seed DLL SHA-256: `36e648ac37b61cb5dd65f03dcf5db555fc297d23243c42e282ef522f15a8a7a1` + +The fixed collector accepts only the pinned source and 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_timing.py \ + --references /tmp/opcua2-native-reload-20261110a/bin/Release/net10.0 \ + --output /tmp/opcua2-native-import-timing-FRESH +``` diff --git a/scripts/validation/opcua-lifecycle-memory/native-retention/NativeImportTimingControl.c b/scripts/validation/opcua-lifecycle-memory/native-retention/NativeImportTimingControl.c new file mode 100644 index 00000000..528ceb8e --- /dev/null +++ b/scripts/validation/opcua-lifecycle-memory/native-retention/NativeImportTimingControl.c @@ -0,0 +1,309 @@ +// Paired timing diagnostic, derived from the immutable completed native bridge control. +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define REPETITIONS 200 +static char root[PATH_MAX],seed_root[PATH_MAX]; +static const char *arm_order; +static int defer_delete; +static struct timespec started; +static unsigned created_refs, released_refs, query_releases; +static double elapsed(void) { + struct timespec now; clock_gettime(CLOCK_MONOTONIC, &now); + return (double)(now.tv_sec-started.tv_sec)+(double)(now.tv_nsec-started.tv_nsec)/1e9; +} +static void die(const char *message) { + fprintf(stdout,"{\"kind\":\"failed\",\"message\":\"%s\",\"createdRefs\":%u,\"releasedRefs\":%u,\"elapsedSeconds\":%.6f}\n",message,created_refs,released_refs,elapsed()); + fflush(stdout); exit(1); +} +static void hex(const unsigned char *bytes, size_t length, char *out) { + const char *alphabet="0123456789abcdef"; + for(size_t i=0;i>4];out[i*2+1]=alphabet[bytes[i]&15];} + out[length*2]=0; +} +static void wipe(void *memory, size_t length) { + volatile unsigned char *bytes=(volatile unsigned char *)memory; + while(length--)*bytes++=0; +} +static OSStatus keychain_path(SecKeychainRef keychain, char *path) { + if(!keychain||CFGetTypeID(keychain)!=SecKeychainGetTypeID())return errSecParam; + UInt32 length=PATH_MAX-1; + OSStatus status=SecKeychainGetPath(keychain,&length,path); + if(status==errSecSuccess && length=sizeof(next))die("Checkpoint path overflow"); + while(access(next,F_OK)!=0){if(elapsed()>180)die("Checkpoint deadline exceeded");struct timespec pause={0,25000000};nanosleep(&pause,NULL);} +} +static int absent(const char *path){struct stat state;if(lstat(path,&state)==0)return 0;return errno==ENOENT;} + +#define BRIDGE_SHA "0333994e217a3d111d36d7f31460eea69a286f9fc819e2d7c746df13d2aec46c" +static int32_t (*import_cert)(const uint8_t*,int32_t,int32_t,CFStringRef,SecKeychainRef,int32_t,SecCertificateRef*,SecIdentityRef*,int32_t*); +static int32_t (*create_key)(const uint8_t*,int32_t,uint32_t,int32_t,SecKeyRef*,CFErrorRef*); +static SecKeyRef (*copy_public)(SecKeyRef); +static int32_t (*external_key)(SecKeyRef,CFDataRef*,CFErrorRef*); +static int32_t (*create_chain)(const char*,int32_t,const uint8_t*,SecKeychainRef*); +static int32_t (*never_lock)(SecKeychainRef); +static int32_t (*import_key)(const uint8_t*,int32_t,int32_t,SecKeyRef*,int32_t*); +static int32_t (*move_key)(SecCertificateRef,SecKeychainRef,SecKeyRef,SecIdentityRef*,int32_t*); +static int32_t (*copy_cert)(SecIdentityRef,SecCertificateRef*); +static int32_t (*copy_chain)(CFTypeRef,SecKeychainRef*); +static int32_t (*delete_chain)(SecKeychainRef); +static unsigned all_created,all_released; +static void owned(CFTypeRef value){if(value)all_created++;} +static void drop(CFTypeRef *value){if(*value){CFRelease(*value);*value=NULL;all_released++;}} +#define DROP(value) drop((CFTypeRef*)&(value)) +static void image_guard(void){ + unsigned bridge_images=0; + for(uint32_t i=0;i<_dyld_image_count();i++){ + const char *name=_dyld_get_image_name(i); + if(strstr(name,"libcoreclr")||strstr(name,"libhostfxr")||strstr(name,"Opc.Ua"))die("Measured child loaded a managed runtime or SDK"); + if(strstr(name,"libSystem.Security.Cryptography.Native.Apple.dylib"))bridge_images++; + } + if(bridge_images!=1)die("Expected exactly one pinned native bridge image"); +} +static void load_bridge(const char *path){ + FILE *file=fopen(path,"rb");if(!file)die("Pinned bridge could not be read"); + CC_SHA256_CTX hash;CC_SHA256_Init(&hash);unsigned char bytes[8192],digest[32];size_t size; + while((size=fread(bytes,1,sizeof(bytes),file)))CC_SHA256_Update(&hash,bytes,(CC_LONG)size); + int bad=ferror(file);fclose(file);if(bad)die("Pinned bridge read failed");CC_SHA256_Final(digest,&hash); + char actual[65];hex(digest,sizeof(digest),actual);if(strcmp(actual,BRIDGE_SHA))die("Pinned bridge hash changed"); + void *library=dlopen(path,RTLD_NOW|RTLD_LOCAL);if(!library)die("Pinned bridge load failed"); +#define LOAD(variable,symbol) do { *(void**)(&variable)=dlsym(library,symbol); if(!variable)die("Pinned bridge export missing"); Dl_info info;char loaded[PATH_MAX],expected[PATH_MAX];if(!dladdr((void*)variable,&info)||!realpath(info.dli_fname,loaded)||!realpath(path,expected)||strcmp(loaded,expected))die("Bridge export image identity changed"); } while(0) + LOAD(import_cert,"AppleCryptoNative_X509ImportCertificate");LOAD(create_key,"AppleCryptoNative_SecKeyCreateWithData"); + LOAD(copy_public,"AppleCryptoNative_SecKeyCopyPublicKey");LOAD(external_key,"AppleCryptoNative_SecKeyCopyExternalRepresentation"); + LOAD(create_chain,"AppleCryptoNative_SecKeychainCreate");LOAD(never_lock,"AppleCryptoNative_SetKeychainNeverLock"); + LOAD(import_key,"AppleCryptoNative_SecKeyImportEphemeral");LOAD(move_key,"AppleCryptoNative_X509MoveToKeychain"); + LOAD(copy_cert,"AppleCryptoNative_X509CopyCertFromIdentity");LOAD(copy_chain,"AppleCryptoNative_SecKeychainItemCopyKeychain"); + LOAD(delete_chain,"AppleCryptoNative_SecKeychainDelete"); +#undef LOAD + image_guard(); +} +struct copy_result { + const char *stage,*action;int32_t status;int present,associated; + unsigned owners_before,owners_after; + int registry_before,registry_after,native_owner_before,native_owner_after; +}; +struct operation_state { + SecCertificateRef cert,copied;SecIdentityRef identity;SecKeyRef modern,public_key,legacy; + CFDataRef external;SecKeychainRef keychain,keychain_identity,outer;CFErrorRef error; + char path[PATH_MAX],backing[PATH_MAX]; + unsigned logical_owners;int initial_owner,registry_active,pending_delete,cert_tracked,copied_tracked,identity_tracked; + int failed,path_verified,deleted,fallback_cleanup,import_state_released,leaf_checked,leaf_matches; + unsigned import_owners_before,import_owners_after; + struct ownership_observation { + const char *stage; + int copy_live,cert_live,copied_live,identity_live,legacy_live; + unsigned owned_outputs_remaining; + } zero, deletion; + int32_t cert_return,cert_status,modern_return,create_status,lock_status,external_return,key_return,key_status,move_return,move_status,copy_cert_status,delete_status,path_status; + CFIndex modern_error,external_error;int modern_error_present,external_error_present;unsigned copy_count; + struct copy_result copies[16]; +}; +static int private_path(struct operation_state *s,SecKeychainRef keychain){ + if(!keychain||CFGetTypeID(keychain)!=SecKeychainGetTypeID())return 0; + char actual[PATH_MAX];memset(actual,0,sizeof(actual));int32_t status=keychain_path(keychain,actual); + return status==0 && (!strcmp(actual,s->path)||!strcmp(actual,s->backing)); +} +static struct ownership_observation observe(const struct operation_state *s,const char *stage,int copy_live){ + return (struct ownership_observation){stage,copy_live,s->cert!=NULL,s->copied!=NULL,s->identity!=NULL,s->legacy!=NULL, + all_created-all_released}; +} +static void delete_owner(struct operation_state *s,const char *stage,int copy_live){ + if(!s->keychain)return; + if(s->deleted){s->failed=1;return;} + s->deletion=observe(s,stage,copy_live); + s->path_verified=private_path(s,s->keychain); + if(s->path_verified){s->delete_status=delete_chain(s->keychain);s->deleted=1;} + if(!s->path_verified||s->delete_status)s->failed=1; + DROP(s->keychain);released_refs++; +} +static void relinquish(struct operation_state *s,const char *stage,int copy_live){ + if(!s->logical_owners){s->failed=1;return;} + if(--s->logical_owners==0){ + s->registry_active=0; // Logical removal happens at the same point in both arms. + s->zero=observe(s,stage,copy_live); + if(defer_delete)s->pending_delete=1; + else delete_owner(s,stage,copy_live); + } +} +static SecKeychainRef copied_chain(struct operation_state *s,CFTypeRef item,const char *stage,int *associated){ + *associated=0;if(!item)return NULL; + CFTypeID type=CFGetTypeID(item);if(type!=SecCertificateGetTypeID()&&type!=SecIdentityGetTypeID()){s->failed=1;return NULL;} + SecKeychainRef result=NULL;int32_t status=copy_chain(item,&result);owned(result); + int type_ok=!result||CFGetTypeID(result)==SecKeychainGetTypeID(); + int belongs=result&&type_ok&&s->keychain_identity==result&&private_path(s,result); + if(s->copy_count<16)s->copies[s->copy_count++]=(struct copy_result){stage,"lookup",status,result!=NULL,belongs, + s->logical_owners,s->logical_owners,s->registry_active,s->registry_active,s->keychain!=NULL,s->keychain!=NULL};else s->failed=1; + if(status || !type_ok || (result&&!belongs))s->failed=1; + *associated=status==0&&belongs;return result; +} +static void record_copy_action(struct operation_state *s,unsigned index,const char *action){ + if(s->copy_count==index+1){struct copy_result *copy=&s->copies[index];copy->action=action; + copy->owners_after=s->logical_owners;copy->registry_after=s->registry_active;copy->native_owner_after=s->keychain!=NULL;} +} +static void track(struct operation_state *s,CFTypeRef item,const char *stage,int *tracked){ + unsigned index=s->copy_count;int associated=0;SecKeychainRef copy=copied_chain(s,item,stage,&associated); + // Installed TrackItem does nothing for a null copy; never invent an owner. + if(associated){if(*tracked||!s->registry_active)s->failed=1;else{s->logical_owners++;*tracked=1;}} + record_copy_action(s,index,"track");DROP(copy); +} +static void untrack(struct operation_state *s,CFTypeRef item,const char *stage,int *tracked){ + if(!item)return; + unsigned index=s->copy_count;int associated=0;SecKeychainRef copy=copied_chain(s,item,stage,&associated); + if(*tracked){if(associated&&s->registry_active){*tracked=0;relinquish(s,stage,copy!=NULL);}else s->failed=1;} + // A still-registered private keychain must never consume an untracked owner. + // After logical retirement no registry entry remains, even if native deletion is deferred. + else if(associated&&s->registry_active)s->failed=1; + record_copy_action(s,index,"untrack"); + // Final logical-owner release above precedes this copy's CFRelease. + DROP(copy); +} +static int leaf_equal(SecCertificateRef cert,const unsigned char *der,size_t length){ + CFDataRef data=SecCertificateCopyData(cert);owned(data); + int equal=data&&CFGetTypeID(data)==CFDataGetTypeID()&&CFDataGetLength(data)==(CFIndex)length&&!memcmp(CFDataGetBytePtr(data),der,length); + DROP(data);return equal; +} +static void import_operation(unsigned iteration,const unsigned char *der,size_t der_length,const unsigned char *key,size_t key_length){ + struct operation_state s;memset(&s,0,sizeof(s)); + s.cert_return=s.cert_status=s.modern_return=s.create_status=s.lock_status=s.external_return=s.key_return=s.key_status=s.move_return=s.move_status=s.copy_cert_status=s.delete_status=s.path_status=INT32_MIN; + unsigned before_created=all_created,before_released=all_released; + unsigned char random[144],password[129];char suffix[33]; + if(SecRandomCopyBytes(kSecRandomDefault,sizeof(random),random))die("Secure random failed"); + hex(random,16,suffix);const unsigned char choices[]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@"; + for(unsigned i=0;i<128;i++)password[i]=choices[random[16+i]&63];password[128]=0;wipe(random,sizeof(random)); + int n=snprintf(s.path,sizeof(s.path),"%s/tmp/native-import-%s.keychain",root,suffix); + if(n<0||(size_t)n>=sizeof(s.path))die("Private fixture path overflow"); + n=snprintf(s.backing,sizeof(s.backing),"%s-db",s.path); + if(n<0||(size_t)n>=sizeof(s.backing)||!absent(s.path)||!absent(s.backing))die("Private fixture path already exists or overflows"); + s.cert_return=import_cert(der,(int32_t)der_length,1,NULL,NULL,1,&s.cert,&s.identity,&s.cert_status);owned(s.cert);owned(s.identity); + track(&s,s.cert,"import-cert-track",&s.cert_tracked); + if(s.identity)track(&s,s.identity,"unexpected-import-identity-track",&s.identity_tracked); + if(s.cert_return!=1||s.cert_status||!s.cert||CFGetTypeID(s.cert)!=SecCertificateGetTypeID()||s.identity||s.failed){s.failed=1;goto cleanup;} + s.modern_return=create_key(key,(int32_t)key_length,2,0,&s.modern,&s.error);owned(s.modern);owned(s.error); + s.modern_error_present=s.error!=NULL;if(s.error){if(CFGetTypeID(s.error)==CFErrorGetTypeID())s.modern_error=CFErrorGetCode(s.error);else s.failed=1;}DROP(s.error); + if(s.modern_return!=1||!s.modern||CFGetTypeID(s.modern)!=SecKeyGetTypeID()||s.modern_error_present||s.failed){s.failed=1;goto cleanup;} + s.public_key=copy_public(s.modern);owned(s.public_key);if(!s.public_key||CFGetTypeID(s.public_key)!=SecKeyGetTypeID()){s.failed=1;goto cleanup;} + s.create_status=create_chain(s.path,129,password,&s.keychain);owned(s.keychain);wipe(password,sizeof(password)); + if(s.keychain){created_refs++;s.keychain_identity=s.keychain;s.initial_owner=1;s.registry_active=1;s.logical_owners=1;char actual[PATH_MAX];s.path_status=keychain_path(s.keychain,actual);s.path_verified=private_path(&s,s.keychain);} + if(s.create_status||!s.keychain||s.path_status||!s.path_verified){s.failed=1;goto cleanup;} + s.lock_status=never_lock(s.keychain);if(s.lock_status){s.failed=1;goto cleanup;} + s.external_return=external_key(s.modern,&s.external,&s.error);owned(s.external);owned(s.error); + s.external_error_present=s.error!=NULL;if(s.error){if(CFGetTypeID(s.error)==CFErrorGetTypeID())s.external_error=CFErrorGetCode(s.error);else s.failed=1;}DROP(s.error); + if(s.external_return!=1||!s.external||CFGetTypeID(s.external)!=CFDataGetTypeID()||s.external_error_present||s.failed||CFDataGetLength(s.external)<=0||CFDataGetLength(s.external)>65536){s.failed=1;goto cleanup;} + s.key_return=import_key(CFDataGetBytePtr(s.external),(int32_t)CFDataGetLength(s.external),1,&s.legacy,&s.key_status);owned(s.legacy); + DROP(s.external);DROP(s.modern);DROP(s.public_key); // Installed SecKeyPair releases private before public. + if(s.key_return!=1||s.key_status||!s.legacy||CFGetTypeID(s.legacy)!=SecKeyGetTypeID()){s.failed=1;goto cleanup;} + int associated=0;s.outer=copied_chain(&s,s.cert,"move-outer-copy",&associated); + untrack(&s,s.cert,"move-cert-untrack",&s.cert_tracked); + if(s.failed)goto cleanup; + s.move_return=move_key(s.cert,s.keychain,s.legacy,&s.identity,&s.move_status);owned(s.identity); + track(&s,s.cert,"move-cert-track",&s.cert_tracked);track(&s,s.identity,"move-identity-track",&s.identity_tracked);DROP(s.outer); + if(s.move_return!=1||s.move_status||!s.identity||CFGetTypeID(s.identity)!=SecIdentityGetTypeID()||s.failed){s.failed=1;goto cleanup;} + s.copy_cert_status=copy_cert(s.identity,&s.copied);owned(s.copied);track(&s,s.copied,"identity-cert-track",&s.copied_tracked); + if(s.copy_cert_status||!s.copied||CFGetTypeID(s.copied)!=SecCertificateGetTypeID()||s.failed){s.failed=1;goto cleanup;} + if(iteration==1||iteration==REPETITIONS){s.leaf_checked=1;s.leaf_matches=leaf_equal(s.copied,der,der_length); + if(!s.leaf_matches){s.failed=1;goto cleanup;}} +cleanup: + wipe(password,sizeof(password));DROP(s.error);DROP(s.external);DROP(s.modern);DROP(s.public_key);DROP(s.outer); + untrack(&s,s.cert,"release-original-cert",&s.cert_tracked);DROP(s.cert); + if(s.initial_owner){s.initial_owner=0;s.import_state_released=1;s.import_owners_before=s.logical_owners; + relinquish(&s,"release-import-state",0);s.import_owners_after=s.logical_owners;} + untrack(&s,s.copied,"release-copied-cert",&s.copied_tracked);DROP(s.copied); + untrack(&s,s.identity,"release-identity",&s.identity_tracked);DROP(s.identity); + DROP(s.legacy); // Explicit lifetime; managed helper does not scope this local with using. + if(defer_delete&&s.pending_delete&&!s.failed){ + // This counts only observed returned CF owners, not bridge-internal references. + if(!s.keychain||s.registry_active||s.logical_owners||s.initial_owner||s.cert_tracked||s.copied_tracked||s.identity_tracked||s.cert||s.copied||s.identity||s.legacy||s.modern||s.public_key||s.external||s.error||s.outer||all_created-all_released!=1){s.failed=1;} + else delete_owner(&s,"after-all-item-releases",0); + } + if(s.keychain){s.failed=1;s.fallback_cleanup=1;delete_owner(&s,"failure-fallback-after-items",0);} + unsigned made=all_created-before_created,released=all_released-before_released; + if(made!=released||s.logical_owners||s.registry_active||s.cert_tracked||s.copied_tracked||s.identity_tracked)s.failed=1; + if(!s.failed&&(s.copy_count!=9||!s.import_state_released||!s.zero.stage||!s.deletion.stage||!s.deleted))s.failed=1; + fprintf(stdout,"{\"kind\":\"operation\",\"iteration\":%u,\"certReturn\":%d,\"certStatus\":%d,\"modernReturn\":%d,\"modernErrorCode\":%ld,\"modernErrorPresent\":%s,\"createStatus\":%d,\"pathStatus\":%d,\"neverLockStatus\":%d,\"externalReturn\":%d,\"externalErrorCode\":%ld,\"externalErrorPresent\":%s,\"legacyReturn\":%d,\"legacyStatus\":%d,\"moveReturn\":%d,\"moveStatus\":%d,\"copyCertStatus\":%d,\"deleteStatus\":%d,\"privatePathVerified\":%s,\"deleteStage\":\"%s\",\"deleteLiveReferences\":{\"copy\":%s,\"originalCertificate\":%s,\"copiedCertificate\":%s,\"identity\":%s,\"legacyKey\":%s},\"ownedOutputsAtDelete\":%u,\"logicalZeroStage\":\"%s\",\"logicalZeroLiveReferences\":{\"copy\":%s,\"originalCertificate\":%s,\"copiedCertificate\":%s,\"identity\":%s,\"legacyKey\":%s},\"ownedOutputsAtLogicalZero\":%u,\"importStateReleased\":%s,\"importOwnersBefore\":%u,\"importOwnersAfter\":%u,\"leafIdentityChecked\":%s,\"leafIdentityMatches\":%s,\"fallbackCleanup\":%s,\"logicalOwnersRemaining\":%u,\"ownedOutputs\":%u,\"releasedOutputs\":%u,\"failed\":%s,\"copies\":[",iteration,s.cert_return,s.cert_status,s.modern_return,(long)s.modern_error,s.modern_error_present?"true":"false",s.create_status,s.path_status,s.lock_status,s.external_return,(long)s.external_error,s.external_error_present?"true":"false",s.key_return,s.key_status,s.move_return,s.move_status,s.copy_cert_status,s.delete_status,s.path_verified?"true":"false",s.deletion.stage?s.deletion.stage:"not-attempted",s.deletion.copy_live?"true":"false",s.deletion.cert_live?"true":"false",s.deletion.copied_live?"true":"false",s.deletion.identity_live?"true":"false",s.deletion.legacy_live?"true":"false",s.deletion.owned_outputs_remaining,s.zero.stage?s.zero.stage:"not-reached",s.zero.copy_live?"true":"false",s.zero.cert_live?"true":"false",s.zero.copied_live?"true":"false",s.zero.identity_live?"true":"false",s.zero.legacy_live?"true":"false",s.zero.owned_outputs_remaining,s.import_state_released?"true":"false",s.import_owners_before,s.import_owners_after,s.leaf_checked?"true":"false",s.leaf_matches?"true":"false",s.fallback_cleanup?"true":"false",s.logical_owners,made,released,s.failed?"true":"false"); + for(unsigned i=0;i=sizeof(path))die("Seed fixture path overflow"); + if(lstat(path,&state)||!S_ISREG(state.st_mode)||(state.st_mode&077)||state.st_nlink!=1||state.st_size<1||state.st_size>65536)die("Expected private bounded regular fixture"); + FILE *file=fopen(path,"rb");if(!file)die("Fixture read failed");*length=(size_t)state.st_size; + unsigned char *bytes=malloc(*length);if(!bytes){fclose(file);die("Fixture allocation failed");} + int bad=fread(bytes,1,*length,file)!=*length||ferror(file);fclose(file);if(bad){wipe(bytes,*length);free(bytes);die("Fixture read incomplete");}return bytes; +} +int main(int argc,char **argv){ + clock_gettime(CLOCK_MONOTONIC,&started); + if(argc!=5||!realpath(argv[1],root)||!realpath(argv[3],seed_root))die("Expected private arm root, pinned bridge and shared seed"); + arm_order=argv[4];if(strcmp(arm_order,"normal")&&strcmp(arm_order,"deferred"))die("Unknown fixed timing arm"); + defer_delete=!strcmp(arm_order,"deferred"); + const char *split=strrchr(root,'/');char expected_seed[PATH_MAX]; + if(!split||strcmp(split+1,arm_order))die("Expected isolated timing arm root"); + int n=snprintf(expected_seed,sizeof(expected_seed),"%.*s/seed",(int)(split-root),root); + if(n<0||(size_t)n>=sizeof(expected_seed)||strcmp(expected_seed,seed_root))die("Seed is not the shared private sibling"); + struct stat state; + if(stat(root,&state)||!S_ISDIR(state.st_mode)||(state.st_mode&077)||stat(seed_root,&state)||!S_ISDIR(state.st_mode)||(state.st_mode&077))die("Fixture roots must be private0700"); + load_bridge(argv[2]);size_t der_length,key_length; + unsigned char *der=read_fixture("leaf.der",&der_length),*key=read_fixture("private.pkcs1",&key_length); + struct fingerprint before=fingerprint("before");checkpoint("before",0); + for(unsigned i=1;i<=REPETITIONS;i++){import_operation(i,der,der_length,key,key_length);if(i==1)checkpoint("first",i);} + checkpoint("repeated",REPETITIONS);struct timespec idle={5,0};while(nanosleep(&idle,&idle)){if(errno!=EINTR)die("Idle delay failed");} + struct fingerprint after=fingerprint("after");image_guard();checkpoint("idle",REPETITIONS); + wipe(key,key_length);free(key);free(der); + if(strcmp(before.default_sha,after.default_sha)||strcmp(before.search_sha,after.search_sha)||before.search_count!=after.search_count||created_refs!=REPETITIONS||released_refs!=REPETITIONS||all_created!=all_released)die("Fingerprint or ownership guard failed"); + fprintf(stdout,"{\"kind\":\"complete\",\"order\":\"%s\",\"operations\":200,\"createdRefs\":%u,\"releasedRefs\":%u,\"ownedOutputs\":%u,\"releasedOutputs\":%u,\"readOnlyDefaultAndSearchListUnchanged\":true,\"managedRuntimePresent\":false,\"bridgeSha256\":\"%s\",\"elapsedSeconds\":%.6f,\"fullSoakGate\":false}\n",arm_order,created_refs,released_refs,all_created,all_released,BRIDGE_SHA,elapsed());fflush(stdout);return 0; +} diff --git a/scripts/validation/opcua-lifecycle-memory/native-retention/evidence/2026-11-10-native-import-timing/deferred-selected-samples.jsonl b/scripts/validation/opcua-lifecycle-memory/native-retention/evidence/2026-11-10-native-import-timing/deferred-selected-samples.jsonl new file mode 100644 index 00000000..3e931af3 --- /dev/null +++ b/scripts/validation/opcua-lifecycle-memory/native-retention/evidence/2026-11-10-native-import-timing/deferred-selected-samples.jsonl @@ -0,0 +1,9 @@ +{"kind":"fingerprint","stage":"before","defaultStatus":0,"defaultPathStatus":0,"searchStatus":0,"searchPathStatus":0,"defaultSha256":"08c2c77dd8eaf8042b4020991ea06c6215cc391532869bbf6ced2cf1126c5e63","searchSha256":"c05fae700986dc7a6a147a938ef2adc4a274173420e137929ed58ad986eb2a3c","searchCount":2,"ownedQueryReferencesReleased":2} +{"kind":"checkpoint","mode":"native-bridge-import-timing","order":"deferred","stage":"before","operations":0,"processId":88965,"utc":"2026-11-10T22:46:59.453Z","elapsedSeconds":0.003065,"rssBytes":7536640,"createdRefs":0,"releasedRefs":0,"managedRuntimePresent":false} +{"kind":"operation","iteration":1,"certReturn":1,"certStatus":0,"modernReturn":1,"modernErrorCode":0,"modernErrorPresent":false,"createStatus":0,"pathStatus":0,"neverLockStatus":0,"externalReturn":1,"externalErrorCode":0,"externalErrorPresent":false,"legacyReturn":1,"legacyStatus":0,"moveReturn":1,"moveStatus":0,"copyCertStatus":0,"deleteStatus":0,"privatePathVerified":true,"deleteStage":"after-all-item-releases","deleteLiveReferences":{"copy":false,"originalCertificate":false,"copiedCertificate":false,"identity":false,"legacyKey":false},"ownedOutputsAtDelete":1,"logicalZeroStage":"release-copied-cert","logicalZeroLiveReferences":{"copy":true,"originalCertificate":false,"copiedCertificate":true,"identity":true,"legacyKey":true},"ownedOutputsAtLogicalZero":5,"importStateReleased":true,"importOwnersBefore":2,"importOwnersAfter":1,"leafIdentityChecked":true,"leafIdentityMatches":true,"fallbackCleanup":false,"logicalOwnersRemaining":0,"ownedOutputs":13,"releasedOutputs":13,"failed":false,"copies":[{"stage":"import-cert-track","action":"track","status":0,"returnedRef":false,"privateAssociation":false,"logicalOwnersBefore":0,"logicalOwnersAfter":0,"registryOwnerBefore":false,"registryOwnerAfter":false,"nativeOwnerBefore":false,"nativeOwnerAfter":false},{"stage":"move-outer-copy","action":"lookup","status":0,"returnedRef":false,"privateAssociation":false,"logicalOwnersBefore":1,"logicalOwnersAfter":1,"registryOwnerBefore":true,"registryOwnerAfter":true,"nativeOwnerBefore":true,"nativeOwnerAfter":true},{"stage":"move-cert-untrack","action":"untrack","status":0,"returnedRef":false,"privateAssociation":false,"logicalOwnersBefore":1,"logicalOwnersAfter":1,"registryOwnerBefore":true,"registryOwnerAfter":true,"nativeOwnerBefore":true,"nativeOwnerAfter":true},{"stage":"move-cert-track","action":"track","status":0,"returnedRef":true,"privateAssociation":true,"logicalOwnersBefore":1,"logicalOwnersAfter":2,"registryOwnerBefore":true,"registryOwnerAfter":true,"nativeOwnerBefore":true,"nativeOwnerAfter":true},{"stage":"move-identity-track","action":"track","status":0,"returnedRef":false,"privateAssociation":false,"logicalOwnersBefore":2,"logicalOwnersAfter":2,"registryOwnerBefore":true,"registryOwnerAfter":true,"nativeOwnerBefore":true,"nativeOwnerAfter":true},{"stage":"identity-cert-track","action":"track","status":0,"returnedRef":true,"privateAssociation":true,"logicalOwnersBefore":2,"logicalOwnersAfter":3,"registryOwnerBefore":true,"registryOwnerAfter":true,"nativeOwnerBefore":true,"nativeOwnerAfter":true},{"stage":"release-original-cert","action":"untrack","status":0,"returnedRef":true,"privateAssociation":true,"logicalOwnersBefore":3,"logicalOwnersAfter":2,"registryOwnerBefore":true,"registryOwnerAfter":true,"nativeOwnerBefore":true,"nativeOwnerAfter":true},{"stage":"release-copied-cert","action":"untrack","status":0,"returnedRef":true,"privateAssociation":true,"logicalOwnersBefore":1,"logicalOwnersAfter":0,"registryOwnerBefore":true,"registryOwnerAfter":false,"nativeOwnerBefore":true,"nativeOwnerAfter":true},{"stage":"release-identity","action":"untrack","status":0,"returnedRef":false,"privateAssociation":false,"logicalOwnersBefore":0,"logicalOwnersAfter":0,"registryOwnerBefore":false,"registryOwnerAfter":false,"nativeOwnerBefore":true,"nativeOwnerAfter":true}]} +{"kind":"checkpoint","mode":"native-bridge-import-timing","order":"deferred","stage":"first","operations":1,"processId":88965,"utc":"2026-11-10T22:47:00.201Z","elapsedSeconds":0.750946,"rssBytes":10878976,"createdRefs":1,"releasedRefs":1,"managedRuntimePresent":false} +{"kind":"operation","iteration":200,"certReturn":1,"certStatus":0,"modernReturn":1,"modernErrorCode":0,"modernErrorPresent":false,"createStatus":0,"pathStatus":0,"neverLockStatus":0,"externalReturn":1,"externalErrorCode":0,"externalErrorPresent":false,"legacyReturn":1,"legacyStatus":0,"moveReturn":1,"moveStatus":0,"copyCertStatus":0,"deleteStatus":0,"privatePathVerified":true,"deleteStage":"after-all-item-releases","deleteLiveReferences":{"copy":false,"originalCertificate":false,"copiedCertificate":false,"identity":false,"legacyKey":false},"ownedOutputsAtDelete":1,"logicalZeroStage":"release-copied-cert","logicalZeroLiveReferences":{"copy":true,"originalCertificate":false,"copiedCertificate":true,"identity":true,"legacyKey":true},"ownedOutputsAtLogicalZero":5,"importStateReleased":true,"importOwnersBefore":2,"importOwnersAfter":1,"leafIdentityChecked":true,"leafIdentityMatches":true,"fallbackCleanup":false,"logicalOwnersRemaining":0,"ownedOutputs":13,"releasedOutputs":13,"failed":false,"copies":[{"stage":"import-cert-track","action":"track","status":0,"returnedRef":false,"privateAssociation":false,"logicalOwnersBefore":0,"logicalOwnersAfter":0,"registryOwnerBefore":false,"registryOwnerAfter":false,"nativeOwnerBefore":false,"nativeOwnerAfter":false},{"stage":"move-outer-copy","action":"lookup","status":0,"returnedRef":false,"privateAssociation":false,"logicalOwnersBefore":1,"logicalOwnersAfter":1,"registryOwnerBefore":true,"registryOwnerAfter":true,"nativeOwnerBefore":true,"nativeOwnerAfter":true},{"stage":"move-cert-untrack","action":"untrack","status":0,"returnedRef":false,"privateAssociation":false,"logicalOwnersBefore":1,"logicalOwnersAfter":1,"registryOwnerBefore":true,"registryOwnerAfter":true,"nativeOwnerBefore":true,"nativeOwnerAfter":true},{"stage":"move-cert-track","action":"track","status":0,"returnedRef":true,"privateAssociation":true,"logicalOwnersBefore":1,"logicalOwnersAfter":2,"registryOwnerBefore":true,"registryOwnerAfter":true,"nativeOwnerBefore":true,"nativeOwnerAfter":true},{"stage":"move-identity-track","action":"track","status":0,"returnedRef":false,"privateAssociation":false,"logicalOwnersBefore":2,"logicalOwnersAfter":2,"registryOwnerBefore":true,"registryOwnerAfter":true,"nativeOwnerBefore":true,"nativeOwnerAfter":true},{"stage":"identity-cert-track","action":"track","status":0,"returnedRef":true,"privateAssociation":true,"logicalOwnersBefore":2,"logicalOwnersAfter":3,"registryOwnerBefore":true,"registryOwnerAfter":true,"nativeOwnerBefore":true,"nativeOwnerAfter":true},{"stage":"release-original-cert","action":"untrack","status":0,"returnedRef":true,"privateAssociation":true,"logicalOwnersBefore":3,"logicalOwnersAfter":2,"registryOwnerBefore":true,"registryOwnerAfter":true,"nativeOwnerBefore":true,"nativeOwnerAfter":true},{"stage":"release-copied-cert","action":"untrack","status":0,"returnedRef":true,"privateAssociation":true,"logicalOwnersBefore":1,"logicalOwnersAfter":0,"registryOwnerBefore":true,"registryOwnerAfter":false,"nativeOwnerBefore":true,"nativeOwnerAfter":true},{"stage":"release-identity","action":"untrack","status":0,"returnedRef":false,"privateAssociation":false,"logicalOwnersBefore":0,"logicalOwnersAfter":0,"registryOwnerBefore":false,"registryOwnerAfter":false,"nativeOwnerBefore":true,"nativeOwnerAfter":true}]} +{"kind":"checkpoint","mode":"native-bridge-import-timing","order":"deferred","stage":"repeated","operations":200,"processId":88965,"utc":"2026-11-10T22:47:10.347Z","elapsedSeconds":10.896725,"rssBytes":14123008,"createdRefs":200,"releasedRefs":200,"managedRuntimePresent":false} +{"kind":"fingerprint","stage":"after","defaultStatus":0,"defaultPathStatus":0,"searchStatus":0,"searchPathStatus":0,"defaultSha256":"08c2c77dd8eaf8042b4020991ea06c6215cc391532869bbf6ced2cf1126c5e63","searchSha256":"c05fae700986dc7a6a147a938ef2adc4a274173420e137929ed58ad986eb2a3c","searchCount":2,"ownedQueryReferencesReleased":4} +{"kind":"checkpoint","mode":"native-bridge-import-timing","order":"deferred","stage":"idle","operations":200,"processId":88965,"utc":"2026-11-10T22:47:16.069Z","elapsedSeconds":16.618717,"rssBytes":14139392,"createdRefs":200,"releasedRefs":200,"managedRuntimePresent":false} +{"kind":"complete","order":"deferred","operations":200,"createdRefs":200,"releasedRefs":200,"ownedOutputs":2402,"releasedOutputs":2402,"readOnlyDefaultAndSearchListUnchanged":true,"managedRuntimePresent":false,"bridgeSha256":"0333994e217a3d111d36d7f31460eea69a286f9fc819e2d7c746df13d2aec46c","elapsedSeconds":17.289693,"fullSoakGate":false} diff --git a/scripts/validation/opcua-lifecycle-memory/native-retention/evidence/2026-11-10-native-import-timing/fixture-before-harness-cleanup.json b/scripts/validation/opcua-lifecycle-memory/native-retention/evidence/2026-11-10-native-import-timing/fixture-before-harness-cleanup.json new file mode 100644 index 00000000..4ef441cc --- /dev/null +++ b/scripts/validation/opcua-lifecycle-memory/native-retention/evidence/2026-11-10-native-import-timing/fixture-before-harness-cleanup.json @@ -0,0 +1,6 @@ +{ + "remainingOwnedKeychainFilesByArm": { + "normal": 0, + "deferred": 0 + } +} diff --git a/scripts/validation/opcua-lifecycle-memory/native-retention/evidence/2026-11-10-native-import-timing/fixture-cleanup.json b/scripts/validation/opcua-lifecycle-memory/native-retention/evidence/2026-11-10-native-import-timing/fixture-cleanup.json new file mode 100644 index 00000000..fff95111 --- /dev/null +++ b/scripts/validation/opcua-lifecycle-memory/native-retention/evidence/2026-11-10-native-import-timing/fixture-cleanup.json @@ -0,0 +1,34 @@ +{ + "allLaunchedChildrenConfirmedReaped": true, + "reapOutcomes": [ + { + "processId": 88920, + "confirmedReaped": true, + "exitCode": 0 + }, + { + "processId": 88924, + "confirmedReaped": true, + "exitCode": 0 + }, + { + "processId": 88928, + "confirmedReaped": true, + "exitCode": 0 + }, + { + "processId": 88929, + "confirmedReaped": true, + "exitCode": 0 + }, + { + "processId": 88965, + "confirmedReaped": true, + "exitCode": 0 + } + ], + "privateMaterialOverwrittenThenUnlinkedAfterChildReaping": true, + "physicalSecureErasureClaimed": false, + "status": "passed", + "ownedFixtureRootRemoved": true +} diff --git a/scripts/validation/opcua-lifecycle-memory/native-retention/evidence/2026-11-10-native-import-timing/native-build.log b/scripts/validation/opcua-lifecycle-memory/native-retention/evidence/2026-11-10-native-import-timing/native-build.log new file mode 100644 index 00000000..0868fa6c --- /dev/null +++ b/scripts/validation/opcua-lifecycle-memory/native-retention/evidence/2026-11-10-native-import-timing/native-build.log @@ -0,0 +1,37 @@ +/.codex/worktrees/opcua2-client-lifecycle/OtOpcUa/scripts/validation/opcua-lifecycle-memory/native-retention/NativeImportTimingControl.c:43:42: warning: 'SecKeychainGetTypeID' is deprecated: first deprecated in macOS 10.10 - SecKeychain is deprecated [-Wdeprecated-declarations] + 43 | if(!keychain||CFGetTypeID(keychain)!=SecKeychainGetTypeID())return errSecParam; + | ^ +/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/Frameworks/Security.framework/Headers/SecKeychain.h:270:10: note: 'SecKeychainGetTypeID' has been explicitly marked deprecated here + 270 | CFTypeID SecKeychainGetTypeID(void) + | ^ +/.codex/worktrees/opcua2-client-lifecycle/OtOpcUa/scripts/validation/opcua-lifecycle-memory/native-retention/NativeImportTimingControl.c:45:21: warning: 'SecKeychainGetPath' is deprecated: first deprecated in macOS 10.10 - SecKeychain is deprecated [-Wdeprecated-declarations] + 45 | OSStatus status=SecKeychainGetPath(keychain,&length,path); + | ^ +/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/Frameworks/Security.framework/Headers/SecKeychain.h:474:10: note: 'SecKeychainGetPath' has been explicitly marked deprecated here + 474 | OSStatus SecKeychainGetPath(SecKeychainRef __nullable keychain, UInt32 *ioPathLength, char *pathName) + | ^ +/.codex/worktrees/opcua2-client-lifecycle/OtOpcUa/scripts/validation/opcua-lifecycle-memory/native-retention/NativeImportTimingControl.c:56:29: warning: 'SecKeychainCopyDefault' is deprecated: first deprecated in macOS 10.10 - SecKeychain is deprecated [-Wdeprecated-declarations] + 56 | OSStatus default_status=SecKeychainCopyDefault(&default_keychain); + | ^ +/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/Frameworks/Security.framework/Headers/SecKeychain.h:384:10: note: 'SecKeychainCopyDefault' has been explicitly marked deprecated here + 384 | OSStatus SecKeychainCopyDefault(SecKeychainRef * __nonnull CF_RETURNS_RETAINED keychain) + | ^ +/.codex/worktrees/opcua2-client-lifecycle/OtOpcUa/scripts/validation/opcua-lifecycle-memory/native-retention/NativeImportTimingControl.c:60:28: warning: 'SecKeychainCopySearchList' is deprecated: first deprecated in macOS 10.10 - SecKeychain is deprecated [-Wdeprecated-declarations] + 60 | OSStatus search_status=SecKeychainCopySearchList(&search); + | ^ +/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/Frameworks/Security.framework/Headers/SecKeychain.h:404:10: note: 'SecKeychainCopySearchList' has been explicitly marked deprecated here + 404 | OSStatus SecKeychainCopySearchList(CFArrayRef * __nonnull CF_RETURNS_RETAINED searchList) + | ^ +/.codex/worktrees/opcua2-client-lifecycle/OtOpcUa/scripts/validation/opcua-lifecycle-memory/native-retention/NativeImportTimingControl.c:157:42: warning: 'SecKeychainGetTypeID' is deprecated: first deprecated in macOS 10.10 - SecKeychain is deprecated [-Wdeprecated-declarations] + 157 | if(!keychain||CFGetTypeID(keychain)!=SecKeychainGetTypeID())return 0; + | ^ +/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/Frameworks/Security.framework/Headers/SecKeychain.h:270:10: note: 'SecKeychainGetTypeID' has been explicitly marked deprecated here + 270 | CFTypeID SecKeychainGetTypeID(void) + | ^ +/.codex/worktrees/opcua2-client-lifecycle/OtOpcUa/scripts/validation/opcua-lifecycle-memory/native-retention/NativeImportTimingControl.c:187:47: warning: 'SecKeychainGetTypeID' is deprecated: first deprecated in macOS 10.10 - SecKeychain is deprecated [-Wdeprecated-declarations] + 187 | int type_ok=!result||CFGetTypeID(result)==SecKeychainGetTypeID(); + | ^ +/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/Frameworks/Security.framework/Headers/SecKeychain.h:270:10: note: 'SecKeychainGetTypeID' has been explicitly marked deprecated here + 270 | CFTypeID SecKeychainGetTypeID(void) + | ^ +6 warnings generated. diff --git a/scripts/validation/opcua-lifecycle-memory/native-retention/evidence/2026-11-10-native-import-timing/normal-selected-samples.jsonl b/scripts/validation/opcua-lifecycle-memory/native-retention/evidence/2026-11-10-native-import-timing/normal-selected-samples.jsonl new file mode 100644 index 00000000..e510798b --- /dev/null +++ b/scripts/validation/opcua-lifecycle-memory/native-retention/evidence/2026-11-10-native-import-timing/normal-selected-samples.jsonl @@ -0,0 +1,9 @@ +{"kind":"fingerprint","stage":"before","defaultStatus":0,"defaultPathStatus":0,"searchStatus":0,"searchPathStatus":0,"defaultSha256":"08c2c77dd8eaf8042b4020991ea06c6215cc391532869bbf6ced2cf1126c5e63","searchSha256":"c05fae700986dc7a6a147a938ef2adc4a274173420e137929ed58ad986eb2a3c","searchCount":2,"ownedQueryReferencesReleased":2} +{"kind":"checkpoint","mode":"native-bridge-import-timing","order":"normal","stage":"before","operations":0,"processId":88929,"utc":"2026-11-10T22:46:42.027Z","elapsedSeconds":0.004969,"rssBytes":7667712,"createdRefs":0,"releasedRefs":0,"managedRuntimePresent":false} +{"kind":"operation","iteration":1,"certReturn":1,"certStatus":0,"modernReturn":1,"modernErrorCode":0,"modernErrorPresent":false,"createStatus":0,"pathStatus":0,"neverLockStatus":0,"externalReturn":1,"externalErrorCode":0,"externalErrorPresent":false,"legacyReturn":1,"legacyStatus":0,"moveReturn":1,"moveStatus":0,"copyCertStatus":0,"deleteStatus":0,"privatePathVerified":true,"deleteStage":"release-copied-cert","deleteLiveReferences":{"copy":true,"originalCertificate":false,"copiedCertificate":true,"identity":true,"legacyKey":true},"ownedOutputsAtDelete":5,"logicalZeroStage":"release-copied-cert","logicalZeroLiveReferences":{"copy":true,"originalCertificate":false,"copiedCertificate":true,"identity":true,"legacyKey":true},"ownedOutputsAtLogicalZero":5,"importStateReleased":true,"importOwnersBefore":2,"importOwnersAfter":1,"leafIdentityChecked":true,"leafIdentityMatches":true,"fallbackCleanup":false,"logicalOwnersRemaining":0,"ownedOutputs":13,"releasedOutputs":13,"failed":false,"copies":[{"stage":"import-cert-track","action":"track","status":0,"returnedRef":false,"privateAssociation":false,"logicalOwnersBefore":0,"logicalOwnersAfter":0,"registryOwnerBefore":false,"registryOwnerAfter":false,"nativeOwnerBefore":false,"nativeOwnerAfter":false},{"stage":"move-outer-copy","action":"lookup","status":0,"returnedRef":false,"privateAssociation":false,"logicalOwnersBefore":1,"logicalOwnersAfter":1,"registryOwnerBefore":true,"registryOwnerAfter":true,"nativeOwnerBefore":true,"nativeOwnerAfter":true},{"stage":"move-cert-untrack","action":"untrack","status":0,"returnedRef":false,"privateAssociation":false,"logicalOwnersBefore":1,"logicalOwnersAfter":1,"registryOwnerBefore":true,"registryOwnerAfter":true,"nativeOwnerBefore":true,"nativeOwnerAfter":true},{"stage":"move-cert-track","action":"track","status":0,"returnedRef":true,"privateAssociation":true,"logicalOwnersBefore":1,"logicalOwnersAfter":2,"registryOwnerBefore":true,"registryOwnerAfter":true,"nativeOwnerBefore":true,"nativeOwnerAfter":true},{"stage":"move-identity-track","action":"track","status":0,"returnedRef":false,"privateAssociation":false,"logicalOwnersBefore":2,"logicalOwnersAfter":2,"registryOwnerBefore":true,"registryOwnerAfter":true,"nativeOwnerBefore":true,"nativeOwnerAfter":true},{"stage":"identity-cert-track","action":"track","status":0,"returnedRef":true,"privateAssociation":true,"logicalOwnersBefore":2,"logicalOwnersAfter":3,"registryOwnerBefore":true,"registryOwnerAfter":true,"nativeOwnerBefore":true,"nativeOwnerAfter":true},{"stage":"release-original-cert","action":"untrack","status":0,"returnedRef":true,"privateAssociation":true,"logicalOwnersBefore":3,"logicalOwnersAfter":2,"registryOwnerBefore":true,"registryOwnerAfter":true,"nativeOwnerBefore":true,"nativeOwnerAfter":true},{"stage":"release-copied-cert","action":"untrack","status":0,"returnedRef":true,"privateAssociation":true,"logicalOwnersBefore":1,"logicalOwnersAfter":0,"registryOwnerBefore":true,"registryOwnerAfter":false,"nativeOwnerBefore":true,"nativeOwnerAfter":false},{"stage":"release-identity","action":"untrack","status":0,"returnedRef":false,"privateAssociation":false,"logicalOwnersBefore":0,"logicalOwnersAfter":0,"registryOwnerBefore":false,"registryOwnerAfter":false,"nativeOwnerBefore":false,"nativeOwnerAfter":false}]} +{"kind":"checkpoint","mode":"native-bridge-import-timing","order":"normal","stage":"first","operations":1,"processId":88929,"utc":"2026-11-10T22:46:42.988Z","elapsedSeconds":0.965802,"rssBytes":10895360,"createdRefs":1,"releasedRefs":1,"managedRuntimePresent":false} +{"kind":"operation","iteration":200,"certReturn":1,"certStatus":0,"modernReturn":1,"modernErrorCode":0,"modernErrorPresent":false,"createStatus":0,"pathStatus":0,"neverLockStatus":0,"externalReturn":1,"externalErrorCode":0,"externalErrorPresent":false,"legacyReturn":1,"legacyStatus":0,"moveReturn":1,"moveStatus":0,"copyCertStatus":0,"deleteStatus":0,"privatePathVerified":true,"deleteStage":"release-copied-cert","deleteLiveReferences":{"copy":true,"originalCertificate":false,"copiedCertificate":true,"identity":true,"legacyKey":true},"ownedOutputsAtDelete":5,"logicalZeroStage":"release-copied-cert","logicalZeroLiveReferences":{"copy":true,"originalCertificate":false,"copiedCertificate":true,"identity":true,"legacyKey":true},"ownedOutputsAtLogicalZero":5,"importStateReleased":true,"importOwnersBefore":2,"importOwnersAfter":1,"leafIdentityChecked":true,"leafIdentityMatches":true,"fallbackCleanup":false,"logicalOwnersRemaining":0,"ownedOutputs":13,"releasedOutputs":13,"failed":false,"copies":[{"stage":"import-cert-track","action":"track","status":0,"returnedRef":false,"privateAssociation":false,"logicalOwnersBefore":0,"logicalOwnersAfter":0,"registryOwnerBefore":false,"registryOwnerAfter":false,"nativeOwnerBefore":false,"nativeOwnerAfter":false},{"stage":"move-outer-copy","action":"lookup","status":0,"returnedRef":false,"privateAssociation":false,"logicalOwnersBefore":1,"logicalOwnersAfter":1,"registryOwnerBefore":true,"registryOwnerAfter":true,"nativeOwnerBefore":true,"nativeOwnerAfter":true},{"stage":"move-cert-untrack","action":"untrack","status":0,"returnedRef":false,"privateAssociation":false,"logicalOwnersBefore":1,"logicalOwnersAfter":1,"registryOwnerBefore":true,"registryOwnerAfter":true,"nativeOwnerBefore":true,"nativeOwnerAfter":true},{"stage":"move-cert-track","action":"track","status":0,"returnedRef":true,"privateAssociation":true,"logicalOwnersBefore":1,"logicalOwnersAfter":2,"registryOwnerBefore":true,"registryOwnerAfter":true,"nativeOwnerBefore":true,"nativeOwnerAfter":true},{"stage":"move-identity-track","action":"track","status":0,"returnedRef":false,"privateAssociation":false,"logicalOwnersBefore":2,"logicalOwnersAfter":2,"registryOwnerBefore":true,"registryOwnerAfter":true,"nativeOwnerBefore":true,"nativeOwnerAfter":true},{"stage":"identity-cert-track","action":"track","status":0,"returnedRef":true,"privateAssociation":true,"logicalOwnersBefore":2,"logicalOwnersAfter":3,"registryOwnerBefore":true,"registryOwnerAfter":true,"nativeOwnerBefore":true,"nativeOwnerAfter":true},{"stage":"release-original-cert","action":"untrack","status":0,"returnedRef":true,"privateAssociation":true,"logicalOwnersBefore":3,"logicalOwnersAfter":2,"registryOwnerBefore":true,"registryOwnerAfter":true,"nativeOwnerBefore":true,"nativeOwnerAfter":true},{"stage":"release-copied-cert","action":"untrack","status":0,"returnedRef":true,"privateAssociation":true,"logicalOwnersBefore":1,"logicalOwnersAfter":0,"registryOwnerBefore":true,"registryOwnerAfter":false,"nativeOwnerBefore":true,"nativeOwnerAfter":false},{"stage":"release-identity","action":"untrack","status":0,"returnedRef":false,"privateAssociation":false,"logicalOwnersBefore":0,"logicalOwnersAfter":0,"registryOwnerBefore":false,"registryOwnerAfter":false,"nativeOwnerBefore":false,"nativeOwnerAfter":false}]} +{"kind":"checkpoint","mode":"native-bridge-import-timing","order":"normal","stage":"repeated","operations":200,"processId":88929,"utc":"2026-11-10T22:46:53.036Z","elapsedSeconds":11.013997,"rssBytes":14548992,"createdRefs":200,"releasedRefs":200,"managedRuntimePresent":false} +{"kind":"fingerprint","stage":"after","defaultStatus":0,"defaultPathStatus":0,"searchStatus":0,"searchPathStatus":0,"defaultSha256":"08c2c77dd8eaf8042b4020991ea06c6215cc391532869bbf6ced2cf1126c5e63","searchSha256":"c05fae700986dc7a6a147a938ef2adc4a274173420e137929ed58ad986eb2a3c","searchCount":2,"ownedQueryReferencesReleased":4} +{"kind":"checkpoint","mode":"native-bridge-import-timing","order":"normal","stage":"idle","operations":200,"processId":88929,"utc":"2026-11-10T22:46:58.718Z","elapsedSeconds":16.696203,"rssBytes":14581760,"createdRefs":200,"releasedRefs":200,"managedRuntimePresent":false} +{"kind":"complete","order":"normal","operations":200,"createdRefs":200,"releasedRefs":200,"ownedOutputs":2402,"releasedOutputs":2402,"readOnlyDefaultAndSearchListUnchanged":true,"managedRuntimePresent":false,"bridgeSha256":"0333994e217a3d111d36d7f31460eea69a286f9fc819e2d7c746df13d2aec46c","elapsedSeconds":17.402910,"fullSoakGate":false} diff --git a/scripts/validation/opcua-lifecycle-memory/native-retention/evidence/2026-11-10-native-import-timing/provenance.json b/scripts/validation/opcua-lifecycle-memory/native-retention/evidence/2026-11-10-native-import-timing/provenance.json new file mode 100644 index 00000000..82bf5408 --- /dev/null +++ b/scripts/validation/opcua-lifecycle-memory/native-retention/evidence/2026-11-10-native-import-timing/provenance.json @@ -0,0 +1,66 @@ +{ + "immutableBCommit": "ce11a5dcfff19f0d5501674e407b1aa331a083f3", + "immutableBSourceSha256": { + "NativeImportControl.c": "ed442bacbdbbc3d420864c4b8ddac9bb4a5799d1e1a1e67d33033678e8401648", + "native_import.py": "fc4092ab69a9a8b8213697d83c171632248c84c5c4bcbcf59ea6d2faffa82189", + "ImportSeed.cs": "feaac1c93bdb0f4e59bd0a9fb213d6de53cb89ece7393356c9d12d318b395c1b", + "pure_apple.py": "1b2148f1c20578529355379ab94b439931d7241608a5d5effce8fc86f4af7bc6" + }, + "sourcesSha256": { + "NativeImportTimingControl.c": "6c63a71684e4c8823596c00639b242c264f12078baab39522d4c0f554a41f3ea", + "native_import_timing.py": "0369c293afdf7db3628992bc71cdc534798927a7ad6e036b8978ecfbee9c57fd" + }, + "nativeBridgeSha256": "0333994e217a3d111d36d7f31460eea69a286f9fc819e2d7c746df13d2aec46c", + "managedCryptographySha256": "f4957d7218a004a27bf8964e45147df6a88092c6e7344582082840d62c6c579a", + "sdkSeedSha256": { + "Opc.Ua.Client.dll": "620da406fabdbc75d11f9827ea7e39d2d3c762471bd2264b0d11f1d6cd9247cd", + "Opc.Ua.Configuration.dll": "6ed6a594c93c3ff35d1a16539a9fe87c1e2640549cc2f48ed6bae333dc8bf0e7", + "Opc.Ua.Core.Schema.dll": "51cfad48657d95d38074bb88be29aea6f5b361a07397247d58838d066b99772e", + "Opc.Ua.Core.Types.dll": "a7dd141b8dabe8848ff28c2fb8e16a85a0508e6a49e6b1a1c1e267175b99af96", + "Opc.Ua.Core.dll": "9d0857aa6b7f3c49037d4ea557b4da8edf07e146069464481783e66c5c9cd35a", + "Opc.Ua.Security.Certificates.dll": "7f4969aceb2f0db5328c6882cb4b117342f3b18905c38acdba600683860c4fd0", + "Opc.Ua.Server.dll": "930e714a2f6e15d393840bcdc95e1afa8012bc740c3738a655f958142721864e", + "Opc.Ua.Types.dll": "c542f69882f4e8a4ebd5ccc28a3881d6967c7ffb6dcc3c56dc6c776f39c2ede1" + }, + "compileCommand": [ + "clang", + "-std=c11", + "-O0", + "-g", + "-Wall", + "-Wextra", + "/.codex/worktrees/opcua2-client-lifecycle/OtOpcUa/scripts/validation/opcua-lifecycle-memory/native-retention/NativeImportTimingControl.c", + "-framework", + "Security", + "-framework", + "CoreFoundation", + "-o", + "/private/tmp/opcua2-native-import-timing-20261110a/NativeImportTimingControl" + ], + "compiler": "Apple clang version 21.0.0 (clang-2100.0.123.102)\nTarget: arm64-apple-darwin25.5.0\nThread model: posix\nInstalledDir: /Library/Developer/CommandLineTools/usr/bin\n", + "os": "ProductName:\t\tmacOS\nProductVersion:\t\t26.5.2\nBuildVersion:\t\t25F84\n", + "architecture": "arm64", + "hostDotnet": ".NET SDK:\n Version: 10.0.403\n Commit: 5a2162bea6\n Workload version: 10.0.400-manifests.a00bebb6\n MSBuild version: 18.9.15+5a2162bea\n\nRuntime Environment:\n OS Name: Mac OS X\n OS Version: 26.5\n OS Platform: Darwin\n RID: osx-arm64\n Base Path: /opt/homebrew/Cellar/dotnet/10.0.403/libexec/sdk/10.0.403/\n\n.NET workloads installed:\nThere are no installed workloads to display.\nConfigured to use workload sets when installing new manifests.\nNo workload sets are installed. Run \"dotnet workload restore\" to install a workload set.\n\nHost:\n Version: 10.0.14\n Architecture: arm64\n Commit: 0427b9abbe\n\n.NET SDKs installed:\n 10.0.403 [/opt/homebrew/Cellar/dotnet/10.0.403/libexec/sdk]\n\n.NET runtimes installed:\n Microsoft.AspNetCore.App 10.0.14 [/opt/homebrew/Cellar/dotnet/10.0.403/libexec/shared/Microsoft.AspNetCore.App]\n Microsoft.NETCore.App 10.0.14 [/opt/homebrew/Cellar/dotnet/10.0.403/libexec/shared/Microsoft.NETCore.App]\n\nOther architectures found:\n None\n\nEnvironment variables:\n Not set\n\nglobal.json file:\n /.codex/worktrees/opcua2-client-lifecycle/OtOpcUa/global.json\n\nLearn more:\n https://aka.ms/dotnet/info\n\nDownload .NET:\n https://aka.ms/dotnet/download\n", + "order": [ + "normal", + "deferred" + ], + "repetitionsPerArm": 200, + "stages": [ + "before", + "first", + "repeated", + "idle" + ], + "idleSeconds": 5, + "fullSoakGate": false, + "managedCallerInMeasuredChildren": false, + "sharedSingleFreshPrivateSeed": true, + "fixtureCreationUmask": "0077", + "onlyTimingChange": "Retire logical ownership at zero in both; defer physical Delete+initial CFRelease until observed item/key/data/copy/error refs are released.", + "observedReturnedOwnershipOnly": true, + "checkpointDeadlineSecondsPerArm": 180, + "binarySha256": "cec035355aea998d6dd9311561b23e1617a1096191847cf99f1a0a90a7b6d112", + "seedBinarySha256": "36e648ac37b61cb5dd65f03dcf5db555fc297d23243c42e282ef522f15a8a7a1", + "linkedLibraries": "/private/tmp/opcua2-native-import-timing-20261110a/NativeImportTimingControl:\n\t/System/Library/Frameworks/Security.framework/Versions/A/Security (compatibility version 1.0.0, current version 61901.101.4)\n\t/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation (compatibility version 150.0.0, current version 4424.1.255)\n\t/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1356.0.0)\n" +} diff --git a/scripts/validation/opcua-lifecycle-memory/native-retention/evidence/2026-11-10-native-import-timing/result.json b/scripts/validation/opcua-lifecycle-memory/native-retention/evidence/2026-11-10-native-import-timing/result.json new file mode 100644 index 00000000..d4c39d60 --- /dev/null +++ b/scripts/validation/opcua-lifecycle-memory/native-retention/evidence/2026-11-10-native-import-timing/result.json @@ -0,0 +1,356 @@ +{ + "status": "passed", + "order": [ + "normal", + "deferred" + ], + "seed": { + "kind": "seed", + "leafSha256": "811a4e6cd2b5b21cc3e28d174b4a57dacc83d1732809dd11f0de3ed4cae7a9cd", + "runtime": "10.0.14", + "sdkVersion": "2.0.1.0", + "managerDisposed": true, + "certificateCoresOutstanding": 0, + "keyFormat": "PKCS1-private-RSA", + "privateFileMode": "0600", + "cryptographyRuntime": { + "informationalVersion": "10.0.14+0427b9abbead3fa64c6655eb4290c537397131fa", + "sha256": "f4957d7218a004a27bf8964e45147df6a88092c6e7344582082840d62c6c579a" + } + }, + "sharedSeedIdentityUnchanged": true, + "wallSecondsIncludingSeedAndBothArms": 35.54497370799072, + "arms": { + "normal": { + "status": "passed", + "order": "normal", + "exitCode": 0, + "samples": [ + { + "kind": "checkpoint", + "mode": "native-bridge-import-timing", + "order": "normal", + "stage": "before", + "operations": 0, + "processId": 88929, + "utc": "2026-11-10T22:46:42.027Z", + "elapsedSeconds": 0.004969, + "rssBytes": 7667712, + "createdRefs": 0, + "releasedRefs": 0, + "managedRuntimePresent": false, + "AppleDatabaseCount": 1, + "AppleDatabaseShallowBytes": 768, + "AppleDatabaseClassAbsent": false, + "databaseManagerClassPresent": true, + "fdCount": 3, + "ownedTemporaryFdCount": 0, + "ownedKeychainFdCount": 0, + "ownedKeychainFileCount": 0 + }, + { + "kind": "checkpoint", + "mode": "native-bridge-import-timing", + "order": "normal", + "stage": "first", + "operations": 1, + "processId": 88929, + "utc": "2026-11-10T22:46:42.988Z", + "elapsedSeconds": 0.965802, + "rssBytes": 10895360, + "createdRefs": 1, + "releasedRefs": 1, + "managedRuntimePresent": false, + "AppleDatabaseCount": 2, + "AppleDatabaseShallowBytes": 1536, + "AppleDatabaseClassAbsent": false, + "databaseManagerClassPresent": true, + "fdCount": 3, + "ownedTemporaryFdCount": 0, + "ownedKeychainFdCount": 0, + "ownedKeychainFileCount": 0 + }, + { + "kind": "checkpoint", + "mode": "native-bridge-import-timing", + "order": "normal", + "stage": "repeated", + "operations": 200, + "processId": 88929, + "utc": "2026-11-10T22:46:53.036Z", + "elapsedSeconds": 11.013997, + "rssBytes": 14548992, + "createdRefs": 200, + "releasedRefs": 200, + "managedRuntimePresent": false, + "AppleDatabaseCount": 201, + "AppleDatabaseShallowBytes": 154368, + "AppleDatabaseClassAbsent": false, + "databaseManagerClassPresent": true, + "fdCount": 3, + "ownedTemporaryFdCount": 0, + "ownedKeychainFdCount": 0, + "ownedKeychainFileCount": 0 + }, + { + "kind": "checkpoint", + "mode": "native-bridge-import-timing", + "order": "normal", + "stage": "idle", + "operations": 200, + "processId": 88929, + "utc": "2026-11-10T22:46:58.718Z", + "elapsedSeconds": 16.696203, + "rssBytes": 14581760, + "createdRefs": 200, + "releasedRefs": 200, + "managedRuntimePresent": false, + "AppleDatabaseCount": 201, + "AppleDatabaseShallowBytes": 154368, + "AppleDatabaseClassAbsent": false, + "databaseManagerClassPresent": true, + "fdCount": 3, + "ownedTemporaryFdCount": 0, + "ownedKeychainFdCount": 0, + "ownedKeychainFileCount": 0 + } + ], + "complete": { + "kind": "complete", + "order": "normal", + "operations": 200, + "createdRefs": 200, + "releasedRefs": 200, + "ownedOutputs": 2402, + "releasedOutputs": 2402, + "readOnlyDefaultAndSearchListUnchanged": true, + "managedRuntimePresent": false, + "bridgeSha256": "0333994e217a3d111d36d7f31460eea69a286f9fc819e2d7c746df13d2aec46c", + "elapsedSeconds": 17.40291, + "fullSoakGate": false + }, + "fingerprints": [ + { + "kind": "fingerprint", + "stage": "before", + "defaultStatus": 0, + "defaultPathStatus": 0, + "searchStatus": 0, + "searchPathStatus": 0, + "defaultSha256": "08c2c77dd8eaf8042b4020991ea06c6215cc391532869bbf6ced2cf1126c5e63", + "searchSha256": "c05fae700986dc7a6a147a938ef2adc4a274173420e137929ed58ad986eb2a3c", + "searchCount": 2, + "ownedQueryReferencesReleased": 2 + }, + { + "kind": "fingerprint", + "stage": "after", + "defaultStatus": 0, + "defaultPathStatus": 0, + "searchStatus": 0, + "searchPathStatus": 0, + "defaultSha256": "08c2c77dd8eaf8042b4020991ea06c6215cc391532869bbf6ced2cf1126c5e63", + "searchSha256": "c05fae700986dc7a6a147a938ef2adc4a274173420e137929ed58ad986eb2a3c", + "searchCount": 2, + "ownedQueryReferencesReleased": 4 + } + ], + "wallSecondsIncludingProbes": 17.802323875017464, + "fullSoakGate": false, + "logicalZeroStages": { + "release-copied-cert": 200 + }, + "actualDeletionStages": { + "release-copied-cert": 200 + }, + "ownedOutputsAtDeleteCounts": { + "5": 200 + }, + "returnedCopyReferencesByStage": { + "import-cert-track": 0, + "move-outer-copy": 0, + "move-cert-untrack": 0, + "move-cert-track": 200, + "move-identity-track": 0, + "identity-cert-track": 200, + "release-original-cert": 200, + "release-copied-cert": 200, + "release-identity": 0 + } + }, + "deferred": { + "status": "passed", + "order": "deferred", + "exitCode": 0, + "samples": [ + { + "kind": "checkpoint", + "mode": "native-bridge-import-timing", + "order": "deferred", + "stage": "before", + "operations": 0, + "processId": 88965, + "utc": "2026-11-10T22:46:59.453Z", + "elapsedSeconds": 0.003065, + "rssBytes": 7536640, + "createdRefs": 0, + "releasedRefs": 0, + "managedRuntimePresent": false, + "AppleDatabaseCount": 1, + "AppleDatabaseShallowBytes": 768, + "AppleDatabaseClassAbsent": false, + "databaseManagerClassPresent": true, + "fdCount": 3, + "ownedTemporaryFdCount": 0, + "ownedKeychainFdCount": 0, + "ownedKeychainFileCount": 0 + }, + { + "kind": "checkpoint", + "mode": "native-bridge-import-timing", + "order": "deferred", + "stage": "first", + "operations": 1, + "processId": 88965, + "utc": "2026-11-10T22:47:00.201Z", + "elapsedSeconds": 0.750946, + "rssBytes": 10878976, + "createdRefs": 1, + "releasedRefs": 1, + "managedRuntimePresent": false, + "AppleDatabaseCount": 2, + "AppleDatabaseShallowBytes": 1536, + "AppleDatabaseClassAbsent": false, + "databaseManagerClassPresent": true, + "fdCount": 3, + "ownedTemporaryFdCount": 0, + "ownedKeychainFdCount": 0, + "ownedKeychainFileCount": 0 + }, + { + "kind": "checkpoint", + "mode": "native-bridge-import-timing", + "order": "deferred", + "stage": "repeated", + "operations": 200, + "processId": 88965, + "utc": "2026-11-10T22:47:10.347Z", + "elapsedSeconds": 10.896725, + "rssBytes": 14123008, + "createdRefs": 200, + "releasedRefs": 200, + "managedRuntimePresent": false, + "AppleDatabaseCount": 201, + "AppleDatabaseShallowBytes": 154368, + "AppleDatabaseClassAbsent": false, + "databaseManagerClassPresent": true, + "fdCount": 3, + "ownedTemporaryFdCount": 0, + "ownedKeychainFdCount": 0, + "ownedKeychainFileCount": 0 + }, + { + "kind": "checkpoint", + "mode": "native-bridge-import-timing", + "order": "deferred", + "stage": "idle", + "operations": 200, + "processId": 88965, + "utc": "2026-11-10T22:47:16.069Z", + "elapsedSeconds": 16.618717, + "rssBytes": 14139392, + "createdRefs": 200, + "releasedRefs": 200, + "managedRuntimePresent": false, + "AppleDatabaseCount": 201, + "AppleDatabaseShallowBytes": 154368, + "AppleDatabaseClassAbsent": false, + "databaseManagerClassPresent": true, + "fdCount": 3, + "ownedTemporaryFdCount": 0, + "ownedKeychainFdCount": 0, + "ownedKeychainFileCount": 0 + } + ], + "complete": { + "kind": "complete", + "order": "deferred", + "operations": 200, + "createdRefs": 200, + "releasedRefs": 200, + "ownedOutputs": 2402, + "releasedOutputs": 2402, + "readOnlyDefaultAndSearchListUnchanged": true, + "managedRuntimePresent": false, + "bridgeSha256": "0333994e217a3d111d36d7f31460eea69a286f9fc819e2d7c746df13d2aec46c", + "elapsedSeconds": 17.289693, + "fullSoakGate": false + }, + "fingerprints": [ + { + "kind": "fingerprint", + "stage": "before", + "defaultStatus": 0, + "defaultPathStatus": 0, + "searchStatus": 0, + "searchPathStatus": 0, + "defaultSha256": "08c2c77dd8eaf8042b4020991ea06c6215cc391532869bbf6ced2cf1126c5e63", + "searchSha256": "c05fae700986dc7a6a147a938ef2adc4a274173420e137929ed58ad986eb2a3c", + "searchCount": 2, + "ownedQueryReferencesReleased": 2 + }, + { + "kind": "fingerprint", + "stage": "after", + "defaultStatus": 0, + "defaultPathStatus": 0, + "searchStatus": 0, + "searchPathStatus": 0, + "defaultSha256": "08c2c77dd8eaf8042b4020991ea06c6215cc391532869bbf6ced2cf1126c5e63", + "searchSha256": "c05fae700986dc7a6a147a938ef2adc4a274173420e137929ed58ad986eb2a3c", + "searchCount": 2, + "ownedQueryReferencesReleased": 4 + } + ], + "wallSecondsIncludingProbes": 17.311765540973283, + "fullSoakGate": false, + "logicalZeroStages": { + "release-copied-cert": 200 + }, + "actualDeletionStages": { + "after-all-item-releases": 200 + }, + "ownedOutputsAtDeleteCounts": { + "1": 200 + }, + "returnedCopyReferencesByStage": { + "import-cert-track": 0, + "move-outer-copy": 0, + "move-cert-untrack": 0, + "move-cert-track": 200, + "move-identity-track": 0, + "identity-cert-track": 200, + "release-original-cert": 200, + "release-copied-cert": 200, + "release-identity": 0 + } + } + }, + "AppleDatabaseCounts": { + "normal": [ + 1, + 2, + 201, + 201 + ], + "deferred": [ + 1, + 2, + 201, + 201 + ] + }, + "normalReproducedOnePerOperation": true, + "fullSoakGate": false, + "nativeInventoryIsNotOpenHandlesOrExclusiveRss": true +} diff --git a/scripts/validation/opcua-lifecycle-memory/native-retention/evidence/2026-11-10-native-import-timing/seed-build.log b/scripts/validation/opcua-lifecycle-memory/native-retention/evidence/2026-11-10-native-import-timing/seed-build.log new file mode 100644 index 00000000..9b23dc67 --- /dev/null +++ b/scripts/validation/opcua-lifecycle-memory/native-retention/evidence/2026-11-10-native-import-timing/seed-build.log @@ -0,0 +1,9 @@ + Determining projects to restore... + Restored /private/tmp/opcua2-native-import-timing-20261110a/ImportSeed.csproj (in 27 ms). + ImportSeed -> /private/tmp/opcua2-native-import-timing-20261110a/bin/Release/net10.0/ImportSeed.dll + +Build succeeded. + 0 Warning(s) + 0 Error(s) + +Time Elapsed 00:00:01.12 diff --git a/scripts/validation/opcua-lifecycle-memory/native-retention/evidence/2026-11-10-native-import-timing/seed.jsonl b/scripts/validation/opcua-lifecycle-memory/native-retention/evidence/2026-11-10-native-import-timing/seed.jsonl new file mode 100644 index 00000000..c2056e85 --- /dev/null +++ b/scripts/validation/opcua-lifecycle-memory/native-retention/evidence/2026-11-10-native-import-timing/seed.jsonl @@ -0,0 +1 @@ +{"kind":"seed","leafSha256":"811a4e6cd2b5b21cc3e28d174b4a57dacc83d1732809dd11f0de3ed4cae7a9cd","runtime":"10.0.14","sdkVersion":"2.0.1.0","managerDisposed":true,"certificateCoresOutstanding":0,"keyFormat":"PKCS1-private-RSA","privateFileMode":"0600","cryptographyRuntime":{"informationalVersion":"10.0.14\u002B0427b9abbead3fa64c6655eb4290c537397131fa","sha256":"f4957d7218a004a27bf8964e45147df6a88092c6e7344582082840d62c6c579a"}} diff --git a/scripts/validation/opcua-lifecycle-memory/native-retention/evidence/2026-11-10-native-import-timing/verification.json b/scripts/validation/opcua-lifecycle-memory/native-retention/evidence/2026-11-10-native-import-timing/verification.json new file mode 100644 index 00000000..92317e0b --- /dev/null +++ b/scripts/validation/opcua-lifecycle-memory/native-retention/evidence/2026-11-10-native-import-timing/verification.json @@ -0,0 +1,73 @@ +{ + "status": "completed-fixed-200-per-arm-same-native-inventory", + "projection": "Replace local user-home prefix only; selected samples are exact raw row subsets. Raw receipts unchanged.", + "rawOutput": "/tmp/opcua2-native-import-timing-20261110a", + "rawSha256": { + "result.json": "f0b4195fe445259934b91e9c6743437c8427844b8fe5e3a9aafe28847f495986", + "provenance.json": "debbe166166bed405fe7099890008ab031f88af88ec827ed81fd951bdacda88a", + "native-build.log": "0cc80245a334560a493c68ccce8cde34fddb976c71f9e76b28527143fe6908d5", + "seed-build.log": "cca7c5c582a3a468bda2f2ce8450e4cccfdbe714b6c17d3f003697e6221322ba", + "seed.jsonl": "9bd84d838438da83590b91b9295c80ea4b001c40900854fe818dfa4511adf46d", + "fixture-cleanup.json": "a67553b8bc66b05f3d798a001b0b727b073cd153316dfe6bf086e658a8f5df03", + "fixture-before-harness-cleanup.json": "ec5b52f033e4b312e1cb8a046521379b49988093f8f9e5ae33d3b9228e1023ad", + "normal/samples.jsonl": "73288755c768065ad0a8b2dad6964696d707f1c6be5a6b8bd531902cb3b4bf51", + "deferred/samples.jsonl": "61691ff4c500d2696f6998cd225d19677a0cb8eb47ed8f7545b01f76e4acc456" + }, + "selectedProjectionSha256": { + "result.json": "f0b4195fe445259934b91e9c6743437c8427844b8fe5e3a9aafe28847f495986", + "provenance.json": "a84e4c30da22fa20b5b9ad260f2e8fe589043b1cc11a9b6399f6b379a1341feb", + "native-build.log": "5de56e2de9449ccb8eb65627c9d63ea10ec4f688fbb777afed199b069bdfe223", + "seed-build.log": "cca7c5c582a3a468bda2f2ce8450e4cccfdbe714b6c17d3f003697e6221322ba", + "seed.jsonl": "9bd84d838438da83590b91b9295c80ea4b001c40900854fe818dfa4511adf46d", + "fixture-cleanup.json": "a67553b8bc66b05f3d798a001b0b727b073cd153316dfe6bf086e658a8f5df03", + "fixture-before-harness-cleanup.json": "ec5b52f033e4b312e1cb8a046521379b49988093f8f9e5ae33d3b9228e1023ad", + "normal-selected-samples.jsonl": "37e19d06a75dc9b8dc9057bd57180a4365a9863b82d36a33c5f308e602faa8cf", + "deferred-selected-samples.jsonl": "5072b01bafd497cfa684413290196ae7eb9a005dcd2f8126b14996f853cc52f6" + }, + "selectedSampleRawOneBasedLineNumbers": { + "normal": [ + 1, + 2, + 3, + 4, + 203, + 204, + 205, + 206, + 207 + ], + "deferred": [ + 1, + 2, + 3, + 4, + 203, + 204, + 205, + 206, + 207 + ] + }, + "rawLocalOnlySha256": { + "normal/result.json": "73d18077c2ce03a46a693725740c5b7920597c8a2688f5247544c86e19a4024e", + "normal/heap-before.txt": "1fe9a6d101d30e43bf40028527cb3db27aa39cd0c363465284e3725c4a51536e", + "normal/lsof-before.txt": "184473dff4da97a200fc7bfbddd1174a7c71225ec9fd8cd7eeccad1eeac20879", + "normal/heap-first.txt": "99c54edaa70e60bcb22715a7b69e1ba1b0e97ce1782423a3cd93d72c50c98f47", + "normal/lsof-first.txt": "31ae587eb32a5eea21e5a6d0d85112006c0d8191ae9f45715546cd4c28683def", + "normal/heap-repeated.txt": "51c8430558863e5fc49805ce5355e07288ab86d3ca5272d400649b3ef6cad83c", + "normal/lsof-repeated.txt": "31ae587eb32a5eea21e5a6d0d85112006c0d8191ae9f45715546cd4c28683def", + "normal/heap-idle.txt": "7b320f3a6dbb3dfdadd7de05508cb917472e9cb8379e2077011f1a13823c2f39", + "normal/lsof-idle.txt": "31ae587eb32a5eea21e5a6d0d85112006c0d8191ae9f45715546cd4c28683def", + "deferred/result.json": "a27a976e772458b1379172601ade808851723df1069cf4a2ded20f233d24bac5", + "deferred/heap-before.txt": "1416ffdfbfc47e6bf4719002b282fa5e57f03e2734d709c6d9f0b40b9141e987", + "deferred/lsof-before.txt": "baf3f74ab37a5778e259a92afb4099b24c437063b050d8ddd7a5dae7595651d5", + "deferred/heap-first.txt": "8a876d3594b33c6899a77f47432da3e58ca5a3f2c7e889673732ad9473ac345d", + "deferred/lsof-first.txt": "a731cbc84c777184be3c2e85d74c91d5e57ef9bd317b8e8a4b52a02a9d754921", + "deferred/heap-repeated.txt": "700ae3d358ae3d197ae735cc8250900ddd1a751942930ede5f8d1842b5b9ba73", + "deferred/lsof-repeated.txt": "a731cbc84c777184be3c2e85d74c91d5e57ef9bd317b8e8a4b52a02a9d754921", + "deferred/heap-idle.txt": "6e40883a16edb59d27904daaf261e10b760ac23cf9d13f895302de7afcd50444", + "deferred/lsof-idle.txt": "a731cbc84c777184be3c2e85d74c91d5e57ef9bd317b8e8a4b52a02a9d754921" + }, + "baselineBCommit": "ce11a5dcfff19f0d5501674e407b1aa331a083f3", + "noRetainingOwnerDefectMitigationOrFullSoakInference": true +} diff --git a/scripts/validation/opcua-lifecycle-memory/native-retention/native_import_timing.py b/scripts/validation/opcua-lifecycle-memory/native-retention/native_import_timing.py new file mode 100644 index 00000000..011f5ca2 --- /dev/null +++ b/scripts/validation/opcua-lifecycle-memory/native-retention/native_import_timing.py @@ -0,0 +1,215 @@ +#!/usr/bin/env python3 +"""One matched fixed-200/200 native keychain deletion timing diagnostic.""" +import argparse +from collections import Counter +import os +from pathlib import Path +import stat +import subprocess +import sys +import time +import xml.etree.ElementTree as ET +from native_import import (BRIDGE, BRIDGE_SHA, CHILDREN, COPY_STAGES, CRYPTO_SHA, DOTNET, EXPECTED_SDK, HERE, + REAP_OUTCOMES, RUNTIME, STAGES, bounded, complete_rows, reap, save, sha, snapshot, + validate_operation, wipe_owned) + +ORDERS = ['normal', 'deferred'] +IMMUTABLE_B = { + 'NativeImportControl.c': 'ed442bacbdbbc3d420864c4b8ddac9bb4a5799d1e1a1e67d33033678e8401648', + 'native_import.py': 'fc4092ab69a9a8b8213697d83c171632248c84c5c4bcbcf59ea6d2faffa82189', + 'ImportSeed.cs': 'feaac1c93bdb0f4e59bd0a9fb213d6de53cb89ece7393356c9d12d318b395c1b', + 'pure_apple.py': '1b2148f1c20578529355379ab94b439931d7241608a5d5effce8fc86f4af7bc6', +} + + +def validate_timing(row, order): + # B's validator replays logical registry ownership. Physical ownership is checked separately here. + projected = {**row, 'deleteStage': row['logicalZeroStage'], 'deleteLiveReferences': row['logicalZeroLiveReferences'], + 'copies': [{**copy, 'nativeOwnerBefore': copy['registryOwnerBefore'], + 'nativeOwnerAfter': copy['registryOwnerAfter']} for copy in row['copies']]} + validate_operation(projected) + if row['ownedOutputsAtLogicalZero'] != 1 + sum(row['logicalZeroLiveReferences'].values()): + raise RuntimeError('Observed native CF ownership did not reconcile at logical zero') + for index, copy in enumerate(row['copies']): + for point in ['Before', 'After']: + expected = copy['registryOwner' + point] if order == 'normal' else index > 0 + if copy['nativeOwner' + point] != expected: + raise RuntimeError('Physical keychain owner lifetime did not match selected timing') + if order == 'normal': + if (row['deleteStage'], row['deleteLiveReferences'], row['ownedOutputsAtDelete']) != ( + row['logicalZeroStage'], row['logicalZeroLiveReferences'], row['ownedOutputsAtLogicalZero']): + raise RuntimeError('Normal delete did not occur at logical zero') + elif row['deleteStage'] != 'after-all-item-releases' or any(row['deleteLiveReferences'].values()) or row['ownedOutputsAtDelete'] != 1: + raise RuntimeError('Deferred delete did not hold exactly its one observed native owner') + + +def seed_stamp(root): + values = [] + for name in ['leaf.der', 'private.pkcs1']: + info = (root / name).lstat() + if not stat.S_ISREG(info.st_mode) or stat.S_IMODE(info.st_mode) != 0o600 or info.st_nlink != 1: + raise RuntimeError('Shared seed inputs must be private0600 regular files') + values.append((info.st_dev, info.st_ino, info.st_size, info.st_mtime_ns)) + return values # Check same files privately; no private-key bytes, hashes or inode IDs in receipts. + + +def fingerprint_key(row): + if any(row[k] for k in ['defaultStatus', 'defaultPathStatus', 'searchStatus', 'searchPathStatus']): + raise RuntimeError('Keychain fingerprint query failed') + return row['defaultSha256'], row['searchSha256'], row['searchCount'] + + +def arm(binary, seed, task, output, order): + task.mkdir(mode=0o700); (task / 'tmp').mkdir(mode=0o700); output.mkdir(mode=0o700) + started = time.monotonic() + with (output / 'samples.jsonl').open('w') as log: + process = subprocess.Popen([str(binary), str(task), str(BRIDGE), str(seed), order], stdout=log, stderr=subprocess.STDOUT, + env=dict(os.environ, TMPDIR=str(task / 'tmp')), start_new_session=True) + CHILDREN.append(process) + try: + deadline = time.monotonic() + 180 + samples = [] + for stage in STAGES: + while True: + rows = complete_rows(output / 'samples.jsonl') + if any(r['kind'] == 'failed' for r in rows): + raise RuntimeError(order + ' timing child failed; preserve raw statuses') + matching = [r for r in rows if r['kind'] == 'checkpoint' and r['stage'] == stage] + if matching: + samples.append({**matching[0], **snapshot(process.pid, task, output, stage)}) + (task / ('continue-' + stage)).touch() + break + if process.poll() is not None or time.monotonic() >= deadline: + raise RuntimeError(order + ' checkpoint exceeded bound: ' + stage) + time.sleep(.05) + status = process.wait(timeout=15) + rows = complete_rows(output / 'samples.jsonl') + operations = [r for r in rows if r['kind'] == 'operation'] + fingerprints = [r for r in rows if r['kind'] == 'fingerprint'] + complete = [r for r in rows if r['kind'] == 'complete'] + checkpoints = [r for r in rows if r['kind'] == 'checkpoint'] + if (status or len(rows) != 207 or len(complete) != 1 or [r['iteration'] for r in operations] != list(range(1, 201)) + or [r['stage'] for r in fingerprints] != ['before', 'after'] or [r['stage'] for r in checkpoints] != STAGES + or [r['operations'] for r in checkpoints] != [0, 1, 200, 200] or any(r['order'] != order or r['managedRuntimePresent'] for r in checkpoints)): + raise RuntimeError(order + ' fixed operation/checkpoint counts failed') + for row in operations: + validate_timing(row, order) + done = complete[0] + if (done['order'] != order or done['operations'] != 200 or done['createdRefs'] != 200 or done['releasedRefs'] != 200 + or done['bridgeSha256'] != BRIDGE_SHA or done['managedRuntimePresent'] or not done['readOnlyDefaultAndSearchListUnchanged'] + or done['ownedOutputs'] != sum(r['ownedOutputs'] for r in operations) or done['releasedOutputs'] != sum(r['releasedOutputs'] for r in operations) + or fingerprint_key(fingerprints[0]) != fingerprint_key(fingerprints[1])): + raise RuntimeError(order + ' completion/ownership/fingerprint guard failed') + if any(samples[-1][k] for k in ['ownedKeychainFdCount', 'ownedKeychainFileCount']): + raise RuntimeError(order + ' private keychain files/descriptors retained') + result = {'status': 'passed', 'order': order, 'exitCode': status, 'samples': samples, 'complete': done, + 'fingerprints': fingerprints, 'wallSecondsIncludingProbes': time.monotonic() - started, 'fullSoakGate': False, + 'logicalZeroStages': dict(Counter(r['logicalZeroStage'] for r in operations)), + 'actualDeletionStages': dict(Counter(r['deleteStage'] for r in operations)), + 'ownedOutputsAtDeleteCounts': dict(Counter(r['ownedOutputsAtDelete'] for r in operations)), + 'returnedCopyReferencesByStage': {stage: sum(c['returnedRef'] for r in operations for c in r['copies'] if c['stage'] == stage) for stage in COPY_STAGES}} + save(output / 'result.json', result) + return result + finally: + reap(process) + + +def main(): + os.umask(0o077) + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('--references', required=True, type=Path) + parser.add_argument('--output', required=True, type=Path, help='Fresh private paired output') + args = parser.parse_args() + if sys.platform != 'darwin' or os.uname().machine != 'arm64': + parser.error('Pinned diagnostic requires macOS arm64') + if {name: sha(HERE / name) for name in IMMUTABLE_B} != IMMUTABLE_B: + raise RuntimeError('Immutable B sources changed') + if sha(BRIDGE) != BRIDGE_SHA or sha(RUNTIME / 'System.Security.Cryptography.dll') != CRYPTO_SHA: + raise RuntimeError('Installed native/managed cryptography identity changed') + references = args.references.resolve() + if {p.name: sha(p) for p in references.glob('Opc.Ua*.dll')} != EXPECTED_SDK: + raise RuntimeError('Pinned SDK2 seed binary set changed') + output = args.output.resolve(); output.mkdir(parents=True, exist_ok=False) + binary = output / 'NativeImportTimingControl' + command = ['clang', '-std=c11', '-O0', '-g', '-Wall', '-Wextra', str(HERE / 'NativeImportTimingControl.c'), + '-framework', 'Security', '-framework', 'CoreFoundation', '-o', str(binary)] + provenance = {'immutableBCommit': 'ce11a5dcfff19f0d5501674e407b1aa331a083f3', 'immutableBSourceSha256': IMMUTABLE_B, + 'sourcesSha256': {name: sha(HERE / name) for name in ['NativeImportTimingControl.c', 'native_import_timing.py']}, + 'nativeBridgeSha256': BRIDGE_SHA, 'managedCryptographySha256': CRYPTO_SHA, 'sdkSeedSha256': EXPECTED_SDK, + 'compileCommand': command, 'compiler': subprocess.check_output(['clang', '--version'], text=True, timeout=10), + 'os': subprocess.check_output(['sw_vers'], text=True, timeout=10), 'architecture': os.uname().machine, + 'hostDotnet': subprocess.check_output([DOTNET, '--info'], text=True, timeout=10), + 'order': ORDERS, 'repetitionsPerArm': 200, 'stages': STAGES, 'idleSeconds': 5, 'fullSoakGate': False, + 'managedCallerInMeasuredChildren': False, 'sharedSingleFreshPrivateSeed': True, 'fixtureCreationUmask': '0077', + 'onlyTimingChange': 'Retire logical ownership at zero in both; defer physical Delete+initial CFRelease until observed item/key/data/copy/error refs are released.', + 'observedReturnedOwnershipOnly': True, 'checkpointDeadlineSecondsPerArm': 180} + save(output / 'provenance.json', provenance) + bounded(command, output / 'native-build.log', 60) + project = ET.Element('Project', Sdk='Microsoft.NET.Sdk'); properties = ET.SubElement(project, 'PropertyGroup') + for name, value in {'OutputType': 'Exe', 'TargetFramework': 'net10.0', 'ImplicitUsings': 'enable', 'Nullable': 'enable', + 'EnableDefaultCompileItems': 'false', 'TreatWarningsAsErrors': 'true', 'UseSharedCompilation': 'false'}.items(): + ET.SubElement(properties, name).text = value + items = ET.SubElement(project, 'ItemGroup'); ET.SubElement(items, 'FrameworkReference', Include='Microsoft.AspNetCore.App') + ET.SubElement(items, 'Compile', Include=str(HERE / 'ImportSeed.cs')) + for name in EXPECTED_SDK: + ref = ET.SubElement(items, 'Reference', Include=Path(name).stem); ET.SubElement(ref, 'HintPath').text = str(references / name) + ET.indent(project); ET.ElementTree(project).write(output / 'ImportSeed.csproj', encoding='unicode') + bounded([DOTNET, 'build', str(output / 'ImportSeed.csproj'), '-c', 'Release', '-m:1', '--disable-build-servers', '--nologo'], output / 'seed-build.log', 120) + provenance['binarySha256'] = sha(binary) + provenance['seedBinarySha256'] = sha(output / 'bin/Release/net10.0/ImportSeed.dll') + provenance['linkedLibraries'] = subprocess.check_output(['otool', '-L', str(binary)], text=True, timeout=10) + save(output / 'provenance.json', provenance) + task = output / 'task'; task.mkdir(mode=0o700); seed = task / 'seed' + for name in ['seed', 'seed-tmp', 'seed-data']: + (task / name).mkdir(mode=0o700) + started = time.monotonic() + try: + bounded([DOTNET, str(output / 'bin/Release/net10.0/ImportSeed.dll'), str(seed)], output / 'seed.jsonl', 75, + env=dict(os.environ, TMPDIR=str(task / 'seed-tmp'), XDG_DATA_HOME=str(task / 'seed-data'))) + rows = complete_rows(output / 'seed.jsonl') + if len(rows) != 1 or rows[0]['kind'] != 'seed' or rows[0]['sdkVersion'] != '2.0.1.0' or rows[0]['cryptographyRuntime']['sha256'] != CRYPTO_SHA: + raise RuntimeError('Seed/runtime count or identity mismatch') + seed_row = rows[0] + if any(p.stat().st_mode & 0o077 for p in seed.rglob('*') if p.is_file()): + raise RuntimeError('Seed file permissions are not private') + wipe_owned(seed / 'pki'); wipe_owned(task / 'seed-tmp'); wipe_owned(task / 'seed-data') + same_seed = seed_stamp(seed) + results = {} + for order in ORDERS: + if seed_stamp(seed) != same_seed or sha(seed / 'leaf.der') != seed_row['leafSha256'] or sha(BRIDGE) != BRIDGE_SHA or sha(binary) != provenance['binarySha256']: + raise RuntimeError('Shared seed or pinned measured binary changed') + results[order] = arm(binary, seed, task / order, output / order, order) + if seed_stamp(seed) != same_seed or sha(seed / 'leaf.der') != seed_row['leafSha256']: + raise RuntimeError('Shared private seed changed across measured arms') + if len({fingerprint_key(row) for result in results.values() for row in result['fingerprints']}) != 1: + raise RuntimeError('Read-only keychain fingerprints differ across paired arms') + counts = {order: [r['AppleDatabaseCount'] for r in results[order]['samples']] for order in ORDERS} + normal = counts['normal'] + save(output / 'result.json', {'status': 'passed', 'order': ORDERS, 'seed': seed_row, 'sharedSeedIdentityUnchanged': True, + 'wallSecondsIncludingSeedAndBothArms': time.monotonic() - started, 'arms': results, 'AppleDatabaseCounts': counts, + 'normalReproducedOnePerOperation': normal[1] == normal[0] + 1 and normal[2] == normal[3] == normal[0] + 200, + 'fullSoakGate': False, 'nativeInventoryIsNotOpenHandlesOrExclusiveRss': True}) + finally: + save(output / 'fixture-before-harness-cleanup.json', {'remainingOwnedKeychainFilesByArm': { + order: sum(p.is_file() and '.keychain' in p.name for p in (task / order / 'tmp').rglob('*')) for order in ORDERS}}) + all_reaped = all(child.returncode is not None for child in CHILDREN) + cleanup = {'allLaunchedChildrenConfirmedReaped': all_reaped, 'reapOutcomes': REAP_OUTCOMES, + 'privateMaterialOverwrittenThenUnlinkedAfterChildReaping': False, 'physicalSecureErasureClaimed': False} + if not all_reaped: + cleanup.update(status='failed-preserved-private-fixture', ownedFixtureRootRemoved=False) + save(output / 'fixture-cleanup.json', cleanup) + raise RuntimeError('A launched child was not reaped; private fixture preserved') + try: + wipe_owned(task) + cleanup.update(status='passed', ownedFixtureRootRemoved=not task.exists(), + privateMaterialOverwrittenThenUnlinkedAfterChildReaping=True) + except Exception as error: + cleanup.update(status='failed', errorType=type(error).__name__, ownedFixtureRootRemoved=not task.exists()) + raise + finally: + save(output / 'fixture-cleanup.json', cleanup) + print(output) + + +if __name__ == '__main__': + main()