)]}'
{
  "commit": "d2ecd9b37065153b38d77f09b35ef4da2bb0ffdf",
  "tree": "e14c0a3525167bbe99666fc4cdfbc47e4936f5f8",
  "parents": [
    "8339a9b480291103fe5a8d030e25f90a8dca1069",
    "331ef99cd8b2b0a36ea256cfb082b64f19a2196f"
  ],
  "author": {
    "name": "Behdad Esfahbod",
    "email": "behdad@behdad.org",
    "time": "Tue Mar 10 08:55:44 2026 -0600"
  },
  "committer": {
    "name": "GitHub",
    "email": "noreply@github.com",
    "time": "Tue Mar 10 08:55:44 2026 -0600"
  },
  "message": "[fuzzing] Fix hb-raster/vector fuzzer issues (#5816)\n\n* [fuzzing] Guard raster/vector create_or_fail results\n\nThe raster and vector fuzzers called create_or_fail() and then\nimmediately dereferenced the returned pointers. Under CIFuzz\u0027s\nallocation-failure coverage, that turns a valid NULL return into a\nharness crash instead of treating it as an out-of-memory case.\n\nReturn early after cleaning up when draw or paint context creation\nfails. This keeps the fuzzers aligned with the create_or_fail()\ncontract and avoids null-deref crashes unrelated to library logic.\n\nTested:\n- meson compile -C build hb-raster-fuzzer hb-vector-fuzzer\n- build/test/fuzzing/hb-vector-fuzzer test/fuzzing/fonts/TwemojiMozilla.subset.ttf\n- build/test/fuzzing/hb-raster-fuzzer test/fuzzing/fonts/TwemojiMozilla.subset.ttf\n\nAssisted-by: OpenAI Codex\n\n* [vector] Avoid OOM leak in paint initialization\n\nhb_vector_paint_ensure_initialized() used group_stack.push() and\nthen treated the returned pointer as a normal success indicator.\nOn allocation failure push() returns the Crap() sentinel, so the\nsubsequent root-\u003ealloc(4096) allocated backing storage into the\nfailure sink and leaked under malloc-failure fuzzing.\n\nUse push_or_fail() instead and only allocate the root body after a\nreal push succeeds.\n\nTested:\n- build/test/fuzzing/hb-vector-fuzzer test/fuzzing/fonts/TwemojiMozilla.subset.ttf\n\nAssisted-by: OpenAI Codex\n\n* [vector] Avoid double-free on blob creation failure\n\nhb_svg_blob_from_buffer() handed buffer ownership to hb_blob_create()\nin the non-recycled path and then ran its own failure cleanup. When\nhb_blob_create() failed, it had already invoked the destroy callback,\nso the local cleanup freed the same buffer again.\n\nUse hb_blob_create_or_fail() there instead so allocation failure\nreturns nullptr without triggering the empty-blob fallback and its\nconflicting cleanup semantics.\n\nTested:\n- meson compile -C build hb-vector-fuzzer\n\nAssisted-by: OpenAI Codex\n\n* [vector] Avoid double-free in draw blob creation\n\nMirror the blob-creation failure fix from vector paint in the SVG\ndraw path. The non-recycled branch handed ownership to\nhb_blob_create(), which may already run the destroy callback on\nfailure, and then fell into local cleanup that freed the same buffer\nagain.\n\nSwitch that branch to hb_blob_create_or_fail() so failure reports\nnullptr without conflicting cleanup.\n\nTested:\n- meson compile -C build hb-vector-fuzzer\n\nAssisted-by: OpenAI Codex\n\n* [vector] Fix OOM handling in SVG subset id collection\n\nhb_svg_add_unique_id() used v-\u003epush() and tested the returned\npointer for null. hb_vector_t::push() reports allocation failure via\nthe vector error state and returns the Crap() sentinel, so the old\ncheck did not reliably stop on OOM.\n\nCheck v-\u003ein_error() after push() before writing into the new slot.\n\nAssisted-by: OpenAI Codex\n\n* [vector] Guard paint render after init failure\n\nhb_vector_paint_render() assumed ensure_initialized() had created\nthe root group body and indexed group_stack[0] unconditionally.\nUnder malloc-failure fuzzing, initialization can fail while extents\nare already set, leaving group_stack empty and causing a null-deref.\n\nReturn nullptr if initialization did not produce the root group.\n\nAssisted-by: OpenAI Codex\n\n* [vector] Propagate paint init failures\n\nhb_vector_paint_ensure_initialized() can fail under malloc-failure\nfuzzing, but several paint callbacks assumed the root group existed\nimmediately afterward and accessed current_body() or group_stack[0].\n\nMake initialization report success and bail out at the callers that\nrequire the root group. Also use push_or_fail() for the extra group\nstack entries created by grouping and glyph caching.\n\nAssisted-by: OpenAI Codex\n\n* [raster] Bound SVG path parsing work\n\nhb_raster_svg_parse_path_data() had no complexity cap, unlike the\nother new raster SVG subsystems that already bound recursion depth,\nattribute count, and gradient stops. Malformed path data could\ntherefore spend excessive time in parsing and bbox/render work under\nfuzzing.\n\nAdd a hard limit on parsed path segments and expose it through\nhb-limits.hh so the budget is defined alongside the other shared\nparser/work limits.\n\nAssisted-by: OpenAI Codex\n\n* [raster] Bound temporary clip mask buffers\n\nhb_raster_paint_push_clip_from_emitter() and the general clip\nrectangle path could allocate and clear full-surface temporary A8 clip\nmasks with no explicit size cap, leading to timeout-scale work on\nmalformed inputs.\n\nReuse the raster buffer size policy for these temporary masks as well,\nand move that limit into hb-limits.hh so raster images and clip masks\nshare the same bound.\n\nAssisted-by: OpenAI Codex\n\n* [raster] Reduce buffer size cap\n\nLower HB_RASTER_MAX_BUFFER_SIZE from 1\u003c\u003c30 to 1\u003c\u003c24 so the shared\nraster image / clip-mask limit meaningfully constrains pathological\nfuzzer inputs instead of allowing timeout-scale temporary buffers.\n\nAssisted-by: OpenAI Codex",
  "tree_diff": []
}
