Skip to content

fix: reject fonts with reserved bit 7 in glyf simple glyph flags#13144

Open
FurryR wants to merge 1 commit into
warpdotdev:masterfrom
FurryR:fix-cjk-render
Open

fix: reject fonts with reserved bit 7 in glyf simple glyph flags#13144
FurryR wants to merge 1 commit into
warpdotdev:masterfrom
FurryR:fix-cjk-render

Conversation

@FurryR

@FurryR FurryR commented Jun 28, 2026

Copy link
Copy Markdown

Description

Reject fonts with reserved bit 7 set on simple glyph coordinate flags in the glyf table. Some Linux fonts (notably DroidSansFallback.ttf from ttf-droid) have this corruption — skrifa/swash correctly enforces the spec and rejects all outlines, causing CJK glyphs to render as .notdef squares.
Detection runs at font-load time using fontdb::with_face_data (reuses fontdb's existing mmap; zero extra I/O for healthy fonts). Corrupted faces are removed from the database so they can never be matched by future fallback queries.

Linked Issue

  • The linked issue is labeled ready-to-spec or ready-to-implement.
  • Where appropriate, screenshots or a short video of the implementation are included below (especially for user-visible or UI changes).

Closes #9372.

Testing

  • I have manually tested my changes locally with ./script/run

Manual verification:

  1. Confirmed has_reserved_bit7_in_glyph_flags returns true for DroidSansFallback.ttf and DroidSansFallbackFull.ttf, false for DroidSansFallbackLegacy.ttf.
  2. Before fix: CJK characters routed to DroidSansFallback.ttf render as empty.
  3. After fix: same characters render correctly — the corrupted font is skipped and fontconfig's next fallback is used.

We can actually use DroidSansFallback.ttf but it requires some hacky patching. If that is allowed then I will refactor this fix.

Agent Mode

  • Warp Agent Mode - This PR was created via Warp's AI Agent Mode

Changelog Entries for Stable

CHANGELOG-BUG-FIX: Reject fonts with corrupted glyf table (reserved bit 7 on simple glyph flags) that caused CJK glyphs to render as .notdef squares on Linux

DroidSansFallback.ttf (and Full variant) has reserved bit 7 set on
19959/19961 simple glyph coordinate flags. While ttf_parser tolerates
this, skrifa/swash enforces the spec and rejects all outlines, causing
CJK glyphs to render as .notdef squares.

Reuse fontdb's mmap'd data to check for this corruption when loading
file-based fonts. Corrupted fonts are removed from the database
immediately so they cannot be matched by future font fallback queries.
Non-corrupted fonts incur zero extra I/O.

Signed-off-by: FurryR <awathefox@gmail.com>
@cla-bot cla-bot Bot added the cla-signed label Jun 28, 2026
@github-actions github-actions Bot added the external-contributor Indicates that a PR has been opened by someone outside the Warp team. label Jun 28, 2026
@oz-for-oss

oz-for-oss Bot commented Jun 28, 2026

Copy link
Copy Markdown
Contributor

@FurryR

I'm starting a first review of this pull request.

You can view the conversation on Warp.

I completed the review and no human review was requested for this pull request.

Comment /oz-review on this pull request to retrigger a review (up to 3 times on the same pull request).

Powered by Oz

@oz-for-oss oz-for-oss Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overview

This PR adds a font-load-time check that removes file-backed fonts whose simple glyf flags use reserved bit 7 so CJK fallback can skip corrupted Linux fonts.

Concerns

  • The corruption check only inspects the first face returned by fontdb, so font collections can still load a corrupted requested face when it is not first.
  • The detector samples a small fixed set of glyphs and checks only the first flag byte in each sampled glyph, so it can miss fonts that still contain invalid simple-glyph flags.
  • This is a user-visible rendering fix, but the PR description has no screenshot or recording demonstrating CJK rendering before/after as required for user-visible changes.

Verdict

Found: 0 critical, 3 important, 0 suggestions

Request changes

Comment /oz-review on this pull request to retrigger a review (up to 3 times on the same pull request).

Powered by Oz

// Corrupted fonts are removed from the database immediately so they
// cannot be matched by future font fallback queries.
if let Some(ref path) = pre_loaded_path {
if let Some(&first_id) = fontdb_ids.first() {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ [IMPORTANT] load_font_source can return multiple faces for a collection, but this checks only first_id; if the requested face is later in the collection and corrupted, it remains loaded and can still be selected. Check each loaded face (or at least the requested index) and remove only the corrupted faces.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice finding. I will fix it later! The final logic would be iterating through font faces until finding a valid one.

if flag_offset >= offe || flag_offset >= glyf_data.len() {
continue;
}
if glyf_data[flag_offset] & 0x80 != 0 {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ [IMPORTANT] This only checks the first flag byte of each sampled glyph, but reserved bit 7 can appear in any flag, including repeated flags and glyphs outside the fixed sample. Parse the full flag stream for every simple glyph before deciding the font is safe.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we parse full flag stream, the performance would be possibly degraded. Since the check already worked for the font, is it better to keep it?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cla-signed external-contributor Indicates that a PR has been opened by someone outside the Warp team.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Font rendering fails again

1 participant