static Image *ReadWMFImage(const ImageInfo *image_info,ExceptionInfo *exception)
{
  double
    bounding_height,
    bounding_width,
    image_height,
    image_height_inch,
    image_width,
    image_width_inch,
    resolution_y,
    resolution_x,
    units_per_inch;

  float
    wmf_width,
    wmf_height;

  Image
    *image;

  MagickBooleanType
    status;

  unsigned long
    wmf_options_flags = 0;

  wmf_error_t
    wmf_error;

  wmf_magick_t
    *ddata = 0;

  wmfAPI
    *API = 0;

  wmfAPI_Options
    wmf_api_options;

  wmfD_Rect
    bbox;

  image=AcquireImage(image_info,exception);
  if (OpenBlob(image_info,image,ReadBinaryBlobMode,exception) == MagickFalse)
    {
      if (image->debug != MagickFalse)
        {
          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
            "  OpenBlob failed");
          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
            "leave ReadWMFImage()");
        }
      image=DestroyImageList(image);
      return((Image *) NULL);
    }

  /*
   * Create WMF API
   *
   */

  /* Register callbacks */
  wmf_options_flags |= WMF_OPT_FUNCTION;
  (void) ResetMagickMemory(&wmf_api_options, 0, sizeof(wmf_api_options));
  wmf_api_options.function = ipa_functions;

  /* Ignore non-fatal errors */
  wmf_options_flags |= WMF_OPT_IGNORE_NONFATAL;

  wmf_error = wmf_api_create(&API, wmf_options_flags, &wmf_api_options);
  if (wmf_error != wmf_E_None)
    {
      if (API)
        wmf_api_destroy(API);
      if (image->debug != MagickFalse)
        {
          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
            "  wmf_api_create failed");
          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
            "leave ReadWMFImage()");
        }
      ThrowReaderException(DelegateError,"UnableToInitializeWMFLibrary");
    }

  /* Register progress monitor */
  wmf_status_function(API,image,magick_progress_callback);

  ddata=WMF_MAGICK_GetData(API);
  ddata->image=image;
  ddata->image_info=image_info;
  ddata->draw_info=CloneDrawInfo(image_info,(const DrawInfo *) NULL);
  ddata->exception=exception;
  ddata->draw_info->font=(char *)
    RelinquishMagickMemory(ddata->draw_info->font);
  ddata->draw_info->text=(char *)
    RelinquishMagickMemory(ddata->draw_info->text);

#if defined(MAGICKCORE_WMF_DELEGATE)
  /* Must initialize font subystem for WMFlite interface */
  lite_font_init (API,&wmf_api_options); /* similar to wmf_ipa_font_init in src/font.c */
  /* wmf_arg_fontdirs (API,options); */ /* similar to wmf_arg_fontdirs in src/wmf.c */

#endif

  /*
   * Open BLOB input via libwmf API
   *
   */
  wmf_error = wmf_bbuf_input(API,ipa_blob_read,ipa_blob_seek,
    ipa_blob_tell,(void*)image);
  if (wmf_error != wmf_E_None)
    {
      wmf_api_destroy(API);
      if (image->debug != MagickFalse)
        {
          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
            "  wmf_bbuf_input failed");
          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
            "leave ReadWMFImage()");
        }
      ThrowFileException(exception,FileOpenError,"UnableToOpenFile",
        image->filename);
      image=DestroyImageList(image);
      return((Image *) NULL);
    }

  /*
   * Scan WMF file
   *
   */
  if (image->debug != MagickFalse)
    (void) LogMagickEvent(CoderEvent,GetMagickModule(),
      "  Scanning WMF to obtain bounding box");
  wmf_error=wmf_scan(API, 0, &bbox);
  if (wmf_error != wmf_E_None)
    {
      wmf_api_destroy(API);
      if (ddata->draw_info != (DrawInfo *) NULL)
        {
          DestroyDrawInfo(ddata->draw_info);
          ddata->draw_info=(DrawInfo *)NULL;
        }
      if (image->debug != MagickFalse)
        {
          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
            "  wmf_scan failed with wmf_error %d", wmf_error);
          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
            "leave ReadWMFImage()");
        }
      ThrowReaderException(DelegateError,"FailedToScanFile");
    }

  /*
   * Compute dimensions and scale factors
   *
   */

  ddata->bbox=bbox;

  /* User specified resolution */
  resolution_y=DefaultResolution;
  if (image->resolution.y != 0.0)
    {
      resolution_y = image->resolution.y;
      if (image->units == PixelsPerCentimeterResolution)
        resolution_y *= CENTIMETERS_PER_INCH;
    }
  resolution_x=DefaultResolution;
  if (image->resolution.x != 0.0)
    {
      resolution_x = image->resolution.x;
      if (image->units == PixelsPerCentimeterResolution)
        resolution_x *= CENTIMETERS_PER_INCH;
    }

  /* Obtain output size expressed in metafile units */
  wmf_error=wmf_size(API,&wmf_width,&wmf_height);
  if (wmf_error != wmf_E_None)
    {
      wmf_api_destroy(API);
      if (image->debug != MagickFalse)
        {
          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
            "  wmf_size failed with wmf_error %d", wmf_error);
          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
            "leave ReadWMFImage()");
        }
      ThrowReaderException(DelegateError,"FailedToComputeOutputSize");
    }

  /* Obtain (or guess) metafile units */
  if ((API)->File->placeable)
    units_per_inch=(API)->File->pmh->Inch;
  else if ( (wmf_width*wmf_height) < 1024*1024)
    units_per_inch=POINTS_PER_INCH;  /* MM_TEXT */
  else
    units_per_inch=TWIPS_PER_INCH;  /* MM_TWIPS */

  /* Calculate image width and height based on specified DPI
     resolution */
  image_width_inch  = (double) wmf_width / units_per_inch;
  image_height_inch = (double) wmf_height / units_per_inch;
  image_width       = image_width_inch * resolution_x;
  image_height      = image_height_inch * resolution_y;

  /* Compute bounding box scale factors and origin translations
   *
   * This all just a hack since libwmf does not currently seem to
   * provide the mapping between LOGICAL coordinates and DEVICE
   * coordinates. This mapping is necessary in order to know
   * where to place the logical bounding box within the image.
   *
   */

  bounding_width  = bbox.BR.x - bbox.TL.x;
  bounding_height = bbox.BR.y - bbox.TL.y;

  ddata->scale_x = image_width/bounding_width;
  ddata->translate_x = 0-bbox.TL.x;
  ddata->rotate = 0;

  /* Heuristic: guess that if the vertical coordinates mostly span
     negative values, then the image must be inverted. */
  if ( fabs(bbox.BR.y) > fabs(bbox.TL.y) )
    {
      /* Normal (Origin at top left of image) */
      ddata->scale_y = (image_height/bounding_height);
      ddata->translate_y = 0-bbox.TL.y;
    }
  else
    {
      /* Inverted (Origin at bottom left of image) */
      ddata->scale_y = (-image_height/bounding_height);
      ddata->translate_y = 0-bbox.BR.y;
    }

  if (image->debug != MagickFalse)
    {
      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
         "  Placeable metafile:          %s",
         (API)->File->placeable ? "Yes" : "No");

      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
        "  Size in metafile units:      %gx%g",wmf_width,wmf_height);
      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
        "  Metafile units/inch:         %g",units_per_inch);
      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
        "  Size in inches:              %gx%g",
        image_width_inch,image_height_inch);
      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
        "  Bounding Box:                %g,%g %g,%g",
        bbox.TL.x, bbox.TL.y, bbox.BR.x, bbox.BR.y);
      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
        "  Bounding width x height:     %gx%g",bounding_width,
        bounding_height);
      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
        "  Output resolution:           %gx%g",resolution_x,resolution_y);
      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
        "  Image size:                  %gx%g",image_width,image_height);
      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
        "  Bounding box scale factor:   %g,%g",ddata->scale_x,
        ddata->scale_y);
      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
        "  Translation:                 %g,%g",
        ddata->translate_x, ddata->translate_y);
    }

#if 0
#if 0
  {
    typedef struct _wmfPlayer_t wmfPlayer_t;
    struct _wmfPlayer_t
    {
      wmfPen   default_pen;
      wmfBrush default_brush;
      wmfFont  default_font;

      wmfDC* dc; /* current dc */
    };

    wmfDC
      *dc;

#define WMF_ELICIT_DC(API) (((wmfPlayer_t*)((API)->player_data))->dc)

    dc = WMF_ELICIT_DC(API);

    printf("dc->Window.Ox     = %d\n", dc->Window.Ox);
    printf("dc->Window.Oy     = %d\n", dc->Window.Oy);
    printf("dc->Window.width  = %d\n", dc->Window.width);
    printf("dc->Window.height = %d\n", dc->Window.height);
    printf("dc->pixel_width   = %g\n", dc->pixel_width);
    printf("dc->pixel_height  = %g\n", dc->pixel_height);
#if defined(MAGICKCORE_WMF_DELEGATE)  /* Only in libwmf 0.3 */
    printf("dc->Ox            = %.d\n", dc->Ox);
    printf("dc->Oy            = %.d\n", dc->Oy);
    printf("dc->width         = %.d\n", dc->width);
    printf("dc->height        = %.d\n", dc->height);
#endif

  }
#endif

#endif

  /*
   * Create canvas image
   *
   */
  image->rows=(unsigned long) ceil(image_height);
  image->columns=(unsigned long) ceil(image_width);

  if (image_info->ping != MagickFalse)
    {
      wmf_api_destroy(API);
      (void) CloseBlob(image);
      if (image->debug != MagickFalse)
        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
          "leave ReadWMFImage()");
      return(GetFirstImageInList(image));
    }
  status=SetImageExtent(image,image->columns,image->rows,exception);
  if (status == MagickFalse)
    return(DestroyImageList(image));
  if (image->debug != MagickFalse)
    (void) LogMagickEvent(CoderEvent,GetMagickModule(),
       "  Creating canvas image with size %lux%lu",(unsigned long) image->rows,
       (unsigned long) image->columns);

  /*
   * Set solid background color
   */
  {
    image->background_color = image_info->background_color;
    if (image->background_color.alpha != OpaqueAlpha)
      image->alpha_trait=BlendPixelTrait;
    (void) SetImageBackgroundColor(image,exception);
  }
  /*
   * Play file to generate Vector drawing commands
   *
   */

  if (image->debug != MagickFalse)
    (void) LogMagickEvent(CoderEvent,GetMagickModule(),
      "  Playing WMF to prepare vectors");

  wmf_error = wmf_play(API, 0, &bbox);
  if (wmf_error != wmf_E_None)
    {
      wmf_api_destroy(API);
      if (image->debug != MagickFalse)
        {
          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
            "  Playing WMF failed with wmf_error %d", wmf_error);
          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
            "leave ReadWMFImage()");
        }
      ThrowReaderException(DelegateError,"FailedToRenderFile");
    }

  /*
   * Scribble on canvas image
   *
   */

  if (image->debug != MagickFalse)
    (void) LogMagickEvent(CoderEvent,GetMagickModule(),
      "  Rendering WMF vectors");
  DrawRender(ddata->draw_wand);

  if (image->debug != MagickFalse)
    (void) LogMagickEvent(CoderEvent,GetMagickModule(),"leave ReadWMFImage()");

  /* Cleanup allocated data */
  wmf_api_destroy(API);
  (void) CloseBlob(image);

  /* Return image */
  return image;
}

In ImageMagick 7.0.6-1, a use-after-free vulnerability was found in the function ReadWMFImage in coders/wmf.c, which allows attackers to cause a denial of service.


