Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

"OSError: -9" saving a multi-layer TIFF via libtiff #5319

Closed
ndjman7 opened this issue Mar 9, 2021 · 7 comments
Closed

"OSError: -9" saving a multi-layer TIFF via libtiff #5319

ndjman7 opened this issue Mar 9, 2021 · 7 comments
Labels

Comments

@ndjman7
Copy link

ndjman7 commented Mar 9, 2021

What did you do?

I did a version up. (5.2.0 -> 6.2.0)
It was the same at 8.1.2.

This is similar to this issue.
https://stackoverflow.com/questions/64683811/how-to-debug-oserror-9-saving-a-tiff-via-libtiff-and-pillow

What did you expect to happen?

I thought dividing the multi-layer TIFF files would work the same way.

What actually happened?

raise Error OSError -9

...
# example
with Image.open(document_file) as tif_images:
    result = []

    for tif_image in ImageSequence.Iterator(tif_images):
        png_image_bytes_io = io.BytesIO()
        tif_image.save(png_image_bytes_io, format='png')  # <- raise Error
        result.append(png_image_bytes_io)
...

What are your OS, Python and Pillow versions?

  • OS: macOS Catalina 10.15.7
  • Python: 3.6.9
  • Pillow: 6.2.0

image

Traceback:

File "/Users/ra/.pyenv/versions/inapi/lib/python3.6/site-packages/django/core/handlers/exception.py" in inner
  34.             response = get_response(request)

File "/Users/ra/.pyenv/versions/inapi/lib/python3.6/site-packages/django/core/handlers/base.py" in _get_response
  115.                 response = self.process_exception_by_middleware(e, request)

File "/Users/ra/.pyenv/versions/inapi/lib/python3.6/site-packages/django/core/handlers/base.py" in _get_response
  113.                 response = wrapped_callback(request, *callback_args, **callback_kwargs)

File "/Users/ra/.pyenv/versions/inapi/lib/python3.6/site-packages/django/views/generic/base.py" in view
  71.             return self.dispatch(request, *args, **kwargs)

File "/Users/ra/.pyenv/versions/inapi/lib/python3.6/site-packages/django/views/generic/base.py" in dispatch
  97.         return handler(request, *args, **kwargs)

File "/Users/ra/Projects/inapi/unsecured/views/upload_fax_document.py" in post
  105.             png_images = convert_tif_file_to_png_files(document_file.file)

File "/Users/ra/Projects/inapi/unsecured/views/upload_fax_document.py" in convert_tif_file_to_png_files
  61.             tif_image.save(png_image_bytes_io, format='png')

File "/Users/ra/.pyenv/versions/inapi/lib/python3.6/site-packages/PIL/Image.py" in save
  2050.         self._ensure_mutable()

File "/Users/ra/.pyenv/versions/inapi/lib/python3.6/site-packages/PIL/Image.py" in _ensure_mutable
  640.             self._copy()

File "/Users/ra/.pyenv/versions/inapi/lib/python3.6/site-packages/PIL/Image.py" in _copy
  633.         self.load()

File "/Users/ra/.pyenv/versions/inapi/lib/python3.6/site-packages/PIL/TiffImagePlugin.py" in load
  1097.             return self._load_libtiff()

File "/Users/ra/.pyenv/versions/inapi/lib/python3.6/site-packages/PIL/TiffImagePlugin.py" in _load_libtiff
  1209.             raise IOError(err)

Exception Type: OSError at /unsecured/upload_fax_document/
Exception Value: -9
@radarhere
Copy link
Member

Ok, so you're trying to iterate through the frames of a TIFF file and save those frames individually, right?

Could we get a copy of the starting image?

@radarhere radarhere added the TIFF label Mar 9, 2021
@ndjman7
Copy link
Author

ndjman7 commented Mar 10, 2021

@radarhere Please wait. It's a fax with sensitive information in it.

@ndjman7
Copy link
Author

ndjman7 commented Mar 11, 2021

@radarhere I replied late.

It's works on under 5.4.1.
pando.tif.zip

@ndjman7
Copy link
Author

ndjman7 commented Mar 24, 2021

@radarhere Did you check by any chance?

@radarhere
Copy link
Member

radarhere commented Mar 24, 2021

I can reproduce the problem with just

from PIL import Image
with Image.open('pando.tif') as im:
    im.save('out.tif')

Debugging, I find that it is failing at

/* overflow check for realloc */
if (INT_MAX / row_byte_size < rows_per_strip) {
state->errcode = IMAGING_CODEC_MEMORY;
return -1;
}

Pillow has determined rows_per_strip is 4294967295 - this is 2**32 - 1.

https://www.awaresystems.be/imaging/tiff/tifftags/rowsperstrip.html

The default is 2**32 - 1, which is effectively infinity. That is, the entire image is one strip. Use of a single strip is not recommended.

So our logic is flagging it as a security problem, but looks like a valid, if uncommon, value.

I don't suppose that you are willing for your image to be included as a test image in the Pillow suite, and distributed under the Pillow license?

@ndjman7
Copy link
Author

ndjman7 commented Mar 25, 2021

@radarhere Yes, you can. Thank you for the solution.

@radarhere
Copy link
Member

This has been resolved instead by #5364. The fix should be a part of 8.2.0 when it is released on April 1.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants