Double free in cairo_scaled_font_destroy
This happens because while thread is waiting on _cairo_scaled_font_map_lock
another thread may resurrect scaled_font
and then destroy it again (into holdovers) and another threads may push it to bottom of holdovers and then it is definitely freed. Then this waiting thread wakes up and put already freed scaled_font
into holovers where it will be pushed again to bottom and double freed.
Gdb:
double free or corruption (out)
Thread 206 "a.out" received signal SIGABRT, Aborted.
[Switching to Thread 0x7ffc86ff5700 (LWP 525180)]
__GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:50
50 ../sysdeps/unix/sysv/linux/raise.c: No such file or directory.
#0 __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:50
#1 0x00007ffff79da859 in __GI_abort () at abort.c:79
#2 0x00007ffff7a453ee in __libc_message (action=action@entry=do_abort, fmt=fmt@entry=0x7ffff7b6f285 "%s\n") at ../sysdeps/posix/libc_fatal.c:155
#3 0x00007ffff7a4d47c in malloc_printerr (str=str@entry=0x7ffff7b71670 "double free or corruption (out)") at malloc.c:5347
#4 0x00007ffff7a4f120 in _int_free (av=0x7ffff7ba0b80 <main_arena>, p=0x7ffcc802b0f0, have_lock=<optimized out>) at malloc.c:4314
#5 0x00007ffff7eb6e08 in _cairo_hash_table_destroy (hash_table=0x7ffc8c02e660) at ../../../../src/cairo-hash.c:221
#6 0x00007ffff7eec864 in _cairo_scaled_font_fini_internal (scaled_font=0x7ffc8c02e440) at ../../../../src/cairo-scaled-font.c:887
#7 0x00007ffff7eecf44 in INT_cairo_scaled_font_destroy (scaled_font=<optimized out>) at ../../../../src/cairo-scaled-font.c:1377
#8 0x000055555555646a in infinite_loop_called_by_multiple_threads (font_face=0x7ffcc800ffd0, m=0x7ffc86ff4c10, options=0x7ffcc801a500) at main.cpp:8
Test case:
#include <cairo.h>
void infinite_loop_called_by_multiple_threads(cairo_font_face_t *font_face, cairo_matrix_t *m, cairo_font_options_t *options)
{
while(true)
{
cairo_scaled_font_t *scaled_font = cairo_scaled_font_create(font_face, m, m, options);
cairo_scaled_font_destroy(scaled_font);
}
}
#include <vector>
#include <thread>
#include <cassert>
#include <cairo-ft.h>
int main()
{
std::vector<std::thread> threads;
for(int t = 0; t < 1000; t++)
threads.emplace_back([t]()
{
FT_Library lib;
FT_Init_FreeType(&lib);
FT_Face face;
assert(!FT_New_Face(lib, "/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf", 0, &face));
cairo_font_face_t *font_face = cairo_ft_font_face_create_for_ft_face(face, FT_LOAD_NO_HINTING | FT_LOAD_NO_BITMAP);
cairo_surface_t *surface = cairo_image_surface_create(CAIRO_FORMAT_RGB24, 100, 100);
cairo_t *ctx = cairo_create(surface);
cairo_matrix_t m;
cairo_matrix_init_identity(&m);
cairo_font_options_t *options = cairo_font_options_create();
infinite_loop_called_by_multiple_threads(font_face, &m, options);
});
for(auto &th : threads)th.join();
return 0;
}