Skip to content

[CFF] Limit total charstring ops that a single glyph can execute - #330

Merged
khaledhosny merged 2 commits into
mainfrom
cff-ops-limit
Oct 13, 2026
Merged

khaledhosny merged 2 commits into
mainfrom
cff-ops-limit

Conversation

@jfkthame

@jfkthame jfkthame commented Sep 18, 2026 •

Copy link
Copy Markdown
Collaborator

[edit: the first change mentioned here is incorrect and has been dropped]

This makes two changes to address a performance cliff (potential DoS) when a CFF charstring calls subroutines many times (and at several levels of recursion) to result in executing many millions of charstring ops.

First, we track subroutines that have already been executed successfully, and don't re-execute them if used multiple times. This greatly improves performance on such a font.

Second, we impose a (somewhat arbitrary) limit on the total number of ops that a single glyph's charstring is allowed to execute, and reject the table if this is exceeded. This is desirable because even after the first commit here, which addresses the exponentially-slow performance in OTS, such a font will also have unacceptable performance in any downstream rasterizer that attempts to use it. I don't think there is any legitimate use case for a charstring that expands to many millions of ops.

A couple of test fonts are added: one that fails the total op limit, and a similar but less extreme one that passes.

(Without the patches here, the fonts both pass, but the "bad" one takes many seconds to process.)

@jfkthame

Copy link
Copy Markdown
Collaborator Author

Hmm, this doesn't actually work reliably, because of subrs making changes to the state of the stack. Reconsidering...

@jfkthame

Copy link
Copy Markdown
Collaborator Author

OK, the first change described here has been dropped as being incorrect, so now we just impose a charstring ops execution limit but don't attempt to skip already-seen subroutine execution.

@jfkthame jfkthame changed the title [CFF] Avoid re-executing the same subrs; limit total ops [CFF] Limit total charstring ops that a single glyph can execute Sep 18, 2026
@jfkthame

Copy link
Copy Markdown
Collaborator Author

@khaledhosny Gentle ping re this one... are you ok with limiting charstring ops (to a huge value)?

@khaledhosny

Copy link
Copy Markdown
Owner

Same suggestion as glyf table composites. If we can drop the offending charstring and keep the rest of the table, that would be ideal.

@jfkthame

Copy link
Copy Markdown
Collaborator Author

I thought you'd say that. :-) Unfortunately, the CFF case is substantially harder. Unlike glyf, we really don't have any support for modifying its contents at present; we just validate, and if accepted, output the original data unchanged.

@khaledhosny

Copy link
Copy Markdown
Owner

I was afraid that would be the case. I guess we will have to live with that.

@khaledhosny
khaledhosny merged commit 6540e90 into main Oct 13, 2026
5 checks passed
@khaledhosny
khaledhosny deleted the cff-ops-limit branch October 13, 2026 11:40
calixteman added a commit to mozilla/pdf.js that referenced this pull request Oct 23, 2026
Bound per-glyph tokens and operand stacks. Also limit Type1 subroutine
nesting and converted charstring length.

See khaledhosny/ots#330.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants