Prechádzať zdrojové kódy

Fix #14273: Prevent race condition crash in FPRTraceBackgroundActivityTracker (#15382)

Jesús Rojas 5 mesiacov pred
rodič
commit
9cb17fca63

+ 3 - 0
FirebasePerformance/CHANGELOG.md

@@ -1,3 +1,6 @@
+# Unreleased
+- [fixed] Prevent race condition crash in FPRTraceBackgroundActivityTracker. (#14273)
+
 # 12.3.0
 - [fixed] Add missing nanopb dependency to fix SwiftPM builds when building
   dynamically linked libraries. (#15276)

+ 19 - 9
FirebasePerformance/Sources/AppActivity/FPRTraceBackgroundActivityTracker.m

@@ -23,6 +23,8 @@
 
 @property(nonatomic, readwrite) FPRTraceState traceBackgroundState;
 
+- (void)registerNotificationObservers;
+
 @end
 
 @implementation FPRTraceBackgroundActivityTracker
@@ -35,21 +37,29 @@
     } else {
       _traceBackgroundState = FPRTraceStateForegroundOnly;
     }
+    __weak typeof(self) weakSelf = self;
     dispatch_async(dispatch_get_main_queue(), ^{
-      [[NSNotificationCenter defaultCenter] addObserver:self
-                                               selector:@selector(applicationDidBecomeActive:)
-                                                   name:UIApplicationDidBecomeActiveNotification
-                                                 object:[UIApplication sharedApplication]];
-
-      [[NSNotificationCenter defaultCenter] addObserver:self
-                                               selector:@selector(applicationDidEnterBackground:)
-                                                   name:UIApplicationDidEnterBackgroundNotification
-                                                 object:[UIApplication sharedApplication]];
+      __strong typeof(weakSelf) strongSelf = weakSelf;
+      if (strongSelf) {
+        [strongSelf registerNotificationObservers];
+      }
     });
   }
   return self;
 }
 
+- (void)registerNotificationObservers {
+  [[NSNotificationCenter defaultCenter] addObserver:self
+                                           selector:@selector(applicationDidBecomeActive:)
+                                               name:UIApplicationDidBecomeActiveNotification
+                                             object:[UIApplication sharedApplication]];
+
+  [[NSNotificationCenter defaultCenter] addObserver:self
+                                           selector:@selector(applicationDidEnterBackground:)
+                                               name:UIApplicationDidEnterBackgroundNotification
+                                             object:[UIApplication sharedApplication]];
+}
+
 - (void)dealloc {
   // Remove all the notification observers registered.
   [[NSNotificationCenter defaultCenter] removeObserver:self];

+ 65 - 0
FirebasePerformance/Tests/Unit/FPRTraceBackgroundActivityTrackerTest.m

@@ -61,4 +61,69 @@
                                }];
 }
 
+/** Tests that synchronous observer registration works correctly and observers are immediately
+ * available. */
+- (void)testObservers_synchronousRegistrationAddsObserver {
+  NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
+  FPRTraceBackgroundActivityTracker *tracker = [[FPRTraceBackgroundActivityTracker alloc] init];
+  XCTAssertNotNil(tracker);
+
+  [notificationCenter postNotificationName:UIApplicationDidBecomeActiveNotification
+                                    object:[UIApplication sharedApplication]];
+  XCTAssertEqual(tracker.traceBackgroundState, FPRTraceStateForegroundOnly);
+
+  tracker = nil;
+  XCTAssertNil(tracker);
+  XCTAssertNoThrow([notificationCenter postNotificationName:UIApplicationDidBecomeActiveNotification
+                                                     object:[UIApplication sharedApplication]]);
+  XCTAssertNoThrow([notificationCenter
+      postNotificationName:UIApplicationDidEnterBackgroundNotification
+                    object:[UIApplication sharedApplication]]);
+}
+
+/** Tests rapid creation and deallocation to verify race condition. */
+- (void)testRapidCreationAndDeallocation_noRaceCondition {
+  for (int i = 0; i < 100; i++) {
+    @autoreleasepool {
+      FPRTraceBackgroundActivityTracker *tracker = [[FPRTraceBackgroundActivityTracker alloc] init];
+      XCTAssertNotNil(tracker);
+
+      [[NSNotificationCenter defaultCenter]
+          postNotificationName:UIApplicationDidBecomeActiveNotification
+                        object:[UIApplication sharedApplication]];
+    }
+  }
+
+  XCTAssertNoThrow([[NSNotificationCenter defaultCenter]
+      postNotificationName:UIApplicationDidBecomeActiveNotification
+                    object:[UIApplication sharedApplication]]);
+  XCTAssertNoThrow([[NSNotificationCenter defaultCenter]
+      postNotificationName:UIApplicationDidEnterBackgroundNotification
+                    object:[UIApplication sharedApplication]]);
+}
+
+/** Tests observer registration when created from background thread. */
+- (void)testObservers_registrationFromBackgroundThread {
+  XCTestExpectation *expectation = [self expectationWithDescription:@"Background thread creation"];
+
+  dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+    FPRTraceBackgroundActivityTracker *tracker = [[FPRTraceBackgroundActivityTracker alloc] init];
+    XCTAssertNotNil(tracker);
+
+    dispatch_async(dispatch_get_main_queue(), ^{
+      [[NSNotificationCenter defaultCenter]
+          postNotificationName:UIApplicationDidBecomeActiveNotification
+                        object:[UIApplication sharedApplication]];
+
+      XCTAssertEqual(tracker.traceBackgroundState, FPRTraceStateForegroundOnly);
+      [expectation fulfill];
+    });
+  });
+
+  [self waitForExpectationsWithTimeout:5.0
+                               handler:^(NSError *error) {
+                                 XCTAssertNil(error, @"Test timed out");
+                               }];
+}
+
 @end