Guard
FBObjectIsBlockwith_FBIsRetainableObjCPointerto prevent TOCTOU crashSummary:
Problem
FBObjectIsBlock(FBBlockStrongLayout.m:339) crashes with EXC_BREAKPOINT whenobject_getClassreads a corrupted isa pointer. The Retain Cycle Detector (RCD) runs on a background serial queue, traversing the object graph viaobject_getIvar. Between the raw pointer read and its use inFBObjectIsBlock, another thread can deallocate the object, leaving a stale pointer whose isa triggers arm64 isa hardening trap.
- MID:
3ca9635da1dc1430f15c20db5d721933(3 SAD over 45 days, employee-only)- 4 sibling MIDs crash at different downstream sites in the same RCD traversal path
Fix
Add
_FBIsRetainableObjCPointer(object)guard at the top ofFBObjectIsBlock, returningNOif the pointer fails validation. This matches the pattern established by D102069542 for capture-slot parsing in the same file._FBIsRetainableObjCPointerperformsvm_read_overwrite-based readability probes, alignment checks, tagged pointer handling, and isa validation — all designed for exactly this TOCTOU failure mode.Returning
NOfor an invalid pointer causes RCD to treat the object as a non-block, wrapping it as a regularFBObjectiveCObjectgraph element. The worst case is a missed retain cycle detection for one object — acceptable for a best-effort diagnostic tool.
- Risk: Very low. Follows the exact same pattern already battle-tested in
_GetStrongReferencesCompactLayoutand_GetStrongReferencesExtendedLayout.- The
object_getClasscalls inFBRetainCycleUtils.m(lines 52, 61) remain unprotected — they require exporting_FBIsRetainableObjCPointerand are left for a follow-up.Context
- D102069542 (sanjaythakur, 2026-08-19) added
_FBIsRetainableObjCPointerto protect capture-slot parsing but deliberately leftFBObjectIsBlockunprotected, acknowledging the residual TOCTOU as a “rare tail.”- D104141469 (akniev) added parent object pinning — partial TOCTOU mitigation.
- D104876331 (camroth) fixed weak reference dangling pointer — another partial fix.
- Prior fads_solver investigation on T290725706 (Sep 2026) identified the same gap at medium quality; no fix was started.
Investigation report | Manifold
Generated by Claude Code, powered by Confucius Session, Trace
Reviewed By: Goos, rgiese
Differential Revision: D126183229
fbshipit-source-id: edbdee7871eab7fa25bf01a80de4101b4d492c09
版权所有:中国计算机学会技术支持:开源发展技术委员会
京ICP备13000930号-9
京公网安备 11010802047560号
FBRetainCycleDetector
An iOS library that finds retain cycles using runtime analysis.
About
Retain cycles are one of the most common ways of creating memory leaks. It’s incredibly easy to create a retain cycle, and tends to be hard to spot it. The goal of FBRetainCycleDetector is to help find retain cycles at runtime. The features of this project were influenced by Circle.
Installation
Carthage
To your Cartfile add:
FBRetainCycleDetectoris built out from non-debug builds, so when you want to test it, useCocoaPods
To your podspec add:
You’ll be able to use
FBRetainCycleDetectorfully only inDebugbuilds. This is controlled by compilation flag that can be provided to the build to make it work in other configurations.Example usage
Let’s quickly dive in
- (NSSet<NSArray<FBObjectiveCGraphElement *> *> *)findRetainCycleswill return a set of arrays of wrapped objects. It’s pretty hard to look at at first, but let’s go through it. Every array in this set will represent one retain cycle. Every element in this array is a wrapper around one object in this retain cycle. Check FBObjectiveCGraphElement.Example output could look like this:
MyObjectthroughsomeObjectproperty retainedNSArraythat it was a part of.FBRetainCycleDetector will look for cycles that are no longer than 10 objects. We can make it bigger (although it’s going to be slower!).
Filters
There could also be retain cycles that we would like to omit. It’s because not every retain cycle is a leak, and we might want to filter them out. To do so we need to specify filters:
Every filter is a block that having two
FBObjectiveCGraphElementobjects can say, if their relation is valid.Check FBStandardGraphEdgeFilters to learn more about how to use filters.
NSTimer
NSTimer can be troublesome as it will retain it’s target. Oftentimes it means a retain cycle.
FBRetainCycleDetectorcan detect those, but if you want to skip them, you can specify that in the configuration you are passing toFBRetainCycleDetector.Associations
Objective-C let’s us set associated objects for every object using objc_setAssociatedObject.
These associated objects can lead to retain cycles if we use retaining policies, like
OBJC_ASSOCIATION_RETAIN_NONATOMIC. FBRetainCycleDetector can catch these kinds of cycles, but to do so we need to set it up. Early in the application’s lifetime, preferably inmain.mwe can add this:In the code above
[FBAssociationManager hook]will use fishhook to interpose functionsobjc_setAssociatedObjectandobjc_resetAssociatedObjectsto track associations before they are made.Getting Candidates
If you want to profile your app, you might want to have an abstraction over how to get candidates for
FBRetainCycleDetector. While you can simply track it your own, you can also use FBAllocationTracker. It’s a small tool we created that can help you track the objects. It offers simple API that you can query for example for all instances of given class, or all class names currently tracked, etc.FBAllocationTrackerandFBRetainCycleDetectorcan work nicely together. We have created a small example and drop-in project called FBMemoryProfiler that leverages both these projects. It offers you very basic UI that you can use to track all allocations and force retain cycle detection from UI.Contributing
See the CONTRIBUTING file for how to help out.
License
FBRetainCycleDetectoris BSD-licensed.