用Rust 加持,图像更安全:GNOME 引入 glycin 替代 GdkPixbuf 加载器

195 阅读7分钟

让 GNOME 的 GdkPixbuf 图像加载更安全

一个名为 glycin 的全新图像加载机制已经开发了一段时间,目前已被 GNOME 默认的图片查看器 Loupe 及其他多个应用所使用。由于采用了 Rust 编程语言以及沙箱机制,glycin 相较于现有方案在安全性上具有显著优势。现在,发行版可以将 glycin 的安全特性和更广泛的格式支持引入到其他 GNOME 应用、缩略图生成器以及 GNOME Shell 中,而无需修改现有软件。这一切得益于 GNOME 传统图像加载库 GdkPixbuf 新增的选项 —— 允许其在内部使用 glycin。

默认启用 glycin 的好处

Matthias 已在 GdkPixbuf 中创建了一个使用 glycin 的加载器,从而为整个 GNOME 生态系统带来了 glycin 提供的几乎所有安全优势。

目前,GdkPixbuf 仍默认启用了旧的加载器(即便在 Linux 上也一样)。加载器的优先级是按字母顺序决定的:glycin 会优先于大多数(但不是全部)传统加载器使用。如果一切顺利,未来在构建 Linux 版本时,这些传统加载器将默认禁用。

如果你现在就想让 GdkPixbuf 使用 glycin,可以使用以下构建选项:

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

这些选项要求系统中已安装 libglycin,并在运行时提供 glycin 加载器。由于 GdkPixbuf 的缩略图生成器也被禁用,因此也需安装 glycin 的缩略图生成器。这些组件自 glycin 版本 2.0.alpha.2 起提供,目前已发布。GdkPixbuf 所需版本为 2.43.0,也即将发布。

目前 glycin 尚未提供 SVG 加载器,但未来的 GNOME 开发周期中可能会支持。

我们在 GNOME 49 开发周期的早期发布这些内容,是希望能尽早获得发行版的反馈,并及时发现问题。

为什么要从头开始

GdkPixbuf 作为图像加载库,已经服务 GNOME 超过 25 年。关于我们为什么重新做一套系统,在我 2023 年发布 Loupe 与 glycin 的博客中已有初步解释。

在我开始开发 Loupe 时,GdkPixbuf 的两位维护者之一 Emmanuele 就建议我尽可能避免使用 GdkPixbuf。不久之后,新的 CVE 漏洞再次验证了他的担忧:2023 年 WebP 中被发现了一个安全漏洞,可能导致远程代码执行。

长期以来,负责解析和解码文件格式的库频繁出现内存相关的安全问题。这意味着 GdkPixbuf 所支持的每一种图像格式都可能成为攻击向量。

幸运的是,如今我们拥有了两项重要技术来应对这些问题:

  • 一种内存安全、性能优秀的语言 —— Rust;
  • 以及 Linux 内核提供的优秀沙箱能力。

已知限制

大规模迁移过程中,出现一些尚未解决的问题是正常的。如果你发现其他问题,欢迎反馈。

当前仅支持 Linux

glycin 目前仅能在 Linux 上运行。这是因为 glycin 所依赖的沙箱机制及其与加载器之间的通信方式。目前看来,在 BSD 或 macOS 上或许能保留一部分功能。关于使用的技术细节,可参阅 glycin 的 README 文档。

作为跨平台的通用解决方案,我计划开发一个机制,将加载器编译进主库。这会牺牲沙箱能力和无需重新编译即可扩展格式的优势,但由于加载器是用 Rust 编写的,安全性依然是一次重大提升。如果你有兴趣支持其他平台,欢迎贡献代码。

对于使用 GdkPixbuf 的用户来说,目前不是问题 —— 传统加载器仍将保留,直到 glycin 支持所有平台为止。

不支持沙箱的环境

glycin 默认使用沙箱(sandbox)。通常使用 bwrap(bubblewrap) 创建沙箱;在 Flatpak 中,则使用 flatpak-spawn --sandbox,该命令会调用 Flatpak Portal 创建一个权限更受限的加载器沙箱环境。

然而,有些环境无法创建沙箱。例如,一些 Linux 发行版的构建服务器运行测试时会遇到限制。我们希望未来能在更多场景启用沙箱,因此建议各大发行版确保其测试环境支持沙箱。

否则,如果 glycin 加载器在后台运行,GdkPixbuf 的测试可能会开始失败。

另一个目前无法支持沙箱的环境是 Canonical 的 Snap。尽管 Snap 自身是沙箱环境,但在处理图像加载器或浏览器进程这类可能解析外部数据的子模块时,额外的沙箱仍然有价值。例如,一款应用可能默认有较大的数据访问权限,或会打开敏感文件 —— 这时额外的保护尤为重要。但据我所知,目前 Snap 不支持这种机制。

不支持的图像格式

glycin 原生支持以下格式:

AVIF、BMP、DDS、Farbfeld、QOI、GIF、HEIC、ICO、JPEG、JPEG XL、OpenEXR、PNG、PNM、SVG、TGA、TIFF、WEBP

但许多格式本身包含子格式,glycin 中除 AVIF、HEIC 和 JPEG XL 外,几乎所有加载器都是用 Rust 重写的,这意味着部分边角标准或许还未支持。

更糟的是,有些相机制造商生成的图像是“破损”的,解码器必须兼容这些不合规范的图片。幸运的是,glycin 已在 Loupe 中使用了两年,得到了 image-rszune-image 项目的广泛修复。

不过,对于 TIFF 格式,目前仍有一些已知问题。我们可能在遇到问题时使用旧版 GdkPixbuf 加载器回退,但其他格式仍使用 glycin。如遇到不支持的格式,请告知我们。

超越安全的优势

glycin 不仅更安全,还为图像加载带来了许多增强功能,因此我们计划最终完全替代 GdkPixbuf。这些优点包括:

更多颜色支持

glycin 已支持部分 HDR 图像格式,并准备支持更多。ICC 色彩配置文件统一管理,更加稳健,未来还将支持超出 sRGB 范围的宽色域显示器。

轻松访问元数据

glycin 自动提供 Exif、XMP 及其他元数据信息,支持所有包含元数据的图像格式。

编辑功能

glycin 支持基础图像编辑操作,如裁剪和旋转,并可保留原始图像的元数据与质量。

性能更快

Rust 编写的图像加载器性能通常与 C/汇编版本相当,甚至更快,并且全部使用内存安全代码。在 JPEG 和 PNG 的实际测试中,我们发现 glycin 明显更快。

崩溃保护

即使加载器崩溃,也不会影响主程序。这是因为 glycin 的加载器在独立进程中运行。这些进程的内存也被限制,从而抵御恶意图片导致内存耗尽的攻击。

更多格式支持

glycin 支持以前无法支持的一些格式,如动画 PNG。更重要的是,像 Fedora 这样的发行版将可安全地支持 8 种以上的图片格式,得益于 glycin 带来的新安全保障。

image.png

图示说明:GdkPixbuf(左)与 glycin(右)生成的缩略图差异对比。GdkPixbuf 缺失某些格式支持,或颜色失真、旋转错误、缩放不正确。

致谢

感谢 Michael 撰写本文初稿,感谢所有直接或间接参与此项目的人,特别是 image-rs 与 zune-image(欢迎捐赠)项目的贡献者们,是你们让这一切成为可能!

glycin 提供 C API 的初期工作由 STF 支持。

支持我的工作:[捐赠链接略]。祝大家骄傲月快乐!

如需获取 glycin 项目更多技术资料、构建方法、或支持方式,可访问其 GitHub 仓库及开发者博客。

本文来自 blogs.gnome.org/sophieh/202…