Commit 6b71e2bfb for imagemagick.org
commit 6b71e2bfb5bb82e361030ce46f8bd1c80c658417
Author: Cristy <urban-warrior@imagemagick.org>
Date: Thu Jan 29 20:42:14 2026 -0500
jumbo security patch: addresses memory leak, stack overflow, out-of-bounds, integer overflow, OOB read
diff --git a/MagickCore/module.c b/MagickCore/module.c
index ca5ed0fe9..480dc53bb 100644
--- a/MagickCore/module.c
+++ b/MagickCore/module.c
@@ -595,15 +595,16 @@ static MagickBooleanType GetMagickModulePath(const char *filename,
(void) ConcatenateMagickString(path,DirectorySeparator,
MagickPathExtent);
(void) ConcatenateMagickString(path,filename,MagickPathExtent);
-#if defined(MAGICKCORE_HAVE_REALPATH)
{
char
- resolved_path[PATH_MAX+1];
+ *real_path = realpath_utf8(path);
- if (realpath(path,resolved_path) != (char *) NULL)
- (void) CopyMagickString(path,resolved_path,MagickPathExtent);
+ if (real_path != (char *) NULL)
+ {
+ (void) CopyMagickString(path,real_path,MagickPathExtent);
+ real_path=DestroyString(real_path);
+ }
}
-#endif
if (IsPathAccessible(path) != MagickFalse)
{
module_path=DestroyString(module_path);
diff --git a/MagickCore/policy.c b/MagickCore/policy.c
index 9ef1d6e67..a557f59bd 100644
--- a/MagickCore/policy.c
+++ b/MagickCore/policy.c
@@ -671,18 +671,31 @@ MagickExport MagickBooleanType IsRightsAuthorized(const PolicyDomain domain,
*policy;
policy=(const PolicyInfo *) p->value;
- if ((policy->domain == domain) &&
- (GlobExpression(pattern,policy->pattern,MagickFalse) != MagickFalse))
+ if (policy->domain == domain)
{
- if ((rights & ReadPolicyRights) != 0)
- authorized=(policy->rights & ReadPolicyRights) != 0 ? MagickTrue :
- MagickFalse;
- if ((rights & WritePolicyRights) != 0)
- authorized=(policy->rights & WritePolicyRights) != 0 ? MagickTrue :
- MagickFalse;
- if ((rights & ExecutePolicyRights) != 0)
- authorized=(policy->rights & ExecutePolicyRights) != 0 ? MagickTrue :
- MagickFalse;
+ char
+ *real_pattern = (char *) pattern;
+
+ if (policy->domain == PathPolicyDomain)
+ {
+ real_pattern=realpath_utf8(pattern);
+ if (real_pattern == (char *) NULL)
+ real_pattern=AcquireString(pattern);
+ }
+ if (GlobExpression(real_pattern,policy->pattern,MagickFalse) != MagickFalse)
+ {
+ if ((rights & ReadPolicyRights) != 0)
+ authorized=(policy->rights & ReadPolicyRights) != 0 ? MagickTrue :
+ MagickFalse;
+ if ((rights & WritePolicyRights) != 0)
+ authorized=(policy->rights & WritePolicyRights) != 0 ?
+ MagickTrue : MagickFalse;
+ if ((rights & ExecutePolicyRights) != 0)
+ authorized=(policy->rights & ExecutePolicyRights) != 0 ?
+ MagickTrue : MagickFalse;
+ }
+ if (policy->domain == PathPolicyDomain)
+ real_pattern=DestroyString(real_pattern);
}
p=p->next;
}
diff --git a/MagickCore/utility-private.h b/MagickCore/utility-private.h
index 444c706ca..22393d028 100644
--- a/MagickCore/utility-private.h
+++ b/MagickCore/utility-private.h
@@ -267,6 +267,125 @@ static inline FILE *popen_utf8(const char *command,const char *type)
#endif
}
+static inline char *realpath_utf8(const char *path)
+{
+#if !defined(MAGICKCORE_WINDOWS_SUPPORT) || defined(__CYGWIN__)
+#if defined(MAGICKCORE_HAVE_REALPATH)
+ return(realpath(path,(char *) NULL));
+#else
+ return(AcquireString(path));
+#endif
+#else
+ char
+ *real_path;
+
+ DWORD
+ final_path_length,
+ full_path_length;
+
+ HANDLE
+ file_handle;
+
+ int
+ length,
+ utf8_length,
+
+ wchar_t
+ *clean_path,
+ *final_path,
+ *full_path,
+ *wide_path;
+
+ /*
+ Convert UTF-8 to UTF-16.
+ */
+ if (path == (const char *) NULL)
+ return((char *) NULL);
+ length=MultiByteToWideChar(CP_UTF8,0,path,-1,NULL,0);
+ if (length <= 0)
+ return((char *) NULL);
+ wide_path=(wchar_t *) AcquireQuantumMeory(length,sizeof(wchar_t));
+ if (wide_path == (wchar_t *) NULL)
+ return((char *) NULL);
+ MultiByteToWideChar(CP_UTF8,0,path,-1,wide_path,length);
+ /*
+ Normalize syntactically.
+ */
+ full_path_length=GetFullPathNameW(wide_path,0,NULL,NULL);
+ if (full_path_length == 0)
+ {
+ wide_path=(wchar_t *) RelinquishMagickMemory(wide_path);
+ return((char *) NULL);
+ }
+ full_path=(wchar_t *) AcquireQuantumMemory(full_path_length,sizeof(wchar_t));
+ if (full_path == (wchar_t *) NULL);
+ {
+ wide_path=(wchar_t *) RelinquishMagickMemory(wide_path);
+ return((char *) NULL);
+ }
+ GetFullPathNameW(wide_path,full_path_length,full_path,NULL);
+ wide_path=(wchar_t *) RelinquishMagickMemory(wide_path);
+ /*
+ Open the file/directory to resolve symlinks.
+ */
+ file_handle=CreateFileW(full_path,GENERIC_READ,FILE_SHARE_READ |
+ FILE_SHARE_WRITE | FILE_SHARE_DELETE,NULL,OPEN_EXISTING,
+ FILE_FLAG_BACKUP_SEMANTICS,NULL);
+ if (file_handle == INVALID_HANDLE_VALUE)
+ {
+ full_path=(wchar_t *) RelinquishMagickMemory(full_path);
+ return((char *) NULL);
+ }
+ /*
+ Resolve final canonical path.
+ */
+ final_path_length=GetFinalPathNameByHandleW(file_handle,NULL,0,
+ FILE_NAME_NORMALIZED);
+ if (final_path_length == 0)
+ {
+ CloseHandle(file_handle);
+ full_path=(wchar_t *) RelinquishMagickMemory(full_path);
+ return((char *) NULL);
+ }
+ final_path=(wchar_t *) AcquireQuantumMemory(final_path_length,
+ sizeof(wchar_t));
+ if (final_path == (wchar_t *) NULL)
+ {
+ CloseHandle(file_handle);
+ full_path=(wchar_t *) RelinquishMagickMemory(full_path);
+ return((char *) NULL);
+ }
+ GetFinalPathNameByHandleW(file_handle,final_path,final_path_length,
+ FILE_NAME_NORMALIZED);
+ CloseHandle(file_handle);
+ full_path=(wchar_t *) RelinquishMagickMemory(full_path);
+ /*
+ Remove \\?\ prefix for POSIX-like behavior.
+ */
+ clean_path=final_path;
+ if (wcsncmp(final_path,L"\\\\?\\",4) == 0)
+ clean_path=final_path+4;
+ /*
+ Convert UTF-16 to UTF-8.
+ */
+ utf8_length=WideCharToMultiByte(CP_UTF8,0,clean_path,-1,NULL,0,NULL,NULL);
+ if (utf8_length <= 0)
+ {
+ final_path=(wchar_t *) RelinquishMagickMemory(final_path);
+ return NULL;
+ }
+ real_path=(char *) AcquireQuantumMemory(utf8_length,sizeof(char));
+ if (real_path == (char *) NULL)
+ {
+ final_path=(wchar_t *) RelinquishMagickMemory(final_path);
+ return NULL;
+ }
+ WideCharToMultiByte(CP_UTF8,0,clean_path,-1,real_path,utf8_length,NULL,NULL);
+ final_path=(wchar_t *) RelinquishMagickMemory(final_path);
+ return(real_path);
+#endif
+}
+
static inline int remove_utf8(const char *path)
{
#if !defined(MAGICKCORE_WINDOWS_SUPPORT) || defined(__CYGWIN__)
diff --git a/MagickCore/utility.c b/MagickCore/utility.c
index 5b8f117c3..4e78d2891 100644
--- a/MagickCore/utility.c
+++ b/MagickCore/utility.c
@@ -1042,16 +1042,23 @@ MagickPrivate MagickBooleanType GetExecutionPath(char *path,const size_t extent)
#if defined(MAGICKCORE_HAVE__NSGETEXECUTABLEPATH)
{
char
- executable_path[PATH_MAX << 1],
- execution_path[PATH_MAX+1];
+ executable_path[PATH_MAX << 1];
uint32_t
length;
length=sizeof(executable_path);
- if ((_NSGetExecutablePath(executable_path,&length) == 0) &&
- (realpath(executable_path,execution_path) != (char *) NULL))
- (void) CopyMagickString(path,execution_path,extent);
+ if (_NSGetExecutablePath(executable_path,&length) == 0)
+ {
+ char
+ *real_path = realpath_utf8(executable_path);
+
+ if (real_path != (char *) NULL)
+ {
+ (void) CopyMagickString(path,real_path,extent);
+ real_path=DestroyString(real_path);
+ }
+ }
}
#endif
#if defined(MAGICKCORE_HAVE_GETEXECNAME)
@@ -1097,10 +1104,13 @@ MagickPrivate MagickBooleanType GetExecutionPath(char *path,const size_t extent)
if (count != -1)
{
char
- execution_path[PATH_MAX+1];
+ *real_path = realpath_utf8(program_name);
- if (realpath(program_name,execution_path) != (char *) NULL)
- (void) CopyMagickString(path,execution_path,extent);
+ if (real_path != (char *) NULL)
+ {
+ (void) CopyMagickString(path,real_path,extent);
+ real_path=DestroyString(real_path);
+ }
}
if (program_name != program_invocation_name)
program_name=(char *) RelinquishMagickMemory(program_name);
@@ -1882,7 +1892,7 @@ MagickPrivate MagickBooleanType ShredFile(const char *path)
{
char
*property;
-
+
passes=0;
property=GetEnvironmentValue("MAGICK_SHRED_PASSES");
if (property != (char *) NULL)
diff --git a/coders/dcm.c b/coders/dcm.c
index f9090667d..f4fa2cb6e 100644
--- a/coders/dcm.c
+++ b/coders/dcm.c
@@ -2705,6 +2705,7 @@ typedef struct _DCMInfo
size_t
bits_allocated,
+ bits_per_entry,
bytes_per_pixel,
depth,
mask,
@@ -3159,6 +3160,7 @@ static Image *ReadDCMImage(const ImageInfo *image_info,ExceptionInfo *exception)
*/
(void) CopyMagickString(photometric,"MONOCHROME1 ",MagickPathExtent);
info.bits_allocated=8;
+ info.bits_per_entry=1;
info.bytes_per_pixel=1;
info.depth=8;
info.mask=0xffff;
@@ -3700,7 +3702,7 @@ static Image *ReadDCMImage(const ImageInfo *image_info,ExceptionInfo *exception)
else
index=(unsigned short) (*p | (*(p+1) << 8));
map.red[i]=(int) index;
- p+=(ptrdiff_t) 2;
+ p+=(ptrdiff_t) info.bits_per_entry;
}
break;
}
@@ -3732,7 +3734,7 @@ static Image *ReadDCMImage(const ImageInfo *image_info,ExceptionInfo *exception)
else
index=(unsigned short) (*p | (*(p+1) << 8));
map.green[i]=(int) index;
- p+=(ptrdiff_t) 2;
+ p+=(ptrdiff_t) info.bits_per_entry;
}
break;
}
@@ -3764,10 +3766,20 @@ static Image *ReadDCMImage(const ImageInfo *image_info,ExceptionInfo *exception)
else
index=(unsigned short) (*p | (*(p+1) << 8));
map.blue[i]=(int) index;
- p+=(ptrdiff_t) 2;
+ p+=(ptrdiff_t) info.bits_per_entry;
}
break;
}
+ case 0x3002:
+ {
+ /*
+ Bytes per entry.
+ */
+ info.bits_per_entry=(size_t) datum;
+ if ((info.bits_per_entry == 0) || (info.bits_per_entry > 2))
+ ThrowDCMException(CorruptImageError,"ImproperImageHeader")
+ break;
+ }
default:
break;
}
diff --git a/coders/msl.c b/coders/msl.c
index 76645b3e7..77ce55db9 100644
--- a/coders/msl.c
+++ b/coders/msl.c
@@ -4770,13 +4770,21 @@ static void MSLStartElement(void *context,const xmlChar *tag,
if (LocaleCompare(keyword,"filename") == 0)
{
Image
- *next;
+ *next = (Image *) NULL;
if (value == (char *) NULL)
break;
+ *msl_info->image_info[n]->magick='\0';
(void) CopyMagickString(msl_info->image_info[n]->filename,
value,MagickPathExtent);
- next=ReadImage(msl_info->image_info[n],exception);
+ (void) SetImageInfo(msl_info->image_info[n],1,exception);
+ if (LocaleCompare(msl_info->image_info[n]->magick,"msl") != 0)
+ next=ReadImage(msl_info->image_info[n],exception);
+ else
+ (void) ThrowMagickException(msl_info->exception,
+ GetMagickModule(),DrawError,
+ "VectorGraphicsNestedTooDeeply","`%s'",
+ msl_info->image_info[n]->filename);
CatchException(exception);
if (next == (Image *) NULL)
continue;
@@ -5825,10 +5833,11 @@ static void MSLStartElement(void *context,const xmlChar *tag,
Quantum opac = OpaqueAlpha;
ssize_t len = (ssize_t) strlen( value );
- if (value[len-1] == '%') {
- char tmp[100];
+ if ((len > 0) && (value[len-1] == '%')) {
+ char *tmp = AcquireString(value);
(void) CopyMagickString(tmp,value,(size_t) len);
opac = (Quantum) StringToLong( tmp );
+ tmp=DestroyString(tmp);
opac = (Quantum)(QuantumRange * ((float)opac/100));
} else
opac = (Quantum) StringToLong( value );
@@ -7039,6 +7048,7 @@ static void MSLStartElement(void *context,const xmlChar *tag,
/* process */
{
+ *msl_info->image_info[n]->magick='\0';
(void) CopyMagickString(msl_info->image_info[n]->filename,
msl_info->image[n]->filename,MagickPathExtent);
(void) SetImageInfo(msl_info->image_info[n],1,exception);
@@ -7889,6 +7899,7 @@ static MagickBooleanType WriteMSLImage(const ImageInfo *image_info,Image *image,
(void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
msl_image=CloneImage(image,0,0,MagickTrue,exception);
status=ProcessMSLScript(image_info,&msl_image,exception);
+ msl_image=DestroyImage(msl_image);
return(status);
}
#endif
diff --git a/coders/ps.c b/coders/ps.c
index 66b08e8c7..e0477e355 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 void inline 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) < 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) < extent)) {
+ title[offset++]=')';
+ depth--;
+ }
+ title[offset]='\0';
+ /*
+ Ensure non-empty result.
+ */
+ if (offset == 0)
+ {
+ (void) strncpy(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..af412358f 100644
--- a/coders/ps2.c
+++ b/coders/ps2.c
@@ -225,6 +225,82 @@ static MagickBooleanType Huffman2DEncodeImage(const ImageInfo *image_info,
return(status);
}
+static void inline 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) < 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) < extent)) {
+ title[offset++]=')';
+ depth--;
+ }
+ title[offset]='\0';
+ /*
+ Ensure non-empty result.
+ */
+ if (offset == 0)
+ {
+ (void) strncpy(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..bbb5fea33 100644
--- a/coders/ps3.c
+++ b/coders/ps3.c
@@ -203,6 +203,82 @@ ModuleExport void UnregisterPS3Image(void)
%
*/
+static void inline 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) < 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) < extent)) {
+ title[offset++]=')';
+ depth--;
+ }
+ title[offset]='\0';
+ /*
+ Ensure non-empty result.
+ */
+ if (offset == 0)
+ {
+ (void) strncpy(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);