-
Notifications
You must be signed in to change notification settings - Fork 6k
iOS: Add null checks on shell dereference #57099
Conversation
`FlutterEngine` at the `_shell` unique_ptr ivar it owns have different lifetimes. `_shell` is initialised transitively from `runWithEntrypoint`, and reset in `[FlutterEngine destroyContext]`, which is called transitively from `[FlutterviewController dealloc]` via `[FlutterEngine notifyViewControllerDeallocated]`. As such, all uses of `_shell` should be checked either via an assertion, in cases we know the shell should be non-null, or via a runtime null check in cases where it's expected that it may be null. Specifically, this guards against a crash that can occur if we get a CoreAnimation transaction commit callback for an inflight frame just as we're shutting down the app (or removing the FlutterView in an add-to-app scenario). Example stack trace: ``` 0 Flutter 0x11b28 -[FlutterEngine platformView] + 53 (weak_ptr.h:53) 1 Flutter 0x11994 -[FlutterEngine updateViewportMetrics:] + 186 (ref_ptr.h:186) 2 Flutter 0x1f854 -[FlutterViewController updateViewportMetricsIfNeeded] + 427 (vector:427) 3 Flutter 0x1f9b8 -[FlutterViewController viewDidLayoutSubviews] + 1411 (FlutterViewController.mm:1411) 4 UIKitCore 0x8c864 -[UIView(CALayerDelegate) layoutSublayersOfLayer:] + 2376 5 QuartzCore 0x1fa0c CA::Layer::layout_if_needed(CA::Transaction*) + 516 6 QuartzCore 0x1ae84c CA::Context::commit_transaction(CA::Transaction*, double, double*) + 516 7 QuartzCore 0x2888 CA::Transaction::commit() + 648 ``` Issue: flutter/flutter#159639
hellohuanlin
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
2 questions ask to learn
| - (flutter::PlatformViewIOS*)iosPlatformView { | ||
| FML_DCHECK(_shell); | ||
| if (!_shell) { | ||
| return nullptr; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
curious why we use WeakPtr in previous line, but raw pointer here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
and RefPtr below? (Should they be WeakPtr as well?)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
use cases:
WeakPtr: A raw pointer wrapper that, in unopt builds, asserts that all dereferences occur on the thread on which it was allocated. We use this to ensure that all derefs ofshelloccur on the platform thread. Does not do refcounting. We use this where we want to enforce threading invariants but object ownership/lifetime is managed by someone else.RefPtr: A refcounted pointer. We use this in the engine where an object ownership is shared between a pool of owners. You could think of this as somewhat similar tostd::shared_ptr. There's other utility code inref_counted.hbuilt around this that supports threading invariants.- raw C/C++ pointers: In cases where we want a non-owning pointer to an object whose lifetime is managed by someone else and where we don't care about threading invariants, we often use raw C/C++ pointers.
When I wrote this patch, I looked and these getters are only really used by FlutterEngine itself, FlutterViewController, and tests. There's no reason we couldn't be consistent and return at raw pointer instead of the WeakPtr in the platformView getter, but that's orthogonal to this fix.
My thinking is that rather than do that though, I'd rather send a followup that eliminates these getters altogether and provides better API instead. For example, engine.iosPlatformView is used by accessibility code in FlutterViewController; the relevant non-view-specific code should simply be refactored into FlutterEngine instead.
…160044) flutter/engine@58e5f9b...23fc43d 2024-12-10 chris@bracken.jp iOS: Add null checks on shell dereference (flutter/engine#57099) 2024-12-10 Breakthrough@users.noreply.github.com [engine] Migrate fuchsia.io Open functions to Open3 (flutter/engine#56818) If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/flutter-engine-flutter-autoroll Please CC jonahwilliams@google.com,zra@google.com on the revert to ensure that a human is aware of the problem. To file a bug in Flutter: https://github.com/flutter/flutter/issues/new/choose To report a problem with the AutoRoller itself, please file a bug: https://issues.skia.org/issues/new?component=1389291&template=1850622 Documentation for the AutoRoller is here: https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md
Eliminates some cases where `FlutterViewController` was relying on `FlutterEngine` internals: * `[FlutterEngine shell]` * `[FlutterEngine platformView]` * `[FlutterEngine iosPlatformView]` Instead, `FlutterEngine` now exposes: * `installFirstFrameCallback:` * `enableSemantics:withFlags:` * `notifyViewCreated` * `notifyViewDestroyed` * `waitForFirstFrameSync:callback:` Also fixes a couple cases where we were relying on transitive header includes: * `FlutterAppController` relied on `FlutterViewController_Internal.h` for `sendDeepLinkToFramework:completionHandler:` This is a refactoring followup to #57099 that introduces no semantic changes. [C++, Objective-C, Java style guides]: https://github.com/flutter/engine/blob/main/CONTRIBUTING.md#style
`FlutterEngine` at the `_shell` unique_ptr ivar it owns have different lifetimes. `_shell` is initialised transitively from `runWithEntrypoint`, and reset in `[FlutterEngine destroyContext]`, which is called transitively from `[FlutterviewController dealloc]` via `[FlutterEngine notifyViewControllerDeallocated]`. As such, all uses of `_shell` should be checked either via an assertion, in cases we know the shell should be non-null, or via a runtime null check in cases where it's expected that it may be null. Specifically, this guards against a crash that can occur if we get a CoreAnimation transaction commit callback for an inflight frame just as we're shutting down the app (or removing the FlutterView in an add-to-app scenario). Example stack trace: ``` 0 Flutter 0x11b28 -[FlutterEngine platformView] + 53 (weak_ptr.h:53) 1 Flutter 0x11994 -[FlutterEngine updateViewportMetrics:] + 186 (ref_ptr.h:186) 2 Flutter 0x1f854 -[FlutterViewController updateViewportMetricsIfNeeded] + 427 (vector:427) 3 Flutter 0x1f9b8 -[FlutterViewController viewDidLayoutSubviews] + 1411 (FlutterViewController.mm:1411) 4 UIKitCore 0x8c864 -[UIView(CALayerDelegate) layoutSublayersOfLayer:] + 2376 5 QuartzCore 0x1fa0c CA::Layer::layout_if_needed(CA::Transaction*) + 516 6 QuartzCore 0x1ae84c CA::Context::commit_transaction(CA::Transaction*, double, double*) + 516 7 QuartzCore 0x2888 CA::Transaction::commit() + 648 ``` Issue: flutter#98735 Issue: flutter#159639 [C++, Objective-C, Java style guides]: https://github.com/flutter/engine/blob/main/CONTRIBUTING.md#style
Eliminates some cases where `FlutterViewController` was relying on `FlutterEngine` internals: * `[FlutterEngine shell]` * `[FlutterEngine platformView]` * `[FlutterEngine iosPlatformView]` Instead, `FlutterEngine` now exposes: * `installFirstFrameCallback:` * `enableSemantics:withFlags:` * `notifyViewCreated` * `notifyViewDestroyed` * `waitForFirstFrameSync:callback:` Also fixes a couple cases where we were relying on transitive header includes: * `FlutterAppController` relied on `FlutterViewController_Internal.h` for `sendDeepLinkToFramework:completionHandler:` This is a refactoring followup to flutter/engine#57099 that introduces no semantic changes. [C++, Objective-C, Java style guides]: https://github.com/flutter/engine/blob/main/CONTRIBUTING.md#style
FlutterEngineat the_shellunique_ptr ivar it owns have different lifetimes._shellis initialised transitively fromrunWithEntrypoint, and reset in[FlutterEngine destroyContext], which is called transitively from[FlutterviewController dealloc]via[FlutterEngine notifyViewControllerDeallocated].As such, all uses of
_shellshould be checked either via an assertion, in cases we know the shell should be non-null, or via a runtime null check in cases where it's expected that it may be null.Specifically, this guards against a crash that can occur if we get a CoreAnimation transaction commit callback for an inflight frame just as we're shutting down the app (or removing the FlutterView in an add-to-app scenario).
Example stack trace:
Issue: flutter/flutter#98735
Issue: flutter/flutter#159639
Pre-launch Checklist
///).If you need help, consider asking for advice on the #hackers-new channel on Discord.