PHAsset 获取图片高宽的 Bug
最近遇到一个在 App 中打开相册 Crash 的问题,但是我们无法复现,于是找用户要到了她要上传的图片的原图,原图是一张微博等平台上常见的超长图片。现在不少专业的博主为了排版方便及全平台统一,都会使用一整张长图来发内容,而不是用图文混排的方式。
图片导入到相册后发现的确会 Crash,于是单步跟进,发现这张图片高度居然是 18446744073709519761
!很明显不正常,因为在电脑上查看图片高度为 33681
,如果 App 认为它高度是那么多,就会照那个大小去申请内存,不 Crash 才怪。那么这个错误的高度与真实高度有什么联系呢?想到 NSUInteger
的 (-1) = 18446744073709551615(64位系统),中间差 31854,同时图片的实际高 33681 ,而:
33681 + 31854 = 65535
于是就有了思路,这就是一个越界的问题。
先看 PHAsset
关于图片高宽的接口的声明:
@interface PHAsset : PHObject
@property (nonatomic, assign, readonly) NSUInteger pixelWidth;
@property (nonatomic, assign, readonly) NSUInteger pixelHeight;
@end
返回的就是一个 NSUInteger
,然而它的底层 C
实现很可能是返回 signed short
,如:
short ph_get_width(void *asset);
short ph_get_height(void *asset);
signed short
最大可表示的值是 32767
,当返回一个 33681
时,隐式转换后的值是 -31854
,然后又被扩展到 64 位的 NSUInteger
,前面填 1
补齐,就成了一个很大的数了。
(33681)
10000011 10010001
(18446744073709519761)
11111111 11111111 11111111 11111111 11111111 11111111 10000011 10010001
修复方法也很简单:
@implementation PHAsset (Bugfix)
+ (void)load
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
[self jr_swizzleMethod:@selector(pixelWidth)
withMethod:@selector(bugfix_pixelWidth)
error:NULL];
[self jr_swizzleMethod:@selector(pixelHeight)
withMethod:@selector(bugfix_pixelHeight)
error:NULL];
});
}
- (NSUInteger)bugfix_pixelWidth
{
NSUInteger width = [self bugfix_pixelWidth];
if (width > 0xffff) {
width &= 0xffff;
}
return width;
}
- (NSUInteger)bugfix_pixelHeight
{
NSUInteger height = [self bugfix_pixelHeight];
if (height > 0xffff) {
height &= 0xffff;
}
return height;
}
@end
相当于越界时直接丢弃前面的 1,但是图片高度依然不可以超过 65535。
此问题已经提交 Apple Bug Report,但是他们回复需要诊断日志,我就懒得搞啦。
Apple Developer Relations
February 23 2017, 5:24 AM
Engineering has requested a sysdiagnose in order to further investigate this issue.
Important: Note the date and time the issue occurred and include this information in your bug report.
Note: It's important to trigger the sysdiagnose process as soon as possible after the problem occurs, even if the logs can’t be synced off the device until later.
最新进展(2017-9-27)
苹果终于回复了!iOS 11 中修复了这个问题:
Hello Ricky,
This is a follow-up regarding regarding Bug ID# 30463403.
Please verify this issue with the iOS 11 GM and update your bug report at https://bugreport.apple.com/ with your results.
iOS 11 GM (15A372)
https://developer.apple.com/download/
Posted Date: Sep 19th, 2017
If the issue persists, please attach a new sysdiagnose captured in the latest build and attach it to the bug report. Thank you.