static Image *ReadWMFImage(const ImageInfo *image_info,ExceptionInfo *exception)
{
  double
    bounding_height,
    bounding_width,
    image_height,
    image_height_inch,
    image_width,
    image_width_inch,
    resolution_y,
    resolution_x,
    units_per_inch;

  float
    wmf_width,
    wmf_height;

  Image
    *image;

  MagickBooleanType
    status;

  unsigned long
    wmf_options_flags = 0;

  wmf_error_t
    wmf_error;

  wmf_magick_t
    *ddata = 0;

  wmfAPI
    *API = 0;

  wmfAPI_Options
    wmf_api_options;

  wmfD_Rect
    bbox;

  image=AcquireImage(image_info,exception);
  if (OpenBlob(image_info,image,ReadBinaryBlobMode,exception) == MagickFalse)
    {
      if (image->debug != MagickFalse)
        {
          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
            "  OpenBlob failed");
          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
            "leave ReadWMFImage()");
        }
      image=DestroyImageList(image);
      return((Image *) NULL);
    }

  /*
   * Create WMF API
   *
   */

  /* Register callbacks */
  wmf_options_flags |= WMF_OPT_FUNCTION;
  (void) ResetMagickMemory(&wmf_api_options, 0, sizeof(wmf_api_options));
  wmf_api_options.function = ipa_functions;

  /* Ignore non-fatal errors */
  wmf_options_flags |= WMF_OPT_IGNORE_NONFATAL;

  wmf_error = wmf_api_create(&API, wmf_options_flags, &wmf_api_options);
  if (wmf_error != wmf_E_None)
    {
      if (image->debug != MagickFalse)
        {
          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
            "  wmf_api_create failed");
          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
            "leave ReadWMFImage()");
        }
      if (API)
        wmf_api_destroy(API);
      ThrowReaderException(DelegateError,"UnableToInitializeWMFLibrary");
    }

  /* Register progress monitor */
  wmf_status_function(API,image,magick_progress_callback);

  ddata=WMF_MAGICK_GetData(API);
  ddata->image=image;
  ddata->image_info=image_info;
  ddata->draw_info=CloneDrawInfo(image_info,(const DrawInfo *) NULL);
  ddata->exception=exception;
  ddata->draw_info->font=(char *)
    RelinquishMagickMemory(ddata->draw_info->font);
  ddata->draw_info->text=(char *)
    RelinquishMagickMemory(ddata->draw_info->text);

#if defined(MAGICKCORE_WMF_DELEGATE)
  /* Must initialize font subystem for WMFlite interface */
  lite_font_init (API,&wmf_api_options); /* similar to wmf_ipa_font_init in src/font.c */
  /* wmf_arg_fontdirs (API,options); */ /* similar to wmf_arg_fontdirs in src/wmf.c */

#endif

  /*
   * Open BLOB input via libwmf API
   *
   */
  wmf_error = wmf_bbuf_input(API,ipa_blob_read,ipa_blob_seek,
    ipa_blob_tell,(void*)image);
  if (wmf_error != wmf_E_None)
    {
      if (image->debug != MagickFalse)
        {
          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
            "  wmf_bbuf_input failed");
          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
            "leave ReadWMFImage()");
        }
      wmf_api_destroy(API);
      ThrowFileException(exception,FileOpenError,"UnableToOpenFile",
        image->filename);
      image=DestroyImageList(image);
      return((Image *) NULL);
    }

  /*
   * Scan WMF file
   *
   */
  if (image->debug != MagickFalse)
    (void) LogMagickEvent(CoderEvent,GetMagickModule(),
      "  Scanning WMF to obtain bounding box");
  wmf_error=wmf_scan(API, 0, &bbox);
  if (wmf_error != wmf_E_None)
    {
      if (ddata->draw_info != (DrawInfo *) NULL)
        {
          DestroyDrawInfo(ddata->draw_info);
          ddata->draw_info=(DrawInfo *)NULL;
        }
      if (image->debug != MagickFalse)
        {
          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
            "  wmf_scan failed with wmf_error %d", wmf_error);
          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
            "leave ReadWMFImage()");
        }
      wmf_api_destroy(API);
      ThrowReaderException(DelegateError,"FailedToScanFile");
    }

  /*
   * Compute dimensions and scale factors
   *
   */

  ddata->bbox=bbox;

  /* User specified resolution */
  resolution_y=DefaultResolution;
  if (image->resolution.y != 0.0)
    {
      resolution_y = image->resolution.y;
      if (image->units == PixelsPerCentimeterResolution)
        resolution_y *= CENTIMETERS_PER_INCH;
    }
  resolution_x=DefaultResolution;
  if (image->resolution.x != 0.0)
    {
      resolution_x = image->resolution.x;
      if (image->units == PixelsPerCentimeterResolution)
        resolution_x *= CENTIMETERS_PER_INCH;
    }

  /* Obtain output size expressed in metafile units */
  wmf_error=wmf_size(API,&wmf_width,&wmf_height);
  if (wmf_error != wmf_E_None)
    {
      if (image->debug != MagickFalse)
        {
          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
            "  wmf_size failed with wmf_error %d", wmf_error);
          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
            "leave ReadWMFImage()");
        }
      wmf_api_destroy(API);
      ThrowReaderException(DelegateError,"FailedToComputeOutputSize");
    }

  /* Obtain (or guess) metafile units */
  if ((API)->File->placeable)
    units_per_inch=(API)->File->pmh->Inch;
  else if ( (wmf_width*wmf_height) < 1024*1024)
    units_per_inch=POINTS_PER_INCH;  /* MM_TEXT */
  else
    units_per_inch=TWIPS_PER_INCH;  /* MM_TWIPS */

  /* Calculate image width and height based on specified DPI
     resolution */
  image_width_inch  = (double) wmf_width / units_per_inch;
  image_height_inch = (double) wmf_height / units_per_inch;
  image_width       = image_width_inch * resolution_x;
  image_height      = image_height_inch * resolution_y;

  /* Compute bounding box scale factors and origin translations
   *
   * This all just a hack since libwmf does not currently seem to
   * provide the mapping between LOGICAL coordinates and DEVICE
   * coordinates. This mapping is necessary in order to know
   * where to place the logical bounding box within the image.
   *
   */

  bounding_width  = bbox.BR.x - bbox.TL.x;
  bounding_height = bbox.BR.y - bbox.TL.y;

  ddata->scale_x = image_width/bounding_width;
  ddata->translate_x = 0-bbox.TL.x;
  ddata->rotate = 0;

  /* Heuristic: guess that if the vertical coordinates mostly span
     negative values, then the image must be inverted. */
  if ( fabs(bbox.BR.y) > fabs(bbox.TL.y) )
    {
      /* Normal (Origin at top left of image) */
      ddata->scale_y = (image_height/bounding_height);
      ddata->translate_y = 0-bbox.TL.y;
    }
  else
    {
      /* Inverted (Origin at bottom left of image) */
      ddata->scale_y = (-image_height/bounding_height);
      ddata->translate_y = 0-bbox.BR.y;
    }

  if (image->debug != MagickFalse)
    {
      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
         "  Placeable metafile:          %s",
         (API)->File->placeable ? "Yes" : "No");

      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
        "  Size in metafile units:      %gx%g",wmf_width,wmf_height);
      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
        "  Metafile units/inch:         %g",units_per_inch);
      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
        "  Size in inches:              %gx%g",
        image_width_inch,image_height_inch);
      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
        "  Bounding Box:                %g,%g %g,%g",
        bbox.TL.x, bbox.TL.y, bbox.BR.x, bbox.BR.y);
      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
        "  Bounding width x height:     %gx%g",bounding_width,
        bounding_height);
      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
        "  Output resolution:           %gx%g",resolution_x,resolution_y);
      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
        "  Image size:                  %gx%g",image_width,image_height);
      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
        "  Bounding box scale factor:   %g,%g",ddata->scale_x,
        ddata->scale_y);
      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
        "  Translation:                 %g,%g",
        ddata->translate_x, ddata->translate_y);
    }

#if 0
#if 0
  {
    typedef struct _wmfPlayer_t wmfPlayer_t;
    struct _wmfPlayer_t
    {
      wmfPen   default_pen;
      wmfBrush default_brush;
      wmfFont  default_font;

      wmfDC* dc; /* current dc */
    };

    wmfDC
      *dc;

#define WMF_ELICIT_DC(API) (((wmfPlayer_t*)((API)->player_data))->dc)

    dc = WMF_ELICIT_DC(API);

    printf("dc->Window.Ox     = %d\n", dc->Window.Ox);
    printf("dc->Window.Oy     = %d\n", dc->Window.Oy);
    printf("dc->Window.width  = %d\n", dc->Window.width);
    printf("dc->Window.height = %d\n", dc->Window.height);
    printf("dc->pixel_width   = %g\n", dc->pixel_width);
    printf("dc->pixel_height  = %g\n", dc->pixel_height);
#if defined(MAGICKCORE_WMF_DELEGATE)  /* Only in libwmf 0.3 */
    printf("dc->Ox            = %.d\n", dc->Ox);
    printf("dc->Oy            = %.d\n", dc->Oy);
    printf("dc->width         = %.d\n", dc->width);
    printf("dc->height        = %.d\n", dc->height);
#endif

  }
#endif

#endif

  /*
   * Create canvas image
   *
   */
  image->rows=(unsigned long) ceil(image_height);
  image->columns=(unsigned long) ceil(image_width);

  if (image_info->ping != MagickFalse)
    {
      wmf_api_destroy(API);
      (void) CloseBlob(image);
      if (image->debug != MagickFalse)
        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
          "leave ReadWMFImage()");
      return(GetFirstImageInList(image));
    }
  status=SetImageExtent(image,image->columns,image->rows,exception);
  if (status == MagickFalse)
    return(DestroyImageList(image));
  if (image->debug != MagickFalse)
    (void) LogMagickEvent(CoderEvent,GetMagickModule(),
       "  Creating canvas image with size %lux%lu",(unsigned long) image->rows,
       (unsigned long) image->columns);

  /*
   * Set solid background color
   */
  {
    image->background_color = image_info->background_color;
    if (image->background_color.alpha != OpaqueAlpha)
      image->alpha_trait=BlendPixelTrait;
    (void) SetImageBackgroundColor(image,exception);
  }
  /*
   * Play file to generate Vector drawing commands
   *
   */

  if (image->debug != MagickFalse)
    (void) LogMagickEvent(CoderEvent,GetMagickModule(),
      "  Playing WMF to prepare vectors");

  wmf_error = wmf_play(API, 0, &bbox);
  if (wmf_error != wmf_E_None)
    {
      if (image->debug != MagickFalse)
        {
          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
            "  Playing WMF failed with wmf_error %d", wmf_error);
          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
            "leave ReadWMFImage()");
        }
      wmf_api_destroy(API);
      ThrowReaderException(DelegateError,"FailedToRenderFile");
    }

  /*
   * Scribble on canvas image
   *
   */

  if (image->debug != MagickFalse)
    (void) LogMagickEvent(CoderEvent,GetMagickModule(),
      "  Rendering WMF vectors");
  DrawRender(ddata->draw_wand);

  if (image->debug != MagickFalse)
    (void) LogMagickEvent(CoderEvent,GetMagickModule(),"leave ReadWMFImage()");

  /* Cleanup allocated data */
  wmf_api_destroy(API);
  (void) CloseBlob(image);

  /* Return image */
  return image;
}


Update-policy 'subdomain' was incorrectly treated as 'zonesub'

resulting in names outside the specified subdomain having the wrong
restrictions for the given key.

