Skip to content

Commit 3db6336

Browse files
author
Mallory Paine
committed
FICImageTable now knows about data protection. Some clients have experienced crashes when trying to read image table data while their app is in the background. These crashes occur when the image table's file has data protection enabled, and the device is passcode-locked. To prevent the client app from crashing, FICImageTable will do its best to prevent accessing the memory pages from a protected imageTable file at times when that data is inaccessible.
1 parent 7044ad1 commit 3db6336

File tree

4 files changed

+75
-10
lines changed

4 files changed

+75
-10
lines changed

FastImageCache/FICImageFormat.h

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,12 @@ typedef NS_OPTIONS(NSUInteger, FICImageFormatStyle) {
2222
FICImageFormatStyle8BitGrayscale,
2323
};
2424

25+
typedef NS_OPTIONS(NSUInteger, FICImageFormatProtectionMode) {
26+
FICImageFormatProtectionModeNone,
27+
FICImageFormatProtectionModeComplete,
28+
FICImageFormatProtectionModeCompletUntilFirstUserAuthentication,
29+
};
30+
2531
/**
2632
`FICImageFormat` acts as a definition for the types of images that are stored in the image cache. Each image format must have a unique name, but multiple formats can belong to the same family.
2733
All images associated with a particular format must have the same image dimentions and opacity preference. You can define the maximum number of entries that an image format can accommodate to
@@ -114,6 +120,10 @@ typedef NS_OPTIONS(NSUInteger, FICImageFormatStyle) {
114120
*/
115121
@property (nonatomic, assign, readonly) BOOL isGrayscale;
116122

123+
124+
@property (nonatomic, assign) FICImageFormatProtectionMode protectionMode;
125+
@property (nonatomic, assign, readonly) NSString *protectionModeString;
126+
117127
/**
118128
The dictionary representation of this image format.
119129
@@ -142,6 +152,6 @@ typedef NS_OPTIONS(NSUInteger, FICImageFormatStyle) {
142152
143153
@return An autoreleased instance of `<FICImageFormat>` or one of its subclasses, if any exist.
144154
*/
145-
+ (instancetype)formatWithName:(NSString *)name family:(NSString *)family imageSize:(CGSize)imageSize style:(FICImageFormatStyle)style maximumCount:(NSInteger)maximumCount devices:(FICImageFormatDevices)devices;
155+
+ (instancetype)formatWithName:(NSString *)name family:(NSString *)family imageSize:(CGSize)imageSize style:(FICImageFormatStyle)style maximumCount:(NSInteger)maximumCount devices:(FICImageFormatDevices)devices protectionMode:(FICImageFormatProtectionMode)protectionMode;
146156

147157
@end

FastImageCache/FICImageFormat.m

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
static NSString *const FICImageFormatStyleKey = @"style";
2020
static NSString *const FICImageFormatMaximumCountKey = @"maximumCount";
2121
static NSString *const FICImageFormatDevicesKey = @"devices";
22+
static NSString *const FICImageFormatProtectionModeKey = @"protectionMode";
2223

2324
#pragma mark - Class Extension
2425

@@ -30,6 +31,7 @@ @interface FICImageFormat () {
3031
FICImageFormatStyle _style;
3132
NSInteger _maximumCount;
3233
FICImageFormatDevices _devices;
34+
FICImageFormatProtectionMode _protectionMode;
3335
}
3436

3537
@end
@@ -45,6 +47,7 @@ @implementation FICImageFormat
4547
@synthesize style = _style;
4648
@synthesize maximumCount = _maximumCount;
4749
@synthesize devices = _devices;
50+
@synthesize protectionMode = _protectionMode;
4851

4952
#pragma mark - Property Accessors
5053

@@ -124,9 +127,25 @@ - (BOOL)isGrayscale {
124127
return isGrayscale;
125128
}
126129

130+
- (NSString *)protectionModeString {
131+
NSString *protectionModeString = nil;
132+
switch (_protectionMode) {
133+
case FICImageFormatProtectionModeNone:
134+
protectionModeString = NSFileProtectionNone;
135+
break;
136+
case FICImageFormatProtectionModeComplete:
137+
protectionModeString = NSFileProtectionComplete;
138+
break;
139+
case FICImageFormatProtectionModeCompletUntilFirstUserAuthentication:
140+
protectionModeString = NSFileProtectionCompleteUntilFirstUserAuthentication;
141+
break;
142+
}
143+
return protectionModeString;
144+
}
145+
127146
#pragma mark - Object Lifecycle
128147

