iOS 26: Fabric index corruption + dangling pointer-interaction registration cause use-after-free crashes
Nessuno ha ancora preso questa issue.
Valutazione
- Difficoltà
- 4/5
- Tempo stimato
- 3-5 giorni
- Idoneità per principianti
- 72/100
- Tipo di issue
- Bug
- Chiarezza
- Specificata chiaramente
- Stato di attività
- Attiva
- Stack tecnologico
- cpp, ios, objective-c, react-native, swift
- Ambito
- mobile-dev
Direzione di ricerca
Leggi ios/LiquidGlassView.mm e ios/LiquidGlassView.swift, concentrandoti sul montaggio dei figli di Fabric e sui percorsi di riciclo/deallocazione. Esegui il ciclo di stress descritto per iOS 26 con viste glass interattive e frequenti cambi di navigazione; il lavoro è completato quando entrambe le firme di crash segnalate continuano a non comparire e la classe fallback continua a compilare sui branch di SDK meno recenti.
Scritto dal modello di indicizzazione a partire dal testo della issue.
Descrizione
Summary
On iOS 26, apps using LiquidGlassView (especially with interactive) crash sporadically with memory-corruption signatures during React Native (Fabric) mount/unmount transactions. We root-caused two distinct mechanisms in the library's iOS implementation and validated fixes in production-style stress testing. Happy to send a PR if you agree with the approach.
Environment
@callstack/liquid-glass0.8.0- react-native 0.81.4 (New Architecture / Fabric)
- iOS 26.5 (simulator) and iOS 26.6 (device, field crashes)
Crash signatures observed
EXC_BAD_ACCESSin-[_UIPointerInteractionAssistant _assistantForView:]/-[UIView(UIPointerEventsPrivate) _containsView:]whileRCTMountingManager performTransactionmounts a child view (use-after-free — the assistant registry enumerates a freed view).NSInvalidArgumentException: -[__NSArrayM insertSublayer:atIndex:]: unrecognized selector(andOS_xpc_*/CALayerArrayvariants) during mounting — a freedCALayer's memory reused by an unrelated object, then messaged as a layer.
Both reproduce with rapid navigation churn across screens containing glass views (open/close product-detail screens with glass buttons/badges, tab switches). In our app this crashed roughly 1 in 3 sessions of ~100 navigations.
Root causes
1. Fabric children are mounted directly into UIVisualEffectView.contentView
- (void)mountChildComponentView:(UIView<RCTComponentViewProtocol> *)childComponentView index:(NSInteger)index {
[_view.contentView insertSubview:childComponentView atIndex:index];
}
Fabric assumes it exclusively owns the index space of the container it mounts into. On iOS 26, UIKit's glass/pointer machinery injects private subviews (e.g. _UIPointerEffectPlatterView) into the effect view hierarchy at runtime. Once an injected view lands in contentView, every Fabric index is off by one: subsequent insert/remove transactions operate on the wrong subviews, detaching views the framework still references — producing the freed-layer insertSublayer crashes above.
2. Live glass effect (and its pointer-interaction registration) survives recycle/dealloc
With isInteractive = true, UIGlassEffect registers the view with UIKit's pointer-interaction system. The component view never tears the effect down:
- when Fabric recycles the component view (
prepareForRecycle), the pooled instance keeps its live effect and registration; - when the view deallocates, the registration is never removed while the view is alive.
The next pointer-interaction registry walk (which UIKit performs during ordinary _addSubview passes) messages the freed view — the _UIPointerInteractionAssistant crash above.
Fix we validated
- Mount Fabric children into a dedicated plain
UIViewcontainer insidecontentView(autoresized to its bounds). UIKit can then inject anything into the effect hierarchy without disturbing Fabric's index space. - Add a
resetForRecycle()onLiquidGlassViewImplthat setseffect = niland clears cached state; call it fromprepareForRecycleanddeallocso UIKit unregisters the interactive-glass pointer machinery while the view is still alive.
Patch (applied via patch-package on 0.8.0):
--- a/ios/LiquidGlassView.mm
+++ b/ios/LiquidGlassView.mm
@@
@implementation LiquidGlassView {
LiquidGlassViewImpl * _view;
+ UIView * _childContainer;
BOOL _needsInvalidateLayer;
}
@@
_view = [[LiquidGlassViewImpl alloc] init];
-
+
+ _childContainer = [[UIView alloc] initWithFrame:CGRectZero];
+ _childContainer.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
+ [_view.contentView addSubview:_childContainer];
+
self.contentView = _view;
}
return self;
}
+- (void)dealloc
+{
+ [_view resetForRecycle];
+}
+
@@
- (void)layoutSubviews {
[super layoutSubviews];
_view.layer.cornerRadius = self.layer.cornerRadius;
_view.layer.cornerCurve = self.layer.cornerCurve;
+ _childContainer.frame = _view.contentView.bounds;
}
+
+- (void)prepareForRecycle
+{
+ [super prepareForRecycle];
+ [_view resetForRecycle];
+}
@@
- (void)mountChildComponentView:(UIView<RCTComponentViewProtocol> *)childComponentView index:(NSInteger)index {
- [_view.contentView insertSubview:childComponentView atIndex:index];
+ [_childContainer insertSubview:childComponentView atIndex:index];
}
--- a/ios/LiquidGlassView.swift
+++ b/ios/LiquidGlassView.swift
@@
+ @objc public func resetForRecycle() {
+ self.effect = nil
+ appliedStyle = nil
+ appliedTintColor = nil
+ appliedInteractive = nil
+ isFirstMount = true
+ effectTintColor = nil
+ interactive = false
+ style = .regular
+ animated = true
+ animationDuration = 0
+ hasAnimationDuration = false
+ }
(Plus an empty resetForRecycle() on the non-iOS-26 fallback class so the ObjC call compiles on older SDK branches.)
Validation
Automated stress loop on iOS 26.5 simulator (Release configuration): repeated product-screen open/close over interactive glass buttons + tab switches, 10 runs x 15 navigation cycles. Before: crashes in ~1/3 of runs (both signatures). After: zero occurrences of either signature.
Related: facebook/react-native#55489 describes the same _UIPointerInteractionAssistant use-after-free pattern from the pointer-interaction side.
- Lingua principale
- Objective-C++
- Stelle
- 1.7k
- Fork
- 66
- Metriche di merge delle PR
- Nessuna PR unita negli ultimi 30g
Preparare l'ambiente
- Nessun Dockerfile né file Docker Compose
- Nessun modello di pull request
- Leggi la guida per i contributori
Come iniziare
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Altre issue di callstack/liquid-glass
-
needs repro
Difficoltà 4/5 3-5 giorni Idoneità per principianti 72/100
callstack/liquid-glass#47 · 1 commento ·
-
bug
Difficoltà 4/5 3-5 giorni Idoneità per principianti 48/100
callstack/liquid-glass#42 · 1 commento ·
-
bug
Difficoltà 3/5 1-2 giorni Idoneità per principianti 52/100
callstack/liquid-glass#41 ·
-
bug
Difficoltà 4/5 3-5 giorni Idoneità per principianti 45/100
callstack/liquid-glass#33 · 15 commenti · 12 reazioni ·
-
Liquid glass for AndroidApertabug
Difficoltà 5/5 Più di una settimana Idoneità per principianti 20/100
callstack/liquid-glass#30 · 1 commento · 3 reazioni ·
Tutte le issue di callstack/liquid-glass
Issue simili
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 78/100
tiann/hapi#2284 · 1 commento ·
I maintainer di solito rispondono entro 3 giorni
-
bug
Difficoltà 2/5 1-3 ore Idoneità per principianti 72/100
PATILYASHH/XPENC#192 ·
-
bug
Difficoltà 2/5 1-3 ore Idoneità per principianti 65/100
LeanBitLab/LeanType#771 ·
I maintainer di solito rispondono entro 1 giorno
-
triage
Difficoltà 2/5 1-3 ore Idoneità per principianti 78/100
readium/kotlin-toolkit#846 ·
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 68/100
I maintainer di solito rispondono entro 2 giorni