configure_zone_ssutable(const cfg_obj_t *zconfig, dns_zone_t *zone,
			const char *zname)
{
	const cfg_obj_t *updatepolicy = NULL;
	const cfg_listelt_t *element, *element2;
	dns_ssutable_t *table = NULL;
	isc_mem_t *mctx = dns_zone_getmctx(zone);
	bool autoddns = false;
	isc_result_t result;

	(void)cfg_map_get(zconfig, "update-policy", &updatepolicy);

	if (updatepolicy == NULL) {
		dns_zone_setssutable(zone, NULL);
		return (ISC_R_SUCCESS);
	}

	if (cfg_obj_isstring(updatepolicy) &&
	    strcmp("local", cfg_obj_asstring(updatepolicy)) == 0) {
		autoddns = true;
		updatepolicy = NULL;
	}

	result = dns_ssutable_create(mctx, &table);
	if (result != ISC_R_SUCCESS)
		return (result);

	for (element = cfg_list_first(updatepolicy);
	     element != NULL;
	     element = cfg_list_next(element))
	{
		const cfg_obj_t *stmt = cfg_listelt_value(element);
		const cfg_obj_t *mode = cfg_tuple_get(stmt, "mode");
		const cfg_obj_t *identity = cfg_tuple_get(stmt, "identity");
		const cfg_obj_t *matchtype = cfg_tuple_get(stmt, "matchtype");
		const cfg_obj_t *dname = cfg_tuple_get(stmt, "name");
		const cfg_obj_t *typelist = cfg_tuple_get(stmt, "types");
		const char *str;
		bool grant = false;
		bool usezone = false;
		dns_ssumatchtype_t mtype = DNS_SSUMATCHTYPE_NAME;
		dns_fixedname_t fname, fident;
		isc_buffer_t b;
		dns_rdatatype_t *types;
		unsigned int i, n;

		str = cfg_obj_asstring(mode);
		if (strcasecmp(str, "grant") == 0) {
			grant = true;
		} else if (strcasecmp(str, "deny") == 0) {
			grant = false;
		} else {
			INSIST(0);
			ISC_UNREACHABLE();
		}

		str = cfg_obj_asstring(matchtype);
		CHECK(dns_ssu_mtypefromstring(str, &mtype));
		if (mtype == dns_ssumatchtype_subdomain) {
			usezone = true;
		}

		dns_fixedname_init(&fident);
		str = cfg_obj_asstring(identity);
		isc_buffer_constinit(&b, str, strlen(str));
		isc_buffer_add(&b, strlen(str));
		result = dns_name_fromtext(dns_fixedname_name(&fident), &b,
					   dns_rootname, 0, NULL);
		if (result != ISC_R_SUCCESS) {
			cfg_obj_log(identity, ns_g_lctx, ISC_LOG_ERROR,
				    "'%s' is not a valid name", str);
			goto cleanup;
		}

		dns_fixedname_init(&fname);
		if (usezone) {
			result = dns_name_copy(dns_zone_getorigin(zone),
					       dns_fixedname_name(&fname),
					       NULL);
			if (result != ISC_R_SUCCESS) {
				cfg_obj_log(identity, ns_g_lctx, ISC_LOG_ERROR,
					    "error copying origin: %s",
					    isc_result_totext(result));
				goto cleanup;
			}
		} else {
			str = cfg_obj_asstring(dname);
			isc_buffer_constinit(&b, str, strlen(str));
			isc_buffer_add(&b, strlen(str));
			result = dns_name_fromtext(dns_fixedname_name(&fname),
						   &b, dns_rootname, 0, NULL);
			if (result != ISC_R_SUCCESS) {
				cfg_obj_log(identity, ns_g_lctx, ISC_LOG_ERROR,
					    "'%s' is not a valid name", str);
				goto cleanup;
			}
		}

		n = ns_config_listcount(typelist);
		if (n == 0)
			types = NULL;
		else {
			types = isc_mem_get(mctx, n * sizeof(dns_rdatatype_t));
			if (types == NULL) {
				result = ISC_R_NOMEMORY;
				goto cleanup;
			}
		}

		i = 0;
		for (element2 = cfg_list_first(typelist);
		     element2 != NULL;
		     element2 = cfg_list_next(element2))
		{
			const cfg_obj_t *typeobj;
			isc_textregion_t r;

			INSIST(i < n);

			typeobj = cfg_listelt_value(element2);
			str = cfg_obj_asstring(typeobj);
			DE_CONST(str, r.base);
			r.length = strlen(str);

			result = dns_rdatatype_fromtext(&types[i++], &r);
			if (result != ISC_R_SUCCESS) {
				cfg_obj_log(identity, ns_g_lctx, ISC_LOG_ERROR,
					    "'%s' is not a valid type", str);
				isc_mem_put(mctx, types,
					    n * sizeof(dns_rdatatype_t));
				goto cleanup;
			}
		}
		INSIST(i == n);

		result = dns_ssutable_addrule(table, grant,
					      dns_fixedname_name(&fident),
					      mtype,
					      dns_fixedname_name(&fname),
					      n, types);
		if (types != NULL)
			isc_mem_put(mctx, types, n * sizeof(dns_rdatatype_t));
		if (result != ISC_R_SUCCESS) {
			goto cleanup;
		}
	}

	/*
	 * If "update-policy local;" and a session key exists,
	 * then use the default policy, which is equivalent to:
	 * update-policy { grant <session-keyname> zonesub any; };
	 */
	if (autoddns) {
		dns_rdatatype_t any = dns_rdatatype_any;

		if (ns_g_server->session_keyname == NULL) {
			isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
				      NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
				      "failed to enable auto DDNS policy "
				      "for zone %s: session key not found",
				      zname);
			result = ISC_R_NOTFOUND;
			goto cleanup;
		}

		result = dns_ssutable_addrule(table, true,
					      ns_g_server->session_keyname,
					      DNS_SSUMATCHTYPE_LOCAL,
					      dns_zone_getorigin(zone),
					      1, &any);

		if (result != ISC_R_SUCCESS)
			goto cleanup;
	}

	result = ISC_R_SUCCESS;
	dns_zone_setssutable(zone, table);

 cleanup:
	dns_ssutable_detach(&table);
	return (result);
}

In BIND 9.9.12 -> 9.9.13, 9.10.7 -> 9.10.8, 9.11.3 -> 9.11.21, 9.12.1 -> 9.16.5, 9.17.0 -> 9.17.3, also affects 9.9.12-S1 -> 9.9.13-S1, 9.11.3-S1 -> 9.11.21-S1 of the BIND 9 Supported Preview Edition, An attacker who has been granted privileges to change a specific subset of the zone's content could abuse these unintended additional privileges to update other contents of the zone.


configure_zone_ssutable(const cfg_obj_t *zconfig, dns_zone_t *zone,
			const char *zname)
{
	const cfg_obj_t *updatepolicy = NULL;
	const cfg_listelt_t *element, *element2;
	dns_ssutable_t *table = NULL;
	isc_mem_t *mctx = dns_zone_getmctx(zone);
	bool autoddns = false;
	isc_result_t result;

	(void)cfg_map_get(zconfig, "update-policy", &updatepolicy);

	if (updatepolicy == NULL) {
		dns_zone_setssutable(zone, NULL);
		return (ISC_R_SUCCESS);
	}

	if (cfg_obj_isstring(updatepolicy) &&
	    strcmp("local", cfg_obj_asstring(updatepolicy)) == 0) {
		autoddns = true;
		updatepolicy = NULL;
	}

	result = dns_ssutable_create(mctx, &table);
	if (result != ISC_R_SUCCESS)
		return (result);

	for (element = cfg_list_first(updatepolicy);
	     element != NULL;
	     element = cfg_list_next(element))
	{
		const cfg_obj_t *stmt = cfg_listelt_value(element);
		const cfg_obj_t *mode = cfg_tuple_get(stmt, "mode");
		const cfg_obj_t *identity = cfg_tuple_get(stmt, "identity");
		const cfg_obj_t *matchtype = cfg_tuple_get(stmt, "matchtype");
		const cfg_obj_t *dname = cfg_tuple_get(stmt, "name");
		const cfg_obj_t *typelist = cfg_tuple_get(stmt, "types");
		const char *str;
		bool grant = false;
		bool usezone = false;
		dns_ssumatchtype_t mtype = DNS_SSUMATCHTYPE_NAME;
		dns_fixedname_t fname, fident;
		isc_buffer_t b;
		dns_rdatatype_t *types;
		unsigned int i, n;

		str = cfg_obj_asstring(mode);
		if (strcasecmp(str, "grant") == 0) {
			grant = true;
		} else if (strcasecmp(str, "deny") == 0) {
			grant = false;
		} else {
			INSIST(0);
			ISC_UNREACHABLE();
		}

		str = cfg_obj_asstring(matchtype);
		CHECK(dns_ssu_mtypefromstring(str, &mtype));
		if (mtype == dns_ssumatchtype_subdomain &&
		    strcasecmp(str, "zonesub") == 0) {
			usezone = true;
		}

		dns_fixedname_init(&fident);
		str = cfg_obj_asstring(identity);
		isc_buffer_constinit(&b, str, strlen(str));
		isc_buffer_add(&b, strlen(str));
		result = dns_name_fromtext(dns_fixedname_name(&fident), &b,
					   dns_rootname, 0, NULL);
		if (result != ISC_R_SUCCESS) {
			cfg_obj_log(identity, ns_g_lctx, ISC_LOG_ERROR,
				    "'%s' is not a valid name", str);
			goto cleanup;
		}

		dns_fixedname_init(&fname);
		if (usezone) {
			result = dns_name_copy(dns_zone_getorigin(zone),
					       dns_fixedname_name(&fname),
					       NULL);
			if (result != ISC_R_SUCCESS) {
				cfg_obj_log(identity, ns_g_lctx, ISC_LOG_ERROR,
					    "error copying origin: %s",
					    isc_result_totext(result));
				goto cleanup;
			}
		} else {
			str = cfg_obj_asstring(dname);
			isc_buffer_constinit(&b, str, strlen(str));
			isc_buffer_add(&b, strlen(str));
			result = dns_name_fromtext(dns_fixedname_name(&fname),
						   &b, dns_rootname, 0, NULL);
			if (result != ISC_R_SUCCESS) {
				cfg_obj_log(identity, ns_g_lctx, ISC_LOG_ERROR,
					    "'%s' is not a valid name", str);
				goto cleanup;
			}
		}

		n = ns_config_listcount(typelist);
		if (n == 0)
			types = NULL;
		else {
			types = isc_mem_get(mctx, n * sizeof(dns_rdatatype_t));
			if (types == NULL) {
				result = ISC_R_NOMEMORY;
				goto cleanup;
			}
		}

		i = 0;
		for (element2 = cfg_list_first(typelist);
		     element2 != NULL;
		     element2 = cfg_list_next(element2))
		{
			const cfg_obj_t *typeobj;
			isc_textregion_t r;

			INSIST(i < n);

			typeobj = cfg_listelt_value(element2);
			str = cfg_obj_asstring(typeobj);
			DE_CONST(str, r.base);
			r.length = strlen(str);

			result = dns_rdatatype_fromtext(&types[i++], &r);
			if (result != ISC_R_SUCCESS) {
				cfg_obj_log(identity, ns_g_lctx, ISC_LOG_ERROR,
					    "'%s' is not a valid type", str);
				isc_mem_put(mctx, types,
					    n * sizeof(dns_rdatatype_t));
				goto cleanup;
			}
		}
		INSIST(i == n);

		result = dns_ssutable_addrule(table, grant,
					      dns_fixedname_name(&fident),
					      mtype,
					      dns_fixedname_name(&fname),
					      n, types);
		if (types != NULL)
			isc_mem_put(mctx, types, n * sizeof(dns_rdatatype_t));
		if (result != ISC_R_SUCCESS) {
			goto cleanup;
		}
	}

	/*
	 * If "update-policy local;" and a session key exists,
	 * then use the default policy, which is equivalent to:
	 * update-policy { grant <session-keyname> zonesub any; };
	 */
	if (autoddns) {
		dns_rdatatype_t any = dns_rdatatype_any;

		if (ns_g_server->session_keyname == NULL) {
			isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
				      NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
				      "failed to enable auto DDNS policy "
				      "for zone %s: session key not found",
				      zname);
			result = ISC_R_NOTFOUND;
			goto cleanup;
		}

		result = dns_ssutable_addrule(table, true,
					      ns_g_server->session_keyname,
					      DNS_SSUMATCHTYPE_LOCAL,
					      dns_zone_getorigin(zone),
					      1, &any);

		if (result != ISC_R_SUCCESS)
			goto cleanup;
	}

	result = ISC_R_SUCCESS;
	dns_zone_setssutable(zone, table);

 cleanup:
	dns_ssutable_detach(&table);
	return (result);
}


