iOS 26: Fabric index corruption + dangling pointer-interaction registration cause use-after-free crashes
Nadie ha tomado este issue todavía.
Evaluación
- Dificultad
- 4/5
- Tiempo estimado
- 3-5 días
- Aptitud para principiantes
- 72/100
- Tipo de issue
- Error
- Claridad
- Bien especificado
- Estado de actividad
- Activo
- Stack tecnológico
- cpp, ios, objective-c, react-native, swift
- Área
- mobile-dev
Línea de trabajo
Lee ios/LiquidGlassView.mm y ios/LiquidGlassView.swift, centrándote en el montaje de hijos de Fabric y en las rutas de reciclaje/desasignación. Ejecuta el bucle de estrés de iOS 26 descrito con vistas de glass interactivas y cambios frecuentes de navegación; se considera completado cuando ambas firmas de crash indicadas siguen sin aparecer y la clase fallback continúa compilando en ramas de SDK antiguas.
Escrito por el modelo de indexación a partir del texto del issue.
Descripción
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.
- Lenguaje dominante
- Objective-C++
- Estrellas
- 1.7k
- Forks
- 66
- Métricas de merge de PR
- Sin PR fusionados en 30 d
Preparar el entorno
- Sin Dockerfile ni archivo de Docker Compose
- Sin plantilla de pull request
- Leer la guía de contribución
Primeros pasos
- Lee el issue completo y luego la guía de contribución del proyecto.
- Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
- Haz un fork del repositorio y trabaja en una rama.
- Abre un pull request que haga referencia al número del issue.
Más de callstack/liquid-glass
-
needs repro
Dificultad 4/5 3-5 días Aptitud para principiantes 72/100
callstack/liquid-glass#47 · 1 comentario ·
-
bug
Dificultad 4/5 3-5 días Aptitud para principiantes 48/100
callstack/liquid-glass#42 · 1 comentario ·
-
bug
Dificultad 3/5 1-2 días Aptitud para principiantes 52/100
callstack/liquid-glass#41 ·
-
bug
Dificultad 4/5 3-5 días Aptitud para principiantes 45/100
callstack/liquid-glass#33 · 15 comentarios · 12 reacciones ·
-
Liquid glass for AndroidAbiertobug
Dificultad 5/5 Más de una semana Aptitud para principiantes 20/100
callstack/liquid-glass#30 · 1 comentario · 3 reacciones ·
Todos los issues de callstack/liquid-glass
Issues similares
-
-priority- blocked good first issue help wanted
Dificultad 2/5 1-3 horas Aptitud para principiantes 68/100
scribe-org/Scribe-Android#761 · 2 comentarios · 1 reacción ·
Los mantenedores suelen responder en 1 día
-
type:feature
Dificultad 2/5 1-3 horas Aptitud para principiantes 65/100
Los mantenedores suelen responder en 2 días
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 78/100
LCNINE/almondyoung-server#1256 ·
Los mantenedores suelen responder en 1 día
-
Type: Possible bug
Dificultad 2/5 1-3 horas Aptitud para principiantes 84/100
AntennaPod/AntennaPod#8895 ·
Los mantenedores suelen responder en 1 día
-
Android: update ServiceControllerImpl comment and naming after Reports paused got its own idAbierto
Dificultad 1/5 Menos de una hora Aptitud para principiantes 90/100
OneBusAway/vehicle-positions#288 ·
Los mantenedores suelen responder en 1 día