Bug 2463016 (CVE-2026-112922) - CVE-2026-112922 ghostscript: Out-of-bounds read in ghostscript shade_next_color via integer overflow of indexed color index in mesh shading
Summary: CVE-2026-112922 ghostscript: Out-of-bounds read in ghostscript shade_next_col...
Keywords:
Status: NEW
Alias: CVE-2026-112922
Product: Security Response
Classification: Other
Component: vulnerability
Version: unspecified
Hardware: All
OS: Linux
low
low
Target Milestone: ---
Assignee: Product Security DevOps Team
QA Contact:
URL:
Whiteboard:
Depends On: 2563297
Blocks:
TreeView+ depends on / blocked
 
Reported: 2026-04-26 20:40 UTC by OSIDB Bzimport
Modified: 2026-10-29 09:52 UTC (History)
3 users (show)

Fixed In Version:
Clone Of:
Environment:
Last Closed:
Embargoed:


Attachments (Terms of Use)

Description OSIDB Bzimport 2026-04-26 20:40:09 UTC
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

Comment 1 Christopher Lusk 2026-06-26 17:26:50 UTC
Tracker filed for rhel-10.3: https://issues.redhat.com/browse/RHEL-189171


Note You need to log in before you can comment on or make changes to this bug.