jpc_math: split jpc_firstone() in int/jpc_fix_t overloads

Fixes CVE-2018-9055 (denial of service via a reachable assertion due
to integer overflow).

Based on a patch from Fridrich Strba <fstrba@suse.com>.  Instead of
switching to `int_fast32_t`, this patch splits jpc_firstone() into two
overloads, one for `int` and one for `jpc_fix_t`.  This is safer
against future changes on `jpc_fix_t`.  To avoid the overhead of 64
bit integer math on 32 bit CPUs, this leaves the `int` overload
around.

Closes https://github.com/jasper-maint/jasper/issues/9

int jpc_firstone(int x)
{
	int n;

	/* The argument must be nonnegative. */
	assert(x >= 0);

	n = -1;
	while (x > 0) {
		x >>= 1;
		++n;
	}
	return n;
}

JasPer 2.0.14 allows denial of service via a reachable assertion in the function jpc_firstone in libjasper/jpc/jpc_math.c.


int jpc_int_firstone(int x)
{
	int n;

	/* The argument must be nonnegative. */
	assert(x >= 0);

	n = -1;
	while (x > 0) {
		x >>= 1;
		++n;
	}
	return n;
}


Fix a buffer overflow caused by invalid chunks

void png_print_chunk(unsigned type, unsigned char* data, unsigned size)
{
	char tag[5];
	unsigned i;

	be_uint32_write(tag, type);
	tag[4] = 0;

	cout << tag << setw(8) << size;

	switch (type) {
		case ADV_MNG_CN_MHDR :
			cout << " width:" << be_uint32_read(data+0) << " height:" << be_uint32_read(data+4) << " frequency:" << be_uint32_read(data+8);
			cout << " simplicity:" << be_uint32_read(data+24);
			cout << "(bit";
			for(i=0;i<32;++i) {
				if (be_uint32_read(data+24) & (1 << i)) {
					cout << "," << i;
				}
			}
			cout << ")";
		break;
		case ADV_MNG_CN_DHDR :
			cout << " id:" << be_uint16_read(data+0);
			switch (data[2]) {
				case 0 : cout << " img:unspecified"; break;
				case 1 : cout << " img:png"; break;
				case 2 : cout << " img:jng"; break;
				default: cout << " img:?"; break;
			}
			switch (data[3]) {
				case 0 : cout << " delta:entire_replacement"; break;
				case 1 : cout << " delta:block_addition"; break;
				case 2 : cout << " delta:block_alpha_addition"; break;
				case 3 : cout << " delta:block_color_addition"; break;
				case 4 : cout << " delta:block_replacement"; break;
				case 5 : cout << " delta:block_alpha_replacement"; break;
				case 6 : cout << " delta:block_color_replacement"; break;
				case 7 : cout << " delta:no_change"; break;
				default: cout << " delta:?"; break;
			}
			if (size >= 12) {
				cout << " width:" << be_uint32_read(data + 4) << " height:" << be_uint32_read(data + 8);
			}
			if (size >= 20) {
				cout << " x:" << (int)be_uint32_read(data + 12) << " y:" << (int)be_uint32_read(data + 16);
			}
		break;
		case ADV_MNG_CN_FRAM :
			if (size >= 1) {
				cout << " mode:" << (unsigned)data[0];
			}
			if (size > 1) {
				i = 1;
				while (i < size && data[i]!=0)
					++i;
				cout << " len:" << i-1;

				if (size >= i+2) {
					cout << " delay_mode:" << (unsigned)data[i+1];
				}

				if (size >= i+3) {
					cout << " timeout:" << (unsigned)data[i+2];
				}

				if (size >= i+4) {
					cout << " clip:" << (unsigned)data[i+3];
				}

				if (size >= i+5) {
					cout << " syncid:" << (unsigned)data[i+4];
				}

				if (size >= i+9) {
					cout << " tick:" << be_uint32_read(data+i+5);
				}

				if (size >= i+13) {
					cout << " timeout:" << be_uint32_read(data+i+9);
				}

				if (size >= i+14) {
					cout << " dt:" << (unsigned)data[i+10];
				}

				if (size >= i+15) {
					cout << " ...";
				}
			}
			break;
		case ADV_MNG_CN_DEFI :
			cout << " id:" << be_uint16_read(data+0);
			if (size >= 3) {
				switch (data[2]) {
					case 0 : cout << " visible:yes"; break;
					case 1 : cout << " visible:no"; break;
					default : cout << " visible:?"; break;
				}
			}
			if (size >= 4) {
				switch (data[3]) {
					case 0 : cout << " concrete:abstract"; break;
					case 1 : cout << " concrete:concrete"; break;
					default : cout << " concrete:?"; break;
				}
			}
			if (size >= 12) {
				cout << " x:" << (int)be_uint32_read(data + 4) << " y:" << (int)be_uint32_read(data + 8);
			}
			if (size >= 28) {
				cout << " left:" << be_uint32_read(data + 12) << " right:" << be_uint32_read(data + 16) << " top:" << be_uint32_read(data + 20) << " bottom:" << be_uint32_read(data + 24);
			}
		break;
		case ADV_MNG_CN_MOVE :
			cout << " id_from:" << be_uint16_read(data+0) << " id_to:" << be_uint16_read(data+2);
			switch (data[4]) {
				case 0 : cout << " type:replace"; break;
				case 1 : cout << " type:add"; break;
				default : cout << " type:?"; break;
			}
			cout << " x:" << (int)be_uint32_read(data + 5) << " y:" << (int)be_uint32_read(data + 9);
			break;
		case ADV_MNG_CN_PPLT :
			switch (data[0]) {
				case 0 : cout << " type:replacement_rgb"; break;
				case 1 : cout << " type:delta_rgb"; break;
				case 2 : cout << " type:replacement_alpha"; break;
				case 3 : cout << " type:delta_alpha"; break;
				case 4 : cout << " type:replacement_rgba"; break;
				case 5 : cout << " type:delta_rgba"; break;
				default : cout << " type:?"; break;
			}
			i = 1;
			while (i<size) {
				unsigned ssize;
				cout << " " << (unsigned)data[i] << ":" << (unsigned)data[i+1];
				if (data[0] == 0 || data[1] == 1)
					ssize = 3;
				else if (data[0] == 2 || data[1] == 3)
					ssize = 1;
				else
					ssize = 4;
				i += 2 + (data[i+1] - data[i] + 1) * ssize;
			}
			break;
		case ADV_PNG_CN_IHDR :
			cout << " width:" << be_uint32_read(data) << " height:" << be_uint32_read(data + 4);
			cout << " depth:" << (unsigned)data[8];
			cout << " color_type:" << (unsigned)data[9];
			cout << " compression:" << (unsigned)data[10];
			cout << " filter:" << (unsigned)data[11];
			cout << " interlace:" << (unsigned)data[12];
		break;
	}

	cout << endl;
}

In AdvanceCOMP 2.1, png_compress in pngex.cc in advpng has an integer overflow upon encountering an invalid PNG size, which results in an attempted memcpy to write into a buffer that is too small. (There is also a heap-based buffer over-read.)


void png_print_chunk(unsigned type, unsigned char* data, unsigned size)
{
	char tag[5];
	unsigned i;

	be_uint32_write(tag, type);
	tag[4] = 0;

	cout << tag << setw(8) << size;

	switch (type) {
		case ADV_MNG_CN_MHDR :
			if (size < 28) {
				cout << " invalid chunk size";
				break;
			}
			cout << " width:" << be_uint32_read(data+0) << " height:" << be_uint32_read(data+4) << " frequency:" << be_uint32_read(data+8);
			cout << " simplicity:" << be_uint32_read(data+24);
			cout << "(bit";
			for(i=0;i<32;++i) {
				if (be_uint32_read(data+24) & (1 << i)) {
					cout << "," << i;
				}
			}
			cout << ")";
		break;
		case ADV_MNG_CN_DHDR :
			if (size < 4) {
				cout << " invalid chunk size";
				break;
			}
			cout << " id:" << be_uint16_read(data+0);
			switch (data[2]) {
				case 0 : cout << " img:unspecified"; break;
				case 1 : cout << " img:png"; break;
				case 2 : cout << " img:jng"; break;
				default: cout << " img:?"; break;
			}
			switch (data[3]) {
				case 0 : cout << " delta:entire_replacement"; break;
				case 1 : cout << " delta:block_addition"; break;
				case 2 : cout << " delta:block_alpha_addition"; break;
				case 3 : cout << " delta:block_color_addition"; break;
				case 4 : cout << " delta:block_replacement"; break;
				case 5 : cout << " delta:block_alpha_replacement"; break;
				case 6 : cout << " delta:block_color_replacement"; break;
				case 7 : cout << " delta:no_change"; break;
				default: cout << " delta:?"; break;
			}
			if (size >= 12) {
				cout << " width:" << be_uint32_read(data + 4) << " height:" << be_uint32_read(data + 8);
			}
			if (size >= 20) {
				cout << " x:" << (int)be_uint32_read(data + 12) << " y:" << (int)be_uint32_read(data + 16);
			}
		break;
		case ADV_MNG_CN_FRAM :
			if (size >= 1) {
				cout << " mode:" << (unsigned)data[0];
			}
			if (size > 1) {
				i = 1;
				while (i < size && data[i]!=0)
					++i;
				cout << " len:" << i-1;

				if (size >= i+2) {
					cout << " delay_mode:" << (unsigned)data[i+1];
				}

				if (size >= i+3) {
					cout << " timeout:" << (unsigned)data[i+2];
				}

				if (size >= i+4) {
					cout << " clip:" << (unsigned)data[i+3];
				}

				if (size >= i+5) {
					cout << " syncid:" << (unsigned)data[i+4];
				}

				if (size >= i+9) {
					cout << " tick:" << be_uint32_read(data+i+5);
				}

				if (size >= i+13) {
					cout << " timeout:" << be_uint32_read(data+i+9);
				}

				if (size >= i+14) {
					cout << " dt:" << (unsigned)data[i+10];
				}

				if (size >= i+15) {
					cout << " ...";
				}
			}
			break;
		case ADV_MNG_CN_DEFI :
			if (size < 2) {
				cout << " invalid chunk size";
				break;
			}
			cout << " id:" << be_uint16_read(data+0);
			if (size >= 3) {
				switch (data[2]) {
					case 0 : cout << " visible:yes"; break;
					case 1 : cout << " visible:no"; break;
					default : cout << " visible:?"; break;
				}
			}
			if (size >= 4) {
				switch (data[3]) {
					case 0 : cout << " concrete:abstract"; break;
					case 1 : cout << " concrete:concrete"; break;
					default : cout << " concrete:?"; break;
				}
			}
			if (size >= 12) {
				cout << " x:" << (int)be_uint32_read(data + 4) << " y:" << (int)be_uint32_read(data + 8);
			}
			if (size >= 28) {
				cout << " left:" << be_uint32_read(data + 12) << " right:" << be_uint32_read(data + 16) << " top:" << be_uint32_read(data + 20) << " bottom:" << be_uint32_read(data + 24);
			}
		break;
		case ADV_MNG_CN_MOVE :
			if (size < 13) {
				cout << " invalid chunk size";
				break;
			}
			cout << " id_from:" << be_uint16_read(data+0) << " id_to:" << be_uint16_read(data+2);
			switch (data[4]) {
				case 0 : cout << " type:replace"; break;
				case 1 : cout << " type:add"; break;
				default : cout << " type:?"; break;
			}
			cout << " x:" << (int)be_uint32_read(data + 5) << " y:" << (int)be_uint32_read(data + 9);
			break;
		case ADV_MNG_CN_PPLT :
			if (size < 1) {
				cout << " invalid chunk size";
				break;
			}
			switch (data[0]) {
				case 0 : cout << " type:replacement_rgb"; break;
				case 1 : cout << " type:delta_rgb"; break;
				case 2 : cout << " type:replacement_alpha"; break;
				case 3 : cout << " type:delta_alpha"; break;
				case 4 : cout << " type:replacement_rgba"; break;
				case 5 : cout << " type:delta_rgba"; break;
				default : cout << " type:?"; break;
			}
			i = 1;
			while (i + 1 < size) {
				unsigned ssize;
				cout << " " << (unsigned)data[i] << ":" << (unsigned)data[i+1];
				if (data[0] == 0 || data[1] == 1)
					ssize = 3;
				else if (data[0] == 2 || data[1] == 3)
					ssize = 1;
				else
					ssize = 4;
				i += 2 + (data[i+1] - data[i] + 1) * ssize;
			}
			break;
		case ADV_PNG_CN_IHDR :
			if (size < 13) {
				cout << " invalid chunk size";
				break;
			}
			cout << " width:" << be_uint32_read(data) << " height:" << be_uint32_read(data + 4);
			cout << " depth:" << (unsigned)data[8];
			cout << " color_type:" << (unsigned)data[9];
			cout << " compression:" << (unsigned)data[10];
			cout << " filter:" << (unsigned)data[11];
			cout << " interlace:" << (unsigned)data[12];
		break;
	}

	cout << endl;
}


bpf: Fix kernel address leakage in atomic fetch

The change in commit 37086bfdc737 ("bpf: Propagate stack bounds to registers
in atomics w/ BPF_FETCH") around check_mem_access() handling is buggy since
this would allow for unprivileged users to leak kernel pointers. For example,
an atomic fetch/and with -1 on a stack destination which holds a spilled
pointer will migrate the spilled register type into a scalar, which can then
be exported out of the program (since scalar != pointer) by dumping it into
a map value.

The original implementation of XADD was preventing this situation by using
a double call to check_mem_access() one with BPF_READ and a subsequent one
with BPF_WRITE, in both cases passing -1 as a placeholder value instead of
register as per XADD semantics since it didn't contain a value fetch. The
BPF_READ also included a check in check_stack_read_fixed_off() which rejects
the program if the stack slot is of __is_pointer_value() if dst_regno < 0.
The latter is to distinguish whether we're dealing with a regular stack spill/
fill or some arithmetical operation which is disallowed on non-scalars, see
also 6e7e63cbb023 ("bpf: Forbid XADD on spilled pointers for unprivileged
users") for more context on check_mem_access() and its handling of placeholder
value -1.

One minimally intrusive option to fix the leak is for the BPF_FETCH case to
initially check the BPF_READ case via check_mem_access() with -1 as register,
followed by the actual load case with non-negative load_reg to propagate
stack bounds to registers.

Fixes: 37086bfdc737 ("bpf: Propagate stack bounds to registers in atomics w/ BPF_FETCH")
Reported-by: <n4ke4mry@gmail.com>
Acked-by: Brendan Jackman <jackmanb@google.com>
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
Signed-off-by: Alexei Starovoitov <ast@kernel.org>

static int check_atomic(struct bpf_verifier_env *env, int insn_idx, struct bpf_insn *insn)
{
	int load_reg;
	int err;

	switch (insn->imm) {
	case BPF_ADD:
	case BPF_ADD | BPF_FETCH:
	case BPF_AND:
	case BPF_AND | BPF_FETCH:
	case BPF_OR:
	case BPF_OR | BPF_FETCH:
	case BPF_XOR:
	case BPF_XOR | BPF_FETCH:
	case BPF_XCHG:
	case BPF_CMPXCHG:
		break;
	default:
		verbose(env, "BPF_ATOMIC uses invalid atomic opcode %02x\n", insn->imm);
		return -EINVAL;
	}

	if (BPF_SIZE(insn->code) != BPF_W && BPF_SIZE(insn->code) != BPF_DW) {
		verbose(env, "invalid atomic operand size\n");
		return -EINVAL;
	}

	/* check src1 operand */
	err = check_reg_arg(env, insn->src_reg, SRC_OP);
	if (err)
		return err;

	/* check src2 operand */
	err = check_reg_arg(env, insn->dst_reg, SRC_OP);
	if (err)
		return err;

	if (insn->imm == BPF_CMPXCHG) {
		/* Check comparison of R0 with memory location */
		err = check_reg_arg(env, BPF_REG_0, SRC_OP);
		if (err)
			return err;
	}

	if (is_pointer_value(env, insn->src_reg)) {
		verbose(env, "R%d leaks addr into mem\n", insn->src_reg);
		return -EACCES;
	}

	if (is_ctx_reg(env, insn->dst_reg) ||
	    is_pkt_reg(env, insn->dst_reg) ||
	    is_flow_key_reg(env, insn->dst_reg) ||
	    is_sk_reg(env, insn->dst_reg)) {
		verbose(env, "BPF_ATOMIC stores into R%d %s is not allowed\n",
			insn->dst_reg,
			reg_type_str[reg_state(env, insn->dst_reg)->type]);
		return -EACCES;
	}

	if (insn->imm & BPF_FETCH) {
		if (insn->imm == BPF_CMPXCHG)
			load_reg = BPF_REG_0;
		else
			load_reg = insn->src_reg;

		/* check and record load of old value */
		err = check_reg_arg(env, load_reg, DST_OP);
		if (err)
			return err;
	} else {
		/* This instruction accesses a memory location but doesn't
		 * actually load it into a register.
		 */
		load_reg = -1;
	}

	/* check whether we can read the memory */
	err = check_mem_access(env, insn_idx, insn->dst_reg, insn->off,
			       BPF_SIZE(insn->code), BPF_READ, load_reg, true);
	if (err)
		return err;

	/* check whether we can write into the same memory */
	err = check_mem_access(env, insn_idx, insn->dst_reg, insn->off,
			       BPF_SIZE(insn->code), BPF_WRITE, -1, true);
	if (err)
		return err;

	return 0;
}

A vulnerability was found in the Linux kernel's eBPF verifier when handling internal data structures. Internal memory locations could be returned to userspace. A local attacker with the permissions to insert eBPF code to the kernel can use this to leak internal kernel memory details defeating some of the exploit mitigations in place for the kernel. This flaws affects kernel versions < v5.16-rc6


static int check_atomic(struct bpf_verifier_env *env, int insn_idx, struct bpf_insn *insn)
{
	int load_reg;
	int err;

	switch (insn->imm) {
	case BPF_ADD:
	case BPF_ADD | BPF_FETCH:
	case BPF_AND:
	case BPF_AND | BPF_FETCH:
	case BPF_OR:
	case BPF_OR | BPF_FETCH:
	case BPF_XOR:
	case BPF_XOR | BPF_FETCH:
	case BPF_XCHG:
	case BPF_CMPXCHG:
		break;
	default:
		verbose(env, "BPF_ATOMIC uses invalid atomic opcode %02x\n", insn->imm);
		return -EINVAL;
	}

	if (BPF_SIZE(insn->code) != BPF_W && BPF_SIZE(insn->code) != BPF_DW) {
		verbose(env, "invalid atomic operand size\n");
		return -EINVAL;
	}

	/* check src1 operand */
	err = check_reg_arg(env, insn->src_reg, SRC_OP);
	if (err)
		return err;

	/* check src2 operand */
	err = check_reg_arg(env, insn->dst_reg, SRC_OP);
	if (err)
		return err;

	if (insn->imm == BPF_CMPXCHG) {
		/* Check comparison of R0 with memory location */
		err = check_reg_arg(env, BPF_REG_0, SRC_OP);
		if (err)
			return err;
	}

	if (is_pointer_value(env, insn->src_reg)) {
		verbose(env, "R%d leaks addr into mem\n", insn->src_reg);
		return -EACCES;
	}

	if (is_ctx_reg(env, insn->dst_reg) ||
	    is_pkt_reg(env, insn->dst_reg) ||
	    is_flow_key_reg(env, insn->dst_reg) ||
	    is_sk_reg(env, insn->dst_reg)) {
		verbose(env, "BPF_ATOMIC stores into R%d %s is not allowed\n",
			insn->dst_reg,
			reg_type_str[reg_state(env, insn->dst_reg)->type]);
		return -EACCES;
	}

	if (insn->imm & BPF_FETCH) {
		if (insn->imm == BPF_CMPXCHG)
			load_reg = BPF_REG_0;
		else
			load_reg = insn->src_reg;

		/* check and record load of old value */
		err = check_reg_arg(env, load_reg, DST_OP);
		if (err)
			return err;
	} else {
		/* This instruction accesses a memory location but doesn't
		 * actually load it into a register.
		 */
		load_reg = -1;
	}

	/* Check whether we can read the memory, with second call for fetch
	 * case to simulate the register fill.
	 */
	err = check_mem_access(env, insn_idx, insn->dst_reg, insn->off,
			       BPF_SIZE(insn->code), BPF_READ, -1, true);
	if (!err && load_reg >= 0)
		err = check_mem_access(env, insn_idx, insn->dst_reg, insn->off,
				       BPF_SIZE(insn->code), BPF_READ, load_reg,
				       true);
	if (err)
		return err;

	/* Check whether we can write into the same memory. */
	err = check_mem_access(env, insn_idx, insn->dst_reg, insn->off,
			       BPF_SIZE(insn->code), BPF_WRITE, -1, true);
	if (err)
		return err;

	return 0;
}


Let child source with 'none' authentication method use collection source authentication

That might be the same as having set NULL authentication method.

Related to https://gitlab.gnome.org/GNOME/evolution-ews/issues/27

e_util_can_use_collection_as_credential_source (ESource *collection_source,
						ESource *child_source)
{
	gboolean can_use_collection = FALSE;

	if (collection_source)
		g_return_val_if_fail (E_IS_SOURCE (collection_source), FALSE);
	g_return_val_if_fail (E_IS_SOURCE (child_source), FALSE);

	if (collection_source && e_source_has_extension (collection_source, E_SOURCE_EXTENSION_COLLECTION)) {
		/* Use the found parent collection source for credentials store only if
		   the child source doesn't have any authentication information, or this
		   information is not filled, or if either the host name or the user name
		   are the same with the collection source.

		   This allows to create a collection of sources which has one source
		   (like message send) on a different server, thus this source uses
		   its own credentials.
		*/
		if (!e_source_has_extension (child_source, E_SOURCE_EXTENSION_AUTHENTICATION)) {
			can_use_collection = TRUE;
		} else if (e_source_has_extension (collection_source, E_SOURCE_EXTENSION_AUTHENTICATION)) {
			ESourceAuthentication *auth_source, *auth_collection;
			gchar *host_source, *host_collection;

			auth_source = e_source_get_extension (child_source, E_SOURCE_EXTENSION_AUTHENTICATION);
			auth_collection = e_source_get_extension (collection_source, E_SOURCE_EXTENSION_AUTHENTICATION);

			host_source = e_source_authentication_dup_host (auth_source);
			host_collection = e_source_authentication_dup_host (auth_collection);

			if (host_source && host_collection && g_ascii_strcasecmp (host_source, host_collection) == 0) {
				can_use_collection = TRUE;
			} else {
				/* Only one of them is filled, then use the collection; otherwise
				   both are filled and they do not match, thus do not use collection. */
				can_use_collection = (host_collection && *host_collection && (!host_source || !*host_source)) ||
						     (host_source && *host_source && (!host_collection || !*host_collection));
			}

			g_free (host_source);
			g_free (host_collection);

			if (can_use_collection) {
				gchar *username_source, *username_collection;

				username_source = e_source_authentication_dup_user (auth_source);
				username_collection = e_source_authentication_dup_user (auth_collection);

				/* Check user name similarly as host name */
				if (username_source && username_collection && g_ascii_strcasecmp (username_source, username_collection) == 0) {
					can_use_collection = TRUE;
				} else {
					can_use_collection = !username_source || !*username_source;
				}

				g_free (username_source);
				g_free (username_collection);
			}

			if (can_use_collection) {
				gchar *method_source, *method_collection;

				/* Also check the method; if different, then rather not use the collection */
				method_source = e_source_authentication_dup_method (auth_source);
				method_collection = e_source_authentication_dup_method (auth_collection);

				can_use_collection = !method_source || !method_collection ||
					g_ascii_strcasecmp (method_source, method_collection) == 0;

				g_free (method_source);
				g_free (method_collection);
			}
		}
	}

	return can_use_collection;
}

It was discovered evolution-ews before 3.31.3 does not check the validity of SSL certificates. An attacker could abuse this flaw to get confidential information by tricking the user into connecting to a fake server without the user noticing the difference.


e_util_can_use_collection_as_credential_source (ESource *collection_source,
						ESource *child_source)
{
	gboolean can_use_collection = FALSE;

	if (collection_source)
		g_return_val_if_fail (E_IS_SOURCE (collection_source), FALSE);
	g_return_val_if_fail (E_IS_SOURCE (child_source), FALSE);

	if (collection_source && e_source_has_extension (collection_source, E_SOURCE_EXTENSION_COLLECTION)) {
		/* Use the found parent collection source for credentials store only if
		   the child source doesn't have any authentication information, or this
		   information is not filled, or if either the host name or the user name
		   are the same with the collection source.

		   This allows to create a collection of sources which has one source
		   (like message send) on a different server, thus this source uses
		   its own credentials.
		*/
		if (!e_source_has_extension (child_source, E_SOURCE_EXTENSION_AUTHENTICATION)) {
			can_use_collection = TRUE;
		} else if (e_source_has_extension (collection_source, E_SOURCE_EXTENSION_AUTHENTICATION)) {
			ESourceAuthentication *auth_source, *auth_collection;
			gchar *host_source, *host_collection;

			auth_source = e_source_get_extension (child_source, E_SOURCE_EXTENSION_AUTHENTICATION);
			auth_collection = e_source_get_extension (collection_source, E_SOURCE_EXTENSION_AUTHENTICATION);

			host_source = e_source_authentication_dup_host (auth_source);
			host_collection = e_source_authentication_dup_host (auth_collection);

			if (host_source && host_collection && g_ascii_strcasecmp (host_source, host_collection) == 0) {
				can_use_collection = TRUE;
			} else {
				/* Only one of them is filled, then use the collection; otherwise
				   both are filled and they do not match, thus do not use collection. */
				can_use_collection = (host_collection && *host_collection && (!host_source || !*host_source)) ||
						     (host_source && *host_source && (!host_collection || !*host_collection));
			}

			g_free (host_source);
			g_free (host_collection);

			if (can_use_collection) {
				gchar *username_source, *username_collection;

				username_source = e_source_authentication_dup_user (auth_source);
				username_collection = e_source_authentication_dup_user (auth_collection);

				/* Check user name similarly as host name */
				if (username_source && username_collection && g_ascii_strcasecmp (username_source, username_collection) == 0) {
					can_use_collection = TRUE;
				} else {
					can_use_collection = !username_source || !*username_source;
				}

				g_free (username_source);
				g_free (username_collection);
			}

			if (can_use_collection) {
				gchar *method_source, *method_collection;

				/* Also check the method; if different, then rather not use the collection.
				   Consider 'none' method on the child as the same as the collection method. */
				method_source = e_source_authentication_dup_method (auth_source);
				method_collection = e_source_authentication_dup_method (auth_collection);

				can_use_collection = !method_source || !method_collection ||
					g_ascii_strcasecmp (method_source, "none") == 0 ||
					g_ascii_strcasecmp (method_source, method_collection) == 0;

				g_free (method_source);
				g_free (method_collection);
			}
		}
	}

	return can_use_collection;
}


Fix #79371: mb_strtolower (UTF-32LE): stack-buffer-overflow

We make sure that negative values are properly compared.

static int convert_case_filter(int c, void *void_data)
{
	struct convert_case_data *data = (struct convert_case_data *) void_data;
	unsigned out[3];
	unsigned len, i;

	/* Handle invalid characters early, as we assign special meaning to
	 * codepoints above 0xffffff. */
	if (UNEXPECTED(c > 0xffffff)) {
		(*data->next_filter->filter_function)(c, data->next_filter);
		return 0;
	}

	switch (data->case_mode) {
		case PHP_UNICODE_CASE_UPPER_SIMPLE:
			out[0] = php_unicode_toupper_simple(c, data->no_encoding);
			len = 1;
			break;

		case PHP_UNICODE_CASE_UPPER:
			len = php_unicode_toupper_full(c, data->no_encoding, out);
			break;

		case PHP_UNICODE_CASE_LOWER_SIMPLE:
			out[0] = php_unicode_tolower_simple(c, data->no_encoding);
			len = 1;
			break;

		case PHP_UNICODE_CASE_LOWER:
			len = php_unicode_tolower_full(c, data->no_encoding, out);
			break;

		case PHP_UNICODE_CASE_FOLD:
			len = php_unicode_tofold_full(c, data->no_encoding, out);
			break;

		case PHP_UNICODE_CASE_FOLD_SIMPLE:
			out[0] = php_unicode_tofold_simple(c, data->no_encoding);
			len = 1;
			break;

		case PHP_UNICODE_CASE_TITLE_SIMPLE:
		case PHP_UNICODE_CASE_TITLE:
		{
			if (data->title_mode) {
				if (data->case_mode == PHP_UNICODE_CASE_TITLE_SIMPLE) {
					out[0] = php_unicode_tolower_simple(c, data->no_encoding);
					len = 1;
				} else {
					len = php_unicode_tolower_full(c, data->no_encoding, out);
				}
			} else {
				if (data->case_mode == PHP_UNICODE_CASE_TITLE_SIMPLE) {
					out[0] = php_unicode_totitle_simple(c, data->no_encoding);
					len = 1;
				} else {
					len = php_unicode_totitle_full(c, data->no_encoding, out);
				}
			}
			if (!php_unicode_is_case_ignorable(c)) {
				data->title_mode = php_unicode_is_cased(c);
			}
			break;
		}
		EMPTY_SWITCH_DEFAULT_CASE()
	}

	for (i = 0; i < len; i++) {
		(*data->next_filter->filter_function)(out[i], data->next_filter);
	}
	return 0;
}

In PHP versions 7.3.x below 7.3.16 and 7.4.x below 7.4.4, while using mb_strtolower() function with UTF-32LE encoding, certain invalid strings could cause PHP to overwrite stack-allocated buffer. This could lead to memory corruption, crashes and potentially code execution.


static int convert_case_filter(int c, void *void_data)
{
	struct convert_case_data *data = (struct convert_case_data *) void_data;
	unsigned out[3];
	unsigned len, i;

	/* Handle invalid characters early, as we assign special meaning to
	 * codepoints above 0xffffff. */
	if (UNEXPECTED((unsigned) c > 0xffffff)) {
		(*data->next_filter->filter_function)(c, data->next_filter);
		return 0;
	}

	switch (data->case_mode) {
		case PHP_UNICODE_CASE_UPPER_SIMPLE:
			out[0] = php_unicode_toupper_simple(c, data->no_encoding);
			len = 1;
			break;

		case PHP_UNICODE_CASE_UPPER:
			len = php_unicode_toupper_full(c, data->no_encoding, out);
			break;

		case PHP_UNICODE_CASE_LOWER_SIMPLE:
			out[0] = php_unicode_tolower_simple(c, data->no_encoding);
			len = 1;
			break;

		case PHP_UNICODE_CASE_LOWER:
			len = php_unicode_tolower_full(c, data->no_encoding, out);
			break;

		case PHP_UNICODE_CASE_FOLD:
			len = php_unicode_tofold_full(c, data->no_encoding, out);
			break;

		case PHP_UNICODE_CASE_FOLD_SIMPLE:
			out[0] = php_unicode_tofold_simple(c, data->no_encoding);
			len = 1;
			break;

		case PHP_UNICODE_CASE_TITLE_SIMPLE:
		case PHP_UNICODE_CASE_TITLE:
		{
			if (data->title_mode) {
				if (data->case_mode == PHP_UNICODE_CASE_TITLE_SIMPLE) {
					out[0] = php_unicode_tolower_simple(c, data->no_encoding);
					len = 1;
				} else {
					len = php_unicode_tolower_full(c, data->no_encoding, out);
				}
			} else {
				if (data->case_mode == PHP_UNICODE_CASE_TITLE_SIMPLE) {
					out[0] = php_unicode_totitle_simple(c, data->no_encoding);
					len = 1;
				} else {
					len = php_unicode_totitle_full(c, data->no_encoding, out);
				}
			}
			if (!php_unicode_is_case_ignorable(c)) {
				data->title_mode = php_unicode_is_cased(c);
			}
			break;
		}
		EMPTY_SWITCH_DEFAULT_CASE()
	}

	for (i = 0; i < len; i++) {
		(*data->next_filter->filter_function)(out[i], data->next_filter);
	}
	return 0;
}


int cancel_extop( Operation *op, SlapReply *rs )
{
	Operation *o;
	int rc;
	int opid;
	BerElementBuffer berbuf;
	BerElement *ber = (BerElement *)&berbuf;

	assert( ber_bvcmp( &slap_EXOP_CANCEL, &op->ore_reqoid ) == 0 );

	if ( op->ore_reqdata == NULL ) {
		rs->sr_text = "no message ID supplied";
		return LDAP_PROTOCOL_ERROR;
	}

	if ( op->ore_reqdata->bv_len == 0 ) {
		rs->sr_text = "empty request data field";
		return LDAP_PROTOCOL_ERROR;
	}

	/* ber_init2 uses reqdata directly, doesn't allocate new buffers */
	ber_init2( ber, op->ore_reqdata, 0 );

	if ( ber_scanf( ber, "{i}", &opid ) == LBER_ERROR ) {
		rs->sr_text = "message ID parse failed";
		return LDAP_PROTOCOL_ERROR;
	}

	Statslog( LDAP_DEBUG_STATS, "%s CANCEL msg=%d\n",
		op->o_log_prefix, opid, 0, 0, 0 );

	if ( opid < 0 ) {
		rs->sr_text = "message ID invalid";
		return LDAP_PROTOCOL_ERROR;
	}

	ldap_pvt_thread_mutex_lock( &op->o_conn->c_mutex );

	if ( op->o_abandon ) {
		/* FIXME: Should instead reject the cancel/abandon of this op, but
		 * it seems unsafe to reset op->o_abandon once it is set. ITS#6138.
		 */
		rc = LDAP_OPERATIONS_ERROR;
		rs->sr_text = "tried to abandon or cancel this operation";
		goto out;
	}

	LDAP_STAILQ_FOREACH( o, &op->o_conn->c_pending_ops, o_next ) {
		if ( o->o_msgid == opid ) {
			/* TODO: We could instead remove the cancelled operation
			 * from c_pending_ops like Abandon does, and send its
			 * response here.  Not if it is pending because of a
			 * congested connection though.
			 */
			rc = LDAP_CANNOT_CANCEL;
			rs->sr_text = "too busy for Cancel, try Abandon instead";
			goto out;
		}
	}

	LDAP_STAILQ_FOREACH( o, &op->o_conn->c_ops, o_next ) {
		if ( o->o_msgid == opid ) {
			break;
		}
	}

	if ( o == NULL ) {
	 	rc = LDAP_NO_SUCH_OPERATION;
		rs->sr_text = "message ID not found";

	} else if ( o->o_tag == LDAP_REQ_BIND
			|| o->o_tag == LDAP_REQ_UNBIND
			|| o->o_tag == LDAP_REQ_ABANDON ) {
		rc = LDAP_CANNOT_CANCEL;

	} else if ( o->o_cancel != SLAP_CANCEL_NONE ) {
		rc = LDAP_OPERATIONS_ERROR;
		rs->sr_text = "message ID already being cancelled";

#if 0
	} else if ( o->o_abandon ) {
		/* TODO: Would this break something when
		 * o_abandon="suppress response"? (ITS#6138)
		 */
		rc = LDAP_TOO_LATE;
#endif

	} else {
		rc = LDAP_SUCCESS;
		o->o_cancel = SLAP_CANCEL_REQ;
		o->o_abandon = 1;
	}

 out:
	ldap_pvt_thread_mutex_unlock( &op->o_conn->c_mutex );

	if ( rc == LDAP_SUCCESS ) {
		LDAP_STAILQ_FOREACH( op->o_bd, &backendDB, be_next ) {
			if( !op->o_bd->be_cancel ) continue;

			op->oq_cancel.rs_msgid = opid;
			if ( op->o_bd->be_cancel( op, rs ) == LDAP_SUCCESS ) {
				return LDAP_SUCCESS;
			}
		}

		do {
			/* Fake a cond_wait with thread_yield, then
			 * verify the result properly mutex-protected.
			 */
			while ( o->o_cancel == SLAP_CANCEL_REQ )
				ldap_pvt_thread_yield();
			ldap_pvt_thread_mutex_lock( &op->o_conn->c_mutex );
			rc = o->o_cancel;
			ldap_pvt_thread_mutex_unlock( &op->o_conn->c_mutex );
		} while ( rc == SLAP_CANCEL_REQ );

		if ( rc == SLAP_CANCEL_ACK ) {
			rc = LDAP_SUCCESS;
		}

		o->o_cancel = SLAP_CANCEL_DONE;
	}

	return rc;
}

A flaw was discovered in OpenLDAP before 2.4.57 leading to an infinite loop in slapd with the cancel_extop Cancel operation, resulting in denial of service.


int cancel_extop( Operation *op, SlapReply *rs )
{
	Operation *o;
	int rc;
	int opid;
	BerElementBuffer berbuf;
	BerElement *ber = (BerElement *)&berbuf;

	assert( ber_bvcmp( &slap_EXOP_CANCEL, &op->ore_reqoid ) == 0 );

	if ( op->ore_reqdata == NULL ) {
		rs->sr_text = "no message ID supplied";
		return LDAP_PROTOCOL_ERROR;
	}

	if ( op->ore_reqdata->bv_len == 0 ) {
		rs->sr_text = "empty request data field";
		return LDAP_PROTOCOL_ERROR;
	}

	/* ber_init2 uses reqdata directly, doesn't allocate new buffers */
	ber_init2( ber, op->ore_reqdata, 0 );

	if ( ber_scanf( ber, "{i}", &opid ) == LBER_ERROR ) {
		rs->sr_text = "message ID parse failed";
		return LDAP_PROTOCOL_ERROR;
	}

	Statslog( LDAP_DEBUG_STATS, "%s CANCEL msg=%d\n",
		op->o_log_prefix, opid, 0, 0, 0 );

	if ( opid < 0 ) {
		rs->sr_text = "message ID invalid";
		return LDAP_PROTOCOL_ERROR;
	}

	if ( opid == op->o_msgid ) {
		op->o_cancel = SLAP_CANCEL_DONE;
		return LDAP_SUCCESS;
	}

	ldap_pvt_thread_mutex_lock( &op->o_conn->c_mutex );

	if ( op->o_abandon ) {
		/* FIXME: Should instead reject the cancel/abandon of this op, but
		 * it seems unsafe to reset op->o_abandon once it is set. ITS#6138.
		 */
		rc = LDAP_OPERATIONS_ERROR;
		rs->sr_text = "tried to abandon or cancel this operation";
		goto out;
	}

	LDAP_STAILQ_FOREACH( o, &op->o_conn->c_pending_ops, o_next ) {
		if ( o->o_msgid == opid ) {
			/* TODO: We could instead remove the cancelled operation
			 * from c_pending_ops like Abandon does, and send its
			 * response here.  Not if it is pending because of a
			 * congested connection though.
			 */
			rc = LDAP_CANNOT_CANCEL;
			rs->sr_text = "too busy for Cancel, try Abandon instead";
			goto out;
		}
	}

	LDAP_STAILQ_FOREACH( o, &op->o_conn->c_ops, o_next ) {
		if ( o->o_msgid == opid ) {
			break;
		}
	}

	if ( o == NULL ) {
	 	rc = LDAP_NO_SUCH_OPERATION;
		rs->sr_text = "message ID not found";

	} else if ( o->o_tag == LDAP_REQ_BIND
			|| o->o_tag == LDAP_REQ_UNBIND
			|| o->o_tag == LDAP_REQ_ABANDON ) {
		rc = LDAP_CANNOT_CANCEL;

	} else if ( o->o_cancel != SLAP_CANCEL_NONE ) {
		rc = LDAP_OPERATIONS_ERROR;
		rs->sr_text = "message ID already being cancelled";

#if 0
	} else if ( o->o_abandon ) {
		/* TODO: Would this break something when
		 * o_abandon="suppress response"? (ITS#6138)
		 */
		rc = LDAP_TOO_LATE;
#endif

	} else {
		rc = LDAP_SUCCESS;
		o->o_cancel = SLAP_CANCEL_REQ;
		o->o_abandon = 1;
	}

 out:
	ldap_pvt_thread_mutex_unlock( &op->o_conn->c_mutex );

	if ( rc == LDAP_SUCCESS ) {
		LDAP_STAILQ_FOREACH( op->o_bd, &backendDB, be_next ) {
			if( !op->o_bd->be_cancel ) continue;

			op->oq_cancel.rs_msgid = opid;
			if ( op->o_bd->be_cancel( op, rs ) == LDAP_SUCCESS ) {
				return LDAP_SUCCESS;
			}
		}

		do {
			/* Fake a cond_wait with thread_yield, then
			 * verify the result properly mutex-protected.
			 */
			while ( o->o_cancel == SLAP_CANCEL_REQ )
				ldap_pvt_thread_yield();
			ldap_pvt_thread_mutex_lock( &op->o_conn->c_mutex );
			rc = o->o_cancel;
			ldap_pvt_thread_mutex_unlock( &op->o_conn->c_mutex );
		} while ( rc == SLAP_CANCEL_REQ );

		if ( rc == SLAP_CANCEL_ACK ) {
			rc = LDAP_SUCCESS;
		}

		o->o_cancel = SLAP_CANCEL_DONE;
	}

	return rc;
}


double-check unpackedBuffer created in DWA uncompress

Signed-off-by: Peter Hillman <peterh@wetafx.co.nz>

DwaCompressor::uncompress
    (const char *inPtr,
     int inSize,
     IMATH_NAMESPACE::Box2i range,
     const char *&outPtr)
{
    int minX = range.min.x;
    int maxX = std::min (range.max.x, _max[0]);
    int minY = range.min.y;
    int maxY = std::min (range.max.y, _max[1]);

    Int64 iSize = static_cast<Int64>( inSize );
    Int64 headerSize = NUM_SIZES_SINGLE*sizeof(Int64);
    if (iSize < headerSize)
    {
        throw IEX_NAMESPACE::InputExc("Error uncompressing DWA data"
                            "(truncated header).");
    }

    //
    // Flip the counters from XDR to NATIVE
    //

    for (int i = 0; i < NUM_SIZES_SINGLE; ++i)
    {
        Int64      *dst =  (((Int64 *)inPtr) + i);
        const char *src = (char *)(((Int64 *)inPtr) + i);

        Xdr::read<CharPtrIO> (src, *dst);
    }

    //
    // Unwind all the counter info
    //

    const Int64 *inPtr64 = (const Int64*) inPtr;

    Int64 version                  = *(inPtr64 + VERSION);
    Int64 unknownUncompressedSize  = *(inPtr64 + UNKNOWN_UNCOMPRESSED_SIZE);
    Int64 unknownCompressedSize    = *(inPtr64 + UNKNOWN_COMPRESSED_SIZE);
    Int64 acCompressedSize         = *(inPtr64 + AC_COMPRESSED_SIZE);
    Int64 dcCompressedSize         = *(inPtr64 + DC_COMPRESSED_SIZE);
    Int64 rleCompressedSize        = *(inPtr64 + RLE_COMPRESSED_SIZE);
    Int64 rleUncompressedSize      = *(inPtr64 + RLE_UNCOMPRESSED_SIZE);
    Int64 rleRawSize               = *(inPtr64 + RLE_RAW_SIZE);

    Int64 totalAcUncompressedCount = *(inPtr64 + AC_UNCOMPRESSED_COUNT);
    Int64 totalDcUncompressedCount = *(inPtr64 + DC_UNCOMPRESSED_COUNT);

    Int64 acCompression            = *(inPtr64 + AC_COMPRESSION);

    Int64 compressedSize           = unknownCompressedSize +
                                     acCompressedSize +
                                     dcCompressedSize +
                                     rleCompressedSize;

    const char *dataPtr            = inPtr + NUM_SIZES_SINGLE * sizeof(Int64);

    /* Both the sum and individual sizes are checked in case of overflow. */
    if (iSize < (headerSize + compressedSize) ||
        iSize < unknownCompressedSize ||
        iSize < acCompressedSize ||
        iSize < dcCompressedSize ||
        iSize < rleCompressedSize)
    {
        throw IEX_NAMESPACE::InputExc("Error uncompressing DWA data"
                            "(truncated file).");
    }

    if ((SInt64)unknownUncompressedSize < 0  ||
        (SInt64)unknownCompressedSize < 0    ||
        (SInt64)acCompressedSize < 0         ||
        (SInt64)dcCompressedSize < 0         ||
        (SInt64)rleCompressedSize < 0        ||
        (SInt64)rleUncompressedSize < 0      ||
        (SInt64)rleRawSize < 0               ||
        (SInt64)totalAcUncompressedCount < 0 ||
        (SInt64)totalDcUncompressedCount < 0)
    {
        throw IEX_NAMESPACE::InputExc("Error uncompressing DWA data"
                            " (corrupt header).");
    }

    if (version < 2)
        initializeLegacyChannelRules();
    else
    {
        unsigned short ruleSize = 0;
        Xdr::read<CharPtrIO>(dataPtr, ruleSize);

        if (ruleSize < Xdr::size<unsigned short>() )
            throw IEX_NAMESPACE::InputExc("Error uncompressing DWA data"
                                " (corrupt header file).");

        headerSize += ruleSize;
        if (iSize < headerSize + compressedSize)
            throw IEX_NAMESPACE::InputExc("Error uncompressing DWA data"
                                " (truncated file).");

        _channelRules.clear();
        ruleSize -= Xdr::size<unsigned short> ();
        while (ruleSize > 0)
        {
            Classifier rule(dataPtr, ruleSize);

            _channelRules.push_back(rule);
            ruleSize -= rule.size();
        }
    }

    size_t outBufferSize = 0;
    initializeBuffers(outBufferSize);

    //
    // Allocate _outBuffer, if we haven't done so already
    //

    if (static_cast<size_t>(_maxScanLineSize * numScanLines()) > _outBufferSize)
    {
        _outBufferSize = static_cast<size_t>(_maxScanLineSize * numScanLines());
        if (_outBuffer != 0)
            delete[] _outBuffer;
        _outBuffer = new char[_maxScanLineSize * numScanLines()];
    }

    char *outBufferEnd = _outBuffer;

    //
    // Find the start of the RLE packed AC components and
    // the DC components for each channel. This will be handy
    // if you want to decode the channels in parallel later on.
    //

    char *packedAcBufferEnd = 0;

    if (_packedAcBuffer)
        packedAcBufferEnd = _packedAcBuffer;

    char *packedDcBufferEnd = 0;

    if (_packedDcBuffer)
        packedDcBufferEnd = _packedDcBuffer;

    //
    // UNKNOWN data is packed first, followed by the
    // Huffman-compressed AC, then the DC values,
    // and then the zlib compressed RLE data.
    //

    const char *compressedUnknownBuf = dataPtr;

    const char *compressedAcBuf      = compressedUnknownBuf +
                                  static_cast<ptrdiff_t>(unknownCompressedSize);
    const char *compressedDcBuf      = compressedAcBuf +
                                  static_cast<ptrdiff_t>(acCompressedSize);
    const char *compressedRleBuf     = compressedDcBuf +
                                  static_cast<ptrdiff_t>(dcCompressedSize);

    //
    // Sanity check that the version is something we expect. Right now,
    // we can decode version 0, 1, and 2. v1 adds 'end of block' symbols
    // to the AC RLE. v2 adds channel classification rules at the
    // start of the data block.
    //

    if (version > 2)
        throw IEX_NAMESPACE::InputExc ("Invalid version of compressed data block");

    setupChannelData(minX, minY, maxX, maxY);

    //
    // Uncompress the UNKNOWN data into _planarUncBuffer[UNKNOWN]
    //

    if (unknownCompressedSize > 0)
    {
        if (unknownUncompressedSize > _planarUncBufferSize[UNKNOWN])
        {
            throw IEX_NAMESPACE::InputExc("Error uncompressing DWA data"
                                "(corrupt header).");
        }

        uLongf outSize = (uLongf)unknownUncompressedSize;

        if (Z_OK != ::uncompress
                        ((Bytef *)_planarUncBuffer[UNKNOWN],
                         &outSize,
                         (Bytef *)compressedUnknownBuf,
                         (uLong)unknownCompressedSize))
        {
            throw IEX_NAMESPACE::BaseExc("Error uncompressing UNKNOWN data.");
        }
    }

    //
    // Uncompress the AC data into _packedAcBuffer
    //

    if (acCompressedSize > 0)
    {
        if (totalAcUncompressedCount*sizeof(unsigned short) > _packedAcBufferSize)
        {
            throw IEX_NAMESPACE::InputExc("Error uncompressing DWA data"
                                "(corrupt header).");
        }

        //
        // Don't trust the user to get it right, look in the file.
        //

        switch (acCompression)
        {
          case STATIC_HUFFMAN:

            hufUncompress
                (compressedAcBuf,
                 (int)acCompressedSize,
                 (unsigned short *)_packedAcBuffer,
                 (int)totalAcUncompressedCount);

            break;

          case DEFLATE:
            {
                uLongf destLen =
                    (int)(totalAcUncompressedCount) * sizeof (unsigned short);

                if (Z_OK != ::uncompress
                                ((Bytef *)_packedAcBuffer,
                                 &destLen,
                                 (Bytef *)compressedAcBuf,
                                 (uLong)acCompressedSize))
                {
                    throw IEX_NAMESPACE::InputExc ("Data decompression (zlib) failed.");
                }

                if (totalAcUncompressedCount * sizeof (unsigned short) !=
                                destLen)
                {
                    throw IEX_NAMESPACE::InputExc ("AC data corrupt.");
                }
            }
            break;

          default:

            throw IEX_NAMESPACE::NoImplExc ("Unknown AC Compression");
            break;
        }
    }

    //
    // Uncompress the DC data into _packedDcBuffer
    //

    if (dcCompressedSize > 0)
    {
        if (totalDcUncompressedCount*sizeof(unsigned short) > _packedDcBufferSize)
        {
            throw IEX_NAMESPACE::InputExc("Error uncompressing DWA data"
                                "(corrupt header).");
        }

        if (static_cast<Int64>(_zip->uncompress
                    (compressedDcBuf, (int)dcCompressedSize, _packedDcBuffer))
            != totalDcUncompressedCount * sizeof (unsigned short))
        {
            throw IEX_NAMESPACE::BaseExc("DC data corrupt.");
        }
    }

    //
    // Uncompress the RLE data into _rleBuffer, then unRLE the results
    // into _planarUncBuffer[RLE]
    //

    if (rleRawSize > 0)
    {
        if (rleUncompressedSize > _rleBufferSize ||
            rleRawSize > _planarUncBufferSize[RLE])
        {
            throw IEX_NAMESPACE::InputExc("Error uncompressing DWA data"
                                "(corrupt header).");
        }

        uLongf dstLen = (uLongf)rleUncompressedSize;

        if (Z_OK != ::uncompress
                        ((Bytef *)_rleBuffer,
                         &dstLen,
                         (Bytef *)compressedRleBuf,
                         (uLong)rleCompressedSize))
        {
            throw IEX_NAMESPACE::BaseExc("Error uncompressing RLE data.");
        }

        if (dstLen != rleUncompressedSize)
            throw IEX_NAMESPACE::BaseExc("RLE data corrupted");

        if (static_cast<Int64>(rleUncompress
                ((int)rleUncompressedSize,
                 (int)rleRawSize,
                 (signed char *)_rleBuffer,
                 _planarUncBuffer[RLE])) != rleRawSize)
        {
            throw IEX_NAMESPACE::BaseExc("RLE data corrupted");
        }
    }

    //
    // Determine the start of each row in the output buffer
    //

    std::vector<bool> decodedChannels (_channelData.size());
    std::vector< std::vector<char *> > rowPtrs (_channelData.size());

    for (unsigned int chan = 0; chan < _channelData.size(); ++chan)
        decodedChannels[chan] = false;

    outBufferEnd = _outBuffer;

    for (int y = minY; y <= maxY; ++y)
    {
        for (unsigned int chan = 0; chan < _channelData.size(); ++chan)
        {
            ChannelData *cd = &_channelData[chan];

            if (IMATH_NAMESPACE::modp (y, cd->ySampling) != 0)
                continue;

            rowPtrs[chan].push_back (outBufferEnd);
            outBufferEnd += cd->width * OPENEXR_IMF_NAMESPACE::pixelTypeSize (cd->type);
        }
    }

    //
    // Setup to decode each block of 3 channels that need to
    // be handled together
    //

    for (unsigned int csc = 0; csc < _cscSets.size(); ++csc)
    {
        int rChan = _cscSets[csc].idx[0];
        int gChan = _cscSets[csc].idx[1];
        int bChan = _cscSets[csc].idx[2];

        LossyDctDecoderCsc decoder
            (rowPtrs[rChan],
             rowPtrs[gChan],
             rowPtrs[bChan],
             packedAcBufferEnd,
             packedDcBufferEnd,
             dwaCompressorToLinear,
             _channelData[rChan].width,
             _channelData[rChan].height,
             _channelData[rChan].type,
             _channelData[gChan].type,
             _channelData[bChan].type);

        decoder.execute();

        packedAcBufferEnd +=
            decoder.numAcValuesEncoded() * sizeof (unsigned short);

        packedDcBufferEnd +=
            decoder.numDcValuesEncoded() * sizeof (unsigned short);

        decodedChannels[rChan] = true;
        decodedChannels[gChan] = true;
        decodedChannels[bChan] = true;
    }

    //
    // Setup to handle the remaining channels by themselves
    //

    for (unsigned int chan = 0; chan < _channelData.size(); ++chan)
    {
        if (decodedChannels[chan])
            continue;

        ChannelData *cd = &_channelData[chan];
        int pixelSize = OPENEXR_IMF_NAMESPACE::pixelTypeSize (cd->type);

        switch (cd->compression)
        {
          case LOSSY_DCT:

            //
            // Setup a single-channel lossy DCT decoder pointing
            // at the output buffer
            //

            {
                const unsigned short *linearLut = 0;

                if (!cd->pLinear)
                    linearLut = dwaCompressorToLinear;

                LossyDctDecoder decoder
                    (rowPtrs[chan],
                     packedAcBufferEnd,
                     packedDcBufferEnd,
                     linearLut,
                     cd->width,
                     cd->height,
                     cd->type);

                decoder.execute();

                packedAcBufferEnd +=
                    decoder.numAcValuesEncoded() * sizeof (unsigned short);

                packedDcBufferEnd +=
                    decoder.numDcValuesEncoded() * sizeof (unsigned short);
            }

            break;

          case RLE:

            //
            // For the RLE case, the data has been un-RLE'd into
            // planarUncRleEnd[], but is still split out by bytes.
            // We need to rearrange the bytes back into the correct
            // order in the output buffer;
            //

            {
                int row = 0;

                for (int y = minY; y <= maxY; ++y)
                {
                    if (IMATH_NAMESPACE::modp (y, cd->ySampling) != 0)
                        continue;

                    char *dst = rowPtrs[chan][row];

                    if (pixelSize == 2)
                    {
                        interleaveByte2 (dst,
                                         cd->planarUncRleEnd[0],
                                         cd->planarUncRleEnd[1],
                                         cd->width);

                        cd->planarUncRleEnd[0] += cd->width;
                        cd->planarUncRleEnd[1] += cd->width;
                    }
                    else
                    {
                        for (int x = 0; x < cd->width; ++x)
                        {
                            for (int byte = 0; byte < pixelSize; ++byte)
                            {
                               *dst++ = *cd->planarUncRleEnd[byte]++;
                            }
                        }
                    }

                    row++;
                }
            }

            break;

          case UNKNOWN:

            //
            // In the UNKNOWN case, data is already in planarUncBufferEnd
            // and just needs to copied over to the output buffer
            //

            {
                int row             = 0;
                int dstScanlineSize = cd->width * OPENEXR_IMF_NAMESPACE::pixelTypeSize (cd->type);

                for (int y = minY; y <= maxY; ++y)
                {
                    if (IMATH_NAMESPACE::modp (y, cd->ySampling) != 0)
                        continue;

                    //
                    // sanity check for buffer data lying within range
                    //
                    if ((cd->planarUncBufferEnd + static_cast<size_t>(dstScanlineSize))  > (_planarUncBuffer[UNKNOWN] + _planarUncBufferSize[UNKNOWN]) )
                    {
                        throw Iex::InputExc("DWA data corrupt");
                    }

                    memcpy (rowPtrs[chan][row],
                            cd->planarUncBufferEnd,
                            dstScanlineSize);

                    cd->planarUncBufferEnd += dstScanlineSize;
                    row++;
                }
            }

            break;

          default:

            throw IEX_NAMESPACE::NoImplExc ("Unhandled compression scheme case");
            break;
        }

        decodedChannels[chan] = true;
    }

    //
    // Return a ptr to _outBuffer
    //

    outPtr = _outBuffer;
    return (int)(outBufferEnd - _outBuffer);
}

A flaw was found in OpenEXR in versions before 3.0.0-beta. A crafted input file supplied by an attacker, that is processed by the Dwa decompression functionality of OpenEXR's IlmImf library, could cause a NULL pointer dereference. The highest threat from this vulnerability is to system availability.