Skip to content

fix(py): grayscale arrays crash image functions #772

Open

Description

@Aniketbhil

Caution

This issue has been claimed by @Aniketbhil, so it is not recommended that you work on it.

For @Aniketbhil:
If you would like to revoke your claim, comment /untake


Affected languages

  • C++
  • C
  • JavaScript
  • Python
  • Other
  • N/A

If you selected "Other", please specify which language.

No response

Describe the issue

The Python API accepts 2D grayscale NumPy arrays in functions that expect four-channel RGBA input. Passing a grayscale array to invert_image, threshold_image, or black_threshold_image aborts the Python process with heap-corruption-related errors instead of raising a Python exception.

The _inject_dimensions decorator in packages/py/img2num/api.py only checks whether the input has at least two dimensions. It does not validate the channel count.

The affected wrappers in bindings/py/src/img2num_pybind.cpp allocate output arrays using the input shape, while the native implementations in core/src/internal/image_utils.cpp interpret the buffer as RGBA data using ImageLib::RGBA_CONVERTER<uint8_t>.

The Python README documents that images must be four-channel uint8 arrays in RGBA channel order.

I reproduced this with a (64, 64) uint8 NumPy array. invert_image and threshold_image aborted with munmap_chunk(): invalid pointer, while black_threshold_image aborted with double free or corruption (out). Each test ran in a separate process and exited with code 134.

Steps to reproduce

  1. Build and install the Python package in the project's development environment.
  2. Run the following Python code:
import numpy as np
import img2num

gray = np.zeros((64, 64), dtype=np.uint8)

img2num.invert_image(gray)
# Aborts with: munmap_chunk(): invalid pointer

img2num.threshold_image(gray, 4)
# Aborts with: munmap_chunk(): invalid pointer

img2num.black_threshold_image(gray, 4)
# Aborts with: double free or corruption (out)
  1. Run each function call in a separate Python process.
  2. Observe that the process aborts instead of raising a Python exception.

Tested in Python 3.13 on Linux x86_64, using branch repro-3 at revision 565d40ee (upstream/dev, PR #747).

What was the expected result?

Inputs that do not satisfy the documented four-channel uint8 RGBA format should be rejected with a clear Python exception, such as ValueError, before native image processing begins. Valid RGBA inputs should continue to work normally.

Please include some screenshots here if necessary.

The more information we have, the better-equipped we will be to resolve this issue.

No response

Would you like to work on this issue?

Yes

Activity

  1. Aniketbhil commented on Oct 15, 2026

    @Aniketbhil
    Author

    /take

  2. moved this from Todo to In progress in Img2Num Roadmapon Oct 17, 2026
  3. Aniketbhil commented on Oct 18, 2026

    @Aniketbhil
    Author

    I found PR #780, which adds RGBA image shape validation in the Python API and appears to address the crashes reported here.

    Since that PR is still open, I'll keep this issue open for now rather than close it prematurely.

    Related PR: #780

  4. Ryan-Millard commented on Oct 18, 2026

    @Ryan-Millard
    Owner

    Hi @Aniketbhil. Thank you for the issue. This is a known issue in the project and I previously assumed that the documentation would be enough, but that was a bad assumption since not everyone gets to reading the documentation or the documentation may just be bad.

    Please would you have a look at #780 and let me know if you think that is the same solution you were considering. We'd really appreciate a review on it since you discovered it and probably know the required fix.

  5. Aniketbhil commented on Oct 18, 2026

    @Aniketbhil
    Author

    Hi @Aniketbhil. Thank you for the issue. This is a known issue in the project and I previously assumed that the documentation would be enough, but that was a bad assumption since not everyone gets to reading the documentation or the documentation may just be bad.
    Please would you have a look at #780 and let me know if you think that is the same solution you were considering. We'd really appreciate a review on it since you discovered it and probably know the required fix.

    Hi Ryan! Thanks for the context and for asking me to review PR #780.
    I inspected the diff, and the new validation requiring an RGBA array with shape (H, W, 4) appears to address the root cause I found. The previous check allowed grayscale and RGB arrays to reach native code that expects RGBA data.
    I'll verify the behavior against my original reproductions and check whether the relevant regression tests are covered. I'll share my findings here once I've completed the review.

  6. Aniketbhil commented on Oct 18, 2026

    @Aniketbhil
    Author

    Hi @Aniketbhil. Thank you for the issue. This is a known issue in the project and I previously assumed that the documentation would be enough, but that was a bad assumption since not everyone gets to reading the documentation or the documentation may just be bad.
    Please would you have a look at #780 and let me know if you think that is the same solution you were considering. We'd really appreciate a review on it since you discovered it and probably know the required fix.

    Hi Ryan! I built PR #780 successfully and tested the changes against my original reproduction cases using the PR's own virtual environment.

    Results:

    • invert_image, threshold_image, and black_threshold_image all reject grayscale (64, 64), RGB (64, 64, 3), and single-channel (64, 64, 1) inputs with ValueError.
    • All three functions accept valid RGBA (64, 64, 4) inputs and return the expected output shape.

    All 12 test cases passed. These results confirm that PR #780 fixes the crash scenarios I reported in this issue.

    I also noticed that the PR doesn't add dedicated regression tests for these cases. Would it be useful for me to contribute them?

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

    bugSomething isn't workingtakenThis item has been claimed

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions