Fedora Account System
Red Hat Associate
Red Hat Customer
AI_ONLY_REPORT package: ghostscript-10.02.1-16.el10_0 ------ Summary: TrueType Outliner Heap Buffer Overflow via nPointsTotal Underflow: inconsistent retry rollback in the TrueType outliner can drive `nPointsTotal` negative, weaken point-capacity checks, and permit a heap out-of-bounds write when parsing crafted fonts. Requirements to exploit: An attacker must supply a crafted TrueType font, or a document embedding one, and cause `ghostscript-10.02.1-16.el10_0` to render it through the affected outliner path with TrueType instruction execution enabled. No prior privileges are required; common interactive workflows require the victim to open or render the content. Component affected: `ghostscript-10.02.1-16.el10_0`, TrueType outliner in `base/ttfmain.c`, function `ttfOutliner__BuildGlyphOutlineAux` Version affected: `ghostscript-10.02.1-16.el10_0` Patch available: no released package fix established; proposed patch included below Version fixed: unknown Upstream coordination: Not notified. CVSS: CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H - 8.8 (HIGH) AV:N - A malicious font or a document embedding it can be delivered through normal remote content channels; strictly local-only deployments may score this lower environmentally. AC:L - The trigger requires crafted glyph data, but the necessary conditions are within attacker-controlled input rather than relying on unusual target state. PR:N - No authentication or prior access is needed. UI:R - In common interactive use, a victim must open or render attacker-supplied content. S:U - The corruption occurs within the Ghostscript process. C:H - A heap out-of-bounds write in native parsing code can plausibly expose process memory if exploited further. I:H - The same primitive can plausibly modify process memory. A:H - The available evidence directly supports severe process disruption, including crash. Impact: Important. This is a native heap out-of-bounds write in a content-parsing path reachable from attacker-controlled font data. It is not Critical because exploitation depends on crafted content being processed and is not a wormable, no-interaction compromise. It exceeds Moderate because the bug yields direct memory corruption and can at least cause denial of service in untrusted document-processing workflows, with plausible confidentiality and integrity impact if successfully exploited further. Embargo: yes Reason: The issue is a content-driven memory corruption flaw in a document/font parsing path, and no released fix is established yet. Acknowledgement: Aisle Research Vulnerability Details: In `ttfOutliner__BuildGlyphOutlineAux`, `self->nPointsTotal` is used as cumulative accounting for the shared point buffers in `exec->pts`. The simple-glyph path increments this counter by `nPoints`, but the retry path entered after `fBadInstruction` subtracts `nPoints + 2` before retrying without hinting. Because the subtraction is larger than the earlier addition, repeated retry-triggering glyphs can drive the signed counter negative. Once that happens, the guard `self->nPointsTotal + nPoints + 2 > exec->n_points` can undercount actual occupancy and admit writes beyond the intended end of the point arrays. ```c nPoints = gOutline->pointCount = endPoints[gOutline->contourCount - 1] + 1; if (nPoints < 0 || self->nPointsTotal + nPoints + 2 > exec->n_points) { error = fBadFontData; goto ex; } ... { short coord = (self->bVertical ? 0 : sideBearing - subglyph.bbox.xMin); F26Dot6* x = pts->org_x; onCurve = pts->touch; while (onCurve < stop) { if ((flag = *onCurve++) & XSHORT) { if (flag & SHORT_X_IS_POS) coord += ttfReader__Byte(r); else coord -= ttfReader__Byte(r); } else if (!(flag & NEXT_X_IS_ZERO)) coord += ttfReader__Short(r); *x++ = Scale_X(&exec->metrics, coord); } } ... self->nContoursTotal += gOutline->contourCount; self->nPointsTotal += nPoints; ... if (error == fBadInstruction && execute_bytecode) { /* reset a load of stuff so we can try again without hinting */ exec = pFont->exec; pts = &exec->pts; usage = tti->usage + tti->usage_top; glyph = NULL; self->nPointsTotal -= (nPoints + 2); nPoints = 0; self->nContoursTotal = gOutline>contourCount; error = fNoError; execute_bytecode = false; r->Seek(r, nPosBeg); goto retry; } ``` The composite-glyph hinting path makes this worse. In that block, `nPoints` is set to `gOutline->pointCount + 2` before `Context_Run(exec, FALSE)`, but `self->nPointsTotal` is not incremented there. If that path also falls into `fBadInstruction`, the same rollback subtracts counts that were not added in that frame, increasing the negative drift further. Composite loading also advances the shared `pts` pointers by the number of points already stored before descending into subglyphs, so inaccurate cumulative accounting directly affects where later point writes land. A standalone PoC font and sanitizer trace are not included here, so this report does not claim a demonstrated end-to-end code-execution exploit. The code-level mismatch is sufficient to support a real heap out-of-bounds write condition, with denial of service as the minimum likely impact. Steps to reproduce: 1. Build `ghostscript-10.02.1-16.el10_0` with sanitizers and debug symbols, for example `-fsanitize=address,undefined -fno-omit-frame-pointer -O1 -g`. 2. Prepare a crafted TrueType font containing composite glyphs and glyph bytecode that triggers interpreter errors in the `TT_Err_Invalid_Opcode` to `TT_Err_Invalid_Displacement` range so that `error` becomes `fBadInstruction`. 3. Ensure cumulative point usage is close to `exec->n_points` so the capacity check is tight. 4. Render content that exercises the crafted glyph through normal Ghostscript font processing. 5. Set breakpoints or watchpoints at the retry block and the capacity gate, and watch `self->nPointsTotal` for negative drift before the check executes. 6. Continue execution and observe a heap-buffer-overflow in or after the point population loops writing `pts->org_x` and `pts->org_y`. Mitigation: Until a fix is available, avoid processing untrusted documents or standalone fonts through the affected TrueType path where possible. If the deployment exposes a way to disable TrueType instruction execution, doing so avoids the `fBadInstruction` retry path described here. Running Ghostscript in a restrictive sandbox reduces blast radius but does not remove the underlying bug. Proposed Fix: Restore the counters to their saved pre-call state on retry instead of applying arithmetic rollback with mismatched deltas. ```diff diff --git a/base/ttfmain.c b/base/ttfmain.c — a/base/ttfmain.c +++ b/base/ttfmain.c @@ -500,6 +500,8 @@ static FontError ttfOutliner__BuildGlyphOutlineAux(ttfOutliner *self, int glyphIndex, const byte *glyph = NULL; int glyph_size; bool execute_bytecode = true; + int nPointsTotalSaved = self->nPointsTotal; + int nContoursTotalSaved = self->nContoursTotal; int nPoints = 0; @@ -861,8 +863,8 @@ ex:; pts = &exec->pts; usage = tti->usage + tti->usage_top; glyph = NULL; self->nPointsTotal -= (nPoints + 2); + self->nPointsTotal = nPointsTotalSaved; nPoints = 0; self->nContoursTotal = gOutline>contourCount; + self->nContoursTotal = nContoursTotalSaved; error = fNoError; execute_bytecode = false; r->Seek(r, nPosBeg); ``` ------ This report was generated using AI technology. Always review AI-generated content prior to use
Tracker filed for rhel-10.3: https://issues.redhat.com/browse/RHEL-189163