[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
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.
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.
For user manual as well as API documentation, check: https://harfbuzz.github.io
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.
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.
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:
hb-decyclerhb-iterhb-ft >h_advance functionHarfBuzz (حرفباز) 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.
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.