129-
+ (instancetype)formatWithName:(NSString *)name family:(NSString *)family imageSize:(CGSize)imageSize style:(FICImageFormatStyle)style maximumCount:(NSInteger)maximumCount devices:(FICImageFormatDevices)devices {
148+
+ (instancetype)formatWithName:(NSString *)name family:(NSString *)family imageSize:(CGSize)imageSize style:(FICImageFormatStyle)style maximumCount:(NSInteger)maximumCount devices:(FICImageFormatDevices)devices protectionMode:(FICImageFormatProtectionMode)protectionMode {
130149
FICImageFormat *imageFormat = [[FICImageFormat alloc] init];
131150

132151
[imageFormat setName:name];
@@ -135,6 +154,7 @@ + (instancetype)formatWithName:(NSString *)name family:(NSString *)family imageS
135154
[imageFormat setStyle:style];
136155
[imageFormat setMaximumCount:maximumCount];
137156
[imageFormat setDevices:devices];
157+
[imageFormat setProtectionMode:protectionMode];
138158

139159
return imageFormat;
140160
}
@@ -151,6 +171,8 @@ - (NSDictionary *)dictionaryRepresentation {
151171
[dictionaryRepresentation setValue:[NSNumber numberWithInt:_style] forKey:FICImageFormatStyleKey];
152172
[dictionaryRepresentation setValue:[NSNumber numberWithUnsignedInteger:_maximumCount] forKey:FICImageFormatMaximumCountKey];
153173
[dictionaryRepresentation setValue:[NSNumber numberWithInt:_devices] forKey:FICImageFormatDevicesKey];
174+
[dictionaryRepresentation setValue:[NSNumber numberWithUnsignedInteger:_protectionMode] forKey:FICImageFormatProtectionModeKey];
175+
154176
[dictionaryRepresentation setValue:[NSNumber numberWithFloat:[[UIScreen mainScreen] scale]] forKey:FICImageTableScreenScaleKey];
155177
[dictionaryRepresentation setValue:[NSNumber numberWithUnsignedInteger:[FICImageTableEntry metadataVersion]] forKey:FICImageTableEntryDataVersionKey];
156178

@@ -170,6 +192,7 @@ - (id)copyWithZone:(NSZone *)zone {
170192
[imageFormatCopy setStyle:[self style]];
171193
[imageFormatCopy setMaximumCount:[self maximumCount]];
172194
[imageFormatCopy setDevices:[self devices]];
195+
[imageFormatCopy setProtectionMode:[self protectionMode]];
173196

174197
return imageFormatCopy;
175198
}

FastImageCache/FICImageTable.m

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,8 @@ @interface FICImageTable () {
6262
NSMutableOrderedSet *_MRUEntries;
6363
NSCountedSet *_inUseEntries;
6464
NSDictionary *_imageFormatDictionary;
65+
66+
BOOL _isFileDataProtected;
6567
}
6668

6769
@end
@@ -157,6 +159,19 @@ - (instancetype)initWithFormat:(FICImageFormat *)imageFormat {
157159

158160
[self _loadMetadata];
159161

162+
NSFileManager *fileManager = [[NSFileManager alloc] init];
163+
if ([fileManager fileExistsAtPath:_filePath] == NO) {
164+
NSMutableDictionary *attributes = [NSMutableDictionary dictionary];
165+
[attributes setValue:[_imageFormat protectionModeString] forKeyPath:NSFileProtectionKey];
166+
[fileManager createFileAtPath:_filePath contents:nil attributes:attributes];
167+
}
168+
169+
NSDictionary *attributes = [fileManager attributesOfItemAtPath:_filePath error:NULL];
170+
NSString *protectionMode = [attributes objectForKey:NSFileProtectionKey];
171+
if (protectionMode) {
172+
_isFileDataProtected = [protectionMode isEqualToString:NSFileProtectionNone] == NO;
173+
}
174+
160175
_fileDescriptor = open([_filePath fileSystemRepresentation], O_RDWR | O_CREAT, 0666);
161176

162177
if (_fileDescriptor >= 0) {
@@ -471,12 +486,24 @@ - (void)_setEntryCount:(NSInteger)entryCount {
471486
}
472487
}
473488

489+
// There's inherently a race condition between when you ask whether the data is
490+
// accessible and when you try to use that data. Sidestep this issue altogether
491+
// by using NSFileProtectionNone
492+
- (BOOL)canAccessEntryData {
493+
BOOL result = YES;
494+
if (_isFileDataProtected) {
495+
result = [[UIApplication sharedApplication] isProtectedDataAvailable];
496+
}
497+
return result;
498+
}
499+
474500
- (FICImageTableEntry *)_entryDataAtIndex:(NSInteger)index {
475501
FICImageTableEntry *entryData = nil;
476502

477503
[_lock lock];
478-
479-
if (index < _entryCount) {
504+
505+
BOOL canAccessData = [self canAccessEntryData];
506+
if (index < _entryCount && canAccessData) {
480507
off_t entryOffset = index * _entryLength;
481508
size_t chunkIndex = (size_t)(entryOffset / _chunkLength);
482509

@@ -503,7 +530,12 @@ - (FICImageTableEntry *)_entryDataAtIndex:(NSInteger)index {
503530
[_lock unlock];
504531

505532
if (!entryData) {
506-
NSString *message = [NSString stringWithFormat:@"*** FIC Error: %s failed to get entry for index %ld.", __PRETTY_FUNCTION__, (long)index];
533+
NSString *message = nil;
534+
if (canAccessData) {
535+
message = [NSString stringWithFormat:@"*** FIC Error: %s failed to get entry for index %ld.", __PRETTY_FUNCTION__, (long)index];
536+
} else {
537+
message = [NSString stringWithFormat:@"*** FIC Error: %s. Cannot get entry data because imageTable's file has data protection enabled and that data is not currently accessible.", __PRETTY_FUNCTION__];
538+
}
507539
[[FICImageCache sharedImageCache] _logMessage:message];
508540
}
509541

FastImageCacheDemo/Classes/FICDAppDelegate.m

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,25 +32,25 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(
3232

3333
// ...32-bit BGR
3434
FICImageFormat *squareImageFormat32BitBGRA = [FICImageFormat formatWithName:FICDPhotoSquareImage32BitBGRAFormatName family:FICDPhotoImageFormatFamily imageSize:FICDPhotoSquareImageSize style:FICImageFormatStyle32BitBGRA
35-
maximumCount:squareImageFormatMaximumCount devices:squareImageFormatDevices];
35+
maximumCount:squareImageFormatMaximumCount devices:squareImageFormatDevices protectionMode:FICImageFormatProtectionModeNone];
3636

3737
[mutableImageFormats addObject:squareImageFormat32BitBGRA];
3838

3939
// ...32-bit BGR
4040
FICImageFormat *squareImageFormat32BitBGR = [FICImageFormat formatWithName:FICDPhotoSquareImage32BitBGRFormatName family:FICDPhotoImageFormatFamily imageSize:FICDPhotoSquareImageSize style:FICImageFormatStyle32BitBGR
41-
maximumCount:squareImageFormatMaximumCount devices:squareImageFormatDevices];
41+
maximumCount:squareImageFormatMaximumCount devices:squareImageFormatDevices protectionMode:FICImageFormatProtectionModeNone];
4242

4343
[mutableImageFormats addObject:squareImageFormat32BitBGR];
4444

4545
// ...16-bit BGR
4646
FICImageFormat *squareImageFormat16BitBGR = [FICImageFormat formatWithName:FICDPhotoSquareImage16BitBGRFormatName family:FICDPhotoImageFormatFamily imageSize:FICDPhotoSquareImageSize style:FICImageFormatStyle16BitBGR
47-
maximumCount:squareImageFormatMaximumCount devices:squareImageFormatDevices];
47+
maximumCount:squareImageFormatMaximumCount devices:squareImageFormatDevices protectionMode:FICImageFormatProtectionModeNone];
4848

4949
[mutableImageFormats addObject:squareImageFormat16BitBGR];
5050

5151
// ...8-bit Grayscale
5252
FICImageFormat *squareImageFormat8BitGrayscale = [FICImageFormat formatWithName:FICDPhotoSquareImage8BitGrayscaleFormatName family:FICDPhotoImageFormatFamily imageSize:FICDPhotoSquareImageSize style:FICImageFormatStyle8BitGrayscale
53-
maximumCount:squareImageFormatMaximumCount devices:squareImageFormatDevices];
53+
maximumCount:squareImageFormatMaximumCount devices:squareImageFormatDevices protectionMode:FICImageFormatProtectionModeNone];
5454

5555
[mutableImageFormats addObject:squareImageFormat8BitGrayscale];
5656

@@ -60,7 +60,7 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(
6060
FICImageFormatDevices pixelImageFormatDevices = FICImageFormatDevicePhone | FICImageFormatDevicePad;
6161

6262
FICImageFormat *pixelImageFormat = [FICImageFormat formatWithName:FICDPhotoPixelImageFormatName family:FICDPhotoImageFormatFamily imageSize:FICDPhotoPixelImageSize style:FICImageFormatStyle32BitBGR
63-
maximumCount:pixelImageFormatMaximumCount devices:pixelImageFormatDevices];
63+
maximumCount:pixelImageFormatMaximumCount devices:pixelImageFormatDevices protectionMode:FICImageFormatProtectionModeNone];
6464

6565
[mutableImageFormats addObject:pixelImageFormat];
6666
}

0 commit comments

Comments
 (0)