Skip to content

SIGBUS when overwriting the file you opened #3496

@lskovlun

Description

@lskovlun

What did you do?

I have a program that takes the palette of one (indexed, 8-bit) bitmap file, applies it to another bitmap file, then attempts to replace the second file in-place with the updated version. Doing so consistently results in a SIGBUS. Apparently there is a minimum file size where this happens; I've seen the test program pass for very small bitmaps. I'm attaching two files of the same size I originally used (but not the same content), if I can get it work.

What did you expect to happen?

No SIGBUS. The use of mmap should be an implementation detail, but its use "leaks out" in this case, and as described, cannot be turned off except by hacking the Pillow installation.

What actually happened?

SIGBUS.
I have tracked the issue down to the mmap functionality in Pillow, which cannot be disabled from the caller. I can think of three workarounds, two of which I've verified make the test program work.

  1. Use os.unlink to make sure that the replacement file gets a new inode (TESTED)
  2. Force use_mmap to False within ImageFile.py (TESTED)
  3. Use the O_TMPFILE trick mentioned in the open(2) manpage (UNTESTED)

Of course, it would be even better if this was handled properly within Pillow.

What are your OS, Python and Pillow versions?

  • OS: Linux (three versions tested, 3.2.x, 4.17.8 and 4.18.10)
  • Python: Version 2.7.3 and 3.7.2rc1
  • Pillow: PIL version 1.1.7 and Pillow version 5.3.0 and 5.4.0

The test program below also provides test modes that disable the getpalette/putpalette calls at the heart of the palette replacement process. These calls turned out to be a red herring, but are included here for completeness.

import os
from PIL import Image
import sys

TestModes = {0: 'No failure: getpalette/putpalette AND unlink',
             1: 'No failure: No getpalette/putpalette BUT unlink',
             2: 'Failure: No unlink BUT getpalette/putpalette',
             3: 'Failure: No getpalette/putpalette AND no unlink'}

if len(sys.argv) < 3:
    print("Syntax: python replanepal.py <image file> <palette file> [<test mode>]")
    sys.exit(0)

FAIL = int(sys.argv[3]) if len(sys.argv) > 3 else 0

print("Running test with test mode: %s" % TestModes[FAIL])

img = Image.open(sys.argv[1])
mainpal = Image.open(sys.argv[2])

if FAIL in [0, 2]:
    palval = mainpal.getpalette()
    img.putpalette(palval)

if FAIL in [0, 1]:
    os.unlink(sys.argv[1])

img.save(sys.argv[1])
print("Apparently, all went well!")

Metadata

Metadata

Assignees

No one assigned

    Labels

    BugAny unexpected behavior, until confirmed feature.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions