Commit message: https://github.com/ImageMagick/ImageMagick/issues/1711
CVE: CVE-2020-27750
CWE: CWE-369
Vulnerability description: A flaw was found in ImageMagick in MagickCore/colorspace-private.h and MagickCore/quantum.h. An attacker who submits a crafted file that is processed by ImageMagick could trigger undefined behavior in the form of values outside the range of type `unsigned char` and math division by zero. This would most likely lead to an impact to application availability, but could potentially cause other problems related to undefined behavior. This flaw affects ImageMagick versions prior to 7.0.8-68.

Project: ImageMagick
File: pixel-iterator.c

WandExport PixelIterator *DestroyPixelIterator(PixelIterator *iterator)
{
  assert(iterator != (const PixelIterator *) NULL);
  assert(iterator->signature == MagickWandSignature);
  if (iterator->debug != MagickFalse)
    (void) LogMagickEvent(WandEvent,GetMagickModule(),"%s",iterator->name);
  iterator->view=DestroyCacheView(iterator->view);
  iterator->pixel_wands=DestroyPixelWands(iterator->pixel_wands,
    iterator->region.width);
  iterator->exception=DestroyExceptionInfo(iterator->exception);
  iterator->signature=(~MagickWandSignature);
  RelinquishWandId(iterator->id);
  iterator=(PixelIterator *) RelinquishMagickMemory(iterator);
  return(iterator);
}

Project: ImageMagick
File: colorspace-private.h

static inline void ConvertCMYKToRGB(PixelInfo *pixel)
{
  pixel->red=((QuantumRange-(QuantumScale*pixel->red*(QuantumRange-
    pixel->black)+pixel->black)));
  pixel->green=((QuantumRange-(QuantumScale*pixel->green*(QuantumRange-
    pixel->black)+pixel->black)));
  pixel->blue=((QuantumRange-(QuantumScale*pixel->blue*(QuantumRange-
    pixel->black)+pixel->black)));
}

Project: ImageMagick
File: pixel-iterator.c

WandExport PixelWand **PixelGetCurrentIteratorRow(PixelIterator *iterator,
  size_t *number_wands)
{
  register const Quantum
    *pixels;

  register ssize_t
    x;

  assert(iterator != (PixelIterator *) NULL);
  assert(iterator->signature == MagickWandSignature);
  if (iterator->debug != MagickFalse)
    (void) LogMagickEvent(WandEvent,GetMagickModule(),"%s",iterator->name);
  *number_wands=0;
  iterator->active=MagickTrue;
  pixels=GetCacheViewVirtualPixels(iterator->view,iterator->region.x,
    iterator->region.y+iterator->y,iterator->region.width,1,
    iterator->exception);
  if (pixels == (const Quantum *) NULL)
    return((PixelWand **) NULL);
  for (x=0; x < (ssize_t) iterator->region.width; x++)
  {
    PixelSetQuantumPixel(GetCacheViewImage(iterator->view),pixels,
      iterator->pixel_wands[x]);
    pixels+=GetPixelChannels(GetCacheViewImage(iterator->view));
  }
  *number_wands=iterator->region.width;
  return(iterator->pixel_wands);
}

Project: ImageMagick
File: pixel-iterator.c

WandExport void PixelSetLastIteratorRow(PixelIterator *iterator)
{
  assert(iterator != (PixelIterator *) NULL);
  assert(iterator->signature == MagickWandSignature);
  if (iterator->debug != MagickFalse)
    (void) LogMagickEvent(WandEvent,GetMagickModule(),"%s",iterator->name);
  iterator->active=MagickFalse;
  iterator->y=(ssize_t) iterator->region.height-1;
}

Project: ImageMagick
File: pixel-iterator.c

WandExport MagickBooleanType IsPixelIterator(const PixelIterator *iterator)
{
  size_t
    length;

  if (iterator == (const PixelIterator *) NULL)
    return(MagickFalse);
  if (iterator->signature != MagickWandSignature)
    return(MagickFalse);
  length=strlen(PixelIteratorId);
  if (LocaleNCompare(iterator->name,PixelIteratorId,length) != 0)
    return(MagickFalse);
  return(MagickTrue);
}

Project: ImageMagick
File: colorspace-private.h

static inline void ConvertRGBToCMYK(PixelInfo *pixel)
{
  MagickRealType
    black,
    blue,
    cyan,
    green,
    magenta,
    red,
    yellow;

  if (pixel->colorspace != sRGBColorspace)
    {
      red=QuantumScale*pixel->red;
      green=QuantumScale*pixel->green;
      blue=QuantumScale*pixel->blue;
    }
  else
    {
      red=QuantumScale*DecodePixelGamma(pixel->red);
      green=QuantumScale*DecodePixelGamma(pixel->green);
      blue=QuantumScale*DecodePixelGamma(pixel->blue);
    }
  if ((fabs((double) red) < MagickEpsilon) &&
      (fabs((double) green) < MagickEpsilon) &&
      (fabs((double) blue) < MagickEpsilon))
    {
      pixel->black=(MagickRealType) QuantumRange;
      return;
    }
  cyan=(MagickRealType) (1.0-red);
  magenta=(MagickRealType) (1.0-green);
  yellow=(MagickRealType) (1.0-blue);
  black=cyan;
  if (magenta < black)
    black=magenta;
  if (yellow < black)
    black=yellow;
  cyan=(MagickRealType) (PerceptibleReciprocal(1.0-black)*(cyan-black));
  magenta=(MagickRealType) (PerceptibleReciprocal(1.0-black)*(magenta-black));
  yellow=(MagickRealType) (PerceptibleReciprocal(1.0-black)*(yellow-black));
  pixel->colorspace=CMYKColorspace;
  pixel->red=QuantumRange*cyan;
  pixel->green=QuantumRange*magenta;
  pixel->blue=QuantumRange*yellow;
  pixel->black=QuantumRange*black;
}

Project: ImageMagick
File: pixel-iterator.c

WandExport PixelIterator *NewPixelRegionIterator(MagickWand *wand,
  const ssize_t x,const ssize_t y,const size_t width,const size_t height)
{
  CacheView
    *view;

  const char
    *quantum;

  ExceptionInfo
    *exception;

  Image
    *image;

  PixelIterator
    *iterator;

  size_t
    depth;

  assert(wand != (MagickWand *) NULL);
  depth=MAGICKCORE_QUANTUM_DEPTH;
  quantum=GetMagickQuantumDepth(&depth);
  if (depth != MAGICKCORE_QUANTUM_DEPTH)
    ThrowWandFatalException(WandError,"QuantumDepthMismatch",quantum);
  if ((width == 0) || (height == 0))
    ThrowWandFatalException(WandError,"ZeroRegionSize",quantum);
  image=GetImageFromMagickWand(wand);
  if (image == (Image *) NULL)
    return((PixelIterator *) NULL);
  exception=AcquireExceptionInfo();
  view=AcquireVirtualCacheView(image,exception);
  if (view == (CacheView *) NULL)
    return((PixelIterator *) NULL);
  iterator=(PixelIterator *) AcquireMagickMemory(sizeof(*iterator));
  if (iterator == (PixelIterator *) NULL)
    ThrowWandFatalException(ResourceLimitFatalError,"MemoryAllocationFailed",
      wand->name);
  (void) memset(iterator,0,sizeof(*iterator));
  iterator->id=AcquireWandId();
  (void) FormatLocaleString(iterator->name,MagickPathExtent,"%s-%.20g",
    PixelIteratorId,(double) iterator->id);
  iterator->exception=exception;
  iterator->view=view;
  SetGeometry(image,&iterator->region);
  iterator->region.width=width;
  iterator->region.height=height;
  iterator->region.x=x;
  iterator->region.y=y;
  iterator->pixel_wands=NewPixelWands(iterator->region.width);
  iterator->y=0;
  iterator->debug=IsEventLogging();
  if (iterator->debug != MagickFalse)
    (void) LogMagickEvent(WandEvent,GetMagickModule(),"%s",iterator->name);
  iterator->signature=MagickWandSignature;
  return(iterator);
}

Project: ImageMagick
File: colorspace-private.h

static inline MagickBooleanType IsCMYKColorspace(
  const ColorspaceType colorspace)
{
  if (colorspace == CMYKColorspace)
    return(MagickTrue);
  return(MagickFalse);
}

Project: ImageMagick
File: pixel-iterator.c

WandExport MagickBooleanType PixelSetIteratorRow(PixelIterator *iterator,
  const ssize_t row)
{
  assert(iterator != (const PixelIterator *) NULL);
  assert(iterator->signature == MagickWandSignature);
  if (iterator->debug != MagickFalse)
    (void) LogMagickEvent(WandEvent,GetMagickModule(),"%s",iterator->name);
  if ((row < 0) || (row >= (ssize_t) iterator->region.height))
    return(MagickFalse);
  iterator->active=MagickTrue;
  iterator->y=row;
  return(MagickTrue);
}

Project: ImageMagick
File: pixel-iterator.c

WandExport void PixelResetIterator(PixelIterator *iterator)
{
  assert(iterator != (PixelIterator *) NULL);
  assert(iterator->signature == MagickWandSignature);
  if (iterator->debug != MagickFalse)
    (void) LogMagickEvent(WandEvent,GetMagickModule(),"%s",iterator->name);
  iterator->active=MagickFalse;
  iterator->y=0;
}

Project: ImageMagick
File: colorspace-private.h

static inline MagickBooleanType IsRGBColorspace(const ColorspaceType colorspace)
{
  if ((colorspace == RGBColorspace) || (colorspace == scRGBColorspace) ||
      (colorspace == LinearGRAYColorspace))
    return(MagickTrue);
  return(MagickFalse);
}

Project: ImageMagick
File: colorspace-private.h

static inline MagickBooleanType IssRGBColorspace(
  const ColorspaceType colorspace)
{
  if ((colorspace == sRGBColorspace) || (colorspace == TransparentColorspace))
    return(MagickTrue);
  return(MagickFalse);
}

Project: ImageMagick
File: pixel-iterator.c

WandExport PixelWand **PixelGetNextIteratorRow(PixelIterator *iterator,
  size_t *number_wands)
{
  register const Quantum
    *pixels;

  register ssize_t
    x;

  assert(iterator != (PixelIterator *) NULL);
  assert(iterator->signature == MagickWandSignature);
  if (iterator->debug != MagickFalse)
    (void) LogMagickEvent(WandEvent,GetMagickModule(),"%s",iterator->name);
  *number_wands=0;
  if (iterator->active != MagickFalse)
    iterator->y++;
  if (PixelSetIteratorRow(iterator,iterator->y) == MagickFalse)
    return((PixelWand **) NULL);
  pixels=GetCacheViewVirtualPixels(iterator->view,iterator->region.x,
    iterator->region.y+iterator->y,iterator->region.width,1,
    iterator->exception);
  if (pixels == (const Quantum *) NULL)
    return((PixelWand **) NULL);
  for (x=0; x < (ssize_t) iterator->region.width; x++)
  {
    PixelSetQuantumPixel(GetCacheViewImage(iterator->view),pixels,
      iterator->pixel_wands[x]);
    pixels+=GetPixelChannels(GetCacheViewImage(iterator->view));
  }
  *number_wands=iterator->region.width;
  return(iterator->pixel_wands);
}

Project: ImageMagick
File: pixel-iterator.c

WandExport char *PixelGetIteratorException(const PixelIterator *iterator,
  ExceptionType *severity)
{
  char
    *description;

  assert(iterator != (const PixelIterator *) NULL);
  assert(iterator->signature == MagickWandSignature);
  if (iterator->debug != MagickFalse)
    (void) LogMagickEvent(WandEvent,GetMagickModule(),"%s",iterator->name);
  assert(severity != (ExceptionType *) NULL);
  *severity=iterator->exception->severity;
  description=(char *) AcquireQuantumMemory(2UL*MagickPathExtent,
    sizeof(*description));
  if (description == (char *) NULL)
    ThrowWandFatalException(ResourceLimitFatalError,"MemoryAllocationFailed",
      iterator->name);
  *description='\0';
  if (iterator->exception->reason != (char *) NULL)
    (void) CopyMagickString(description,GetLocaleExceptionMessage(
      iterator->exception->severity,iterator->exception->reason),MagickPathExtent);
  if (iterator->exception->description != (char *) NULL)
    {
      (void) ConcatenateMagickString(description," (",MagickPathExtent);
      (void) ConcatenateMagickString(description,GetLocaleExceptionMessage(
        iterator->exception->severity,iterator->exception->description),
        MagickPathExtent);
      (void) ConcatenateMagickString(description,")",MagickPathExtent);
    }
  return(description);
}

Project: ImageMagick
File: pixel-iterator.c

WandExport ssize_t PixelGetIteratorRow(PixelIterator *iterator)
{
  assert(iterator != (const PixelIterator *) NULL);
  assert(iterator->signature == MagickWandSignature);
  if (iterator->debug != MagickFalse)
    (void) LogMagickEvent(WandEvent,GetMagickModule(),"%s",iterator->name);
  return(iterator->y);
}

Project: ImageMagick
File: pixel-iterator.c

