From 314cb9e97a10cca4c5a1dd23ca159bfefa42603b Mon Sep 17 00:00:00 2001 From: "E. McConville" Date: Mon, 14 Sep 2020 09:24:56 -0500 Subject: [PATCH] Buffer overflow in Image.connected_components with ImageMagick 7.0.10 Fixes #496 --- wand/cdefs/structures.py | 22 +++++++++++++++++++--- wand/image.py | 37 +++++++++++++++++++++++++++++++++---- 2 files changed, 52 insertions(+), 7 deletions(-) diff --git a/wand/cdefs/structures.py b/wand/cdefs/structures.py index 9f74f889..426b36d6 100644 --- a/wand/cdefs/structures.py +++ b/wand/cdefs/structures.py @@ -3,11 +3,12 @@ .. versionadded:: 0.5.0 """ -from ctypes import POINTER, Structure, c_double, c_int, c_size_t +from ctypes import POINTER, Structure, c_bool, c_double, c_int, c_size_t from wand.cdefs.wandtypes import c_ssize_t, c_magick_real_t, c_magick_size_t -__all__ = ('AffineMatrix', 'ChannelFeature', 'GeometryInfo', 'KernelInfo', - 'MagickPixelPacket', 'PixelInfo', 'PointInfo', 'RectangleInfo') +__all__ = ('AffineMatrix', 'CCMaxMetrics', 'CCObjectInfo', 'CCObjectInfo70A', + 'ChannelFeature', 'GeometryInfo', 'KernelInfo', 'MagickPixelPacket', + 'PixelInfo', 'PointInfo', 'RectangleInfo') class AffineMatrix(Structure): @@ -173,6 +174,21 @@ class CCObjectInfo(Structure): ('census', c_double)] +CCMaxMetrics = 16 + + +class CCObjectInfo70A(Structure): + CCMaxMetrics = CCMaxMetrics + _fields_ = [('_id', c_ssize_t), + ('bounding_box', RectangleInfo), + ('color', PixelInfo), + ('centroid', PointInfo), + ('area', c_double), + ('census', c_double), + ('merge', c_bool), + ('metric', c_double * CCMaxMetrics)] + + # All this will change with IM7, so let's not implement this just yet. # # class ImageChannelStatistics(Structure): diff --git a/wand/image.py b/wand/image.py index 01322ef7..95462199 100644 --- a/wand/image.py +++ b/wand/image.py @@ -24,8 +24,8 @@ WandRuntimeError, WandLibraryVersionError) from .font import Font from .resource import DestroyedResourceError, Resource -from .cdefs.structures import (CCObjectInfo, ChannelFeature, GeometryInfo, - PixelInfo, RectangleInfo) +from .cdefs.structures import (CCObjectInfo, CCObjectInfo70A, ChannelFeature, + GeometryInfo, PixelInfo, RectangleInfo) from .version import MAGICK_VERSION_NUMBER, MAGICK_HDRI @@ -3965,13 +3965,16 @@ def connected_components(self, connectivity=4, area_threshold=None, key, b'{0}'.format(remove)) objects_ptr = ctypes.c_void_p(0) - ccoi_mem_size = ctypes.sizeof(CCObjectInfo) + CCObjectInfoStructure = CCObjectInfo + if MAGICK_VERSION_NUMBER > 0x709: + CCObjectInfoStructure = CCObjectInfo70A + ccoi_mem_size = ctypes.sizeof(CCObjectInfoStructure) r = library.MagickConnectedComponentsImage(self.wand, connectivity, ctypes.byref(objects_ptr)) objects = [] if r and objects_ptr.value: for i in xrange(self.colors): - temp = CCObjectInfo() + temp = CCObjectInfoStructure() src_addr = objects_ptr.value + (i * ccoi_mem_size) ctypes.memmove(ctypes.addressof(temp), src_addr, ccoi_mem_size) objects.append(ConnectedComponentObject(temp)) @@ -9841,6 +9844,8 @@ class ConnectedComponentObject(object): ` method. .. versionadded:: 0.5.5 + .. versionchanged:: 0.6.3 + Added :attr:`merge` & :attr:`metric` for ImageMagick 7.0.10 """ #: (:class:`numbers.Integral`) Serialized object identifier #: starting at `0`. @@ -9876,9 +9881,22 @@ class ConnectedComponentObject(object): #: shape. mean_color = None + #: (:class:`bool`) Object merge flag. Only avaliable after + #: ImageMagick-7.0.10. + #: ..versionadded:: 0.6.3 + merge = None + + #: (:class:`list`) List of doubles used by metric. Only avaliable after + #: ImageMagick-7.0.10. + #: ..versionadded:: 0.6.3 + metric = None + def __init__(self, cc_object=None): if isinstance(cc_object, CCObjectInfo): self.clone_from_cc_object_info(cc_object) + if isinstance(cc_object, CCObjectInfo70A): + self.clone_from_cc_object_info(cc_object) + self.clone_from_extra_70A_info(cc_object) @property def size(self): @@ -9915,6 +9933,17 @@ def clone_from_cc_object_info(self, cc_object): pinfo_size) self.mean_color = Color(raw=raw_buffer) + def clone_from_extra_70A_info(self, cc_object): + """Copy the additional values from CCObjectInfo structure. This is the + :attr:`merge` & :attr:`metric` properties added in ImageMagick 7.0.10. + + .. versionadded:: 0.6.3 + """ + self.merge = cc_object.merge + self.metric = [] + for i in range(cc_object.CCMaxMetrics): + self.metric.append(cc_object.metric[i]) + def __repr__(self): fmt = ("{name}({_id}: {width}x{height}+{left}+{top} {center_x:.2f}," "{center_y:.2f} {area:.0f} {mean_color})")