Commit 26088a83d for imagemagick.org
commit 26088a83d71e9daa203d54a56fe3c31f3f85463d
Author: Dirk Lemstra <dirk@lemstra.org>
Date: Fri Feb 6 21:28:50 2026 +0100
Prevent code injection via PostScript header (https://github.com/ImageMagick/ImageMagick/security/advisories/GHSA-rw6c-xp26-225v)
diff --git a/coders/ps.c b/coders/ps.c
index 66b08e8c7..d2ea34588 100644
--- a/coders/ps.c
+++ b/coders/ps.c
@@ -1086,6 +1086,82 @@ static inline unsigned char *PopHexPixel(const char hex_digits[][3],
return(pixels);
}
+static inline void FilenameToTitle(const char *filename,char *title,
+ const size_t extent)
+{
+ int
+ depth = 0;
+
+ ssize_t
+ i,
+ offset = 0;
+
+ if (extent == 0)
+ return;
+ for (i=0; (filename[i] != '\0') && ((offset+1) < (ssize_t) extent); i++)
+ {
+ unsigned char
+ c = filename[i];
+
+ /*
+ Only allow printable ASCII.
+ */
+ if ((c < 32) || (c > 126))
+ {
+ title[offset++]='_';
+ continue;
+ }
+ /*
+ Percent signs break DSC parsing.
+ */
+ if (c == '%')
+ {
+ title[offset++]='_';
+ continue;
+ }
+ /*
+ Parentheses must remain balanced.
+ */
+ if (c == '(')
+ {
+ depth++;
+ title[offset++] = '(';
+ continue;
+ }
+ if (c == ')')
+ {
+ if (depth <= 0)
+ title[offset++]='_';
+ else
+ {
+ depth--;
+ title[offset++]=')';
+ }
+ continue;
+ }
+ /*
+ Everything else is allowed.
+ */
+ title[offset++]=c;
+ }
+ /*
+ If parentheses remain unbalanced, close them.
+ */
+ while ((depth > 0) && ((offset+1) < (ssize_t) extent)) {
+ title[offset++]=')';
+ depth--;
+ }
+ title[offset]='\0';
+ /*
+ Ensure non-empty result.
+ */
+ if (offset == 0)
+ {
+ (void) CopyMagickString(title,"Untitled",extent-1);
+ title[extent-1]='\0';
+ }
+}
+
static MagickBooleanType WritePSImage(const ImageInfo *image_info,Image *image,
ExceptionInfo *exception)
{
@@ -1554,6 +1630,9 @@ static MagickBooleanType WritePSImage(const ImageInfo *image_info,Image *image,
text_size=(size_t) (MultilineCensus(value)*pointsize+12);
if (page == 1)
{
+ char
+ title[MagickPathExtent];
+
/*
Output Postscript header.
*/
@@ -1564,8 +1643,9 @@ static MagickBooleanType WritePSImage(const ImageInfo *image_info,Image *image,
MagickPathExtent);
(void) WriteBlobString(image,buffer);
(void) WriteBlobString(image,"%%Creator: (ImageMagick)\n");
+ FilenameToTitle(image->filename,title,MagickPathExtent);
(void) FormatLocaleString(buffer,MagickPathExtent,"%%%%Title: (%s)\n",
- image->filename);
+ title);
(void) WriteBlobString(image,buffer);
timer=GetMagickTime();
(void) FormatMagickTime(timer,sizeof(date),date);
diff --git a/coders/ps2.c b/coders/ps2.c
index 82935dc8e..009129a98 100644
--- a/coders/ps2.c
+++ b/coders/ps2.c
@@ -225,6 +225,82 @@ static MagickBooleanType Huffman2DEncodeImage(const ImageInfo *image_info,
return(status);
}
+static inline void FilenameToTitle(const char *filename,char *title,
+ const size_t extent)
+{
+ int
+ depth = 0;
+
+ ssize_t
+ i,
+ offset = 0;
+
+ if (extent == 0)
+ return;
+ for (i=0; (filename[i] != '\0') && ((offset+1) < (ssize_t) extent); i++)
+ {
+ unsigned char
+ c = filename[i];
+
+ /*
+ Only allow printable ASCII.
+ */
+ if ((c < 32) || (c > 126))
+ {
+ title[offset++]='_';
+ continue;
+ }
+ /*
+ Percent signs break DSC parsing.
+ */
+ if (c == '%')
+ {
+ title[offset++]='_';
+ continue;
+ }
+ /*
+ Parentheses must remain balanced.
+ */
+ if (c == '(')
+ {
+ depth++;
+ title[offset++] = '(';
+ continue;
+ }
+ if (c == ')')
+ {
+ if (depth <= 0)
+ title[offset++]='_';
+ else
+ {
+ depth--;
+ title[offset++]=')';
+ }
+ continue;
+ }
+ /*
+ Everything else is allowed.
+ */
+ title[offset++]=c;
+ }
+ /*
+ If parentheses remain unbalanced, close them.
+ */
+ while ((depth > 0) && ((offset+1) < (ssize_t) extent)) {
+ title[offset++]=')';
+ depth--;
+ }
+ title[offset]='\0';
+ /*
+ Ensure non-empty result.
+ */
+ if (offset == 0)
+ {
+ (void) CopyMagickString(title,"Untitled",extent-1);
+ title[extent-1]='\0';
+ }
+}
+
static MagickBooleanType WritePS2Image(const ImageInfo *image_info,Image *image,
ExceptionInfo *exception)
{
@@ -547,6 +623,9 @@ static MagickBooleanType WritePS2Image(const ImageInfo *image_info,Image *image,
text_size=(size_t) (MultilineCensus(value)*pointsize+12);
if (page == 1)
{
+ char
+ title[MagickPathExtent];
+
/*
Output Postscript header.
*/
@@ -557,8 +636,9 @@ static MagickBooleanType WritePS2Image(const ImageInfo *image_info,Image *image,
MagickPathExtent);
(void) WriteBlobString(image,buffer);
(void) WriteBlobString(image,"%%Creator: (ImageMagick)\n");
+ FilenameToTitle(image->filename,title,MagickPathExtent);
(void) FormatLocaleString(buffer,MagickPathExtent,"%%%%Title: (%s)\n",
- image->filename);
+ title);
(void) WriteBlobString(image,buffer);
timer=GetMagickTime();
(void) FormatMagickTime(timer,sizeof(date),date);
diff --git a/coders/ps3.c b/coders/ps3.c
index 77ddf050b..2b02d49b5 100644
--- a/coders/ps3.c
+++ b/coders/ps3.c
@@ -203,6 +203,82 @@ ModuleExport void UnregisterPS3Image(void)
%
*/
+static inline void FilenameToTitle(const char *filename,char *title,
+ const size_t extent)
+{
+ int
+ depth = 0;
+
+ ssize_t
+ i,
+ offset = 0;
+
+ if (extent == 0)
+ return;
+ for (i=0; (filename[i] != '\0') && ((offset+1) < (ssize_t) extent); i++)
+ {
+ unsigned char
+ c = filename[i];
+
+ /*
+ Only allow printable ASCII.
+ */
+ if ((c < 32) || (c > 126))
+ {
+ title[offset++]='_';
+ continue;
+ }
+ /*
+ Percent signs break DSC parsing.
+ */
+ if (c == '%')
+ {
+ title[offset++]='_';
+ continue;
+ }
+ /*
+ Parentheses must remain balanced.
+ */
+ if (c == '(')
+ {
+ depth++;
+ title[offset++] = '(';
+ continue;
+ }
+ if (c == ')')
+ {
+ if (depth <= 0)
+ title[offset++]='_';
+ else
+ {
+ depth--;
+ title[offset++]=')';
+ }
+ continue;
+ }
+ /*
+ Everything else is allowed.
+ */
+ title[offset++]=c;
+ }
+ /*
+ If parentheses remain unbalanced, close them.
+ */
+ while ((depth > 0) && ((offset+1) < (ssize_t) extent)) {
+ title[offset++]=')';
+ depth--;
+ }
+ title[offset]='\0';
+ /*
+ Ensure non-empty result.
+ */
+ if (offset == 0)
+ {
+ (void) CopyMagickString(title,"Untitled",extent-1);
+ title[extent-1]='\0';
+ }
+}
+
static MagickBooleanType Huffman2DEncodeImage(const ImageInfo *image_info,
Image *image,Image *inject_image,ExceptionInfo *exception)
{
@@ -1007,6 +1083,9 @@ static MagickBooleanType WritePS3Image(const ImageInfo *image_info,Image *image,
is_gray=IdentifyImageCoderGray(image,exception);
if (page == 1)
{
+ char
+ title[MagickPathExtent];
+
/*
Postscript header on the first page.
*/
@@ -1019,8 +1098,9 @@ static MagickBooleanType WritePS3Image(const ImageInfo *image_info,Image *image,
(void) FormatLocaleString(buffer,MagickPathExtent,
"%%%%Creator: ImageMagick %s\n",MagickLibVersionText);
(void) WriteBlobString(image,buffer);
+ FilenameToTitle(image->filename,title,MagickPathExtent);
(void) FormatLocaleString(buffer,MagickPathExtent,"%%%%Title: %s\n",
- image->filename);
+ title);
(void) WriteBlobString(image,buffer);
timer=GetMagickTime();
(void) FormatMagickTime(timer,sizeof(date),date);