WandExport PixelIterator *ClonePixelIterator(const PixelIterator *iterator)
{
  PixelIterator
    *clone_iterator;

  assert(iterator != (PixelIterator *) NULL);
  assert(iterator->signature == MagickWandSignature);
  if (iterator->debug != MagickFalse)
    (void) LogMagickEvent(WandEvent,GetMagickModule(),"%s",iterator->name);
  clone_iterator=(PixelIterator *) AcquireMagickMemory(sizeof(*clone_iterator));
  if (clone_iterator == (PixelIterator *) NULL)
    ThrowWandFatalException(ResourceLimitFatalError,"MemoryAllocationFailed",
      iterator->name);
  (void) memset(clone_iterator,0,sizeof(*clone_iterator));
  clone_iterator->id=AcquireWandId();
  (void) FormatLocaleString(clone_iterator->name,MagickPathExtent,"%s-%.20g",
    PixelIteratorId,(double) clone_iterator->id);
  clone_iterator->exception=AcquireExceptionInfo();
  InheritException(clone_iterator->exception,iterator->exception);
  clone_iterator->view=CloneCacheView(iterator->view);
  clone_iterator->region=iterator->region;
  clone_iterator->active=iterator->active;
  clone_iterator->y=iterator->y;
  clone_iterator->pixel_wands=ClonePixelWands((const PixelWand **)
    iterator->pixel_wands,iterator->region.width);
  clone_iterator->debug=iterator->debug;
  if (clone_iterator->debug != MagickFalse)
    (void) LogMagickEvent(WandEvent,GetMagickModule(),"%s",
      clone_iterator->name);
  clone_iterator->signature=MagickWandSignature;
  return(clone_iterator);
}

Project: ImageMagick
File: pixel-iterator.c

WandExport ExceptionType PixelGetIteratorExceptionType(
  const PixelIterator *iterator)
{
  assert(iterator != (const PixelIterator *) NULL);
  assert(iterator->signature == MagickWandSignature);
  if (iterator->debug != MagickFalse)
    (void) LogMagickEvent(WandEvent,GetMagickModule(),"%s",iterator->name);
  return(iterator->exception->severity);
}

Project: ImageMagick
File: colorspace-private.h

static inline MagickBooleanType IsGrayColorspace(
  const ColorspaceType colorspace)
{
  if ((colorspace == LinearGRAYColorspace) || (colorspace == GRAYColorspace))
    return(MagickTrue);
  return(MagickFalse);
}

Project: ImageMagick
File: pixel-iterator.c

WandExport void PixelSetFirstIteratorRow(PixelIterator *iterator)
{
  assert(iterator != (PixelIterator *) NULL);
  assert(iterator->signature == MagickWandSignature);
  if (iterator->debug != MagickFalse)
    (void) LogMagickEvent(WandEvent,GetMagickModule(),"%s",iterator->name);
  iterator->active=MagickFalse;
  iterator->y=iterator->region.y;
}

Project: ImageMagick
File: pixel-iterator.c

WandExport PixelIterator *NewPixelIterator(MagickWand *wand)
{
  const char
    *quantum;

  ExceptionInfo
    *exception;

  Image
    *image;

  PixelIterator
    *iterator;

  size_t
    depth;

  CacheView
    *view;

  depth=MAGICKCORE_QUANTUM_DEPTH;
  quantum=GetMagickQuantumDepth(&depth);
  if (depth != MAGICKCORE_QUANTUM_DEPTH)
    ThrowWandFatalException(WandError,"QuantumDepthMismatch",quantum);
  assert(wand != (MagickWand *) NULL);
  image=GetImageFromMagickWand(wand);
  if (image == (Image *) NULL)
    return((PixelIterator *) NULL);
  exception=AcquireExceptionInfo();
  view=AcquireVirtualCacheView(image,exception);
  if (view == (CacheView *) NULL)
    return((PixelIterator *) NULL);
  iterator=(PixelIterator *) AcquireMagickMemory(sizeof(*iterator));
  if (iterator == (PixelIterator *) NULL)
    ThrowWandFatalException(ResourceLimitFatalError,"MemoryAllocationFailed",
      GetExceptionMessage(errno));
  (void) memset(iterator,0,sizeof(*iterator));
  iterator->id=AcquireWandId();
  (void) FormatLocaleString(iterator->name,MagickPathExtent,"%s-%.20g",
    PixelIteratorId,(double) iterator->id);
  iterator->exception=exception;
  iterator->view=view;
  SetGeometry(image,&iterator->region);
  iterator->region.width=image->columns;
  iterator->region.height=image->rows;
  iterator->region.x=0;
  iterator->region.y=0;
  iterator->pixel_wands=NewPixelWands(iterator->region.width);
  iterator->y=0;
  iterator->debug=IsEventLogging();
  if (iterator->debug != MagickFalse)
    (void) LogMagickEvent(WandEvent,GetMagickModule(),"%s",iterator->name);
  iterator->signature=MagickWandSignature;
  return(iterator);
}

