Fedora Account System
Red Hat Associate
Red Hat Customer
AI_ONLY_REPORT package: ghostscript-10.02.1-16.el10_0 ------ Summary: Out-of-bounds Read in shade_next_color via Integer Overflow of Indexed Color Index: a crafted PDF or PostScript mesh shading can force an oversized Indexed color value that bypasses bounds checks and reads past the palette buffer, causing limited memory disclosure or a crash. Requirements to exploit: An attacker must cause Ghostscript to process a crafted PDF or PostScript file containing a mesh shading that uses an `Indexed` color space, attacker-controlled `Decode` values, and a sufficiently large decoded color component. No authentication or local access is required, but the target must process the file with Ghostscript. Sanitizer-enabled builds make the invalid read easy to observe; on non-sanitized builds, the exact manifestation depends on compiler/architecture handling of out-of-range float-to-int conversion and surrounding memory layout. Component affected: `ghostscript-10.02.1-16.el10_0`: mesh shading handling for `Indexed` color spaces in `base/gxshade.c` (`shade_next_color`) and `base/gscolor2.c` (`gs_cspace_indexed_lookup`) 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:L/I:N/A:H - 7.1 (HIGH) AV:N - A remote attacker can deliver a crafted PDF or PostScript file to a target that invokes Ghostscript. AC:L - The attack requires only a crafted mesh shading with oversized `Decode` values; no race or secondary condition is required beyond normal parsing. PR:N - No privileges are required. UI:R - The target must be induced to open or otherwise process the crafted file. S:U - The impact remains within the Ghostscript process handling the document. C:L - The out-of-bounds lookup reads adjacent heap bytes and copies them into color component values, creating a limited disclosure path if rendered output is observable. I:N - No out-of-bounds write or data modification primitive is demonstrated. A:H - The out-of-bounds read can terminate the Ghostscript process handling the document. Impact: Important. This issue is not Critical because it requires the target to process a crafted document and no code execution or privilege escalation is shown. It still fits Important because a remote attacker can use normal document parsing to trigger a denial of service in the Ghostscript process, and the out-of-bounds read may expose a limited amount of adjacent heap data through rendered color values. Embargo: no Reason: The currently demonstrated impact is an out-of-bounds read with crash or limited disclosure during document processing rather than code execution, and the fix is a narrow bounds-validation change. Acknowledgement: Aisle Research Vulnerability Details: In `ghostscript-10.02.1-16.el10_0`, `shade_next_color` converts a decoded floating-point color value to `int` before the value is fully range-checked: ```c if (code < 0) return code; if (cf < 0) return_error(gs_error_rangecheck); ci = (int)cf; if (ci >= gs_cspace_indexed_num_entries(pcs)) return_error(gs_error_rangecheck); code = gs_cspace_indexed_lookup(pcs, ci, &cc); ``` A large positive `cf` can exceed the range of `int`. On common builds this can produce a negative `ci`; in C, the precise result of an out-of-range float-to-int conversion is implementation-defined or undefined, so the exact manifestation is build-dependent. Because the code rejects only negative `cf` values and only checks `ci` against the upper bound, a negative `ci` can pass validation and reach the indexed lookup path. The indexed lookup then uses the unchecked index directly for pointer arithmetic and copies the resulting bytes into color component values: ```c } else { const gs_color_space *pbcs = pcs->base_space; int m = cs_num_components(pbcs); const byte *pcomp = pip->lookup.table.data + m * index; switch (m) { default: { /* DeviceN */ int i; for (i = 0; i < m; ++i) pcc->paint.values[i] = pcomp[i] * (1.0 / 255.0); } ``` This produces an out-of-bounds read before the indexed lookup table. The observed outcomes are incorrect rendered color data and process crashes; because the read bytes are copied into output color values, limited disclosure of adjacent heap data is also plausible if the rendered output is observable. No write primitive is demonstrated. Reachability is supported by the mesh shading parsers. `psi/zshade.c` accepts mesh `Decode` arrays via `dict_floats_param(...)`, `pdf/pdf_shading.c` accepts them via `fill_float_array_from_dict(...)`, and both allow `BitsPerComponent` up to 16. For PDF inputs, `pdfi_create_indexed` sets `pcs->params.indexed.use_proc = 0`, so normal `Indexed` color space handling reaches the direct table lookup path shown above. Steps to reproduce: 1. Build Ghostscript with AddressSanitizer and UndefinedBehaviorSanitizer enabled, for example using `CFLAGS="-O1 -g -fsanitize=address,undefined -fno-omit-frame-pointer"`. 2. Create a PDF or PostScript file containing a mesh shading with `ColorSpace = Indexed`, `BitsPerComponent = 16`, and a large color `Decode` upper bound such as `3000000000`. 3. Render the file with Ghostscript, for example `gs ... <poc-file>`. 4. Observe an ASan/UBSan report or a process crash in the path `shade_next_color` -> `gs_cspace_indexed_lookup`, with the invalid read occurring around `table.data + m * index`. A minimal proof of concept can be shared privately upon request. Mitigation: Until a fixed package is available, avoid running Ghostscript on untrusted PDF or PostScript files in the same privilege context as trusted workloads. Where untrusted rendering is required, isolate Ghostscript in a dedicated low-privilege worker or container and, where feasible, reject or sanitize documents that contain mesh shadings using `Indexed` color spaces or unusually large `Decode` ranges. Proposed Fix: Validate the decoded float against the valid indexed entry range before casting, and add a defensive lower-bound check in `gs_cspace_indexed_lookup`. ```diff diff --git a/base/gxshade.c b/base/gxshade.c — a/base/gxshade.c +++ b/base/gxshade.c @@ -258,6 +258,7 @@ shade_next_color(shade_coord_stream_t * cs, float *pc) if (index == gs_color_space_index_Indexed) { int ncomp = gs_color_space_num_components(gs_cspace_base_space(pcs)); + int nentries = gs_cspace_indexed_num_entries(pcs); int ci; float cf; int code = cs->get_decoded(cs, num_bits, decode, &cf); @@ -267,10 +268,12 @@ shade_next_color(shade_coord_stream_t * cs, float *pc) if (code < 0) return code; if (cf < 0) + if (nentries <= 0) return_error(gs_error_rangecheck); + if (!(cf >= 0.0f && cf < (float)nentries)) + return_error(gs_error_rangecheck); ci = (int)cf; if (ci >= gs_cspace_indexed_num_entries(pcs)) + if (ci < 0 || ci >= nentries) return_error(gs_error_rangecheck); code = gs_cspace_indexed_lookup(pcs, ci, &cc); if (code < 0) diff --git a/base/gscolor2.c b/base/gscolor2.c a/base/gscolor2.c +++ b/base/gscolor2.c @@ -572,6 +572,8 @@ gs_cspace_indexed_lookup(const gs_color_space *pcs, int index, gs_client_color *pcc) { const gs_indexed_params *pip = &pcs->params.indexed; + if (index < 0 || index > pip->hival) + return_error(gs_error_rangecheck); if (pip->use_proc) { return pip->lookup.map->proc.lookup_index (pcs, index, &pcc->paint.values[0]); ``` ------ 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-189171