FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

Exporting an image created by Image.fromarrow with __arrow_c_array__ crashes with a segmentation fault · Issue #9896 · python-pillow/Pillow · GitHub

Exporting an image created by Image.fromarrow with __arrow_c_array__ crashes with a segmentation fault #9896

Description

What did you do?

Created an image with Image.fromarrow and passed it to pyarrow.array.

from PIL import Image
import pyarrow as pa

im = Image.fromarrow(pa.array([0] * 100, type=pa.uint8()), "RGBA", (5, 5))
im.getpixel((0, 0))  # fine
pa.array(im)         # segmentation fault

The same happens with a single band image ("L" from 25 values), with im.__arrow_c_array__() called directly, and on Pillow 11.3.0, 12.3.0 and current main (098de8a). After any change to the image (im.putpixel((0, 0), (1, 2, 3, 4)), which copies the storage) the export works.

What did you expect to happen?

An Arrow array with the same values, as for any other image, or a ValueError if a borrowed image cannot be exported. The docstring of fromarrow shows both directions (Image.fromarrow(...) and pa.array(im)), so a round trip is the documented usage.

What actually happened?

The process dies with SIGSEGV (exit code 139), nothing is printed.

Why

ImagingBorrowArrow (src/libImaging/Storage.c) fills only the row pointers im->image[y] of the borrowed image and leaves im->block and im->blocks NULL.

export_single_channel_array and export_fixed_pixel_array (src/libImaging/Arrow.c) take the data pointer from im->block, or from im->blocks[0].ptr when block is NULL:

    if (im->block) {
        array->buffers[1] = im->block;
    } else {
        array->buffers[1] = im->blocks[0].ptr;
    }

For a borrowed image blocks is NULL, so this dereferences NULL. The im->blocks_count > 1 check above it does not catch this case, blocks_count is 0.

Tests/test_pyarrow.py compares fromarrow images with getpixel, so the round trip is not covered.

Possible fix

A borrowed array is accepted only when its length matches the image exactly, so its rows are contiguous and the export can use the first row. Adding a branch at both places:

    if (im->block) {
        array->buffers[1] = im->block;
    } else if (im->arrow_array_capsule) {
        array->buffers[1] = im->image[0];
    } else {
        array->buffers[1] = im->blocks[0].ptr;
    }

With this change (built from main) the example returns fixed_size_list<RGBA: uint8 not null>[4] with the original values, and the "L" case returns the 25 values.

Context

pillow-heif is moving its plugin to Image.fromarrow, so every HEIF image opened with Image.open would be a borrowed image, and pa.array(im) on such an image would crash. We are holding that release until this is fixed in Pillow.

What are your OS, Python and Pillow versions?

  • OS: macOS 26.5 arm64
  • Python: 3.14.5
  • Pillow: 11.3.0, 12.3.0, main (098de8a)
  • pyarrow: 25.0.1

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


    Back | FazBrowse Home | New Git URL