Project: ImageMagick
File: pixel-iterator.c

WandExport PixelWand **PixelGetPreviousIteratorRow(PixelIterator *iterator,
  size_t *number_wands)
{
  register const Quantum
    *pixels;

  register ssize_t
    x;

  assert(iterator != (PixelIterator *) NULL);
  assert(iterator->signature == MagickWandSignature);
  if (iterator->debug != MagickFalse)
    (void) LogMagickEvent(WandEvent,GetMagickModule(),"%s",iterator->name);
  *number_wands=0;
  if (iterator->active != MagickFalse)
    iterator->y--;
  if (PixelSetIteratorRow(iterator,iterator->y) == MagickFalse)
    return((PixelWand **) NULL);
  pixels=GetCacheViewVirtualPixels(iterator->view,iterator->region.x,
    iterator->region.y+iterator->y,iterator->region.width,1,
    iterator->exception);
  if (pixels == (const Quantum *) NULL)
    return((PixelWand **) NULL);
  for (x=0; x < (ssize_t) iterator->region.width; x++)
  {
    PixelSetQuantumPixel(GetCacheViewImage(iterator->view),pixels,
      iterator->pixel_wands[x]);
    pixels+=GetPixelChannels(GetCacheViewImage(iterator->view));
  }
  *number_wands=iterator->region.width;
  return(iterator->pixel_wands);
}

Project: ImageMagick
File: colorspace-private.h

static inline MagickBooleanType IssRGBCompatibleColorspace(
  const ColorspaceType colorspace)
{
  if ((colorspace == sRGBColorspace) || (colorspace == RGBColorspace) ||
      (colorspace == scRGBColorspace) || (colorspace == TransparentColorspace) ||
      (colorspace == GRAYColorspace) || (colorspace == LinearGRAYColorspace))
    return(MagickTrue);
  return(MagickFalse);
}

Project: ImageMagick
File: pixel-iterator.c

WandExport void ClearPixelIterator(PixelIterator *iterator)
{
  assert(iterator != (const PixelIterator *) NULL);
  assert(iterator->signature == MagickWandSignature);
  if (iterator->debug != MagickFalse)
    (void) LogMagickEvent(WandEvent,GetMagickModule(),"%s",iterator->name);
  iterator->pixel_wands=DestroyPixelWands(iterator->pixel_wands,
    iterator->region.width);
  ClearMagickException(iterator->exception);
  iterator->pixel_wands=NewPixelWands(iterator->region.width);
  iterator->active=MagickFalse;
  iterator->y=0;
  iterator->debug=IsEventLogging();
}

Project: ImageMagick
File: pixel-iterator.c

WandExport MagickBooleanType PixelSyncIterator(PixelIterator *iterator)
{
  MagickBooleanType
    status;

  register Quantum
    *magick_restrict pixels;

  register ssize_t
    x;

  assert(iterator != (const PixelIterator *) NULL);
  assert(iterator->signature == MagickWandSignature);
  if (iterator->debug != MagickFalse)
    (void) LogMagickEvent(WandEvent,GetMagickModule(),"%s",iterator->name);
  status=SetCacheViewStorageClass(iterator->view,DirectClass,
    iterator->exception);
  if (status == MagickFalse)
    return(MagickFalse);
  pixels=GetCacheViewAuthenticPixels(iterator->view,iterator->region.x,
    iterator->region.y+iterator->y,iterator->region.width,1,
    iterator->exception);
  if (pixels == (Quantum *) NULL)
    return(MagickFalse);
  for (x=0; x < (ssize_t) iterator->region.width; x++)
  {
    PixelGetQuantumPixel(GetCacheViewImage(iterator->view),
      iterator->pixel_wands[x],pixels);
    pixels+=GetPixelChannels(GetCacheViewImage(iterator->view));
  }
  if (SyncCacheViewAuthenticPixels(iterator->view,iterator->exception) == MagickFalse)
    return(MagickFalse);
  return(MagickTrue);
}

Project: ImageMagick
File: pixel-iterator.c

WandExport MagickBooleanType PixelClearIteratorException(
  PixelIterator *iterator)
{
  assert(iterator != (PixelIterator *) NULL);
  assert(iterator->signature == MagickWandSignature);
  if (iterator->debug != MagickFalse)
    (void) LogMagickEvent(WandEvent,GetMagickModule(),"%s",iterator->name);
  ClearMagickException(iterator->exception);
  return(MagickTrue);
}