Skip to content

Cap total component visits in glyf/gvar composite outlining - #224

Open
scadastrangelove wants to merge 1 commit into
harfbuzz:mainfrom
scadastrangelove:fix/glyf-gvar-component-visit-budget
Open

Cap total component visits in glyf/gvar composite outlining#224
scadastrangelove wants to merge 1 commit into
harfbuzz:mainfrom
scadastrangelove:fix/glyf-gvar-component-visit-budget

Conversation

@scadastrangelove

Copy link
Copy Markdown

Closes #220.

Adds glyf::MAX_COMPONENT_VISITS: u32 = 100_000 and threads a budget: &mut u32 parameter
through outline_impl (decremented once per call, checked_subNone on exhaustion aborts
the whole traversal same as the existing depth-cap None) and through
gvar::outline_var_impl (same mechanism, reusing glyf::MAX_COMPONENT_VISITS). Both public
call sites (glyf::Table::outline, gvar::Table::outline) initialize a fresh budget per
top-level call.

This is deliberately a total node-visit budget, not a depth or branching-factor cap — it's
the shared-subtree reuse that defeats MAX_COMPONENTS, not depth itself, so the fix needs to
bound total work rather than tree shape.

Testing

  • Full suite green: cargo test --all-features --release — 148/149 (pre-existing unrelated
    failure, see Guard CFF2 BLEND operator against empty argument stack #222 for detail).
  • The depth=18/branching=3 case (16.8s before) now completes in ~3ms; depth=20/branching=3
    (previously didn't complete in 20s) now completes in ~2.5ms. Both return the same outcome as
    before (this glyph's outline is empty either way) — only the cost changed.
  • Measured against the real-font baseline in the linked issue (DejaVu Sans, 6253 glyphs,
    8.66ms/6253 glyphs): the capped worst case is now well within the same order of magnitude as
    legitimate composite-glyph parsing, not 13,600× beyond it.

Discovered by the rust-in-peace security pipeline.

MAX_COMPONENTS bounds the depth of a composite-glyph chain, but nothing
bounds how many times a shared component-glyph subtree can be revisited
via sibling components at the same level. A composite glyph whose
components all reference one shared child glyph forces branching^depth
calls to outline_impl while the depth cap is never reached, needing only
depth+1 distinct glyphs to construct. The identical pattern exists in
gvar::outline_var_impl.

Adds glyf::MAX_COMPONENT_VISITS = 100_000 and threads a total-visit
budget through both outline_impl and outline_var_impl, independent of
depth or branching factor.

Discovered by the rust-in-peace security pipeline
(https://github.com/scadastrangelove/rust-in-peace/).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant