Making GNOME’s GdkPixbuf Image Loading Safer

A new image loading machinery, called glycin, has been in the works for a while. It is already used by GNOME’s default Image Viewer (Loupe), as well as by a bunch of other apps. Glycin provides many security benefits over existing solutions due to the use of the Rust programming language and sandboxing. Distributions will now be able to use the security benefits and broader format support of glycin for other GNOME apps, thumbnailers, and GNOME Shell, whithout changing any existing software. This is made possible by a new option for GNOME’s legacy image-loading library, GdkPixbuf, to use glycin internally.

Getting the Benefits of glycin by Default

Matthias has created a loader within GdkPixbuf that uses glycin internally, bringing basically all the security benefits of glycin to the rest of GNOME’s ecosystem.

The older GdkPixbuf loaders are still enabled by default, even on Linux for the time being. Precedence is alphabetical: The glycin loader will be preferred over most, but not all, of the traditional loaders. If all goes well, the traditional loaders will be disabled by default when building for Linux in the near future.

If you want to switch GdkPixbuf to glycin today, you can build GdkPixbuf with the following options:

    -Dbuiltin_loaders=glycin
    -Dthumbnailer=disabled
    -Dpng=disabled
    -Dtiff=disabled
    -Djpeg=disabled
    -Dgif=disabled

These options require libglycin to be available, and glycin loaders to be installed at runtime. Since GdkPixbuf’s thumbnailer is also disabled, the glycin-thumbnailer need to be installed as well. All these components are available as part of glycin, starting with version 2.0.alpha.2, that is now available. The required version for GdkPixbuf is 2.43.0 and will be published soon.

We are not providing an SVG loader via glycin yet. This will likely change in a future GNOME cycle.

We are proving these release relatively early in the GNOME 49 development cycle to gather feedback from distributions and to catch issues early.

Why Start from Scratch

With GdkPixbuf, we already have a library for image loading, that has served GNOME well for more than 25 years. I already touched upon the reasons in my original blog post introducing Loupe and glycin in 2023. When I started working on Loupe, one of the two maintainers of GdkPixbuf, Emmanuele, asked me to avoid GdkPixbuf, if at all possible. This was before the next CVE would prove his concerns right again: In 2023, a security issue was found in WebP, that would allow remote code execution. Libraries for parsing and decoding file formats have been notorious for memory-related safety issues in the past. That meant that GdkPixbuf had to consider that every supported file format was just another potential attack vector.

Luckily, we now have two technologies to combat these issues: A memory safe language with good performance, which is Rust, and good sandboxing capabilities within the Linux Kernel.

Known Caveats

As to expect in such a large transition, there are some things that are not working yet. Let us know if you are finding any other issues.

Not Available Outside of Linux yet

The biggest caveat today is that glycin will only work on Linux. The reason for this is the used sandbox mechanisms and the way in which the glycin communicates with its loaders. It seems likely that it is possible to preserve a small amount of benefits of these mechanisms on BSD and maybe macOS. There is more information on the used technology available in the readme. As a general solution for other platforms, I am planning a mechanism to compile the loaders into the library. This will not provide sandboxing and format extendability without recompilation. But since most loaders are written in Rust, this is still a huge step-up security wise. Contributions for support on other platforms are welcome. For GdkPixbuf users, this will not pose an immediate issue since traditional gdk-pixbuf loaders are not going away until all platforms it supported, are supported by glycin.

Environments that don’t Support Sandboxing

Glycin defaults to using a sandbox. Usually, sandboxes are created by calling bwrap (bubblewrap). Inside Flatpaks, flatpak-spawn --sandbox is used, which invokes the Flatpak portal to create a new Flatpak environment for the loaders, that is even more limited than the app running inside the Flatpak sandbox.

However, there are environments that don’t allow creating a sandbox. One example are build servers of Linux distributions that are running tests. Since we want to see more sandboxing in the future, it would be great if Linux distributions could ensure that sandboxes are working in their test environments. Until then, tests using GdkPixbuf might start failing, if glycin loaders are used under the hood.

The other environment known to not support sandboxing is Canonical’s Snap format. While apps themselves are sandboxed in Snaps, there are good reasons for wanting to have even stricter sandboxing for things like image loaders or browser processes: An app still might have access to a lot of data by default, or the app might be used to open sensitive files. In this case, additional sandboxing of potentially vulnerable code can be very important, but is currently, to my knowledge, not possible.

Unsupported Image Formats

Glycin supports the AVIF, BMP, DDS, Farbfeld, QOI, GIF, HEIC, ICO, JPEG, JPEG XL, OpenEXR, PNG, PNM, SVG, TGA, TIFF, and WEBP format out of the box. However, many of the formats include a lot of sub-formats within themselves. Since almost all image loaders (except for AVIF, HEIC, and JPEG XL) were completely rewritten in Rust, there is a chance that there is some part of the standard that is not yet supported. Or, even worse, that some camera manufacturer relies on decoders being able to deal with the broken images it generates. Thanks to glycin having been used for two years within Loupe, the image-rs, and zune-image contributors have been able to fix a lot of these issues. But especially for the TIFF format, a few known issues remain. Depending on the progress wrt to these issues, we might instead fall back to the old GdkPixbuf loader for TIFF, but offer using glycin for all other formats. If you run into any unsupported format, please let us know.

Benefits Beyond Security

Glycin provides many additional features over GdkPixbuf when used directly. That’s the reason why we are planning to replace GdkPixbuf completely in the long run. These benefits include:

    • More colors: Glycin already supports some HDR image formats and is ready to support more. ICC color profiles are handled in a unified and more robust way and will soon support monitors with a color gamut wider than the one of sRGB.
    • Easy metadata access: Glycin automatically provides Exif, XMP, and even more metadata for all image formats that support it.
    • Editing capabilities: Glycin supports basic image editing operations like crop and rotate out of the box, with the possibility to preserve the metadata and quality in the images.
    • Speed: The image loaders written in Rust are usually on-par with, and sometimes even faster than, their C/Assembly counterparts, while only using memory safe code. In practical tests, we did find considerable speed improvements, especially for JPEG and PNG images.
    • Crash protection: Even if it is possible to trigger a crash in an image loader, this will not cause a crash in the software using glycin. This is due to loaders being run in separate processes. On top, these processes are limited in their memory-consumptions, also giving some protection against malicious images that intentionally consume a lot of memory.
    • More formats: Glycin brings support for some formats that weren’t supported before, like animated PNGs. The biggest difference, however, will be that distributions like Fedora are now able to ship more than their current default of eight supported image formats due to the new security guarantees.
Difference between thumbnails generated with GdkPixbuf (left) and glycin (right). For GdkPixbuf, some formats are not supported, while others deviate in colors, are missing rotation, or have wrong scaling.
Difference between thumbnails generated with GdkPixbuf (left) and glycin (right). For GdkPixbuf, some formats are not supported, while others deviate in colors, are missing rotation, or have wrong scaling.

Thanks

Thanks to Michael who drafted the initial version of this post. Many thanks to everyone who contributed this project directly or indirectly. Special thanks to the fine folks from the image-rs and zune-image (donate) projects that make all of this possible! The initial work to create a C API for glycin was supported by the STF.

You can support my work here. Happy pride month 🏳️‍🌈🏳️‍⚧️

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.