Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit 6a2afca

Browse files
authored
[iOS] Fix use-after-free in setBinaryMessenger (#44294)
Previously, when setting the binary messenger to the current binary messenger, we were freeing the current binary messenger before setting the new (current) binary messenger, triggering a use after free. [C++, Objective-C, Java style guides]: https://github.com/flutter/engine/blob/main/CONTRIBUTING.md#style
1 parent 85e7c5d commit 6a2afca

File tree

2 files changed

+35
-3
lines changed

2 files changed

+35
-3
lines changed

shell/platform/darwin/ios/framework/Source/FlutterEngine.mm

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1217,9 +1217,11 @@ - (void)flutterViewAccessibilityDidCall {
12171217
// remove this.
12181218
- (void)setBinaryMessenger:(FlutterBinaryMessengerRelay*)binaryMessenger {
12191219
// Discard the previous messenger and keep the new one.
1220-
_binaryMessenger.parent = nil;
1221-
[_binaryMessenger release];
1222-
_binaryMessenger = [binaryMessenger retain];
1220+
if (binaryMessenger != _binaryMessenger) {
1221+
_binaryMessenger.parent = nil;
1222+
[_binaryMessenger release];
1223+
_binaryMessenger = [binaryMessenger retain];
1224+
}
12231225
}
12241226

12251227
#pragma mark - FlutterBinaryMessenger

shell/platform/darwin/ios/framework/Source/FlutterEngineTest.mm

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,22 @@ @interface FlutterEngine () <FlutterTextInputDelegate>
2222

2323
@end
2424

25+
/// FlutterBinaryMessengerRelay used for testing that setting FlutterEngine.binaryMessenger to
26+
/// the current instance doesn't trigger a use-after-free bug.
27+
///
28+
/// See: testSetBinaryMessengerToSameBinaryMessenger
29+
@interface FakeBinaryMessengerRelay : FlutterBinaryMessengerRelay
30+
@property(nonatomic, assign) BOOL failOnDealloc;
31+
@end
32+
33+
@implementation FakeBinaryMessengerRelay
34+
- (void)dealloc {
35+
if (_failOnDealloc) {
36+
XCTFail("FakeBinaryMessageRelay should not be deallocated");
37+
}
38+
}
39+
@end
40+
2541
@interface FlutterEngineTest : XCTestCase
2642
@end
2743

@@ -148,6 +164,20 @@ - (void)testNotifyPluginOfDealloc {
148164
OCMVerify([plugin detachFromEngineForRegistrar:[OCMArg any]]);
149165
}
150166

167+
- (void)testSetBinaryMessengerToSameBinaryMessenger {
168+
FakeBinaryMessengerRelay* fakeBinaryMessenger = [[FakeBinaryMessengerRelay alloc] init];
169+
170+
FlutterEngine* engine = [[FlutterEngine alloc] init];
171+
[engine setBinaryMessenger:fakeBinaryMessenger];
172+
173+
// Verify that the setter doesn't free the old messenger before setting the new messenger.
174+
fakeBinaryMessenger.failOnDealloc = YES;
175+
[engine setBinaryMessenger:fakeBinaryMessenger];
176+
177+
// Don't fail when ARC releases the binary messenger.
178+
fakeBinaryMessenger.failOnDealloc = NO;
179+
}
180+
151181
- (void)testRunningInitialRouteSendsNavigationMessage {
152182
id mockBinaryMessenger = OCMClassMock([FlutterBinaryMessengerRelay class]);
153183

0 commit comments

Comments
 (0)