Skip to content

[iOS] SIGABRT in makeImageSnapshot when canvas size is -1 — MTLTextureDescriptor width (uint64)-1 assertion #4029

Description

@woosanggyu

Description

We are seeing a production iOS crash (SIGABRT) in RNSkView::makeImageSnapshot. When the Skia view's canvas size is -1 (view not yet laid out, or being detached), the size is passed without validation into the offscreen surface creation, and Metal aborts on texture descriptor validation:

failed assertion `Texture Descriptor Validation
MTLTextureDescriptor has width (18446744073709551615) greater than the maximum allowed size of 32768.
MTLTextureDescriptor has height (18446744073709551615) greater than the maximum allowed size of 32768.'

18446744073709551615 is (uint64)-1, i.e. the unlaid-out canvas size cast to unsigned.

Stack trace (symbolicated, production)

Metal                -[MTLTextureDescriptorInternal validateWithDevice:]  (failed assertion → abort)
AGXMetalG18P         -[AGXTexture initWithDevice:desc:isSuballocDisabled:]
NOVA                 OffscreenRenderContext::OffscreenRenderContext (MetalContext.h:33)
NOVA                 MetalContext::MakeOffscreen (MetalContext.h:51)
NOVA                 RNSkia::RNSkOffscreenCanvasProvider::RNSkOffscreenCanvasProvider (RNSkView.h:77)
NOVA                 RNSkia::RNSkView::makeImageSnapshot (RNSkView.h:207)
NOVA                 lambda in RNSkJsiViewApi.h:241  (makeImageSnapshotAsync main-queue block)
libdispatch          _dispatch_main_queue_drain

Analysis

  • JS calls canvasRef.makeImageSnapshotAsync(). RNSkJsiViewApi dispatches the actual snapshot to the main queue (RNSkJsiViewApi.h:241).
  • Between the JS call and the main-queue block executing, the view's layout state can change (view detached / not yet laid out) — so JS-side guards (checking onLayout completion / mount state before calling) cannot fully prevent this; it is a native-side TOCTOU.
  • RNSkView::makeImageSnapshot (cpp/rnskia/RNSkView.h) constructs RNSkOffscreenCanvasProvider with _canvasProvider->getWidth()/getHeight() without any size validation, so -1 flows straight into makeOffscreenSurface.

No deterministic repro (production crash on app-store build; the window is a race between snapshot dispatch and view teardown/layout). Not found among existing issues.

Suggested fix

Early-return nullptr when the canvas size is not positive. nullptr already flows into the existing "Failed to make snapshot from view." promise rejection, which callers can handle gracefully:

sk_sp<SkImage> makeImageSnapshot(SkRect *bounds) {
  if (_canvasProvider->getWidth() <= 0 || _canvasProvider->getHeight() <= 0) {
    return nullptr;
  }
  ...

We are running exactly this as a local patch on 2.10.1 and it degrades the crash into the already-handled rejection path. Happy to open a PR if useful.

Environment

  • @shopify/react-native-skia: 2.10.1
  • react-native: 0.86.0 (new arch), Expo SDK 57
  • iOS (observed on iPhone / AGXMetalG18P), production build

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions