Permalink
Browse files

iOS: Fix dequeueTasks crash in image loader

Summary:
This fixes a crash occurring [on this line](https://github.com/facebook/react-native/blob/f9f32eb426b5385e199ef6e1d2b610dfe20e60e7/Libraries/Image/RCTImageLoader.m#L253). It was reported in a comment in #10433.

The problem is that `task` is deallocated at this point and is unsafe to use. Removing it from `_pendingTasks` dropped its ref count to 0. [The ARC docs](http://clang.llvm.org/docs/AutomaticReferenceCounting.html#fast-enumeration-iteration-variables) state that, by default, loop variables in fast enumeration loops are not retained. That's why `task`'s ref count is 0.

It's likely we ran into this bug because the code disobeyed the [reverseObjectEnumerator docs](https://developer.apple.com/reference/foundation/nsarray/1415734-reverseobjectenumerator) which state that "you must not modify the array during enumeration". The default retention policy for fast enumeration seems to assume you follow this.

To fix this bug and avoid other potential pitfalls, the code now follows the docs and `_pendingTa
Closes #11296

Differential Revision: D4277167

Pulled By: javache

fbshipit-source-id: 1211b32935bab7f4080dc00b88d85348786e859a
  • Loading branch information...
1 parent 6432839 commit d043775d000efff57d0f65bc94144458864a8bf1 @rigdern rigdern committed with mkonicek Dec 5, 2016
Showing with 13 additions and 2 deletions.
  1. +13 −2 Libraries/Image/RCTImageLoader.m
@@ -236,10 +236,14 @@ - (void)dequeueTasks
{
dispatch_async(_URLRequestQueue, ^{
// Remove completed tasks
+ NSMutableArray *tasksToRemove = nil;
for (RCTNetworkTask *task in self->_pendingTasks.reverseObjectEnumerator) {
switch (task.status) {
case RCTNetworkTaskFinished:
- [self->_pendingTasks removeObject:task];
+ if (!tasksToRemove) {
+ tasksToRemove = [NSMutableArray new];
+ }
+ [tasksToRemove addObject:task];
self->_activeTasks--;
break;
case RCTNetworkTaskPending:
@@ -248,13 +252,20 @@ - (void)dequeueTasks
// Check task isn't "stuck"
if (task.requestToken == nil) {
RCTLogWarn(@"Task orphaned for request %@", task.request);
- [self->_pendingTasks removeObject:task];
+ if (!tasksToRemove) {
+ tasksToRemove = [NSMutableArray new];
+ }
+ [tasksToRemove addObject:task];
self->_activeTasks--;
[task cancel];
}
break;
}
}
+
+ if (tasksToRemove) {
+ [self->_pendingTasks removeObjectsInArray:tasksToRemove];
+ }
// Start queued decode
NSInteger activeDecodes = self->_scheduledDecodes - self->_pendingDecodes.count;

0 comments on commit d043775

Please sign in to comment.