From 3ca21aac245dac483d43faed7714379868c951dc Mon Sep 17 00:00:00 2001 From: Denis Chilik Date: Tue, 21 Apr 2026 09:51:32 -0400 Subject: [PATCH] fix: guard _midOverride access with os_unfair_lock to prevent UAF crash Direct reads/writes of the file-scope `_midOverride` NSString* from multiple threads race on the ARC-managed retain/release of the static. A reader loading the pointer and then being preempted can end up retaining memory that was just released by another writer, yielding a dangling NSString. That dangling value later gets captured into an NSMutableDictionary and, when mParticle serializes the dictionary on `com.mparticle.messageQueue`, `NSJSONSerialization` crashes in `_NSIsNSString` / `object_getMethodImplementation` while probing the dead isa. Wrap `_midOverride` and `_willOverrideMid` behind file-scope accessors protected by `os_unfair_lock` and take an immutable local snapshot inside the `sendRequestWithMarketingCloudId:` completion block so the static is read only once under the lock instead of twice through `_midOverride ?: ...`. --- mParticle-Adobe/MPKitAdobe.m | 52 +++++++++++++++++++++++++++++++----- 1 file changed, 45 insertions(+), 7 deletions(-) diff --git a/mParticle-Adobe/MPKitAdobe.m b/mParticle-Adobe/MPKitAdobe.m index 6d5400f..497787d 100644 --- a/mParticle-Adobe/MPKitAdobe.m +++ b/mParticle-Adobe/MPKitAdobe.m @@ -1,5 +1,6 @@ #import "MPKitAdobe.h" #import "MPIAdobe.h" +#import static NSString *const marketingCloudIdIntegrationAttributeKey = @"mid"; static NSString *const blobIntegrationAttributeKey = @"aamb"; @@ -33,6 +34,40 @@ @implementation MPKitAdobe static NSString *_midOverride = nil; static BOOL _willOverrideMid = NO; +static os_unfair_lock _midOverrideLock = OS_UNFAIR_LOCK_INIT; + +// Thread-safe accessors for the file-scope statics above. +// Direct reads/writes of `_midOverride` from multiple threads race on the +// ARC-managed retain/release of the static, which can cause a value that is +// about to be released to be captured into an NSDictionary and later trigger +// a use-after-free when the dictionary is serialized on the mParticle +// message queue. +static NSString * _Nullable MPKitAdobeCopyMidOverride(void) { + os_unfair_lock_lock(&_midOverrideLock); + NSString *snapshot = _midOverride; + os_unfair_lock_unlock(&_midOverrideLock); + return snapshot; +} + +static void MPKitAdobeSetMidOverride(NSString * _Nullable value) { + NSString *copied = [value copy]; + os_unfair_lock_lock(&_midOverrideLock); + _midOverride = copied; + os_unfair_lock_unlock(&_midOverrideLock); +} + +static BOOL MPKitAdobeGetWillOverrideMid(void) { + os_unfair_lock_lock(&_midOverrideLock); + BOOL value = _willOverrideMid; + os_unfair_lock_unlock(&_midOverrideLock); + return value; +} + +static void MPKitAdobeSetWillOverrideMid(BOOL value) { + os_unfair_lock_lock(&_midOverrideLock); + _willOverrideMid = value; + os_unfair_lock_unlock(&_midOverrideLock); +} + (NSNumber *)kitCode { return @124; @@ -47,15 +82,16 @@ + (void)load { static __weak MPKitAdobe *_sharedInstance = nil; + (void)overrideMarketingCloudId:(NSString *)mid { - _midOverride = mid; - if (mid) { - [[MParticle sharedInstance] setIntegrationAttributes:@{marketingCloudIdIntegrationAttributeKey: mid} forKit:[[self class] kitCode]]; + NSString *midSnapshot = [mid copy]; + MPKitAdobeSetMidOverride(midSnapshot); + if (midSnapshot) { + [[MParticle sharedInstance] setIntegrationAttributes:@{marketingCloudIdIntegrationAttributeKey: midSnapshot} forKit:[[self class] kitCode]]; } [_sharedInstance performSelectorOnMainThread:@selector(sendNetworkRequest) withObject:nil waitUntilDone:NO]; } + (void)willOverrideMarketingCloudId:(BOOL)willOverrideMid { - _willOverrideMid = willOverrideMid; + MPKitAdobeSetWillOverrideMid(willOverrideMid); } #pragma mark MPKitInstanceProtocol methods @@ -140,11 +176,12 @@ - (NSString *)pushToken { } - (void)sendNetworkRequest { - if (_willOverrideMid && !_midOverride) { + NSString *midOverrideSnapshot = MPKitAdobeCopyMidOverride(); + if (MPKitAdobeGetWillOverrideMid() && !midOverrideSnapshot) { return; } - NSString *marketingCloudId = _midOverride; + NSString *marketingCloudId = midOverrideSnapshot; if (!marketingCloudId) { marketingCloudId = [self marketingCloudIdFromIntegrationAttributes]; if (!marketingCloudId) { @@ -172,9 +209,10 @@ - (void)sendNetworkRequest { return; } + NSString *midOverrideForCompletion = MPKitAdobeCopyMidOverride(); NSMutableDictionary *integrationAttributes = [NSMutableDictionary dictionary]; if (marketingCloudId.length) { - [integrationAttributes setObject:(_midOverride ?: marketingCloudId) forKey:marketingCloudIdIntegrationAttributeKey]; + [integrationAttributes setObject:(midOverrideForCompletion ?: marketingCloudId) forKey:marketingCloudIdIntegrationAttributeKey]; } if (locationHint.length) { [integrationAttributes setObject:locationHint forKey:locationHintIntegrationAttributeKey];