[cairo] cairo_xcb_surface_create() segfaults on second call with different xcb info

Uli Schlachter psychon at znc.in
Thu Nov 8 20:36:17 UTC 2018


Hi Ryan,

On 08.11.18 04:48, Ryan Flannery wrote:
> I'm guessing I'm either missing some tear-down/cleanup between calls
> that I'm not seeing from the documentation and google'ing, or there's
> some state/etc within the cairo API I'm missing.
> 
> Anyone seen something similar or have any idea why this might be caused?

Cairo has to get quite some information from the X11 server. Which
extensions are supported? In which version? Does shared memory work?
Stuff like this.

Obviously, querying this all the time would be slow. Thus, cairo caches
this information. Namely, there is an instance of cairo_device_t. This
instance is kept around even when all surfaces using this device are
destroyed.

When a new cairo surface is created, it needs to get an instance of
cairo_device_t for all of this information. To identify an
already-existing device, the xcb_connection_t* pointer is used. Same
pointer means same XCB connection. Thus, when you call xcb_disconnect(),
the cache now contains a dangling pointer. The next call to
xcb_connect() might very well allocate an xcb_connection_t* with the
same pointer. Thus, you now get a cache hit even though there is a new
XCB connection. Bad things happen afterwards.

(But right now I do not know where you crash comes from)

> Sample code below. The "test()" function is where I create an x window
> (and related bits), followed by a cairo surface and cairo_t, and then
> simply sleep for a second and tear everything down. The second call to
> test() will reliably segfault on cairo_xcb_surface_create().

Fun fact: Does not crash here on Debian testing.
Dies in a failed assertion when run under xtrace (sometimes known as
x11trace).

[...]
>    cairo_destroy(c);

Add this here:

  cairo_device_finish(cairo_surface_get_device(cs));

>    cairo_surface_destroy(cs);
>    xcb_destroy_window(x, w);
>    xcb_disconnect(x);
> }

If you want to keep the device around for later (i.e. have multiple
surface using the same device), you can save a pointer via:

  cairo_device_t *device = cairo_device_reference(....);

Now, you have to later call cairo_device_destroy() when you no longer
need the reference, but you get a pointer to the cairo_device_t
independent of a cairo xcb surface.

Oh and: You have to finish the device before you call xcb_disconnect().
Finishing the device can still cause X11 requests to be sent (i.e. to
send out pending drawing that was not yet done).

Also: Finishing the device automatically finishes all cairo_surface_t
instances for this XCB devices.

Cheers,
Uli

P.S.: Someone ran into the same problem just a few days ago and asked in
IRC. There, the symptom was X11 errors, because cairo was trying to use
XIDs which were already freed. Must be season for this kind of problem. :-)
-- 
"Why make things difficult, when it is possible to make them cryptic
and totally illogical, with just a little bit more effort?" -- A. P. J.


More information about the cairo mailing list