Title: [Bug]: Type1Font allocates /Subrs array from declared count before validating the body · Issue #31962 · matplotlib/matplotlib · GitHub
Open Graph Title: [Bug]: Type1Font allocates /Subrs array from declared count before validating the body · Issue #31962 · matplotlib/matplotlib
X Title: [Bug]: Type1Font allocates /Subrs array from declared count before validating the body · Issue #31962 · matplotlib/matplotlib
Description: Bug summary The Type1 font parser trusts the declared /Subrs count and allocates a Python list of that size before validating that the following body actually contains that many subroutine definitions. A compact malformed Type1 font can ...
Open Graph Description: Bug summary The Type1 font parser trusts the declared /Subrs count and allocates a Python list of that size before validating that the following body actually contains that many subroutine definiti...
X Description: Bug summary The Type1 font parser trusts the declared /Subrs count and allocates a Python list of that size before validating that the following body actually contains that many subroutine definiti...
Opengraph URL: https://github.com/matplotlib/matplotlib/issues/31962
X: @github
Domain: github.com
{"@context":"https://schema.org","@type":"DiscussionForumPosting","headline":"[Bug]: Type1Font allocates /Subrs array from declared count before validating the body","articleBody":"### Bug summary\n\nThe Type1 font parser trusts the declared `/Subrs` count and allocates a Python list of that size before validating that the following body actually contains that many subroutine definitions. A compact malformed Type1 font can therefore cause avoidable large allocation before parsing fails.\n\n\n### Code for reproduction\n\n```Python\nimport pathlib\nimport resource\nimport tempfile\nimport time\n\nfrom matplotlib._type1font import Type1Font\n\n\ndef main():\n subrs_count = 5_000_000\n\n plaintext = (\n b\"/FontName /LargeSubrsCount def\\n\"\n b\"/FontBBox [0 0 1 1] def\\n\"\n + f\"/Subrs {subrs_count} array\\n\".encode(\"ascii\")\n )\n\n encrypted_hex = Type1Font._encrypt(plaintext, \"eexec\").hex().encode(\"ascii\")\n pfa = (\n b\"%!PS-AdobeFont-1.0: LargeSubrsCount 001.000\\n\"\n b\"eexec\\n\"\n + encrypted_hex\n + b\"\\n\"\n + (b\"0\" * 512)\n + b\"\\ncleartomark\\n\"\n )\n\n font_path = pathlib.Path(tempfile.mkdtemp()) / \"large-subrs-count.pfa\"\n font_path.write_bytes(pfa)\n\n rss_before = resource.getrusage(resource.RUSAGE_SELF).ru_maxrss\n start = time.time()\n try:\n Type1Font(str(font_path))\n except StopIteration:\n parse_result = \"body_rejected_after_allocation\"\n except MemoryError:\n parse_result = \"memory_error\"\n else:\n raise RuntimeError(\"malformed font unexpectedly parsed\")\n\n elapsed = time.time() - start\n rss_after = resource.getrusage(resource.RUSAGE_SELF).ru_maxrss\n\n print(f\"subrs_count={subrs_count}\")\n print(f\"parse_result={parse_result}\")\n print(f\"elapsed_seconds={elapsed:.3f}\")\n print(f\"rss_before_kb={rss_before}\")\n print(f\"rss_after_kb={rss_after}\")\n print(f\"rss_delta_kb={rss_after - rss_before}\")\n\n\nif __name__ == \"__main__\":\n main()\n```\n\n### Actual outcome\n\nIn a source-checkout verification run, a compact generated PFA declaring `/Subrs 5000000 array` caused the parser to allocate based on the declared count before the malformed body was rejected:\n\n```text\nsubrs_count=5000000\nparse_result=body_rejected_after_allocation\nelapsed_seconds=0.009\nrss_before_kb=45168\nrss_after_kb=84208\nrss_delta_kb=39040\n```\n\n\n### Expected outcome\n\nMatplotlib should validate the `/Subrs` body structure before allocating a count-sized list, or enforce a reasonable maximum `/Subrs` count before allocation.\n\n### Additional information\n\n\nThe relevant parser path is `lib/matplotlib/_type1font.py`:\n\n```python\ndef _parse_subrs(self, tokens, _data):\n count_token = next(tokens)\n ...\n count = count_token.value()\n array = [None] * count\n next(t for t in tokens if t.is_keyword('array'))\n```\n\nThe allocation happens immediately after reading the count token. The parser only confirms the `array` keyword and the following `dup` entries after the list has already been allocated.\n\nThis report is intended as a robustness bug, not as a security vulnerability report. It requires processing an untrusted Type1 PFA/PFB font.\n\n\n### Operating system\n\nLinux\n\n### Matplotlib Version\n\nmain branch source checkout; reproduced on commit `5a33910eb7827d8a6188ff42bc788951c015ff48`; same parser shape was still present on current `main` commit `996f4e877f6a7a50a68e00879183b8db8761461d`.\n\n### Matplotlib Backend\n\nNot backend-specific\n\n### Python version\n\nPython 3.12\n\n### Jupyter version\n\nNot applicable\n\n### Installation\n\ngit checkout","author":{"url":"https://github.com/Str1ckl4nd","@type":"Person","name":"Str1ckl4nd"},"datePublished":"2026-06-27T13:45:49.000Z","interactionStatistic":{"@type":"InteractionCounter","interactionType":"https://schema.org/CommentAction","userInteractionCount":2},"url":"https://github.com/31962/matplotlib/issues/31962"}
| route-pattern | /_view_fragments/issues/show/:user_id/:repository/:id/issue_layout(.:format) |
| route-controller | voltron_issues_fragments |
| route-action | issue_layout |
| fetch-nonce | v2:bb3709ff-c76c-846b-fda9-b156d29bbb12 |
| current-catalog-service-hash | 81bb79d38c15960b92d99bca9288a9108c7a47b18f2423d0f6438c5b7bcd2114 |
| request-id | C22A:35213E:1113FDF:174EF08:6A62DD89 |
| html-safe-nonce | 8d06f79b6a0b22677369063e311d77ce65395d312f408e16b3a36a3783f9daa4 |
| visitor-payload | eyJyZWZlcnJlciI6IiIsInJlcXVlc3RfaWQiOiJDMjJBOjM1MjEzRToxMTEzRkRGOjE3NEVGMDg6NkE2MkREODkiLCJ2aXNpdG9yX2lkIjoiMjk3NzEzNDE0NTgyMTY2MjYwMSIsInJlZ2lvbl9lZGdlIjoiaWFkIiwicmVnaW9uX3JlbmRlciI6ImlhZCJ9 |
| visitor-hmac | 0aa4b6c3db5617f5b09614c7f58d6221b3b912eafc2106a9a2e7f8e36653d863 |
| hovercard-subject-tag | issue:4758543490 |
| github-keyboard-shortcuts | repository,issues,copilot |
| google-site-verification | Apib7-x98H0j5cPqHWwSMm6dNU4GmODRoqxLiDzdx9I |
| octolytics-url | https://collector.github.com/github/collect |
| analytics-location | / |
| fb:app_id | 1401488693436528 |
| apple-itunes-app | app-id=1477376905, app-argument=https://github.com/_view_fragments/issues/show/matplotlib/matplotlib/31962/issue_layout |
| twitter:image | https://opengraph.githubassets.com/6ae6a0ba72f029730bf1dd0cc6b0a50ed61fc99bd615e14893c699c0f43e94bf/matplotlib/matplotlib/issues/31962 |
| twitter:card | summary_large_image |
| og:image | https://opengraph.githubassets.com/6ae6a0ba72f029730bf1dd0cc6b0a50ed61fc99bd615e14893c699c0f43e94bf/matplotlib/matplotlib/issues/31962 |
| og:image:alt | Bug summary The Type1 font parser trusts the declared /Subrs count and allocates a Python list of that size before validating that the following body actually contains that many subroutine definiti... |
| og:image:width | 1200 |
| og:image:height | 600 |
| og:site_name | GitHub |
| og:type | object |
| og:author:username | Str1ckl4nd |
| hostname | github.com |
| expected-hostname | github.com |
| None | df33b1b61ee7b9a0af988199bfc3503c9c1acafb1f1d40e1f140ea7c84f890dd |
| turbo-cache-control | no-preview |
| go-import | github.com/matplotlib/matplotlib git https://github.com/matplotlib/matplotlib.git |
| octolytics-dimension-user_id | 215947 |
| octolytics-dimension-user_login | matplotlib |
| octolytics-dimension-repository_id | 1385122 |
| octolytics-dimension-repository_nwo | matplotlib/matplotlib |
| octolytics-dimension-repository_public | true |
| octolytics-dimension-repository_is_fork | false |
| octolytics-dimension-repository_network_root_id | 1385122 |
| octolytics-dimension-repository_network_root_nwo | matplotlib/matplotlib |
| turbo-body-classes | logged-out env-production page-responsive |
| disable-turbo | false |
| browser-stats-url | https://api.github.com/_private/browser/stats |
| browser-errors-url | https://api.github.com/_private/browser/errors |
| release | d41cd1bdb290013455c0ac430fa755621733f5eb |
| ui-target | canary-2 |
| theme-color | #1e2327 |
| color-scheme | light dark |
Links:
Viewport: width=device-width