Bug 708517

Summary: Stack-based buffer overflow in pdfmark_coerce_dest
Product: Ghostscript Reporter: Piotr Kajda <petermasterperfect>
Component: Security (public)Assignee: Chris Liddell (chrisl) <chris.liddell>
Status: RESOLVED FIXED    
Severity: normal CC: carnil, dr, jsmeix, ken.sharp, marc.deslauriers, robin.watts, sam, till.kamppeter, zdohnal
Priority: P2    
Version: unspecified   
Hardware: PC   
OS: Linux   
Customer: Word Size: ---
Attachments: Proof of concept
Pdf test file

Description Piotr Kajda 2025-05-07 23:23:32 UTC
Created attachment 26763 [details]
Proof of concept

# Title: Stack based buffer overflow pdfmark_coerce_dest
# Date Found: 2025-05-07
# Version: Tested on commit 175a68c67343442d5a4b9513aeddb5dbb21282a6
# Author: Piotr Kajda
# Tested On: Arch Linux

I discovered a buffer overflow in the pdfmark_coerce_dest function, located in devices/vector/gdevpdfm.c at line 206. The overflow occurs because the destination buffer is statically allocated on the stack, and there is no proper bounds check before the memcpy call. I include to my report "poc" file which contains pdfmark code genereting error, "example.pdf" just normal pdf.

Step to reproduce with address sanitizer output:
$ sanbin/gs -dBATCH -dNOPAUSE -sDEVICE=pdfwrite -sOutputFile=test.pdf -dPDFSETTINGS=/prepress example.pdf poc 
GPL Ghostscript GIT PRERELEASE 10.06.0 (2025-04-29)
Copyright (C) 2025 Artifex Software, Inc.  All rights reserved.
This software is supplied under the GNU AGPLv3 and comes with NO WARRANTY:
see the file COPYING for details.
Processing pages 1 through 2.
Page 1
Page 2
=================================================================
==19828==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x75b11dadd4b0 at pc 0x75b1242faf59 bp 0x7ffc263bedc0 sp 0x7ffc263be568
WRITE of size 331 at 0x75b11dadd4b0 thread T0
    #0 0x75b1242faf58 in memcpy /usr/src/debug/gcc/gcc/libsanitizer/sanitizer_common/sanitizer_common_interceptors_memintrinsics.inc:115
    #1 0x5def9ee4963d in pdfmark_coerce_dest devices/vector/gdevpdfm.c:206
    #2 0x5def9ee4ef29 in pdfmark_put_ao_pairs devices/vector/gdevpdfm.c:884
    #3 0x5def9ee527f9 in pdfmark_annot devices/vector/gdevpdfm.c:1211
    #4 0x5def9ee52dbd in pdfmark_ANN devices/vector/gdevpdfm.c:1248
    #5 0x5def9ee64e8d in pdfmark_process devices/vector/gdevpdfm.c:3321
    #6 0x5def9ee738c4 in gdev_pdf_put_params_impl devices/vector/gdevpdfp.c:421
    #7 0x5def9ee7a976 in gdev_pdf_put_params devices/vector/gdevpdfp.c:1060
    #8 0x5def9f2bfdf5 in gs_putdeviceparams base/gsdparam.c:1137
    #9 0x5def9f91ec1a in zputdeviceparams psi/zdevice.c:567
    #10 0x5def9f82dc10 in do_call_operator psi/interp.c:91
    #11 0x5def9f83ba2e in interp psi/interp.c:1772
    #12 0x5def9f82fa39 in gs_call_interp psi/interp.c:535
    #13 0x5def9f82f05c in gs_interpret psi/interp.c:488
    #14 0x5def9f801d09 in gs_main_interpret psi/imain.c:257
    #15 0x5def9f806bb8 in gs_main_run_string_end psi/imain.c:945
    #16 0x5def9f806564 in gs_main_run_string_with_length psi/imain.c:889
    #17 0x5def9f8064d6 in gs_main_run_string psi/imain.c:870
    #18 0x5def9f813b55 in run_string psi/imainarg.c:1188
    #19 0x5def9f813863 in runarg psi/imainarg.c:1147
    #20 0x5def9f8130c1 in argproc psi/imainarg.c:1069
    #21 0x5def9f80d3b2 in gs_main_init_with_args01 psi/imainarg.c:250
    #22 0x5def9f80d91f in gs_main_init_with_args psi/imainarg.c:304
    #23 0x5def9f819339 in psapi_init_with_args psi/psapi.c:294
    #24 0x5def9fc15072 in gsapi_init_with_args psi/iapi.c:253
    #25 0x5def9e2d0721 in main psi/gs.c:104
    #26 0x75b123035487  (/usr/lib/libc.so.6+0x27487) (BuildId: 0b707b217b15b106c25fe51df3724b25848310c0)
    #27 0x75b12303554b in __libc_start_main (/usr/lib/libc.so.6+0x2754b) (BuildId: 0b707b217b15b106c25fe51df3724b25848310c0)
    #28 0x5def9e2d0404 in _start (/home/zxcv/ghostpdl_org/sanbin/gs+0x3af404) (BuildId: b55e2778634b27ffff7667672ea9f1b096d14146)

Address 0x75b11dadd4b0 is located in stack of thread T0 at offset 1200 in frame
    #0 0x5def9ee4aeec in pdfmark_put_ao_pairs devices/vector/gdevpdfm.c:466

...

My fix:
diff --git a/devices/vector/gdevpdfm.c b/devices/vector/gdevpdfm.c
index 5aa3644e2..61be80d6b 100644
--- a/devices/vector/gdevpdfm.c
+++ b/devices/vector/gdevpdfm.c
@@ -200,6 +200,8 @@ pdfmark_coerce_dest(gs_param_string *dstr, char dest[MAX_DEST_STRING])
 {
     const byte *data = dstr->data;
     uint size = dstr->size;
+    if (size > MAX_DEST_STRING)
+        return_error(gs_error_limitcheck);
     if (size == 0 || data[0] != '(')
         return 0;
Comment 1 Piotr Kajda 2025-05-07 23:24:23 UTC
Created attachment 26764 [details]
Pdf test file
Comment 2 Ken Sharp 2025-05-08 10:56:02 UTC
Fixed in 6dab38fb211f15226c242ab7a83fa53e4b0ff781

As noted in the commit message I found another similar problem which I addressed in the same commit.