Author: Byte Mobile Technology — Chen Yi

background

Since September last year, after many users upgraded to iOS 14, many imageio-related stack Crash problems appeared online, and almost all apps in the company appeared, and some apps even reached the Top 3 Crash.

Thanks to the precise data collection mechanism of the APM platform and rich onsite exception information, we collected detailed Crash log information and analyzed it.

Problem orientation

The stack information

From the stack information, Crash occurred when ImageIO parses the image information, and the last method called seems to be related to INameSpacePrefixMap. And the realization of the this method CGImageSourceCopyPropertiesAtIndex speculated that Crash should be concerned.

Problem aggregation characteristics

Models are concentrated in iOS14 and above, and appear in the background

Analysis of the

Do a preliminary analysis from CrashLog

  • From the stack information, this code is in the photo in the child thread through analytical imageSource CGImageSourceCopyPropertiesAtIndex image information, and wild pointer Crash happened.

  • Only one input imageSource CGImageSourceCopyPropertiesAtIndex, imageSource generated by the data of the picture, the call stack is not multithreaded operations, It can be ruled out that multi-threaded imageSource, data operations caused the Crash.

  • Look at the stack is parsing the PNG image, by sending the image format to JPG format, found that the magnitude does not decrease. It is assumed that Crash is not caused by a particular image format.

Disassembly analysis

Disassembly preparation

  • IPhone 8 with iOS 14.3
  • ImageIO system libraries: ~ / Library/Developer/Xcode/iOS DeviceSupport directory to find corresponding iOS 14.3 ImageIO
  • A CrashLog on iOS 14.3 and iPhone 8
  • Hopper

The disassembly

  1. Obtain the instruction offset address 2555072 corresponding to Crash from the CrashLog

  1. Open ImageIO with Hopper and jump to the instruction offset address 2555072

Navigate => Go To File Offset 2555072

  1. The command for Crash should be0000000181b09cc0 ldr x8, [x8, #0x10], you can see that it should be access[x8, #0x10]There is a memory error pointing to

  1. Check the value of the register in Crashlogfar: 0x000021a1ee2fa271, and the X8 register is already an incorrect value0x000021a1ee2fa261

  1. Go back to the source of X8
  • 0000000181b09CBC LDR x8, [x20] x8 is in memory pointed to by X20

  • 0000000181b09c98 LDR x20, [x21, #0x8] x20 exists in the memory pointed to by [x21, #0x8]

  • 0000000181b09c8c adrp x21, #0x1da0ed000, 0000000181b09c90 add x21, x21, #0xe10 x21 Maybe the global variable is wild, or some memory (X20) referenced by the global variable is wild

  1. What is the value of registers for x8, x20, x21
  • X21 should be a global Map from the name of the memory address
ImageIO`AdobeXMPCore_Int::ManageDefaultNameSpacePrefixMap(bool)::sDefaultNameSpacePrefixMap
Copy the code

  1. In terms of Hopper, thissDefaultNameSpacePrefixMapOnly in the

AdobeXMPCore_Int: : ManageDefaultNameSpacePrefixMap (bool) this function call. It is possible to call this function in multiple threads, resulting in the presence of this global variable. Data Race causes wild Pointers.

__ZZN16AdobeXMPCore_IntL31ManageDefaultNameSpacePrefixMapEbE26sDefaultNameSpacePrefixMap:        // AdobeXMPCore_Int::ManageDefaultNameSpacePrefixMap(bool)::sDefaultNameSpacePrefixMap

00000001da0ede10         dq         0x0000000000000000                          ; DATA XREF=__ZN16AdobeXMPCore_IntL31ManageDefaultNameSpacePrefixMapEb+44, __ZN16AdobeXMPCore_IntL31ManageDefaultNameSpacePrefixMapEb+120, __ZN16AdobeXMPCore_IntL31ManageDefaultNameSpacePrefixMapEb+392
Copy the code
  1. After repeated debugging at run time, this

AdobeXMPCore_Int: : ManageDefaultNameSpacePrefixMap (bool) will call in multiple methods (and add the lock, all calls are unlikely to appear data race) :

  • AdobeXMPCore_Int::INameSpacePrefixMap_I::CreateDefaultNameSpacePrefixMap()

  • AdobeXMPCore_Int::INameSpacePrefixMap_I::InsertInDefaultNameSpacePrefixMap(char const*, unsigned long long, char const*, unsigned long long)

  • AdobeXMPCore_Int::INameSpacePrefixMap_I::DestroyDefaultNameSapcePrefixMap()

  1. Access global variables in background threadssDefaultNameSpacePrefixMapIt is speculated that after the user manually kills the process, the global variable has been destroyed in the main thread, and the background thread continues to access the global variable, resulting in an abnormal access to the wild pointer. The _exit call was also found on the main thread stack of the Crash log, which can be determined to be caused by global variable destruction.

Cause of Crash:

After the user manually kills the process, the main thread destroys the global variable, and then the child thread accesses the global variable again.

Recurring problems

Try the child thread constantly invoke CFDictionaryRef CGImageSourceCopyPropertiesAtIndex (CGImageSourceRef isrc, size_t index, CFDictionaryRef options); And manually killing the process triggers the crash

Successful repetition

The above reasoning can be proved to be correct.

conclusion

  • CFDictionaryRef CGImageSourceCopyPropertiesAtIndex(CGImageSourceRef isrc, size_t index, CFDictionaryRef options); This method will eventually access the global variable when parsing part of the image

    ImageIO`AdobeXMPCore_Int::ManageDefaultNameSpacePrefixMap(bool)::sDefaultNameSpacePrefixMap
    Copy the code
  • After the user manual kill process, this sDefaultNameSpacePrefixMap destructor, if by this time the child thread to be accessed again wild pointer problems may appear

Fixed the ImageIO Crash scheme

Because sDefaultNameSpacePrefixMap is the global variables within the system library, can’t change them, only to avoid the child thread call CGImageSourceCopyPropertiesAtIndex method

  • Method one: CGImageSourceCopyPropertiesAtIndex is used to obtain high image width, imageOrientation, dynamic information such as photo frame, choose to replace with other methods, um participant. The width and height are obtained using CGImageRef

  • Method 2: will be calling thread CGImageSourceCopyPropertiesAtIndex convergence, call atexit function to register a callback function end of the process, the end of the process will terminate

About the Byte Mobile Platform team

Bytedance Client Infrastructure is the industry leader in big front-end Infrastructure, responsible for the construction of big front-end Infrastructure in The entire China region of Bytedance, improving the performance, stability and engineering efficiency of the company’s entire product line. The supported products include but are not limited to Douyin, Toutiao, Watermelon video, Huoshan small video, etc., which have been researched in depth on mobile terminal, Web, Desktop and other terminals.

Now is the time! Client/front-end/server/intelligent algorithm/test development for global recruitment! Let’s use technology to change the world. If you are interested, please contact us at [email protected]. Email subject: Resume – Name – Objective – Phone number.