Repository navigation
[iOS] Image.compress (manual) crashes the app in Release builds (vImageScale_ARGB8888 use-after-free) and on unreadable images #430
Description
Title: [iOS] Image.compress (manual) crashes the app in Release builds (vImageScale_ARGB8888 use-after-free) and on unreadable images
Current behavior
Image.compress(uri, { compressionMethod: 'manual', maxWidth, maxHeight, quality }) crashes the whole app on iOS in two cases. Both are in ios/Image/ImageCompressor.swift and are unchanged in 2.0.4.
1. Release builds crash while resizing (EXC_BAD_ACCESS)
EXC_BAD_ACCESS
vVertical_Scale_ARGB_8888_Accelerate
vImageScale_ARGB8888
ImageCompressor.manualResize(_:maxWidth:maxHeight:)
manualResize passes &sourceData / &targetData (Swift [UInt8] arrays) into CGContext(data:) and vImage_Buffer(data:). A pointer from &array is only valid for the duration of the call that receives it. With -O, Swift can release sourceData before vImageScale_ARGB8888 reads it, so vImage reads freed memory. Debug builds keep the array alive longer, which is why this only shows up in App Store / TestFlight builds.
Related issues in the same function:
free(&targetData)is called on Swift-managed memory (undefined behaviour).targetRef!is force-unwrapped.- The source size is taken from
image.size(points) instead ofcgImage.width/cgImage.height(pixels).
2. A missing or unreadable image terminates the app instead of rejecting
When loadImage returns nil, manualCompressHandler (and autoCompressHandler) call NSException(...).raise(). Swift's do/catch in ImageMain.image_compress can't catch an Objective-C exception, so the app aborts. writeImage and copyExifInfo also crash on failure through force unwraps:
jpegData(...)!andpngData()!image.cgImage!CGImageSourceCreateWithURL(...)!CGImageDestinationCreateWithData(...)!
3. (Minor) Some images come out stretched and sideways
scaleAndRotateImage creates its CGContext with the source's bitsPerComponent and bitmapInfo. For 16-bit PNG, PNG with alpha and 10-bit HEIC, that context fails to create, so the function returns the unrotated image. manualResize then scales it to the rotated dimensions, which stretches it.
Expected behavior
- Resizing works in Release builds without memory errors.
- A missing, corrupt or unsupported image rejects the promise with an error instead of crashing the app.
- Images with an EXIF orientation come out upright and correctly proportioned, whatever their pixel format.
Suggested fixes (we have these working in a local patch and can open a PR):
- Resize with vImage-owned buffers (
vImage_Buffer(cgImage:)→vImageScale_ARGB8888→vImageCreateCGImageFromBuffer). This also lowers peak memory for a 12 MP photo from about 150 MB to about 60 MB. - Make the handlers
throwsand replaceraise()and!with Swift errors, so thatimage_compresscallsreject(...). - In
scaleAndRotateImage, retry with an 8-bit sRGB context when the source-format context can't be created.
Platform
- Android
- iOS
React Native Version
0.87.1 (New Architecture, react-native-nitro-modules 0.37.1)
React Native Compressor Version
2.0.3 (the iOS image code is unchanged in 2.0.4)
Reproducible Steps And Demo
Crash 1: resize in a Release build
- Build the app in the Release configuration (TestFlight / App Store, or
Releaseon a device or simulator). - Take or pick a 12 MP or 48 MP photo with the camera.
- Call:
await Image.compress(photoUri, { compressionMethod: 'manual', maxWidth: 1080, maxHeight: 1920, quality: 0.9, });
- The app crashes intermittently with
EXC_BAD_ACCESSinvVertical_Scale_ARGB_8888_Accelerate. We saw it in production on an iPhone 11 running iOS 27.0.1. The same code never crashes in Debug.
You can also reproduce it outside React Native: compile manualResize on its own with swiftc -O, resize a 12 MP image, and it crashes at the same frame. With -Onone, it doesn't.
Crash 2: missing or unreadable image
- Any build configuration.
- Call:
You can also pass a truncated JPEG instead.
await Image.compress('file:///path/that/does/not/exist.jpg', { compressionMethod: 'manual', });
- The app aborts with an uncaught
NSException(unsupported_value) instead of the promise rejecting.
Issue 3: stretched, sideways image
- Create a 16-bit PNG, or a PNG with alpha, tagged with EXIF Orientation 6.
- Compress it with the options from crash 1.
- The output is stretched and rotated 90°.
Activity
github-actions commentedon Oct 26, 2026
👋 @ArjunPatidar-dev
Thanks for opening your issue here! If you find this package useful hit the star🌟!