I was faced a problem when using arcade library. It uses Pillow under the hood to work with images. When I tried to draw game score text, I got random crash. I localized the problem and found out that crash is related to the way the Pillow works with font and frees up a resources.
Crash with an error:
munmap_chunk(): invalid pointer
Process finished with exit code 134 (interrupted by signal 6: SIGABRT)
Crashing code example (munmap_chunk(): invalid pointer):
import PIL.Image
import PIL.ImageDraw
import PIL.ImageFont
while True:
image = PIL.Image.new("RGBA", (10, 10))
draw = PIL.ImageDraw.Draw(image)
font = PIL.ImageFont.truetype("Arial.ttf", 12)
print(draw.multiline_textsize("Sample", font=font))
Works without any problem:
import PIL.Image
import PIL.ImageDraw
import PIL.ImageFont
font = PIL.ImageFont.truetype("Arial.ttf", 12)
while True:
image = PIL.Image.new("RGBA", (10, 10))
draw = PIL.ImageDraw.Draw(image)
print(draw.multiline_textsize("Sample", font=font))
How many iterations of the loop does it take to reproduce the problem?
@radarhere 28 iterations. Interesting fact: when I changed the code to:
import PIL.Image
import PIL.ImageDraw
import PIL.ImageFont
i = 0
while True:
i += 1
print(i)
image = PIL.Image.new("RGBA", (10, 10))
draw = PIL.ImageDraw.Draw(image)
font = PIL.ImageFont.truetype("Arial.ttf", 12)
print(draw.multiline_textsize("Sample", font=font))
it start to crash on 218 iteration.
I can confirm the issue (which is apparently the same) on:
I can reproduce the issue above using the code above.
Also, the results below only serve as a confirmation but don't offer new information other than that different types of crashes ("heisenbugs") causing core dumps occur intermittently in practical use.
details
It is hard to reproduce in my case. Considering the original issue report above, the reason mine is hard to reproduce may be that in my library I only load the font object once then cache it (I cache the return of ImageFont.truetype): https://github.com/poikilos/pypicolcd
I could get any of the following, but always when using the font to draw (on _d.text( where _d is an ImageDraw.Draw lock, which is after self.blab("* drawing text ' which shows when using the --verbose=True option):
Segmentation fault (core dumped)or 2.
corrupted size vs. prev_size
Aborted (core dumped)
or 3.
munmap_chunk(): invalid pointer
lcd-fb.service: Main process exited, code=dumped, status=6/ABRT
lcd-fb.service: Failed with result 'core-dump'.
I have this problem too, but it only happened if I install Pillow from wheel file or just run pip install Pillow, which will use wheel file by default.
If I install with this command pip install --compile --install-option=-O1 Pillow, it will build from source and the problem is gone. I test it many times still working perfectly.
My environment: Fedora 31, Python 3.6 (in venv), Pillow 7.0.0
I note Fedora is a common factor in these three reports.
I note Fedora is a common factor in these three reports.
It also happened on CentOS 8 too, the problem also gone if I install from source instead of wheel file.
Another Fedora 31 user here with the same issue. CentOS 8 is based off Fedora 28 (like RHEL 8) so I suspect this might affect older Fedora versions. That or something's been backported. As with @cwt, installing the non-binary package (pip install --no-binary=Pillow Pillow==6.2.0 --force) resolves this issue.
Edit: For anyone else stumbling upon this, I needed to install the following packages to build things correctly:
sudo dnf install libjpeg-devel zlib-devel freetype-devel
I'm experiencing the same problem on Debian. The original code example crashes on the third iteration here (though with another font: Vera.ttf).
I have the same problem on Arch Linux using a Verdana.ttf font.
Solved with compiling myself pip install --compile --install-option=-O1 Pillow.
Still an issue in 8.0.1. on Python 3.8.6. OS Archlinux, x86_64.
from PIL import Image, ImageDraw, ImageFont
for try_n in range(100):
print(try_n+1)
text = "TEXT"
img = Image.new("RGBA", (100, 100))
draw = ImageDraw.Draw(img)
font = ImageFont.truetype("/usr/share/fonts/liberation/LiberationSerif-Regular.ttf", 15)
draw.text((5,5), text, font=font)
Fails consistently on the 3rd try. Font Size or actual text don't matter. If the font is removed, it works. Issue persists with different fonts, although it fails on different tries for each font (but is still consistent for every font I tried, i.e., NimbusSans-Regular fails on second attempt).
Noteworthy: Works flawlessly on Raspberry Pi 3!
I also got this issue
It happens randomly but mostly on 3rd or 4th iteration.
Also confirm the issue.
Recent reporters, are you doing this with the shipped wheels or did you compile yourself?
Shipped wheels, pip has installed pillow from
https://files.pythonhosted.org/packages/00/08/aa5071a3c552c0f53107b618a8828aaa1fff2e25bca37bcae27a2e688d38/Pillow-8.0.1-cp38-cp38-manylinux1_x86_64.whl#sha256=92c882b70a40c79de9f5294dc99390671e07fc0b0113d472cbea3fde15db1792
Shipped. Works fine with pip install --compile --install-option=-O1 Pillow
Yup, shipped from PyPI
Ok, I can replicate this on Ubuntu 20.04 with the shipped image. Of course, it doesn't do it with builds on that machine. It does appear that there's something funky going on with the Font Face references through raqm/harfbuzz/freetype.
I'm seeing invalid read/writes on:
==83319== Invalid read of size 4
==83319== at 0x7881FF0: FT_Reference_Face (in /usr/lib/x86_64-linux-gnu/libfreetype.so.6.17.1)
==83319== by 0x786D881: _raqm_set_freetype_face (raqm.c:672)
...
==83319== Invalid read of size 4
==83319== at 0x7881FF0: FT_Reference_Face (in /usr/lib/x86_64-linux-gnu/libfreetype.so.6.17.1)
==83319== by 0x7A0114C: hb_ft_font_create_referenced (in /usr/lib/x86_64-linux-gnu/libharfbuzz.so.0.20600.4)
==83319== by 0x786D8BF: _raqm_create_hb_font.isra.0 (raqm.c:633)
...
==83319== Invalid read of size 4
==83319== at 0x788A2E0: FT_Done_Face (in /usr/lib/x86_64-linux-gnu/libfreetype.so.6.17.1)
==83319== by 0x786D789: _raqm_free_text_info.isra.0.part.0 (raqm.c:246)
==83319== by 0x786DA05: _raqm_free_text_info (raqm.c:240)
==83319== by 0x786DA05: raqm_destroy (raqm.c:370)
...
==83319== Invalid read of size 4
==83319== at 0x788A2E0: FT_Done_Face (in /usr/lib/x86_64-linux-gnu/libfreetype.so.6.17.1)
==83319== by 0x79533FD: hb_blob_destroy (in /usr/lib/x86_64-linux-gnu/libharfbuzz.so.0.20600.4)
==83319== by 0x795A46F: ??? (in /usr/lib/x86_64-linux-gnu/libharfbuzz.so.0.20600.4)
==83319== by 0x795AA5E: hb_face_destroy (in /usr/lib/x86_64-linux-gnu/libharfbuzz.so.0.20600.4)
==83319== by 0x7967F90: hb_font_destroy (in /usr/lib/x86_64-linux-gnu/libharfbuzz.so.0.20600.4)
==83319== by 0x786DA2A: _raqm_free_runs (raqm.c:347)
==83319== by 0x786DA2A: raqm_destroy (raqm.c:371)
which leads to:
Traceback (most recent call last):
File "4225_unmap.py", line 11, in <module>
draw.text((5,5), text, font=font)
File "/home/ubuntu/vpy38-dbg2/lib/python3.8/site-packages/PIL/ImageDraw.py", line 379, in text
draw_text(ink)
File "/home/ubuntu/vpy38-dbg2/lib/python3.8/site-packages/PIL/ImageDraw.py", line 324, in draw_text
mask, offset = font.getmask2(
File "/home/ubuntu/vpy38-dbg2/lib/python3.8/site-packages/PIL/ImageFont.py", line 651, in getmask2
size, offset = self.font.getsize(
OSError: invalid face handle
==83319== Invalid read of size 8
And then it fails with a Segfault. This doesn't happen with a locally compiled version here, nor does valgrind trigger on anything in PIL. I've got all of the dependencies installed here, so we have the all of the same dependencies, though not necessarily the same versions. Stepping through, they appear to be following the same code paths.
In the production versions, it's not clear that the font faces are invalid, but enough of the code has been optimized out that I'm not sure that things are behaving correctly or not. But they're not obviously wrong.
So a few things need to happen to narrow this down.
If this is only happening with Raqm, I wonder if it is related to #3066, which I'm pretty sure is caused by the way Raqm is loaded dynamically, but FreeType compiled in statically.
If so, one possible solution I have been thinking about could be to include Raqm in wheels but modify it to use a shim to load FriBiDi dynamically, similarly to how #2753 links Raqm. That would also allow dropping support code for old Raqm versions.
Including Raqm statically would avoid passing FreeType structs between different builds of FreeType, and the FriBiDi interface only passes objects for the FriBiDi library. FriBiDi should also be more widely available (it is a dependency of Raqm, so it should be everywhere where there is Raqm).
It is happening with RAQM, but it's also not happening with RAQM. It's the particular build of it that's the issue. But I do agree that the issue is likely the same as #3066.
It's possible that dynamically linking fribidi would work, but I haven't looked at how complicated that interface is.
If this is only happening with Raqm
I meant "If a workaround is to use layout_engine=ImageFont.LAYOUT_BASIC".
It's possible that dynamically linking fribidi would work, but I haven't looked at how complicated that interface is.
I just had a brief look and I think it might even be simpler than the Raqm interface. I don't see any structs being passed, only typedef-ed ints. There is also one large enum, but the single required value can be hardcoded. There is a single FriBiDi related #ifdef USE_FRIBIDI_EX_API, but it looks easy to detect at load time.
I can give this a try at some point, but I don't expect to have the time in the next few weeks. See #5062.
I can also confirm the issue.
Most helpful comment
Another Fedora 31 user here with the same issue. CentOS 8 is based off Fedora 28 (like RHEL 8) so I suspect this might affect older Fedora versions. That or something's been backported. As with @cwt, installing the non-binary package (
pip install --no-binary=Pillow Pillow==6.2.0 --force) resolves this issue.Edit: For anyone else stumbling upon this, I needed to install the following packages to build things correctly: