-
-
Notifications
You must be signed in to change notification settings - Fork 2.4k
Description
If I open an LZW-compressed binary TIFF file using pillow and load its data, pillow calls the "close" syscall on one of the two relevant file descriptors twice. The second call results in a "EBADF (Bad file descriptor)" error. The program doesn't crash and everything seems to work ok. But if multithreading is used and a race condition happens, this might crash the program.
In this example, the expected behaviour would be that there is only one "close" syscall on each of the two relevant file descriptors and that there is no "EBADF" error. Below, I show how to reproduce the problem.
$ cat create_tiff_file.py
#!/usr/bin/env python
import numpy as np
import PIL.Image as Image
Image.fromarray(np.random.rand(500, 600) > 0.9) \
.save("example.tif", format="TIFF", compression="tiff_lzw")
$ cat read_tiff_file.py
#!/usr/bin/env python
import PIL.Image as Image
print("Right before the with-block", flush=True)
with Image.open("example.tif") as mask:
print("Right in the beginning of the with-block", flush=True)
mask.load()
print("Right after load()", flush=True)
print("Right after the with-block", flush=True)
$ mamba create -n pillow_double_close_env --override-channels --channel anaconda python=3.11 # omitting the output
$ mamba activate pillow_double_close_env # omitting the output
$ pip install numpy pillow # omitting the output
$ conda list
# packages in environment at /home/philip/soft/mambaforge/envs/pillow_double_close_env:
#
# Name Version Build Channel
_libgcc_mutex 0.1 main anaconda
_openmp_mutex 5.1 1_gnu anaconda
bzip2 1.0.8 h7b6447c_0 anaconda
ca-certificates 2023.01.10 h06a4308_0 anaconda
certifi 2022.9.24 py311h06a4308_0 anaconda
ld_impl_linux-64 2.38 h1181459_1 anaconda
libffi 3.4.2 h6a678d5_6 anaconda
libgcc-ng 11.2.0 h1234567_1 anaconda
libgomp 11.2.0 h1234567_1 anaconda
libstdcxx-ng 11.2.0 h1234567_1 anaconda
libuuid 1.41.5 h5eee18b_0 anaconda
ncurses 6.4 h6a678d5_0 anaconda
numpy 1.24.2 pypi_0 pypi
openssl 1.1.1s h7f8727e_0 anaconda
pillow 9.4.0 pypi_0 pypi
pip 22.2.2 py311h06a4308_0 anaconda
python 3.11.0 h7a1cb2a_2 anaconda
readline 8.2 h5eee18b_0 anaconda
setuptools 65.5.0 py311h06a4308_0 anaconda
sqlite 3.40.1 h5082296_0 anaconda
tk 8.6.12 h1ccaba5_0 anaconda
tzdata 2022a hda174b7_0 anaconda
wheel 0.37.1 pyhd3eb1b0_0 anaconda
xz 5.2.10 h5eee18b_1 anaconda
zlib 1.2.13 h5eee18b_0 anaconda
$ pip freeze
certifi @ file:///croot/certifi_1669749558946/work/certifi
numpy==1.24.2
Pillow==9.4.0
$ python create_tiff_file.py
$ strace -e decode-fds=path -e trace=openat,close ./read_tiff_file.py
# omitting all the output except for the few last lines, which are the most relevant
Right in the beginning of the with-block
close(4</pillow_double_close_bug_report/example.tif>) = 0
close(4) = -1 EBADF (Bad file descriptor)
close(3</pillow_double_close_bug_report/example.tif>) = 0
Right after load()
Right after the with-block
+++ exited with 0 +++Also, I want to note that this caused my training of a neural network to crash once. It happened because Pytorch's DataLoader uses multithreading and a race condition happened.