[fuzzing] Fix hb-raster/vector fuzzer issues (#5816)

* [fuzzing] Guard raster/vector create_or_fail results

The raster and vector fuzzers called create_or_fail() and then
immediately dereferenced the returned pointers. Under CIFuzz's
allocation-failure coverage, that turns a valid NULL return into a
harness crash instead of treating it as an out-of-memory case.

Return early after cleaning up when draw or paint context creation
fails. This keeps the fuzzers aligned with the create_or_fail()
contract and avoids null-deref crashes unrelated to library logic.

Tested:
- meson compile -C build hb-raster-fuzzer hb-vector-fuzzer
- build/test/fuzzing/hb-vector-fuzzer test/fuzzing/fonts/TwemojiMozilla.subset.ttf
- build/test/fuzzing/hb-raster-fuzzer test/fuzzing/fonts/TwemojiMozilla.subset.ttf

Assisted-by: OpenAI Codex

* [vector] Avoid OOM leak in paint initialization

hb_vector_paint_ensure_initialized() used group_stack.push() and
then treated the returned pointer as a normal success indicator.
On allocation failure push() returns the Crap() sentinel, so the
subsequent root->alloc(4096) allocated backing storage into the
failure sink and leaked under malloc-failure fuzzing.

Use push_or_fail() instead and only allocate the root body after a
real push succeeds.

Tested:
- build/test/fuzzing/hb-vector-fuzzer test/fuzzing/fonts/TwemojiMozilla.subset.ttf

Assisted-by: OpenAI Codex

* [vector] Avoid double-free on blob creation failure

hb_svg_blob_from_buffer() handed buffer ownership to hb_blob_create()
in the non-recycled path and then ran its own failure cleanup. When
hb_blob_create() failed, it had already invoked the destroy callback,
so the local cleanup freed the same buffer again.

Use hb_blob_create_or_fail() there instead so allocation failure
returns nullptr without triggering the empty-blob fallback and its
conflicting cleanup semantics.

Tested:
- meson compile -C build hb-vector-fuzzer

Assisted-by: OpenAI Codex

* [vector] Avoid double-free in draw blob creation

Mirror the blob-creation failure fix from vector paint in the SVG
draw path. The non-recycled branch handed ownership to
hb_blob_create(), which may already run the destroy callback on
failure, and then fell into local cleanup that freed the same buffer
again.

Switch that branch to hb_blob_create_or_fail() so failure reports
nullptr without conflicting cleanup.

Tested:
- meson compile -C build hb-vector-fuzzer

Assisted-by: OpenAI Codex

* [vector] Fix OOM handling in SVG subset id collection

hb_svg_add_unique_id() used v->push() and tested the returned
pointer for null. hb_vector_t::push() reports allocation failure via
the vector error state and returns the Crap() sentinel, so the old
check did not reliably stop on OOM.

Check v->in_error() after push() before writing into the new slot.

Assisted-by: OpenAI Codex

* [vector] Guard paint render after init failure

hb_vector_paint_render() assumed ensure_initialized() had created
the root group body and indexed group_stack[0] unconditionally.
Under malloc-failure fuzzing, initialization can fail while extents
are already set, leaving group_stack empty and causing a null-deref.

Return nullptr if initialization did not produce the root group.

Assisted-by: OpenAI Codex

* [vector] Propagate paint init failures

hb_vector_paint_ensure_initialized() can fail under malloc-failure
fuzzing, but several paint callbacks assumed the root group existed
immediately afterward and accessed current_body() or group_stack[0].

Make initialization report success and bail out at the callers that
require the root group. Also use push_or_fail() for the extra group
stack entries created by grouping and glyph caching.

Assisted-by: OpenAI Codex

* [raster] Bound SVG path parsing work

hb_raster_svg_parse_path_data() had no complexity cap, unlike the
other new raster SVG subsystems that already bound recursion depth,
attribute count, and gradient stops. Malformed path data could
therefore spend excessive time in parsing and bbox/render work under
fuzzing.

Add a hard limit on parsed path segments and expose it through
hb-limits.hh so the budget is defined alongside the other shared
parser/work limits.

Assisted-by: OpenAI Codex

* [raster] Bound temporary clip mask buffers

hb_raster_paint_push_clip_from_emitter() and the general clip
rectangle path could allocate and clear full-surface temporary A8 clip
masks with no explicit size cap, leading to timeout-scale work on
malformed inputs.

Reuse the raster buffer size policy for these temporary masks as well,
and move that limit into hb-limits.hh so raster images and clip masks
share the same bound.

Assisted-by: OpenAI Codex

* [raster] Reduce buffer size cap

Lower HB_RASTER_MAX_BUFFER_SIZE from 1<<30 to 1<<24 so the shared
raster image / clip-mask limit meaningfully constrains pathological
fuzzer inputs instead of allowing timeout-scale temporary buffers.

Assisted-by: OpenAI Codex
tree: e14c0a3525167bbe99666fc4cdfbc47e4936f5f8
  1. .ci/
  2. .github/
  3. docs/
  4. perf/
  5. src/
  6. subprojects/
  7. test/
  8. util/
  9. .clang-format
  10. .codecov.yml
  11. .editorconfig
  12. AGENTS.md
  13. AUTHORS
  14. BUILD.md
  15. CMakeLists.txt
  16. CONFIG.md
  17. COPYING
  18. harfbuzz.doap
  19. HarfBuzz.jpg
  20. HarfBuzz.png
  21. meson.build
  22. meson_options.txt
  23. NEWS
  24. README.md
  25. README.mingw.md
  26. README.python.md
  27. RELEASING.md
  28. replace-enum-strings.cmake
  29. SECURITY.md
  30. TESTING.md
  31. THANKS
  32. xkcd.png
README.md

HarfBuzz

Linux CI Status macoOS CI Status Windows CI Status OSS-Fuzz Status Coverity Scan Build Status Packaging status OpenSSF Scorecard

HarfBuzz is a text shaping engine. It primarily supports OpenType, but also Apple Advanced Typography. Check “What is HarfBuzz?” chapter in the user manual for more inforamation on what HarfBuzz do and what it doesn’t do.

The canonical source tree and bug trackers are available on github. Both development and user support discussion around HarfBuzz happen on github as well.

For license information, see COPYING.

API stability

The API that comes with hb.h will not change incompatibly. Other, peripheral, headers are more likely to go through minor modifications, but again, we do our best to never change API in an incompatible way. We will never break the ABI.

The API and ABI are stable even across major version number jumps. In fact, current HarfBuzz is API/ABI compatible all the way back to the 0.9.x series. If one day we need to break the API/ABI, that would be called a new library.

As such, we bump the major version number only when we add major new features, the minor version when there is new API, and the micro version when there are bug fixes.

Documentation

For user manual as well as API documentation, check: https://harfbuzz.github.io

Download

Tarball releases of HarfBuzz are available on github releases page. At the same place you will also find Win32/Win64 binary bundles that include libharfbuzz DLL, hb-view.exe, hb-shape.exe, and all dependencies.

Development

For build information, see BUILD.md.

For custom configurations, see CONFIG.md.

For testing and profiling, see TESTING.md.

For using with Python, see README.python.md. There is also uharfbuzz.

For cross-compiling to Windows from Linux or macOS, see README.mingw.md.

To report bugs or submit patches please use github issues and pull-requests.

Developer documents

To get a better idea of where HarfBuzz stands in the text rendering stack you may want to read State of Text Rendering 2024. Here are a few presentation slides about HarfBuzz at the Internationalization and Unicode Conference over the years:

More presentations and papers are available on behdad's website. In particular, the following studies are relevant to HarfBuzz development:

Name

HarfBuzz (حرف‌باز) is the literal Persian translation of “OpenType”, transliterated using the Latin script. It also means “talkative” or “glib” (also a nod to the GNOME project where HarfBuzz originates from).

Background: Originally there was this font format called TrueType. People and companies started calling their type engines all things ending in Type: FreeType, CoolType, ClearType, etc. And then came OpenType, which is the successor of TrueType. So, for my OpenType implementation, I decided to stick with the concept but use the Persian translation. Which is fitting given that Persian is written in the Arabic script, and OpenType is an extension of TrueType that adds support for complex script rendering, and HarfBuzz is an implementation of OpenType complex text shaping.

Users

HarfBuzz is used in Android, Chrome, ChromeOS, Firefox, GNOME, GTK+, KDE, Qt, LibreOffice, OpenJDK, XeTeX, Scribus, PlayStation, Microsoft Edge, Amazon Kindle, Adobe Photoshop, Illustrator, InDesign, Godot Engine, Unreal Engine, QuarkXPress, Figma, and other places.

Distribution

Packaging status