  sfnt_init_face( FT_Stream      stream,
                  TT_Face        face,
                  FT_Int         face_instance_index,
                  FT_Int         num_params,
                  FT_Parameter*  params )
  {
    FT_Error      error;
    FT_Memory     memory = face->root.memory;
    FT_Library    library = face->root.driver->root.library;
    SFNT_Service  sfnt;
    FT_Int        face_index;

    /* for now, parameters are unused */
    FT_UNUSED( num_params );
    FT_UNUSED( params );

    sfnt = (SFNT_Service)face->sfnt;
    if ( !sfnt )
    {
      sfnt = (SFNT_Service)FT_Get_Module_Interface( library, "sfnt" );
      if ( !sfnt )
      {
        FT_ERROR(( "sfnt_init_face: cannot access `sfnt' module\n" ));
        return FT_THROW( Missing_Module );
      }

      face->sfnt       = sfnt;
      face->goto_table = sfnt->goto_table;
    }

    FT_FACE_FIND_GLOBAL_SERVICE( face, face->psnames, POSTSCRIPT_CMAPS );

#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
    if ( !face->mm )
    {
      /* we want the MM interface from the `truetype' module only */
      FT_Module  tt_module = FT_Get_Module( library, "truetype" );

      face->mm = ft_module_get_service( tt_module,
                                        FT_SERVICE_ID_MULTI_MASTERS,
                                        0 );
    }

    if ( !face->var )
    {
      /* we want the metrics variations interface */
      /* from the `truetype' module only          */
      FT_Module  tt_module = FT_Get_Module( library, "truetype" );

      face->var = ft_module_get_service( tt_module,
                                         FT_SERVICE_ID_METRICS_VARIATIONS,
                                         0 );
    }
#endif

    FT_TRACE2(( "SFNT driver\n" ));

    error = sfnt_open_font( stream, face );
    if ( error )
      return error;

    /* Stream may have changed in sfnt_open_font. */
    stream = face->root.stream;

    FT_TRACE2(( "sfnt_init_face: %08p, %d\n", face, face_instance_index ));

    face_index = FT_ABS( face_instance_index ) & 0xFFFF;

    /* value -(N+1) requests information on index N */
    if ( face_instance_index < 0 )
      face_index--;

    if ( face_index >= face->ttc_header.count )
    {
      if ( face_instance_index >= 0 )
        return FT_THROW( Invalid_Argument );
      else
        face_index = 0;
    }

    if ( FT_STREAM_SEEK( face->ttc_header.offsets[face_index] ) )
      return error;

    /* check whether we have a valid TrueType file */
    error = sfnt->load_font_dir( face, stream );
    if ( error )
      return error;

#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
    {
      FT_ULong  fvar_len;

      FT_ULong  version;
      FT_ULong  offset;

      FT_UShort  num_axes;
      FT_UShort  axis_size;
      FT_UShort  num_instances;
      FT_UShort  instance_size;

      FT_Int  instance_index;

      FT_Byte*  default_values  = NULL;
      FT_Byte*  instance_values = NULL;

      face->is_default_instance = 1;

      instance_index = FT_ABS( face_instance_index ) >> 16;

      /* test whether current face is a GX font with named instances */
      if ( face->goto_table( face, TTAG_fvar, stream, &fvar_len ) ||
           fvar_len < 20                                          ||
           FT_READ_ULONG( version )                               ||
           FT_READ_USHORT( offset )                               ||
           FT_STREAM_SKIP( 2 ) /* reserved */                     ||
           FT_READ_USHORT( num_axes )                             ||
           FT_READ_USHORT( axis_size )                            ||
           FT_READ_USHORT( num_instances )                        ||
           FT_READ_USHORT( instance_size )                        )
      {
        version       = 0;
        offset        = 0;
        num_axes      = 0;
        axis_size     = 0;
        num_instances = 0;
        instance_size = 0;
      }

      /* check that the data is bound by the table length */
      if ( version != 0x00010000UL                    ||
           axis_size != 20                            ||
           num_axes == 0                              ||
           /* `num_axes' limit implied by 16-bit `instance_size' */
           num_axes > 0x3FFE                          ||
           !( instance_size == 4 + 4 * num_axes ||
              instance_size == 6 + 4 * num_axes )     ||
           /* `num_instances' limit implied by limited range of name IDs */
           num_instances > 0x7EFF                     ||
           offset                          +
             axis_size * num_axes          +
             instance_size * num_instances > fvar_len )
        num_instances = 0;
      else
        face->variation_support |= TT_FACE_FLAG_VAR_FVAR;

      /*
       *  As documented in the OpenType specification, an entry for the
       *  default instance may be omitted in the named instance table.  In
       *  particular this means that even if there is no named instance
       *  table in the font we actually do have a named instance, namely the
       *  default instance.
       *
       *  For consistency, we always want the default instance in our list
       *  of named instances.  If it is missing, we try to synthesize it
       *  later on.  Here, we have to adjust `num_instances' accordingly.
       */

      if ( !( FT_ALLOC( default_values, num_axes * 2 )  ||
              FT_ALLOC( instance_values, num_axes * 2 ) ) )
      {
        /* the current stream position is 16 bytes after the table start */
        FT_ULong  array_start = FT_STREAM_POS() - 16 + offset;
        FT_ULong  default_value_offset, instance_offset;

        FT_Byte*  p;
        FT_UInt   i;

        default_value_offset = array_start + 8;
        p                    = default_values;

        for ( i = 0; i < num_axes; i++ )
        {
          (void)FT_STREAM_READ_AT( default_value_offset, p, 2 );

          default_value_offset += axis_size;
          p                    += 2;
        }

        instance_offset = array_start + axis_size * num_axes + 4;

        for ( i = 0; i < num_instances; i++ )
        {
          (void)FT_STREAM_READ_AT( instance_offset,
                                   instance_values,
                                   num_axes * 2 );

          if ( !ft_memcmp( default_values, instance_values, num_axes * 2 ) )
            break;

          instance_offset += instance_size;
        }

        if ( i == num_instances )
        {
          /* no default instance in named instance table; */
          /* we thus have to synthesize it                */
          num_instances++;
        }
      }

       FT_FREE( default_values );
       FT_FREE( instance_values );

      /* we don't support Multiple Master CFFs yet */
       if ( face->goto_table( face, TTAG_glyf, stream, 0 ) &&
            !face->goto_table( face, TTAG_CFF, stream, 0 ) )
         num_instances = 0;
      if ( instance_index > num_instances )
      {
        if ( face_instance_index >= 0 )
          return FT_THROW( Invalid_Argument );
        else
          num_instances = 0;
      }

      face->root.style_flags = (FT_Long)num_instances << 16;
    }
#endif

    face->root.num_faces  = face->ttc_header.count;
    face->root.face_index = face_instance_index;

    return error;
  }

FreeType 2 before 2017-03-08 has an out-of-bounds write caused by a heap-based buffer overflow related to the TT_Get_MM_Var function in truetype/ttgxvar.c and the sfnt_init_face function in sfnt/sfobjs.c.


  sfnt_init_face( FT_Stream      stream,
                  TT_Face        face,
                  FT_Int         face_instance_index,
                  FT_Int         num_params,
                  FT_Parameter*  params )
  {
    FT_Error      error;
    FT_Memory     memory = face->root.memory;
    FT_Library    library = face->root.driver->root.library;
    SFNT_Service  sfnt;
    FT_Int        face_index;

    /* for now, parameters are unused */
    FT_UNUSED( num_params );
    FT_UNUSED( params );

    sfnt = (SFNT_Service)face->sfnt;
    if ( !sfnt )
    {
      sfnt = (SFNT_Service)FT_Get_Module_Interface( library, "sfnt" );
      if ( !sfnt )
      {
        FT_ERROR(( "sfnt_init_face: cannot access `sfnt' module\n" ));
        return FT_THROW( Missing_Module );
      }

      face->sfnt       = sfnt;
      face->goto_table = sfnt->goto_table;
    }

    FT_FACE_FIND_GLOBAL_SERVICE( face, face->psnames, POSTSCRIPT_CMAPS );

#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
    if ( !face->mm )
    {
      /* we want the MM interface from the `truetype' module only */
      FT_Module  tt_module = FT_Get_Module( library, "truetype" );

      face->mm = ft_module_get_service( tt_module,
                                        FT_SERVICE_ID_MULTI_MASTERS,
                                        0 );
    }

    if ( !face->var )
    {
      /* we want the metrics variations interface */
      /* from the `truetype' module only          */
      FT_Module  tt_module = FT_Get_Module( library, "truetype" );

      face->var = ft_module_get_service( tt_module,
                                         FT_SERVICE_ID_METRICS_VARIATIONS,
                                         0 );
    }
#endif

    FT_TRACE2(( "SFNT driver\n" ));

    error = sfnt_open_font( stream, face );
    if ( error )
      return error;

    /* Stream may have changed in sfnt_open_font. */
    stream = face->root.stream;

    FT_TRACE2(( "sfnt_init_face: %08p, %d\n", face, face_instance_index ));

    face_index = FT_ABS( face_instance_index ) & 0xFFFF;

    /* value -(N+1) requests information on index N */
    if ( face_instance_index < 0 )
      face_index--;

    if ( face_index >= face->ttc_header.count )
    {
      if ( face_instance_index >= 0 )
        return FT_THROW( Invalid_Argument );
      else
        face_index = 0;
    }

    if ( FT_STREAM_SEEK( face->ttc_header.offsets[face_index] ) )
      return error;

    /* check whether we have a valid TrueType file */
    error = sfnt->load_font_dir( face, stream );
    if ( error )
      return error;

#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
    {
      FT_ULong  fvar_len;

      FT_ULong  version;
      FT_ULong  offset;

      FT_UShort  num_axes;
      FT_UShort  axis_size;
      FT_UShort  num_instances;
      FT_UShort  instance_size;

      FT_Int  instance_index;

      FT_Byte*  default_values  = NULL;
      FT_Byte*  instance_values = NULL;

      face->is_default_instance = 1;

      instance_index = FT_ABS( face_instance_index ) >> 16;

      /* test whether current face is a GX font with named instances */
      if ( face->goto_table( face, TTAG_fvar, stream, &fvar_len ) ||
           fvar_len < 20                                          ||
           FT_READ_ULONG( version )                               ||
           FT_READ_USHORT( offset )                               ||
           FT_STREAM_SKIP( 2 ) /* reserved */                     ||
           FT_READ_USHORT( num_axes )                             ||
           FT_READ_USHORT( axis_size )                            ||
           FT_READ_USHORT( num_instances )                        ||
           FT_READ_USHORT( instance_size )                        )
      {
        version       = 0;
        offset        = 0;
        num_axes      = 0;
        axis_size     = 0;
        num_instances = 0;
        instance_size = 0;
      }

      /* check that the data is bound by the table length */
      if ( version != 0x00010000UL                    ||
           axis_size != 20                            ||
           num_axes == 0                              ||
           /* `num_axes' limit implied by 16-bit `instance_size' */
           num_axes > 0x3FFE                          ||
           !( instance_size == 4 + 4 * num_axes ||
              instance_size == 6 + 4 * num_axes )     ||
           /* `num_instances' limit implied by limited range of name IDs */
           num_instances > 0x7EFF                     ||
           offset                          +
             axis_size * num_axes          +
             instance_size * num_instances > fvar_len )
        num_instances = 0;
      else
        face->variation_support |= TT_FACE_FLAG_VAR_FVAR;

      /*
       *  As documented in the OpenType specification, an entry for the
       *  default instance may be omitted in the named instance table.  In
       *  particular this means that even if there is no named instance
       *  table in the font we actually do have a named instance, namely the
       *  default instance.
       *
       *  For consistency, we always want the default instance in our list
       *  of named instances.  If it is missing, we try to synthesize it
       *  later on.  Here, we have to adjust `num_instances' accordingly.
       */

      if ( !( FT_ALLOC( default_values, num_axes * 2 )  ||
              FT_ALLOC( instance_values, num_axes * 2 ) ) )
      {
        /* the current stream position is 16 bytes after the table start */
        FT_ULong  array_start = FT_STREAM_POS() - 16 + offset;
        FT_ULong  default_value_offset, instance_offset;

        FT_Byte*  p;
        FT_UInt   i;

        default_value_offset = array_start + 8;
        p                    = default_values;

        for ( i = 0; i < num_axes; i++ )
        {
          (void)FT_STREAM_READ_AT( default_value_offset, p, 2 );

          default_value_offset += axis_size;
          p                    += 2;
        }

        instance_offset = array_start + axis_size * num_axes + 4;

        for ( i = 0; i < num_instances; i++ )
        {
          (void)FT_STREAM_READ_AT( instance_offset,
                                   instance_values,
                                   num_axes * 2 );

          if ( !ft_memcmp( default_values, instance_values, num_axes * 2 ) )
            break;

          instance_offset += instance_size;
        }

        if ( i == num_instances )
        {
          /* no default instance in named instance table; */
          /* we thus have to synthesize it                */
          num_instances++;
        }
      }

       FT_FREE( default_values );
       FT_FREE( instance_values );

      /* we don't support Multiple Master CFFs yet; */
      /* note that `glyf' or `CFF2' have precedence */
       if ( face->goto_table( face, TTAG_glyf, stream, 0 ) &&
           face->goto_table( face, TTAG_CFF2, stream, 0 ) &&
            !face->goto_table( face, TTAG_CFF, stream, 0 ) )
         num_instances = 0;
      if ( instance_index > num_instances )
      {
        if ( face_instance_index >= 0 )
          return FT_THROW( Invalid_Argument );
        else
          num_instances = 0;
      }

      face->root.style_flags = (FT_Long)num_instances << 16;
    }
#endif

    face->root.num_faces  = face->ttc_header.count;
    face->root.face_index = face_instance_index;

    return error;
  }


pdfunite: Fix crash with broken documents

Sometimes we can't parse pages so check before accessing them

Thanks to Jiaqi Peng for the report

Fixes bugs #101153 and #101149

int main (int argc, char *argv[])
{
  int objectsCount = 0;
  Guint numOffset = 0;
  std::vector<Object> pages;
  std::vector<Guint> offsets;
  XRef *yRef, *countRef;
  FILE *f;
  OutStream *outStr;
  int i;
  int j, rootNum;
  std::vector<PDFDoc *>docs;
  int majorVersion = 0;
  int minorVersion = 0;
  char *fileName = argv[argc - 1];
  int exitCode;

  exitCode = 99;
  const GBool ok = parseArgs (argDesc, &argc, argv);
  if (!ok || argc < 3 || printVersion || printHelp) {
    fprintf(stderr, "pdfunite version %s\n", PACKAGE_VERSION);
    fprintf(stderr, "%s\n", popplerCopyright);
    fprintf(stderr, "%s\n", xpdfCopyright);
    if (!printVersion) {
      printUsage("pdfunite", "<PDF-sourcefile-1>..<PDF-sourcefile-n> <PDF-destfile>",
	argDesc);
    }
    if (printVersion || printHelp)
      exitCode = 0;
    return exitCode;
  }
  exitCode = 0;
  globalParams = new GlobalParams();

  for (i = 1; i < argc - 1; i++) {
    GooString *gfileName = new GooString(argv[i]);
    PDFDoc *doc = new PDFDoc(gfileName, NULL, NULL, NULL);
    if (doc->isOk() && !doc->isEncrypted()) {
      docs.push_back(doc);
      if (doc->getPDFMajorVersion() > majorVersion) {
        majorVersion = doc->getPDFMajorVersion();
        minorVersion = doc->getPDFMinorVersion();
      } else if (doc->getPDFMajorVersion() == majorVersion) {
        if (doc->getPDFMinorVersion() > minorVersion) {
          minorVersion = doc->getPDFMinorVersion();
        }
      }
    } else if (doc->isOk()) {
      error(errUnimplemented, -1, "Could not merge encrypted files ('{0:s}')", argv[i]);
      return -1;
    } else {
      error(errSyntaxError, -1, "Could not merge damaged documents ('{0:s}')", argv[i]);
      return -1;
    }
  }

  if (!(f = fopen(fileName, "wb"))) {
    error(errIO, -1, "Could not open file '{0:s}'", fileName);
    return -1;
  }
  outStr = new FileOutStream(f, 0);

  yRef = new XRef();
  countRef = new XRef();
  yRef->add(0, 65535, 0, gFalse);
  PDFDoc::writeHeader(outStr, majorVersion, minorVersion);

  Object intents;
  Object afObj;
  Object ocObj;
  Object names;
  if (docs.size() >= 1) {
    Object catObj;
    docs[0]->getXRef()->getCatalog(&catObj);
    Dict *catDict = catObj.getDict();
     catDict->lookup("OutputIntents", &intents);
     catDict->lookupNF("AcroForm", &afObj);
     Ref *refPage = docs[0]->getCatalog()->getPageRef(1);
    if (!afObj.isNull()) {
       docs[0]->markAcroForm(&afObj, yRef, countRef, 0, refPage->num, refPage->num);
     }
     catDict->lookupNF("OCProperties", &ocObj);
    if (!ocObj.isNull() && ocObj.isDict()) {
       docs[0]->markPageObjects(ocObj.getDict(), yRef, countRef, 0, refPage->num, refPage->num);
     }
     catDict->lookup("Names", &names);
    if (!names.isNull() && names.isDict()) {
       docs[0]->markPageObjects(names.getDict(), yRef, countRef, 0, refPage->num, refPage->num);
     }
     if (intents.isArray() && intents.arrayGetLength() > 0) {
      for (i = 1; i < (int) docs.size(); i++) {
        Object pagecatObj, pageintents;
        docs[i]->getXRef()->getCatalog(&pagecatObj);
        Dict *pagecatDict = pagecatObj.getDict();
        pagecatDict->lookup("OutputIntents", &pageintents);
        if (pageintents.isArray() && pageintents.arrayGetLength() > 0) {
          for (j = intents.arrayGetLength() - 1; j >= 0; j--) {
            Object intent;
            intents.arrayGet(j, &intent, 0);
            if (intent.isDict()) {
              Object idf;
              intent.dictLookup("OutputConditionIdentifier", &idf);
              if (idf.isString()) {
                GooString *gidf = idf.getString();
                GBool removeIntent = gTrue;
                for (int k = 0; k < pageintents.arrayGetLength(); k++) {
                  Object pgintent;
                  pageintents.arrayGet(k, &pgintent, 0);
                  if (pgintent.isDict()) {
                    Object pgidf;
                    pgintent.dictLookup("OutputConditionIdentifier", &pgidf);
                    if (pgidf.isString()) {
                      GooString *gpgidf = pgidf.getString();
                      if (gpgidf->cmp(gidf) == 0) {
                        pgidf.free();
                        removeIntent = gFalse;
                        break;
                      }
                    }
                    pgidf.free();
                  }
                }
                if (removeIntent) {
                  intents.arrayRemove(j);
                  error(errSyntaxWarning, -1, "Output intent {0:s} missing in pdf {1:s}, removed",
                   gidf->getCString(), docs[i]->getFileName()->getCString());
                }
              } else {
                intents.arrayRemove(j);
                error(errSyntaxWarning, -1, "Invalid output intent dict, missing required OutputConditionIdentifier");
              }
              idf.free();
            } else {
              intents.arrayRemove(j);
            }
            intent.free();
          }
        } else {
          error(errSyntaxWarning, -1, "Output intents differs, remove them all");
          intents.free();
          break;
        }
        pagecatObj.free();
        pageintents.free();
      }
    }
    if (intents.isArray() && intents.arrayGetLength() > 0) {
      for (j = intents.arrayGetLength() - 1; j >= 0; j--) {
        Object intent;
        intents.arrayGet(j, &intent, 0);
        if (intent.isDict()) {
          docs[0]->markPageObjects(intent.getDict(), yRef, countRef, numOffset, 0, 0);
        } else {
          intents.arrayRemove(j);
        }
        intent.free();
      }
    }
    catObj.free();
  }

   for (i = 0; i < (int) docs.size(); i++) {
     for (j = 1; j <= docs[i]->getNumPages(); j++) {
       PDFRectangle *cropBox = NULL;
       if (docs[i]->getCatalog()->getPage(j)->isCropped())
         cropBox = docs[i]->getCatalog()->getPage(j)->getCropBox();
      Object page;
      docs[i]->getXRef()->fetch(refPage->num, refPage->gen, &page);
      Dict *pageDict = page.getDict();
      Dict *resDict = docs[i]->getCatalog()->getPage(j)->getResourceDict();
      if (resDict) {
        Object *newResource = new Object();
        newResource->initDict(resDict);
        pageDict->set("Resources", newResource);
        delete newResource;
      }
      pages.push_back(page);
      offsets.push_back(numOffset);
      docs[i]->markPageObjects(pageDict, yRef, countRef, numOffset, refPage->num, refPage->num);
      Object annotsObj;
      pageDict->lookupNF("Annots", &annotsObj);
      if (!annotsObj.isNull()) {
        docs[i]->markAnnotations(&annotsObj, yRef, countRef, numOffset, refPage->num, refPage->num);
        annotsObj.free();
      }
    }
    Object pageCatObj, pageNames, pageForm;
    docs[i]->getXRef()->getCatalog(&pageCatObj);
    Dict *pageCatDict = pageCatObj.getDict();
    pageCatDict->lookup("Names", &pageNames);
    if (!pageNames.isNull() && pageNames.isDict()) {
      if (!names.isDict()) {
        names.free();
        names.initDict(yRef);
      }
      doMergeNameDict(docs[i], yRef, countRef, 0, 0, names.getDict(), pageNames.getDict(), numOffset);
    }
    pageCatDict->lookup("AcroForm", &pageForm);
    if (i > 0 && !pageForm.isNull() && pageForm.isDict()) {
      if (afObj.isNull()) {
        pageCatDict->lookupNF("AcroForm", &afObj);
      } else if (afObj.isDict()) {
        doMergeFormDict(afObj.getDict(), pageForm.getDict(), numOffset);
      }
    }
    pageForm.free();
    pageNames.free();
    pageCatObj.free();
    objectsCount += docs[i]->writePageObjects(outStr, yRef, numOffset, gTrue);
    numOffset = yRef->getNumObjects() + 1;
  }

  rootNum = yRef->getNumObjects() + 1;
  yRef->add(rootNum, 0, outStr->getPos(), gTrue);
  outStr->printf("%d 0 obj\n", rootNum);
  outStr->printf("<< /Type /Catalog /Pages %d 0 R", rootNum + 1);
  if (intents.isArray() && intents.arrayGetLength() > 0) {
    outStr->printf(" /OutputIntents [");
    for (j = 0; j < intents.arrayGetLength(); j++) {
      Object intent;
      intents.arrayGet(j, &intent, 0);
      if (intent.isDict()) {
        PDFDoc::writeObject(&intent, outStr, yRef, 0, NULL, cryptRC4, 0, 0, 0);
      }
      intent.free();
    }
    outStr->printf("]");
  }
  intents.free();
  if (!afObj.isNull()) {
    outStr->printf(" /AcroForm ");
    PDFDoc::writeObject(&afObj, outStr, yRef, 0, NULL, cryptRC4, 0, 0, 0);
    afObj.free();
  }
  if (!ocObj.isNull() && ocObj.isDict()) {
    outStr->printf(" /OCProperties ");
    PDFDoc::writeObject(&ocObj, outStr, yRef, 0, NULL, cryptRC4, 0, 0, 0);
    ocObj.free();
  }
  if (!names.isNull() && names.isDict()) {
    outStr->printf(" /Names ");
    PDFDoc::writeObject(&names, outStr, yRef, 0, NULL, cryptRC4, 0, 0, 0);
    names.free();
  }
  outStr->printf(">>\nendobj\n");
  objectsCount++;

  yRef->add(rootNum + 1, 0, outStr->getPos(), gTrue);
  outStr->printf("%d 0 obj\n", rootNum + 1);
  outStr->printf("<< /Type /Pages /Kids [");
  for (j = 0; j < (int) pages.size(); j++)
    outStr->printf(" %d 0 R", rootNum + j + 2);
  outStr->printf(" ] /Count %zd >>\nendobj\n", pages.size());
  objectsCount++;

  for (i = 0; i < (int) pages.size(); i++) {
    yRef->add(rootNum + i + 2, 0, outStr->getPos(), gTrue);
    outStr->printf("%d 0 obj\n", rootNum + i + 2);
    outStr->printf("<< ");
    Dict *pageDict = pages[i].getDict();
    for (j = 0; j < pageDict->getLength(); j++) {
      if (j > 0)
	outStr->printf(" ");
      const char *key = pageDict->getKey(j);
      Object value;
      pageDict->getValNF(j, &value);
      if (strcmp(key, "Parent") == 0) {
        outStr->printf("/Parent %d 0 R", rootNum + 1);
      } else {
        outStr->printf("/%s ", key);
        PDFDoc::writeObject(&value, outStr, yRef, offsets[i], NULL, cryptRC4, 0, 0, 0);
      }
      value.free();
    }
    outStr->printf(" >>\nendobj\n");
    objectsCount++;
  }
  Goffset uxrefOffset = outStr->getPos();
  Ref ref;
  ref.num = rootNum;
  ref.gen = 0;
  Dict *trailerDict = PDFDoc::createTrailerDict(objectsCount, gFalse, 0, &ref, yRef,
                                                fileName, outStr->getPos());
  PDFDoc::writeXRefTableTrailer(trailerDict, yRef, gTrue, // write all entries according to ISO 32000-1, 7.5.4 Cross-Reference Table: "For a file that has never been incrementally updated, the cross-reference section shall contain only one subsection, whose object numbering begins at 0."
                                uxrefOffset, outStr, yRef);
  delete trailerDict;

  outStr->close();
  delete outStr;
  fclose(f);
  delete yRef;
  delete countRef;
  for (j = 0; j < (int) pages.size (); j++) pages[j].free();
  for (i = 0; i < (int) docs.size (); i++) delete docs[i];
  delete globalParams;
  return exitCode;
}

poppler since version 0.17.3 has been vulnerable to NULL pointer dereference in pdfunite triggered by specially crafted documents.


int main (int argc, char *argv[])
{
  int objectsCount = 0;
  Guint numOffset = 0;
  std::vector<Object> pages;
  std::vector<Guint> offsets;
  XRef *yRef, *countRef;
  FILE *f;
  OutStream *outStr;
  int i;
  int j, rootNum;
  std::vector<PDFDoc *>docs;
  int majorVersion = 0;
  int minorVersion = 0;
  char *fileName = argv[argc - 1];
  int exitCode;

  exitCode = 99;
  const GBool ok = parseArgs (argDesc, &argc, argv);
  if (!ok || argc < 3 || printVersion || printHelp) {
    fprintf(stderr, "pdfunite version %s\n", PACKAGE_VERSION);
    fprintf(stderr, "%s\n", popplerCopyright);
    fprintf(stderr, "%s\n", xpdfCopyright);
    if (!printVersion) {
      printUsage("pdfunite", "<PDF-sourcefile-1>..<PDF-sourcefile-n> <PDF-destfile>",
	argDesc);
    }
    if (printVersion || printHelp)
      exitCode = 0;
    return exitCode;
  }
  exitCode = 0;
  globalParams = new GlobalParams();

  for (i = 1; i < argc - 1; i++) {
    GooString *gfileName = new GooString(argv[i]);
    PDFDoc *doc = new PDFDoc(gfileName, NULL, NULL, NULL);
    if (doc->isOk() && !doc->isEncrypted()) {
      docs.push_back(doc);
      if (doc->getPDFMajorVersion() > majorVersion) {
        majorVersion = doc->getPDFMajorVersion();
        minorVersion = doc->getPDFMinorVersion();
      } else if (doc->getPDFMajorVersion() == majorVersion) {
        if (doc->getPDFMinorVersion() > minorVersion) {
          minorVersion = doc->getPDFMinorVersion();
        }
      }
    } else if (doc->isOk()) {
      error(errUnimplemented, -1, "Could not merge encrypted files ('{0:s}')", argv[i]);
      return -1;
    } else {
      error(errSyntaxError, -1, "Could not merge damaged documents ('{0:s}')", argv[i]);
      return -1;
    }
  }

  if (!(f = fopen(fileName, "wb"))) {
    error(errIO, -1, "Could not open file '{0:s}'", fileName);
    return -1;
  }
  outStr = new FileOutStream(f, 0);

  yRef = new XRef();
  countRef = new XRef();
  yRef->add(0, 65535, 0, gFalse);
  PDFDoc::writeHeader(outStr, majorVersion, minorVersion);

  Object intents;
  Object afObj;
  Object ocObj;
  Object names;
  if (docs.size() >= 1) {
    Object catObj;
    docs[0]->getXRef()->getCatalog(&catObj);
    Dict *catDict = catObj.getDict();
     catDict->lookup("OutputIntents", &intents);
     catDict->lookupNF("AcroForm", &afObj);
     Ref *refPage = docs[0]->getCatalog()->getPageRef(1);
    if (!afObj.isNull() && refPage) {
       docs[0]->markAcroForm(&afObj, yRef, countRef, 0, refPage->num, refPage->num);
     }
     catDict->lookupNF("OCProperties", &ocObj);
    if (!ocObj.isNull() && ocObj.isDict() && refPage) {
       docs[0]->markPageObjects(ocObj.getDict(), yRef, countRef, 0, refPage->num, refPage->num);
     }
     catDict->lookup("Names", &names);
    if (!names.isNull() && names.isDict() && refPage) {
       docs[0]->markPageObjects(names.getDict(), yRef, countRef, 0, refPage->num, refPage->num);
     }
     if (intents.isArray() && intents.arrayGetLength() > 0) {
      for (i = 1; i < (int) docs.size(); i++) {
        Object pagecatObj, pageintents;
        docs[i]->getXRef()->getCatalog(&pagecatObj);
        Dict *pagecatDict = pagecatObj.getDict();
        pagecatDict->lookup("OutputIntents", &pageintents);
        if (pageintents.isArray() && pageintents.arrayGetLength() > 0) {
          for (j = intents.arrayGetLength() - 1; j >= 0; j--) {
            Object intent;
            intents.arrayGet(j, &intent, 0);
            if (intent.isDict()) {
              Object idf;
              intent.dictLookup("OutputConditionIdentifier", &idf);
              if (idf.isString()) {
                GooString *gidf = idf.getString();
                GBool removeIntent = gTrue;
                for (int k = 0; k < pageintents.arrayGetLength(); k++) {
                  Object pgintent;
                  pageintents.arrayGet(k, &pgintent, 0);
                  if (pgintent.isDict()) {
                    Object pgidf;
                    pgintent.dictLookup("OutputConditionIdentifier", &pgidf);
                    if (pgidf.isString()) {
                      GooString *gpgidf = pgidf.getString();
                      if (gpgidf->cmp(gidf) == 0) {
                        pgidf.free();
                        removeIntent = gFalse;
                        break;
                      }
                    }
                    pgidf.free();
                  }
                }
                if (removeIntent) {
                  intents.arrayRemove(j);
                  error(errSyntaxWarning, -1, "Output intent {0:s} missing in pdf {1:s}, removed",
                   gidf->getCString(), docs[i]->getFileName()->getCString());
                }
              } else {
                intents.arrayRemove(j);
                error(errSyntaxWarning, -1, "Invalid output intent dict, missing required OutputConditionIdentifier");
              }
              idf.free();
            } else {
              intents.arrayRemove(j);
            }
            intent.free();
          }
        } else {
          error(errSyntaxWarning, -1, "Output intents differs, remove them all");
          intents.free();
          break;
        }
        pagecatObj.free();
        pageintents.free();
      }
    }
    if (intents.isArray() && intents.arrayGetLength() > 0) {
      for (j = intents.arrayGetLength() - 1; j >= 0; j--) {
        Object intent;
        intents.arrayGet(j, &intent, 0);
        if (intent.isDict()) {
          docs[0]->markPageObjects(intent.getDict(), yRef, countRef, numOffset, 0, 0);
        } else {
          intents.arrayRemove(j);
        }
        intent.free();
      }
    }
    catObj.free();
  }

   for (i = 0; i < (int) docs.size(); i++) {
     for (j = 1; j <= docs[i]->getNumPages(); j++) {
      if (!docs[i]->getCatalog()->getPage(j)) {
        continue;
      }

       PDFRectangle *cropBox = NULL;
       if (docs[i]->getCatalog()->getPage(j)->isCropped())
         cropBox = docs[i]->getCatalog()->getPage(j)->getCropBox();
      Object page;
      docs[i]->getXRef()->fetch(refPage->num, refPage->gen, &page);
      Dict *pageDict = page.getDict();
      Dict *resDict = docs[i]->getCatalog()->getPage(j)->getResourceDict();
      if (resDict) {
        Object *newResource = new Object();
        newResource->initDict(resDict);
        pageDict->set("Resources", newResource);
        delete newResource;
      }
      pages.push_back(page);
      offsets.push_back(numOffset);
      docs[i]->markPageObjects(pageDict, yRef, countRef, numOffset, refPage->num, refPage->num);
      Object annotsObj;
      pageDict->lookupNF("Annots", &annotsObj);
      if (!annotsObj.isNull()) {
        docs[i]->markAnnotations(&annotsObj, yRef, countRef, numOffset, refPage->num, refPage->num);
        annotsObj.free();
      }
    }
    Object pageCatObj, pageNames, pageForm;
    docs[i]->getXRef()->getCatalog(&pageCatObj);
    Dict *pageCatDict = pageCatObj.getDict();
    pageCatDict->lookup("Names", &pageNames);
    if (!pageNames.isNull() && pageNames.isDict()) {
      if (!names.isDict()) {
        names.free();
        names.initDict(yRef);
      }
      doMergeNameDict(docs[i], yRef, countRef, 0, 0, names.getDict(), pageNames.getDict(), numOffset);
    }
    pageCatDict->lookup("AcroForm", &pageForm);
    if (i > 0 && !pageForm.isNull() && pageForm.isDict()) {
      if (afObj.isNull()) {
        pageCatDict->lookupNF("AcroForm", &afObj);
      } else if (afObj.isDict()) {
        doMergeFormDict(afObj.getDict(), pageForm.getDict(), numOffset);
      }
    }
    pageForm.free();
    pageNames.free();
    pageCatObj.free();
    objectsCount += docs[i]->writePageObjects(outStr, yRef, numOffset, gTrue);
    numOffset = yRef->getNumObjects() + 1;
  }

  rootNum = yRef->getNumObjects() + 1;
  yRef->add(rootNum, 0, outStr->getPos(), gTrue);
  outStr->printf("%d 0 obj\n", rootNum);
  outStr->printf("<< /Type /Catalog /Pages %d 0 R", rootNum + 1);
  if (intents.isArray() && intents.arrayGetLength() > 0) {
    outStr->printf(" /OutputIntents [");
    for (j = 0; j < intents.arrayGetLength(); j++) {
      Object intent;
      intents.arrayGet(j, &intent, 0);
      if (intent.isDict()) {
        PDFDoc::writeObject(&intent, outStr, yRef, 0, NULL, cryptRC4, 0, 0, 0);
      }
      intent.free();
    }
    outStr->printf("]");
  }
  intents.free();
  if (!afObj.isNull()) {
    outStr->printf(" /AcroForm ");
    PDFDoc::writeObject(&afObj, outStr, yRef, 0, NULL, cryptRC4, 0, 0, 0);
    afObj.free();
  }
  if (!ocObj.isNull() && ocObj.isDict()) {
    outStr->printf(" /OCProperties ");
    PDFDoc::writeObject(&ocObj, outStr, yRef, 0, NULL, cryptRC4, 0, 0, 0);
    ocObj.free();
  }
  if (!names.isNull() && names.isDict()) {
    outStr->printf(" /Names ");
    PDFDoc::writeObject(&names, outStr, yRef, 0, NULL, cryptRC4, 0, 0, 0);
    names.free();
  }
  outStr->printf(">>\nendobj\n");
  objectsCount++;

  yRef->add(rootNum + 1, 0, outStr->getPos(), gTrue);
  outStr->printf("%d 0 obj\n", rootNum + 1);
  outStr->printf("<< /Type /Pages /Kids [");
  for (j = 0; j < (int) pages.size(); j++)
    outStr->printf(" %d 0 R", rootNum + j + 2);
  outStr->printf(" ] /Count %zd >>\nendobj\n", pages.size());
  objectsCount++;

  for (i = 0; i < (int) pages.size(); i++) {
    yRef->add(rootNum + i + 2, 0, outStr->getPos(), gTrue);
    outStr->printf("%d 0 obj\n", rootNum + i + 2);
    outStr->printf("<< ");
    Dict *pageDict = pages[i].getDict();
    for (j = 0; j < pageDict->getLength(); j++) {
      if (j > 0)
	outStr->printf(" ");
      const char *key = pageDict->getKey(j);
      Object value;
      pageDict->getValNF(j, &value);
      if (strcmp(key, "Parent") == 0) {
        outStr->printf("/Parent %d 0 R", rootNum + 1);
      } else {
        outStr->printf("/%s ", key);
        PDFDoc::writeObject(&value, outStr, yRef, offsets[i], NULL, cryptRC4, 0, 0, 0);
      }
      value.free();
    }
    outStr->printf(" >>\nendobj\n");
    objectsCount++;
  }
  Goffset uxrefOffset = outStr->getPos();
  Ref ref;
  ref.num = rootNum;
  ref.gen = 0;
  Dict *trailerDict = PDFDoc::createTrailerDict(objectsCount, gFalse, 0, &ref, yRef,
                                                fileName, outStr->getPos());
  PDFDoc::writeXRefTableTrailer(trailerDict, yRef, gTrue, // write all entries according to ISO 32000-1, 7.5.4 Cross-Reference Table: "For a file that has never been incrementally updated, the cross-reference section shall contain only one subsection, whose object numbering begins at 0."
                                uxrefOffset, outStr, yRef);
  delete trailerDict;

  outStr->close();
  delete outStr;
  fclose(f);
  delete yRef;
  delete countRef;
  for (j = 0; j < (int) pages.size (); j++) pages[j].free();
  for (i = 0; i < (int) docs.size (); i++) delete docs[i];
  delete globalParams;
  return exitCode;
}


main (int argc _GL_UNUSED, char **argv)
{
  struct timespec result;
  struct timespec result2;
  struct timespec expected;
  struct timespec now;
  const char *p;
  int i;
  long gmtoff;
  time_t ref_time = 1304250918;

  /* Set the time zone to US Eastern time with the 2012 rules.  This
     should disable any leap second support.  Otherwise, there will be
     a problem with glibc on sites that default to leap seconds; see
     <http://bugs.gnu.org/12206>.  */
  setenv ("TZ", "EST5EDT,M3.2.0,M11.1.0", 1);

  gmtoff = gmt_offset (ref_time);

  /* ISO 8601 extended date and time of day representation,
     'T' separator, local time zone */
  p = "2011-05-01T11:55:18";
  expected.tv_sec = ref_time - gmtoff;
  expected.tv_nsec = 0;
  ASSERT (parse_datetime (&result, p, 0));
  LOG (p, expected, result);
  ASSERT (expected.tv_sec == result.tv_sec
          && expected.tv_nsec == result.tv_nsec);

  /* ISO 8601 extended date and time of day representation,
     ' ' separator, local time zone */
  p = "2011-05-01 11:55:18";
  expected.tv_sec = ref_time - gmtoff;
  expected.tv_nsec = 0;
  ASSERT (parse_datetime (&result, p, 0));
  LOG (p, expected, result);
  ASSERT (expected.tv_sec == result.tv_sec
          && expected.tv_nsec == result.tv_nsec);

  /* ISO 8601, extended date and time of day representation,
     'T' separator, UTC */
  p = "2011-05-01T11:55:18Z";
  expected.tv_sec = ref_time;
  expected.tv_nsec = 0;
  ASSERT (parse_datetime (&result, p, 0));
  LOG (p, expected, result);
  ASSERT (expected.tv_sec == result.tv_sec
          && expected.tv_nsec == result.tv_nsec);

  /* ISO 8601, extended date and time of day representation,
     ' ' separator, UTC */
  p = "2011-05-01 11:55:18Z";
  expected.tv_sec = ref_time;
  expected.tv_nsec = 0;
  ASSERT (parse_datetime (&result, p, 0));
  LOG (p, expected, result);
  ASSERT (expected.tv_sec == result.tv_sec
          && expected.tv_nsec == result.tv_nsec);

  /* ISO 8601 extended date and time of day representation,
     'T' separator, w/UTC offset */
  p = "2011-05-01T11:55:18-07:00";
  expected.tv_sec = 1304276118;
  expected.tv_nsec = 0;
  ASSERT (parse_datetime (&result, p, 0));
  LOG (p, expected, result);
  ASSERT (expected.tv_sec == result.tv_sec
          && expected.tv_nsec == result.tv_nsec);

  /* ISO 8601 extended date and time of day representation,
     ' ' separator, w/UTC offset */
  p = "2011-05-01 11:55:18-07:00";
  expected.tv_sec = 1304276118;
  expected.tv_nsec = 0;
  ASSERT (parse_datetime (&result, p, 0));
  LOG (p, expected, result);
  ASSERT (expected.tv_sec == result.tv_sec
          && expected.tv_nsec == result.tv_nsec);

  /* ISO 8601 extended date and time of day representation,
     'T' separator, w/hour only UTC offset */
  p = "2011-05-01T11:55:18-07";
  expected.tv_sec = 1304276118;
  expected.tv_nsec = 0;
  ASSERT (parse_datetime (&result, p, 0));
  LOG (p, expected, result);
  ASSERT (expected.tv_sec == result.tv_sec
          && expected.tv_nsec == result.tv_nsec);

  /* ISO 8601 extended date and time of day representation,
     ' ' separator, w/hour only UTC offset */
  p = "2011-05-01 11:55:18-07";
  expected.tv_sec = 1304276118;
  expected.tv_nsec = 0;
  ASSERT (parse_datetime (&result, p, 0));
  LOG (p, expected, result);
  ASSERT (expected.tv_sec == result.tv_sec
          && expected.tv_nsec == result.tv_nsec);

  now.tv_sec = 4711;
  now.tv_nsec = 1267;
  p = "now";
  ASSERT (parse_datetime (&result, p, &now));
  LOG (p, now, result);
  ASSERT (now.tv_sec == result.tv_sec && now.tv_nsec == result.tv_nsec);

  now.tv_sec = 4711;
  now.tv_nsec = 1267;
  p = "tomorrow";
  ASSERT (parse_datetime (&result, p, &now));
  LOG (p, now, result);
  ASSERT (now.tv_sec + 24 * 60 * 60 == result.tv_sec
          && now.tv_nsec == result.tv_nsec);

  now.tv_sec = 4711;
  now.tv_nsec = 1267;
  p = "yesterday";
  ASSERT (parse_datetime (&result, p, &now));
  LOG (p, now, result);
  ASSERT (now.tv_sec - 24 * 60 * 60 == result.tv_sec
          && now.tv_nsec == result.tv_nsec);

  now.tv_sec = 4711;
  now.tv_nsec = 1267;
  p = "4 hours";
  ASSERT (parse_datetime (&result, p, &now));
  LOG (p, now, result);
  ASSERT (now.tv_sec + 4 * 60 * 60 == result.tv_sec
          && now.tv_nsec == result.tv_nsec);

  /* test if timezone is not being ignored for day offset */
  now.tv_sec = 4711;
  now.tv_nsec = 1267;
  p = "UTC+400 +24 hours";
  ASSERT (parse_datetime (&result, p, &now));
  LOG (p, now, result);
  p = "UTC+400 +1 day";
  ASSERT (parse_datetime (&result2, p, &now));
  LOG (p, now, result2);
  ASSERT (result.tv_sec == result2.tv_sec
          && result.tv_nsec == result2.tv_nsec);

  /* test if several time zones formats are handled same way */
  now.tv_sec = 4711;
  now.tv_nsec = 1267;
  p = "UTC+14:00";
  ASSERT (parse_datetime (&result, p, &now));
  LOG (p, now, result);
  p = "UTC+14";
  ASSERT (parse_datetime (&result2, p, &now));
  LOG (p, now, result2);
  ASSERT (result.tv_sec == result2.tv_sec
          && result.tv_nsec == result2.tv_nsec);
  p = "UTC+1400";
  ASSERT (parse_datetime (&result2, p, &now));
  LOG (p, now, result2);
  ASSERT (result.tv_sec == result2.tv_sec
          && result.tv_nsec == result2.tv_nsec);

  now.tv_sec = 4711;
  now.tv_nsec = 1267;
  p = "UTC-14:00";
  ASSERT (parse_datetime (&result, p, &now));
  LOG (p, now, result);
  p = "UTC-14";
  ASSERT (parse_datetime (&result2, p, &now));
  LOG (p, now, result2);
  ASSERT (result.tv_sec == result2.tv_sec
          && result.tv_nsec == result2.tv_nsec);
  p = "UTC-1400";
  ASSERT (parse_datetime (&result2, p, &now));
  LOG (p, now, result2);
  ASSERT (result.tv_sec == result2.tv_sec
          && result.tv_nsec == result2.tv_nsec);

  now.tv_sec = 4711;
  now.tv_nsec = 1267;
  p = "UTC+0:15";
  ASSERT (parse_datetime (&result, p, &now));
  LOG (p, now, result);
  p = "UTC+0015";
  ASSERT (parse_datetime (&result2, p, &now));
  LOG (p, now, result2);
  ASSERT (result.tv_sec == result2.tv_sec
          && result.tv_nsec == result2.tv_nsec);

  now.tv_sec = 4711;
  now.tv_nsec = 1267;
  p = "UTC-1:30";
  ASSERT (parse_datetime (&result, p, &now));
  LOG (p, now, result);
  p = "UTC-130";
  ASSERT (parse_datetime (&result2, p, &now));
  LOG (p, now, result2);
  ASSERT (result.tv_sec == result2.tv_sec
          && result.tv_nsec == result2.tv_nsec);

  /* TZ out of range should cause parse_datetime failure */
  now.tv_sec = 4711;
  now.tv_nsec = 1267;
  p = "UTC+25:00";
  ASSERT (!parse_datetime (&result, p, &now));

        /* Check for several invalid countable dayshifts */
  now.tv_sec = 4711;
  now.tv_nsec = 1267;
  p = "UTC+4:00 +40 yesterday";
  ASSERT (!parse_datetime (&result, p, &now));
  p = "UTC+4:00 next yesterday";
  ASSERT (!parse_datetime (&result, p, &now));
  p = "UTC+4:00 tomorrow ago";
  ASSERT (!parse_datetime (&result, p, &now));
  p = "UTC+4:00 tomorrow hence";
  ASSERT (!parse_datetime (&result, p, &now));
  p = "UTC+4:00 40 now ago";
  ASSERT (!parse_datetime (&result, p, &now));
  p = "UTC+4:00 last tomorrow";
  ASSERT (!parse_datetime (&result, p, &now));
  p = "UTC+4:00 -4 today";
  ASSERT (!parse_datetime (&result, p, &now));

  /* And check correct usage of dayshifts */
  now.tv_sec = 4711;
  now.tv_nsec = 1267;
  p = "UTC+400 tomorrow";
  ASSERT (parse_datetime (&result, p, &now));
  LOG (p, now, result);
  p = "UTC+400 +1 day";
  ASSERT (parse_datetime (&result2, p, &now));
  LOG (p, now, result2);
  ASSERT (result.tv_sec == result2.tv_sec
          && result.tv_nsec == result2.tv_nsec);
  p = "UTC+400 1 day hence";
  ASSERT (parse_datetime (&result2, p, &now));
  LOG (p, now, result2);
  ASSERT (result.tv_sec == result2.tv_sec
          && result.tv_nsec == result2.tv_nsec);
  now.tv_sec = 4711;
  now.tv_nsec = 1267;
  p = "UTC+400 yesterday";
  ASSERT (parse_datetime (&result, p, &now));
  LOG (p, now, result);
  p = "UTC+400 1 day ago";
  ASSERT (parse_datetime (&result2, p, &now));
  LOG (p, now, result2);
  ASSERT (result.tv_sec == result2.tv_sec
          && result.tv_nsec == result2.tv_nsec);
  now.tv_sec = 4711;
  now.tv_nsec = 1267;
  p = "UTC+400 now";
  ASSERT (parse_datetime (&result, p, &now));
  LOG (p, now, result);
  p = "UTC+400 +0 minutes"; /* silly, but simple "UTC+400" is different*/
  ASSERT (parse_datetime (&result2, p, &now));
  LOG (p, now, result2);
  ASSERT (result.tv_sec == result2.tv_sec
          && result.tv_nsec == result2.tv_nsec);

  /* Check that some "next Monday", "last Wednesday", etc. are correct.  */
  setenv ("TZ", "UTC0", 1);
  for (i = 0; day_table[i]; i++)
    {
      unsigned int thur2 = 7 * 24 * 3600; /* 2nd thursday */
      char tmp[32];
      sprintf (tmp, "NEXT %s", day_table[i]);
      now.tv_sec = thur2 + 4711;
      now.tv_nsec = 1267;
      ASSERT (parse_datetime (&result, tmp, &now));
      LOG (tmp, now, result);
      ASSERT (result.tv_nsec == 0);
      ASSERT (result.tv_sec == thur2 + (i == 4 ? 7 : (i + 3) % 7) * 24 * 3600);

      sprintf (tmp, "LAST %s", day_table[i]);
      now.tv_sec = thur2 + 4711;
      now.tv_nsec = 1267;
      ASSERT (parse_datetime (&result, tmp, &now));
      LOG (tmp, now, result);
      ASSERT (result.tv_nsec == 0);
      ASSERT (result.tv_sec == thur2 + ((i + 3) % 7 - 7) * 24 * 3600);
    }

  p = "THURSDAY UTC+00";  /* The epoch was on Thursday.  */
  now.tv_sec = 0;
  now.tv_nsec = 0;
  ASSERT (parse_datetime (&result, p, &now));
  LOG (p, now, result);
  ASSERT (result.tv_sec == now.tv_sec
          && result.tv_nsec == now.tv_nsec);

  p = "FRIDAY UTC+00";
  now.tv_sec = 0;
  now.tv_nsec = 0;
  ASSERT (parse_datetime (&result, p, &now));
  LOG (p, now, result);
  ASSERT (result.tv_sec == 24 * 3600
          && result.tv_nsec == now.tv_nsec);

  /* Exercise a sign-extension bug.  Before July 2012, an input
     starting with a high-bit-set byte would be treated like "0".  */
  ASSERT ( ! parse_datetime (&result, "\xb0", &now));

  /* Exercise TZ="" parsing code.  */
  /* These two would infloop or segfault before Feb 2014.  */
  ASSERT ( ! parse_datetime (&result, "TZ=\"\"\"", &now));
  ASSERT ( ! parse_datetime (&result, "TZ=\"\" \"", &now));
  /* Exercise invalid patterns.  */
  ASSERT ( ! parse_datetime (&result, "TZ=\"", &now));
  ASSERT ( ! parse_datetime (&result, "TZ=\"\\\"", &now));
  ASSERT ( ! parse_datetime (&result, "TZ=\"\\n", &now));
  ASSERT ( ! parse_datetime (&result, "TZ=\"\\n\"", &now));
  /* Exercise valid patterns.  */
  ASSERT (   parse_datetime (&result, "TZ=\"\"", &now));
  ASSERT (   parse_datetime (&result, "TZ=\"\" ", &now));
  ASSERT (   parse_datetime (&result, " TZ=\"\"", &now));
   ASSERT (   parse_datetime (&result, "TZ=\"\\\\\"", &now));
   ASSERT (   parse_datetime (&result, "TZ=\"\\\"\"", &now));

   return 0;
 }

Gnulib before 2017-04-26 has a heap-based buffer overflow with the TZ environment variable. The error is in the save_abbr function in time_rz.c.


main (int argc _GL_UNUSED, char **argv)
{
  struct timespec result;
  struct timespec result2;
  struct timespec expected;
  struct timespec now;
  const char *p;
  int i;
  long gmtoff;
  time_t ref_time = 1304250918;

  /* Set the time zone to US Eastern time with the 2012 rules.  This
     should disable any leap second support.  Otherwise, there will be
     a problem with glibc on sites that default to leap seconds; see
     <http://bugs.gnu.org/12206>.  */
  setenv ("TZ", "EST5EDT,M3.2.0,M11.1.0", 1);

  gmtoff = gmt_offset (ref_time);

  /* ISO 8601 extended date and time of day representation,
     'T' separator, local time zone */
  p = "2011-05-01T11:55:18";
  expected.tv_sec = ref_time - gmtoff;
  expected.tv_nsec = 0;
  ASSERT (parse_datetime (&result, p, 0));
  LOG (p, expected, result);
  ASSERT (expected.tv_sec == result.tv_sec
          && expected.tv_nsec == result.tv_nsec);

  /* ISO 8601 extended date and time of day representation,
     ' ' separator, local time zone */
  p = "2011-05-01 11:55:18";
  expected.tv_sec = ref_time - gmtoff;
  expected.tv_nsec = 0;
  ASSERT (parse_datetime (&result, p, 0));
  LOG (p, expected, result);
  ASSERT (expected.tv_sec == result.tv_sec
          && expected.tv_nsec == result.tv_nsec);

  /* ISO 8601, extended date and time of day representation,
     'T' separator, UTC */
  p = "2011-05-01T11:55:18Z";
  expected.tv_sec = ref_time;
  expected.tv_nsec = 0;
  ASSERT (parse_datetime (&result, p, 0));
  LOG (p, expected, result);
  ASSERT (expected.tv_sec == result.tv_sec
          && expected.tv_nsec == result.tv_nsec);

  /* ISO 8601, extended date and time of day representation,
     ' ' separator, UTC */
  p = "2011-05-01 11:55:18Z";
  expected.tv_sec = ref_time;
  expected.tv_nsec = 0;
  ASSERT (parse_datetime (&result, p, 0));
  LOG (p, expected, result);
  ASSERT (expected.tv_sec == result.tv_sec
          && expected.tv_nsec == result.tv_nsec);

  /* ISO 8601 extended date and time of day representation,
     'T' separator, w/UTC offset */
  p = "2011-05-01T11:55:18-07:00";
  expected.tv_sec = 1304276118;
  expected.tv_nsec = 0;
  ASSERT (parse_datetime (&result, p, 0));
  LOG (p, expected, result);
  ASSERT (expected.tv_sec == result.tv_sec
          && expected.tv_nsec == result.tv_nsec);

  /* ISO 8601 extended date and time of day representation,
     ' ' separator, w/UTC offset */
  p = "2011-05-01 11:55:18-07:00";
  expected.tv_sec = 1304276118;
  expected.tv_nsec = 0;
  ASSERT (parse_datetime (&result, p, 0));
  LOG (p, expected, result);
  ASSERT (expected.tv_sec == result.tv_sec
          && expected.tv_nsec == result.tv_nsec);

  /* ISO 8601 extended date and time of day representation,
     'T' separator, w/hour only UTC offset */
  p = "2011-05-01T11:55:18-07";
  expected.tv_sec = 1304276118;
  expected.tv_nsec = 0;
  ASSERT (parse_datetime (&result, p, 0));
  LOG (p, expected, result);
  ASSERT (expected.tv_sec == result.tv_sec
          && expected.tv_nsec == result.tv_nsec);

  /* ISO 8601 extended date and time of day representation,
     ' ' separator, w/hour only UTC offset */
  p = "2011-05-01 11:55:18-07";
  expected.tv_sec = 1304276118;
  expected.tv_nsec = 0;
  ASSERT (parse_datetime (&result, p, 0));
  LOG (p, expected, result);
  ASSERT (expected.tv_sec == result.tv_sec
          && expected.tv_nsec == result.tv_nsec);

  now.tv_sec = 4711;
  now.tv_nsec = 1267;
  p = "now";
  ASSERT (parse_datetime (&result, p, &now));
  LOG (p, now, result);
  ASSERT (now.tv_sec == result.tv_sec && now.tv_nsec == result.tv_nsec);

  now.tv_sec = 4711;
  now.tv_nsec = 1267;
  p = "tomorrow";
  ASSERT (parse_datetime (&result, p, &now));
  LOG (p, now, result);
  ASSERT (now.tv_sec + 24 * 60 * 60 == result.tv_sec
          && now.tv_nsec == result.tv_nsec);

  now.tv_sec = 4711;
  now.tv_nsec = 1267;
  p = "yesterday";
  ASSERT (parse_datetime (&result, p, &now));
  LOG (p, now, result);
  ASSERT (now.tv_sec - 24 * 60 * 60 == result.tv_sec
          && now.tv_nsec == result.tv_nsec);

  now.tv_sec = 4711;
  now.tv_nsec = 1267;
  p = "4 hours";
  ASSERT (parse_datetime (&result, p, &now));
  LOG (p, now, result);
  ASSERT (now.tv_sec + 4 * 60 * 60 == result.tv_sec
          && now.tv_nsec == result.tv_nsec);

  /* test if timezone is not being ignored for day offset */
  now.tv_sec = 4711;
  now.tv_nsec = 1267;
  p = "UTC+400 +24 hours";
  ASSERT (parse_datetime (&result, p, &now));
  LOG (p, now, result);
  p = "UTC+400 +1 day";
  ASSERT (parse_datetime (&result2, p, &now));
  LOG (p, now, result2);
  ASSERT (result.tv_sec == result2.tv_sec
          && result.tv_nsec == result2.tv_nsec);

  /* test if several time zones formats are handled same way */
  now.tv_sec = 4711;
  now.tv_nsec = 1267;
  p = "UTC+14:00";
  ASSERT (parse_datetime (&result, p, &now));
  LOG (p, now, result);
  p = "UTC+14";
  ASSERT (parse_datetime (&result2, p, &now));
  LOG (p, now, result2);
  ASSERT (result.tv_sec == result2.tv_sec
          && result.tv_nsec == result2.tv_nsec);
  p = "UTC+1400";
  ASSERT (parse_datetime (&result2, p, &now));
  LOG (p, now, result2);
  ASSERT (result.tv_sec == result2.tv_sec
          && result.tv_nsec == result2.tv_nsec);

  now.tv_sec = 4711;
  now.tv_nsec = 1267;
  p = "UTC-14:00";
  ASSERT (parse_datetime (&result, p, &now));
  LOG (p, now, result);
  p = "UTC-14";
  ASSERT (parse_datetime (&result2, p, &now));
  LOG (p, now, result2);
  ASSERT (result.tv_sec == result2.tv_sec
          && result.tv_nsec == result2.tv_nsec);
  p = "UTC-1400";
  ASSERT (parse_datetime (&result2, p, &now));
  LOG (p, now, result2);
  ASSERT (result.tv_sec == result2.tv_sec
          && result.tv_nsec == result2.tv_nsec);

  now.tv_sec = 4711;
  now.tv_nsec = 1267;
  p = "UTC+0:15";
  ASSERT (parse_datetime (&result, p, &now));
  LOG (p, now, result);
  p = "UTC+0015";
  ASSERT (parse_datetime (&result2, p, &now));
  LOG (p, now, result2);
  ASSERT (result.tv_sec == result2.tv_sec
          && result.tv_nsec == result2.tv_nsec);

  now.tv_sec = 4711;
  now.tv_nsec = 1267;
  p = "UTC-1:30";
  ASSERT (parse_datetime (&result, p, &now));
  LOG (p, now, result);
  p = "UTC-130";
  ASSERT (parse_datetime (&result2, p, &now));
  LOG (p, now, result2);
  ASSERT (result.tv_sec == result2.tv_sec
          && result.tv_nsec == result2.tv_nsec);

  /* TZ out of range should cause parse_datetime failure */
  now.tv_sec = 4711;
  now.tv_nsec = 1267;
  p = "UTC+25:00";
  ASSERT (!parse_datetime (&result, p, &now));

        /* Check for several invalid countable dayshifts */
  now.tv_sec = 4711;
  now.tv_nsec = 1267;
  p = "UTC+4:00 +40 yesterday";
  ASSERT (!parse_datetime (&result, p, &now));
  p = "UTC+4:00 next yesterday";
  ASSERT (!parse_datetime (&result, p, &now));
  p = "UTC+4:00 tomorrow ago";
  ASSERT (!parse_datetime (&result, p, &now));
  p = "UTC+4:00 tomorrow hence";
  ASSERT (!parse_datetime (&result, p, &now));
  p = "UTC+4:00 40 now ago";
  ASSERT (!parse_datetime (&result, p, &now));
  p = "UTC+4:00 last tomorrow";
  ASSERT (!parse_datetime (&result, p, &now));
  p = "UTC+4:00 -4 today";
  ASSERT (!parse_datetime (&result, p, &now));

  /* And check correct usage of dayshifts */
  now.tv_sec = 4711;
  now.tv_nsec = 1267;
  p = "UTC+400 tomorrow";
  ASSERT (parse_datetime (&result, p, &now));
  LOG (p, now, result);
  p = "UTC+400 +1 day";
  ASSERT (parse_datetime (&result2, p, &now));
  LOG (p, now, result2);
  ASSERT (result.tv_sec == result2.tv_sec
          && result.tv_nsec == result2.tv_nsec);
  p = "UTC+400 1 day hence";
  ASSERT (parse_datetime (&result2, p, &now));
  LOG (p, now, result2);
  ASSERT (result.tv_sec == result2.tv_sec
          && result.tv_nsec == result2.tv_nsec);
  now.tv_sec = 4711;
  now.tv_nsec = 1267;
  p = "UTC+400 yesterday";
  ASSERT (parse_datetime (&result, p, &now));
  LOG (p, now, result);
  p = "UTC+400 1 day ago";
  ASSERT (parse_datetime (&result2, p, &now));
  LOG (p, now, result2);
  ASSERT (result.tv_sec == result2.tv_sec
          && result.tv_nsec == result2.tv_nsec);
  now.tv_sec = 4711;
  now.tv_nsec = 1267;
  p = "UTC+400 now";
  ASSERT (parse_datetime (&result, p, &now));
  LOG (p, now, result);
  p = "UTC+400 +0 minutes"; /* silly, but simple "UTC+400" is different*/
  ASSERT (parse_datetime (&result2, p, &now));
  LOG (p, now, result2);
  ASSERT (result.tv_sec == result2.tv_sec
          && result.tv_nsec == result2.tv_nsec);

  /* Check that some "next Monday", "last Wednesday", etc. are correct.  */
  setenv ("TZ", "UTC0", 1);
  for (i = 0; day_table[i]; i++)
    {
      unsigned int thur2 = 7 * 24 * 3600; /* 2nd thursday */
      char tmp[32];
      sprintf (tmp, "NEXT %s", day_table[i]);
      now.tv_sec = thur2 + 4711;
      now.tv_nsec = 1267;
      ASSERT (parse_datetime (&result, tmp, &now));
      LOG (tmp, now, result);
      ASSERT (result.tv_nsec == 0);
      ASSERT (result.tv_sec == thur2 + (i == 4 ? 7 : (i + 3) % 7) * 24 * 3600);

      sprintf (tmp, "LAST %s", day_table[i]);
      now.tv_sec = thur2 + 4711;
      now.tv_nsec = 1267;
      ASSERT (parse_datetime (&result, tmp, &now));
      LOG (tmp, now, result);
      ASSERT (result.tv_nsec == 0);
      ASSERT (result.tv_sec == thur2 + ((i + 3) % 7 - 7) * 24 * 3600);
    }

  p = "THURSDAY UTC+00";  /* The epoch was on Thursday.  */
  now.tv_sec = 0;
  now.tv_nsec = 0;
  ASSERT (parse_datetime (&result, p, &now));
  LOG (p, now, result);
  ASSERT (result.tv_sec == now.tv_sec
          && result.tv_nsec == now.tv_nsec);

  p = "FRIDAY UTC+00";
  now.tv_sec = 0;
  now.tv_nsec = 0;
  ASSERT (parse_datetime (&result, p, &now));
  LOG (p, now, result);
  ASSERT (result.tv_sec == 24 * 3600
          && result.tv_nsec == now.tv_nsec);

  /* Exercise a sign-extension bug.  Before July 2012, an input
     starting with a high-bit-set byte would be treated like "0".  */
  ASSERT ( ! parse_datetime (&result, "\xb0", &now));

  /* Exercise TZ="" parsing code.  */
  /* These two would infloop or segfault before Feb 2014.  */
  ASSERT ( ! parse_datetime (&result, "TZ=\"\"\"", &now));
  ASSERT ( ! parse_datetime (&result, "TZ=\"\" \"", &now));
  /* Exercise invalid patterns.  */
  ASSERT ( ! parse_datetime (&result, "TZ=\"", &now));
  ASSERT ( ! parse_datetime (&result, "TZ=\"\\\"", &now));
  ASSERT ( ! parse_datetime (&result, "TZ=\"\\n", &now));
  ASSERT ( ! parse_datetime (&result, "TZ=\"\\n\"", &now));
  /* Exercise valid patterns.  */
  ASSERT (   parse_datetime (&result, "TZ=\"\"", &now));
  ASSERT (   parse_datetime (&result, "TZ=\"\" ", &now));
  ASSERT (   parse_datetime (&result, " TZ=\"\"", &now));
   ASSERT (   parse_datetime (&result, "TZ=\"\\\\\"", &now));
   ASSERT (   parse_datetime (&result, "TZ=\"\\\"\"", &now));

  /* Outlandishly-long time zone abbreviations should not cause problems.  */
  {
    static char const bufprefix[] = "TZ=\"";
    enum { tzname_len = 2000 };
    static char const bufsuffix[] = "0\" 1970-01-01 01:02:03.123456789";
    enum { bufsize = sizeof bufprefix - 1 + tzname_len + sizeof bufsuffix };
    char buf[bufsize];
    memcpy (buf, bufprefix, sizeof bufprefix - 1);
    memset (buf + sizeof bufprefix - 1, 'X', tzname_len);
    strcpy (buf + bufsize - sizeof bufsuffix, bufsuffix);
    ASSERT (parse_datetime (&result, buf, &now));
    LOG (buf, now, result);
    ASSERT (result.tv_sec == 1 * 60 * 60 + 2 * 60 + 3
            && result.tv_nsec == 123456789);
  }

   return 0;
 }


  ft_var_readpackedpoints( FT_Stream  stream,
                           FT_UInt   *point_cnt )
  {
    FT_UShort *points;
    FT_Int     n;
    FT_Int     runcnt;
    FT_Int     i;
     FT_Int     j;
     FT_Int     first;
     FT_Memory  memory = stream->memory;
    FT_Error   error = TT_Err_Ok;

     FT_UNUSED( error );

    *point_cnt = n = FT_GET_BYTE();
    if ( n == 0 )
      return ALL_POINTS;

    if ( n & GX_PT_POINTS_ARE_WORDS )
      n = FT_GET_BYTE() | ( ( n & GX_PT_POINT_RUN_COUNT_MASK ) << 8 );

    if ( FT_NEW_ARRAY( points, n ) )
      return NULL;

    i = 0;
    while ( i < n )
    {
      runcnt = FT_GET_BYTE();
      if ( runcnt & GX_PT_POINTS_ARE_WORDS )
      {
         runcnt = runcnt & GX_PT_POINT_RUN_COUNT_MASK;
         first  = points[i++] = FT_GET_USHORT();

        if ( runcnt < 1 )
           goto Exit;

         /* first point not included in runcount */
        for ( j = 0; j < runcnt; ++j )
          points[i++] = (FT_UShort)( first += FT_GET_USHORT() );
      }
      else
       {
         first = points[i++] = FT_GET_BYTE();

        if ( runcnt < 1 )
           goto Exit;

         for ( j = 0; j < runcnt; ++j )
          points[i++] = (FT_UShort)( first += FT_GET_BYTE() );
      }
    }

  Exit:
    return points;
  }

Buffer overflow in the ft_var_readpackedpoints function in truetype/ttgxvar.c in FreeType 2.4.3 and earlier allows remote attackers to cause a denial of service (application crash) or possibly execute arbitrary code via a crafted TrueType GX font.


  ft_var_readpackedpoints( FT_Stream  stream,
                           FT_UInt   *point_cnt )
  {
    FT_UShort *points;
    FT_Int     n;
    FT_Int     runcnt;
    FT_Int     i;
     FT_Int     j;
     FT_Int     first;
     FT_Memory  memory = stream->memory;
    FT_Error   error  = TT_Err_Ok;

     FT_UNUSED( error );

    *point_cnt = n = FT_GET_BYTE();
    if ( n == 0 )
      return ALL_POINTS;

    if ( n & GX_PT_POINTS_ARE_WORDS )
      n = FT_GET_BYTE() | ( ( n & GX_PT_POINT_RUN_COUNT_MASK ) << 8 );

    if ( FT_NEW_ARRAY( points, n ) )
      return NULL;

    i = 0;
    while ( i < n )
    {
      runcnt = FT_GET_BYTE();
      if ( runcnt & GX_PT_POINTS_ARE_WORDS )
      {
         runcnt = runcnt & GX_PT_POINT_RUN_COUNT_MASK;
         first  = points[i++] = FT_GET_USHORT();

        if ( runcnt < 1 || i + runcnt >= n )
           goto Exit;

         /* first point not included in runcount */
        for ( j = 0; j < runcnt; ++j )
          points[i++] = (FT_UShort)( first += FT_GET_USHORT() );
      }
      else
       {
         first = points[i++] = FT_GET_BYTE();

        if ( runcnt < 1 || i + runcnt >= n )
           goto Exit;

         for ( j = 0; j < runcnt; ++j )
          points[i++] = (FT_UShort)( first += FT_GET_BYTE() );
      }
    }

  Exit:
    return points;
  }


 set_text_distance(gs_point *pdist, double dx, double dy, const gs_matrix *pmat)
 {
    int code = gs_distance_transform_inverse(dx, dy, pmat, pdist);
     double rounded;

     if (code == gs_error_undefinedresult) {
         /* The CTM is degenerate.
            Can't know the distance in user space.
    } else if (code < 0)
        return code;
    /* If the distance is very close to integers, round it. */
    if (fabs(pdist->x - (rounded = floor(pdist->x + 0.5))) < 0.0005)
        pdist->x = rounded;
    if (fabs(pdist->y - (rounded = floor(pdist->y + 0.5))) < 0.0005)
        pdist->y = rounded;
    return 0;
}

The set_text_distance function in devices/vector/gdevpdts.c in the pdfwrite component in Artifex Ghostscript through 9.22 does not prevent overflows in text-positioning calculation, which allows remote attackers to cause a denial of service (application crash) or possibly have unspecified other impact via a crafted PDF document.


 set_text_distance(gs_point *pdist, double dx, double dy, const gs_matrix *pmat)
 {
    int code;
     double rounded;

    if (dx > 1e38 || dy > 1e38)
        code = gs_error_undefinedresult;
    else
        code = gs_distance_transform_inverse(dx, dy, pmat, pdist);

     if (code == gs_error_undefinedresult) {
         /* The CTM is degenerate.
            Can't know the distance in user space.
    } else if (code < 0)
        return code;
    /* If the distance is very close to integers, round it. */
    if (fabs(pdist->x - (rounded = floor(pdist->x + 0.5))) < 0.0005)
        pdist->x = rounded;
    if (fabs(pdist->y - (rounded = floor(pdist->y + 0.5))) < 0.0005)
        pdist->y = rounded;
    return 0;
}


BUG/CRITICAL: h2: fix incorrect frame length check

The incoming H2 frame length was checked against the max_frame_size
setting instead of being checked against the bufsize. The max_frame_size
only applies to outgoing traffic and not to incoming one, so if a large
enough frame size is advertised in the SETTINGS frame, a wrapped frame
will be defragmented into a temporary allocated buffer where the second
fragment my overflow the heap by up to 16 kB.

It is very unlikely that this can be exploited for code execution given
that buffers are very short lived and their address not realistically
predictable in production, but the likeliness of an immediate crash is
absolutely certain.

This fix must be backported to 1.8.

Many thanks to Jordan Zebor from F5 Networks for reporting this issue
in a responsible way.

static void h2_process_demux(struct h2c *h2c)
{
	struct h2s *h2s;

	if (h2c->st0 >= H2_CS_ERROR)
		return;

	if (unlikely(h2c->st0 < H2_CS_FRAME_H)) {
		if (h2c->st0 == H2_CS_PREFACE) {
			if (unlikely(h2c_frt_recv_preface(h2c) <= 0)) {
				/* RFC7540#3.5: a GOAWAY frame MAY be omitted */
				if (h2c->st0 == H2_CS_ERROR)
					h2c->st0 = H2_CS_ERROR2;
				goto fail;
			}

			h2c->max_id = 0;
			h2c->st0 = H2_CS_SETTINGS1;
		}

		if (h2c->st0 == H2_CS_SETTINGS1) {
			struct h2_fh hdr;

			/* ensure that what is pending is a valid SETTINGS frame
			 * without an ACK.
			 */
			if (!h2_get_frame_hdr(h2c->dbuf, &hdr)) {
				/* RFC7540#3.5: a GOAWAY frame MAY be omitted */
				if (h2c->st0 == H2_CS_ERROR)
					h2c->st0 = H2_CS_ERROR2;
				goto fail;
			}

			if (hdr.sid || hdr.ft != H2_FT_SETTINGS || hdr.ff & H2_F_SETTINGS_ACK) {
				/* RFC7540#3.5: a GOAWAY frame MAY be omitted */
				h2c_error(h2c, H2_ERR_PROTOCOL_ERROR);
				h2c->st0 = H2_CS_ERROR2;
                                goto fail;
                        }

                       if ((int)hdr.len < 0 || (int)hdr.len > h2c->mfs) {
                                /* RFC7540#3.5: a GOAWAY frame MAY be omitted */
                                h2c_error(h2c, H2_ERR_FRAME_SIZE_ERROR);
                                h2c->st0 = H2_CS_ERROR2;
				goto fail;
			}

			/* that's OK, switch to FRAME_P to process it */
			h2c->dfl = hdr.len;
			h2c->dsi = hdr.sid;
			h2c->dft = hdr.ft;
			h2c->dff = hdr.ff;
			h2c->dpl = 0;
			h2c->st0 = H2_CS_FRAME_P;
		}
	}

	/* process as many incoming frames as possible below */
	while (h2c->dbuf->i) {
		int ret = 0;

		if (h2c->st0 >= H2_CS_ERROR)
			break;

		if (h2c->st0 == H2_CS_FRAME_H) {
			struct h2_fh hdr;

                        if (!h2_peek_frame_hdr(h2c->dbuf, &hdr))
                                break;

                       if ((int)hdr.len < 0 || (int)hdr.len > h2c->mfs) {
                                h2c_error(h2c, H2_ERR_FRAME_SIZE_ERROR);
                                h2c->st0 = H2_CS_ERROR;
                                break;
			}

			h2c->dfl = hdr.len;
			h2c->dsi = hdr.sid;
			h2c->dft = hdr.ft;
			h2c->dff = hdr.ff;
			h2c->dpl = 0;
			h2c->st0 = H2_CS_FRAME_P;
			h2_skip_frame_hdr(h2c->dbuf);
		}

		/* Only H2_CS_FRAME_P and H2_CS_FRAME_A here */
		h2s = h2c_st_by_id(h2c, h2c->dsi);

		if (h2c->st0 == H2_CS_FRAME_E)
			goto strm_err;

		if (h2s->st == H2_SS_IDLE &&
		    h2c->dft != H2_FT_HEADERS && h2c->dft != H2_FT_PRIORITY) {
			/* RFC7540#5.1: any frame other than HEADERS or PRIORITY in
			 * this state MUST be treated as a connection error
			 */
			h2c_error(h2c, H2_ERR_PROTOCOL_ERROR);
			h2c->st0 = H2_CS_ERROR;
			break;
		}

		if (h2s->st == H2_SS_HREM && h2c->dft != H2_FT_WINDOW_UPDATE &&
		    h2c->dft != H2_FT_RST_STREAM && h2c->dft != H2_FT_PRIORITY) {
			/* RFC7540#5.1: any frame other than WU/PRIO/RST in
			 * this state MUST be treated as a stream error
			 */
			h2s_error(h2s, H2_ERR_STREAM_CLOSED);
			h2c->st0 = H2_CS_FRAME_E;
			goto strm_err;
		}

		/* Below the management of frames received in closed state is a
		 * bit hackish because the spec makes strong differences between
		 * streams closed by receiving RST, sending RST, and seeing ES
		 * in both directions. In addition to this, the creation of a
		 * new stream reusing the identifier of a closed one will be
		 * detected here. Given that we cannot keep track of all closed
		 * streams forever, we consider that unknown closed streams were
		 * closed on RST received, which allows us to respond with an
		 * RST without breaking the connection (eg: to abort a transfer).
		 * Some frames have to be silently ignored as well.
		 */
		if (h2s->st == H2_SS_CLOSED && h2c->dsi) {
			if (h2c->dft == H2_FT_HEADERS || h2c->dft == H2_FT_PUSH_PROMISE) {
				/* #5.1.1: The identifier of a newly
				 * established stream MUST be numerically
				 * greater than all streams that the initiating
				 * endpoint has opened or reserved. This
				 * governs streams that are opened using a
				 * HEADERS frame and streams that are reserved
				 * using PUSH_PROMISE. An endpoint that
				 * receives an unexpected stream identifier
				 * MUST respond with a connection error.
				 */
				h2c_error(h2c, H2_ERR_STREAM_CLOSED);
				goto strm_err;
			}

			if (h2s->flags & H2_SF_RST_RCVD) {
				/* RFC7540#5.1:closed: an endpoint that
				 * receives any frame other than PRIORITY after
				 * receiving a RST_STREAM MUST treat that as a
				 * stream error of type STREAM_CLOSED.
				 *
				 * Note that old streams fall into this category
				 * and will lead to an RST being sent.
				 */
				h2s_error(h2s, H2_ERR_STREAM_CLOSED);
				h2c->st0 = H2_CS_FRAME_E;
				goto strm_err;
			}

			/* RFC7540#5.1:closed: if this state is reached as a
			 * result of sending a RST_STREAM frame, the peer that
			 * receives the RST_STREAM might have already sent
			 * frames on the stream that cannot be withdrawn. An
			 * endpoint MUST ignore frames that it receives on
			 * closed streams after it has sent a RST_STREAM
			 * frame. An endpoint MAY choose to limit the period
			 * over which it ignores frames and treat frames that
			 * arrive after this time as being in error.
			 */
			if (!(h2s->flags & H2_SF_RST_SENT)) {
				/* RFC7540#5.1:closed: any frame other than
				 * PRIO/WU/RST in this state MUST be treated as
				 * a connection error
				 */
				if (h2c->dft != H2_FT_RST_STREAM &&
				    h2c->dft != H2_FT_PRIORITY &&
				    h2c->dft != H2_FT_WINDOW_UPDATE) {
					h2c_error(h2c, H2_ERR_STREAM_CLOSED);
					goto strm_err;
				}
			}
		}

#if 0

		/* graceful shutdown, ignore streams whose ID is higher than
		 * the one advertised in GOAWAY. RFC7540#6.8.
		 */
		if (unlikely(h2c->last_sid >= 0) && h2c->dsi > h2c->last_sid) {
			ret = MIN(h2c->dbuf->i, h2c->dfl);
			bi_del(h2c->dbuf, ret);
			h2c->dfl -= ret;
			ret = h2c->dfl == 0;
			goto strm_err;
		}
#endif

		switch (h2c->dft) {
		case H2_FT_SETTINGS:
			if (h2c->st0 == H2_CS_FRAME_P)
				ret = h2c_handle_settings(h2c);

			if (h2c->st0 == H2_CS_FRAME_A)
				ret = h2c_ack_settings(h2c);
			break;

		case H2_FT_PING:
			if (h2c->st0 == H2_CS_FRAME_P)
				ret = h2c_handle_ping(h2c);

			if (h2c->st0 == H2_CS_FRAME_A)
				ret = h2c_ack_ping(h2c);
			break;

		case H2_FT_WINDOW_UPDATE:
			if (h2c->st0 == H2_CS_FRAME_P)
				ret = h2c_handle_window_update(h2c, h2s);
			break;

		case H2_FT_CONTINUATION:
			/* we currently don't support CONTINUATION frames since
			 * we have nowhere to store the partial HEADERS frame.
			 * Let's abort the stream on an INTERNAL_ERROR here.
			 */
			if (h2c->st0 == H2_CS_FRAME_P) {
				h2s_error(h2s, H2_ERR_INTERNAL_ERROR);
				h2c->st0 = H2_CS_FRAME_E;
			}
			break;

		case H2_FT_HEADERS:
			if (h2c->st0 == H2_CS_FRAME_P)
				ret = h2c_frt_handle_headers(h2c, h2s);
			break;

		case H2_FT_DATA:
			if (h2c->st0 == H2_CS_FRAME_P)
				ret = h2c_frt_handle_data(h2c, h2s);

			if (h2c->st0 == H2_CS_FRAME_A)
				ret = h2c_send_strm_wu(h2c);
			break;

		case H2_FT_PRIORITY:
			if (h2c->st0 == H2_CS_FRAME_P)
				ret = h2c_handle_priority(h2c);
			break;

		case H2_FT_RST_STREAM:
			if (h2c->st0 == H2_CS_FRAME_P)
				ret = h2c_handle_rst_stream(h2c, h2s);
			break;

		case H2_FT_GOAWAY:
			if (h2c->st0 == H2_CS_FRAME_P)
				ret = h2c_handle_goaway(h2c);
			break;

		case H2_FT_PUSH_PROMISE:
			/* not permitted here, RFC7540#5.1 */
			h2c_error(h2c, H2_ERR_PROTOCOL_ERROR);
			break;

			/* implement all extra frame types here */
		default:
			/* drop frames that we ignore. They may be larger than
			 * the buffer so we drain all of their contents until
			 * we reach the end.
			 */
			ret = MIN(h2c->dbuf->i, h2c->dfl);
			bi_del(h2c->dbuf, ret);
			h2c->dfl -= ret;
			ret = h2c->dfl == 0;
		}

	strm_err:
		/* We may have to send an RST if not done yet */
		if (h2s->st == H2_SS_ERROR)
			h2c->st0 = H2_CS_FRAME_E;

		if (h2c->st0 == H2_CS_FRAME_E)
			ret = h2c_send_rst_stream(h2c, h2s);

		/* error or missing data condition met above ? */
		if (ret <= 0)
			break;

		if (h2c->st0 != H2_CS_FRAME_H) {
			bi_del(h2c->dbuf, h2c->dfl);
			h2c->st0 = H2_CS_FRAME_H;
		}
	}

	if (h2c->rcvd_c > 0 &&
	    !(h2c->flags & (H2_CF_MUX_MFULL | H2_CF_DEM_MBUSY | H2_CF_DEM_MROOM)))
		h2c_send_conn_wu(h2c);

 fail:
	/* we can go here on missing data, blocked response or error */
	return;
}

An issue was discovered in HAProxy before 1.8.8. The incoming H2 frame length was checked against the max_frame_size setting instead of being checked against the bufsize. The max_frame_size only applies to outgoing traffic and not to incoming, so if a large enough frame size is advertised in the SETTINGS frame, a wrapped frame will be defragmented into a temporary allocated buffer where the second fragment may overflow the heap by up to 16 kB. It is very unlikely that this can be exploited for code execution given that buffers are very short lived and their addresses not realistically predictable in production, but the likelihood of an immediate crash is absolutely certain.


static void h2_process_demux(struct h2c *h2c)
{
	struct h2s *h2s;

	if (h2c->st0 >= H2_CS_ERROR)
		return;

	if (unlikely(h2c->st0 < H2_CS_FRAME_H)) {
		if (h2c->st0 == H2_CS_PREFACE) {
			if (unlikely(h2c_frt_recv_preface(h2c) <= 0)) {
				/* RFC7540#3.5: a GOAWAY frame MAY be omitted */
				if (h2c->st0 == H2_CS_ERROR)
					h2c->st0 = H2_CS_ERROR2;
				goto fail;
			}

			h2c->max_id = 0;
			h2c->st0 = H2_CS_SETTINGS1;
		}

		if (h2c->st0 == H2_CS_SETTINGS1) {
			struct h2_fh hdr;

			/* ensure that what is pending is a valid SETTINGS frame
			 * without an ACK.
			 */
			if (!h2_get_frame_hdr(h2c->dbuf, &hdr)) {
				/* RFC7540#3.5: a GOAWAY frame MAY be omitted */
				if (h2c->st0 == H2_CS_ERROR)
					h2c->st0 = H2_CS_ERROR2;
				goto fail;
			}

			if (hdr.sid || hdr.ft != H2_FT_SETTINGS || hdr.ff & H2_F_SETTINGS_ACK) {
				/* RFC7540#3.5: a GOAWAY frame MAY be omitted */
				h2c_error(h2c, H2_ERR_PROTOCOL_ERROR);
				h2c->st0 = H2_CS_ERROR2;
                                goto fail;
                        }

                       if ((int)hdr.len < 0 || (int)hdr.len > global.tune.bufsize) {
                                /* RFC7540#3.5: a GOAWAY frame MAY be omitted */
                                h2c_error(h2c, H2_ERR_FRAME_SIZE_ERROR);
                                h2c->st0 = H2_CS_ERROR2;
				goto fail;
			}

			/* that's OK, switch to FRAME_P to process it */
			h2c->dfl = hdr.len;
			h2c->dsi = hdr.sid;
			h2c->dft = hdr.ft;
			h2c->dff = hdr.ff;
			h2c->dpl = 0;
			h2c->st0 = H2_CS_FRAME_P;
		}
	}

	/* process as many incoming frames as possible below */
	while (h2c->dbuf->i) {
		int ret = 0;

		if (h2c->st0 >= H2_CS_ERROR)
			break;

		if (h2c->st0 == H2_CS_FRAME_H) {
			struct h2_fh hdr;

                        if (!h2_peek_frame_hdr(h2c->dbuf, &hdr))
                                break;

                       if ((int)hdr.len < 0 || (int)hdr.len > global.tune.bufsize) {
                                h2c_error(h2c, H2_ERR_FRAME_SIZE_ERROR);
                                h2c->st0 = H2_CS_ERROR;
                                break;
			}

			h2c->dfl = hdr.len;
			h2c->dsi = hdr.sid;
			h2c->dft = hdr.ft;
			h2c->dff = hdr.ff;
			h2c->dpl = 0;
			h2c->st0 = H2_CS_FRAME_P;
			h2_skip_frame_hdr(h2c->dbuf);
		}

		/* Only H2_CS_FRAME_P and H2_CS_FRAME_A here */
		h2s = h2c_st_by_id(h2c, h2c->dsi);

		if (h2c->st0 == H2_CS_FRAME_E)
			goto strm_err;

		if (h2s->st == H2_SS_IDLE &&
		    h2c->dft != H2_FT_HEADERS && h2c->dft != H2_FT_PRIORITY) {
			/* RFC7540#5.1: any frame other than HEADERS or PRIORITY in
			 * this state MUST be treated as a connection error
			 */
			h2c_error(h2c, H2_ERR_PROTOCOL_ERROR);
			h2c->st0 = H2_CS_ERROR;
			break;
		}

		if (h2s->st == H2_SS_HREM && h2c->dft != H2_FT_WINDOW_UPDATE &&
		    h2c->dft != H2_FT_RST_STREAM && h2c->dft != H2_FT_PRIORITY) {
			/* RFC7540#5.1: any frame other than WU/PRIO/RST in
			 * this state MUST be treated as a stream error
			 */
			h2s_error(h2s, H2_ERR_STREAM_CLOSED);
			h2c->st0 = H2_CS_FRAME_E;
			goto strm_err;
		}

		/* Below the management of frames received in closed state is a
		 * bit hackish because the spec makes strong differences between
		 * streams closed by receiving RST, sending RST, and seeing ES
		 * in both directions. In addition to this, the creation of a
		 * new stream reusing the identifier of a closed one will be
		 * detected here. Given that we cannot keep track of all closed
		 * streams forever, we consider that unknown closed streams were
		 * closed on RST received, which allows us to respond with an
		 * RST without breaking the connection (eg: to abort a transfer).
		 * Some frames have to be silently ignored as well.
		 */
		if (h2s->st == H2_SS_CLOSED && h2c->dsi) {
			if (h2c->dft == H2_FT_HEADERS || h2c->dft == H2_FT_PUSH_PROMISE) {
				/* #5.1.1: The identifier of a newly
				 * established stream MUST be numerically
				 * greater than all streams that the initiating
				 * endpoint has opened or reserved. This
				 * governs streams that are opened using a
				 * HEADERS frame and streams that are reserved
				 * using PUSH_PROMISE. An endpoint that
				 * receives an unexpected stream identifier
				 * MUST respond with a connection error.
				 */
				h2c_error(h2c, H2_ERR_STREAM_CLOSED);
				goto strm_err;
			}

			if (h2s->flags & H2_SF_RST_RCVD) {
				/* RFC7540#5.1:closed: an endpoint that
				 * receives any frame other than PRIORITY after
				 * receiving a RST_STREAM MUST treat that as a
				 * stream error of type STREAM_CLOSED.
				 *
				 * Note that old streams fall into this category
				 * and will lead to an RST being sent.
				 */
				h2s_error(h2s, H2_ERR_STREAM_CLOSED);
				h2c->st0 = H2_CS_FRAME_E;
				goto strm_err;
			}

			/* RFC7540#5.1:closed: if this state is reached as a
			 * result of sending a RST_STREAM frame, the peer that
			 * receives the RST_STREAM might have already sent
			 * frames on the stream that cannot be withdrawn. An
			 * endpoint MUST ignore frames that it receives on
			 * closed streams after it has sent a RST_STREAM
			 * frame. An endpoint MAY choose to limit the period
			 * over which it ignores frames and treat frames that
			 * arrive after this time as being in error.
			 */
			if (!(h2s->flags & H2_SF_RST_SENT)) {
				/* RFC7540#5.1:closed: any frame other than
				 * PRIO/WU/RST in this state MUST be treated as
				 * a connection error
				 */
				if (h2c->dft != H2_FT_RST_STREAM &&
				    h2c->dft != H2_FT_PRIORITY &&
				    h2c->dft != H2_FT_WINDOW_UPDATE) {
					h2c_error(h2c, H2_ERR_STREAM_CLOSED);
					goto strm_err;
				}
			}
		}

#if 0

		/* graceful shutdown, ignore streams whose ID is higher than
		 * the one advertised in GOAWAY. RFC7540#6.8.
		 */
		if (unlikely(h2c->last_sid >= 0) && h2c->dsi > h2c->last_sid) {
			ret = MIN(h2c->dbuf->i, h2c->dfl);
			bi_del(h2c->dbuf, ret);
			h2c->dfl -= ret;
			ret = h2c->dfl == 0;
			goto strm_err;
		}
#endif

		switch (h2c->dft) {
		case H2_FT_SETTINGS:
			if (h2c->st0 == H2_CS_FRAME_P)
				ret = h2c_handle_settings(h2c);

			if (h2c->st0 == H2_CS_FRAME_A)
				ret = h2c_ack_settings(h2c);
			break;

		case H2_FT_PING:
			if (h2c->st0 == H2_CS_FRAME_P)
				ret = h2c_handle_ping(h2c);

			if (h2c->st0 == H2_CS_FRAME_A)
				ret = h2c_ack_ping(h2c);
			break;

		case H2_FT_WINDOW_UPDATE:
			if (h2c->st0 == H2_CS_FRAME_P)
				ret = h2c_handle_window_update(h2c, h2s);
			break;

		case H2_FT_CONTINUATION:
			/* we currently don't support CONTINUATION frames since
			 * we have nowhere to store the partial HEADERS frame.
			 * Let's abort the stream on an INTERNAL_ERROR here.
			 */
			if (h2c->st0 == H2_CS_FRAME_P) {
				h2s_error(h2s, H2_ERR_INTERNAL_ERROR);
				h2c->st0 = H2_CS_FRAME_E;
			}
			break;

		case H2_FT_HEADERS:
			if (h2c->st0 == H2_CS_FRAME_P)
				ret = h2c_frt_handle_headers(h2c, h2s);
			break;

		case H2_FT_DATA:
			if (h2c->st0 == H2_CS_FRAME_P)
				ret = h2c_frt_handle_data(h2c, h2s);

			if (h2c->st0 == H2_CS_FRAME_A)
				ret = h2c_send_strm_wu(h2c);
			break;

		case H2_FT_PRIORITY:
			if (h2c->st0 == H2_CS_FRAME_P)
				ret = h2c_handle_priority(h2c);
			break;

		case H2_FT_RST_STREAM:
			if (h2c->st0 == H2_CS_FRAME_P)
				ret = h2c_handle_rst_stream(h2c, h2s);
			break;

		case H2_FT_GOAWAY:
			if (h2c->st0 == H2_CS_FRAME_P)
				ret = h2c_handle_goaway(h2c);
			break;

		case H2_FT_PUSH_PROMISE:
			/* not permitted here, RFC7540#5.1 */
			h2c_error(h2c, H2_ERR_PROTOCOL_ERROR);
			break;

			/* implement all extra frame types here */
		default:
			/* drop frames that we ignore. They may be larger than
			 * the buffer so we drain all of their contents until
			 * we reach the end.
			 */
			ret = MIN(h2c->dbuf->i, h2c->dfl);
			bi_del(h2c->dbuf, ret);
			h2c->dfl -= ret;
			ret = h2c->dfl == 0;
		}

	strm_err:
		/* We may have to send an RST if not done yet */
		if (h2s->st == H2_SS_ERROR)
			h2c->st0 = H2_CS_FRAME_E;

		if (h2c->st0 == H2_CS_FRAME_E)
			ret = h2c_send_rst_stream(h2c, h2s);

		/* error or missing data condition met above ? */
		if (ret <= 0)
			break;

		if (h2c->st0 != H2_CS_FRAME_H) {
			bi_del(h2c->dbuf, h2c->dfl);
			h2c->st0 = H2_CS_FRAME_H;
		}
	}

	if (h2c->rcvd_c > 0 &&
	    !(h2c->flags & (H2_CF_MUX_MFULL | H2_CF_DEM_MBUSY | H2_CF_DEM_MROOM)))
		h2c_send_conn_wu(h2c);

 fail:
	/* we can go here on missing data, blocked response or error */
	return;
}


Status XvMCGetDRInfo(Display *dpy, XvPortID port,
		     char **name, char **busID,
		     int *major, int *minor,
		     int *patchLevel,
		     int *isLocal)
{
    XExtDisplayInfo *info = xvmc_find_display(dpy);
    xvmcGetDRInfoReply rep;
    xvmcGetDRInfoReq  *req;
    CARD32 magic;

#ifdef HAVE_SHMAT
    volatile CARD32 *shMem;
    struct timezone here;
    struct timeval now;
    here.tz_minuteswest = 0;
    here.tz_dsttime = 0;
#endif

    *name = NULL;
    *busID = NULL;

    XvMCCheckExtension (dpy, info, BadImplementation);

    LockDisplay (dpy);
    XvMCGetReq (GetDRInfo, req);

    req->port = port;
    magic = 0;
    req->magic = 0;
#ifdef HAVE_SHMAT
    req->shmKey = shmget(IPC_PRIVATE, 1024, IPC_CREAT | 0600);

    /*
     * We fill a shared memory page with a repetitive pattern. If the
     * X server can read this pattern, we probably have a local connection.
     * Note that we can trigger the remote X server to read any shared
     * page on the remote machine, so we shouldn't be able to guess and verify
     * any complicated data on those pages. Thats the explanation of this
     * otherwise stupid-looking pattern algorithm.
     */

    if (req->shmKey >= 0) {
	shMem = (CARD32 *) shmat(req->shmKey, NULL, 0);
	shmctl( req->shmKey, IPC_RMID, NULL);
	if ( shMem ) {

	    register volatile CARD32 *shMemC = shMem;
	    register int i;

	    gettimeofday( &now, &here);
	    magic = now.tv_usec & 0x000FFFFF;
	    req->magic = magic;
	    i = 1024 / sizeof(CARD32);
	    while(i--) {
	        *shMemC++ = magic;
	        magic = ~magic;
	    }
	} else {
	    req->shmKey = -1;
	}
    }
#else
    req->shmKey = 0;
#endif
    if (!_XReply (dpy, (xReply *) &rep, 0, xFalse)) {
        UnlockDisplay (dpy);
        SyncHandle ();
#ifdef HAVE_SHMAT
	if ( req->shmKey >= 0) {
	    shmdt( (const void *) shMem );
	}
#endif
        return -1;
    }
#ifdef HAVE_SHMAT
    shmdt( (const void *) shMem );
#endif

    if (rep.length > 0) {
	unsigned long realSize = 0;
	char *tmpBuf = NULL;

	if ((rep.length < (INT_MAX >> 2)) &&
	    /* protect against overflow in strncpy below */
	    (rep.nameLen + rep.busIDLen > rep.nameLen)) {
	    realSize = rep.length << 2;
	    if (realSize >= (rep.nameLen + rep.busIDLen)) {
		tmpBuf = Xmalloc(realSize);
		*name = Xmalloc(rep.nameLen);
		*busID = Xmalloc(rep.busIDLen);
	    }
	}

 	if (*name && *busID && tmpBuf) {
 	    _XRead(dpy, tmpBuf, realSize);
 	    strncpy(*name,tmpBuf,rep.nameLen);
	    (*name)[rep.nameLen - 1] = '\0';
 	    strncpy(*busID,tmpBuf+rep.nameLen,rep.busIDLen);
	    (*busID)[rep.busIDLen - 1] = '\0';
 	    XFree(tmpBuf);
 	} else {
 	    XFree(*name);
	    *name = NULL;
	    XFree(*busID);
	    *busID = NULL;
	    XFree(tmpBuf);

	    _XEatDataWords(dpy, rep.length);
	    UnlockDisplay (dpy);
	    SyncHandle ();
	    return -1;

	}
    }

    UnlockDisplay (dpy);
    SyncHandle ();
    *major = rep.major;
    *minor = rep.minor;
    *patchLevel = rep.patchLevel;
    *isLocal = (req->shmKey > 0) ? rep.isLocal : 1;
    return (rep.length > 0) ? Success : BadImplementation;
}

Buffer underflow in X.org libXvMC before 1.0.10 allows remote X servers to have unspecified impact via an empty string.


Status XvMCGetDRInfo(Display *dpy, XvPortID port,
		     char **name, char **busID,
		     int *major, int *minor,
		     int *patchLevel,
		     int *isLocal)
{
    XExtDisplayInfo *info = xvmc_find_display(dpy);
    xvmcGetDRInfoReply rep;
    xvmcGetDRInfoReq  *req;
    CARD32 magic;

#ifdef HAVE_SHMAT
    volatile CARD32 *shMem;
    struct timezone here;
    struct timeval now;
    here.tz_minuteswest = 0;
    here.tz_dsttime = 0;
#endif

    *name = NULL;
    *busID = NULL;

    XvMCCheckExtension (dpy, info, BadImplementation);

    LockDisplay (dpy);
    XvMCGetReq (GetDRInfo, req);

    req->port = port;
    magic = 0;
    req->magic = 0;
#ifdef HAVE_SHMAT
    req->shmKey = shmget(IPC_PRIVATE, 1024, IPC_CREAT | 0600);

    /*
     * We fill a shared memory page with a repetitive pattern. If the
     * X server can read this pattern, we probably have a local connection.
     * Note that we can trigger the remote X server to read any shared
     * page on the remote machine, so we shouldn't be able to guess and verify
     * any complicated data on those pages. Thats the explanation of this
     * otherwise stupid-looking pattern algorithm.
     */

    if (req->shmKey >= 0) {
	shMem = (CARD32 *) shmat(req->shmKey, NULL, 0);
	shmctl( req->shmKey, IPC_RMID, NULL);
	if ( shMem ) {

	    register volatile CARD32 *shMemC = shMem;
	    register int i;

	    gettimeofday( &now, &here);
	    magic = now.tv_usec & 0x000FFFFF;
	    req->magic = magic;
	    i = 1024 / sizeof(CARD32);
	    while(i--) {
	        *shMemC++ = magic;
	        magic = ~magic;
	    }
	} else {
	    req->shmKey = -1;
	}
    }
#else
    req->shmKey = 0;
#endif
    if (!_XReply (dpy, (xReply *) &rep, 0, xFalse)) {
        UnlockDisplay (dpy);
        SyncHandle ();
#ifdef HAVE_SHMAT
	if ( req->shmKey >= 0) {
	    shmdt( (const void *) shMem );
	}
#endif
        return -1;
    }
#ifdef HAVE_SHMAT
    shmdt( (const void *) shMem );
#endif

    if (rep.length > 0) {
	unsigned long realSize = 0;
	char *tmpBuf = NULL;

	if ((rep.length < (INT_MAX >> 2)) &&
	    /* protect against overflow in strncpy below */
	    (rep.nameLen + rep.busIDLen > rep.nameLen)) {
	    realSize = rep.length << 2;
	    if (realSize >= (rep.nameLen + rep.busIDLen)) {
		tmpBuf = Xmalloc(realSize);
		*name = Xmalloc(rep.nameLen);
		*busID = Xmalloc(rep.busIDLen);
	    }
	}

 	if (*name && *busID && tmpBuf) {
 	    _XRead(dpy, tmpBuf, realSize);
 	    strncpy(*name,tmpBuf,rep.nameLen);
	    (*name)[rep.nameLen == 0 ? 0 : rep.nameLen - 1] = '\0';
 	    strncpy(*busID,tmpBuf+rep.nameLen,rep.busIDLen);
	    (*busID)[rep.busIDLen == 0 ? 0 : rep.busIDLen - 1] = '\0';
 	    XFree(tmpBuf);
 	} else {
 	    XFree(*name);
	    *name = NULL;
	    XFree(*busID);
	    *busID = NULL;
	    XFree(tmpBuf);

	    _XEatDataWords(dpy, rep.length);
	    UnlockDisplay (dpy);
	    SyncHandle ();
	    return -1;

	}
    }

    UnlockDisplay (dpy);
    SyncHandle ();
    *major = rep.major;
    *minor = rep.minor;
    *patchLevel = rep.patchLevel;
    *isLocal = (req->shmKey > 0) ? rep.isLocal : 1;
    return (rep.length > 0) ? Success : BadImplementation;
}


XFixesFetchRegionAndBounds (Display	    *dpy,
			    XserverRegion   region,
			    int		    *nrectanglesRet,
			    XRectangle	    *bounds)
{
    XFixesExtDisplayInfo	*info = XFixesFindDisplay (dpy);
    xXFixesFetchRegionReq	*req;
    xXFixesFetchRegionReply	rep;
    XRectangle			*rects;
    int    			nrects;
    long    			nbytes;
    long			nread;

    XFixesCheckExtension (dpy, info, NULL);
    LockDisplay (dpy);
    GetReq (XFixesFetchRegion, req);
    req->reqType = info->codes->major_opcode;
    req->xfixesReqType = X_XFixesFetchRegion;
    req->region = region;
    *nrectanglesRet = 0;
    if (!_XReply (dpy, (xReply *) &rep, 0, xFalse))
    {
	UnlockDisplay (dpy);
	SyncHandle ();
	return NULL;
    }
    bounds->x = rep.x;
    bounds->y = rep.y;
     bounds->y = rep.y;
     bounds->width = rep.width;
     bounds->height = rep.height;
    nbytes = (long) rep.length << 2;
    nrects = rep.length >> 1;
    rects = Xmalloc (nrects * sizeof (XRectangle));
     if (!rects)
     {
 	_XEatDataWords(dpy, rep.length);
	_XEatData (dpy, (unsigned long) (nbytes - nread));
    }
    UnlockDisplay (dpy);
    SyncHandle();
    *nrectanglesRet = nrects;
    return rects;
}

Integer overflow in X.org libXfixes before 5.0.3 on 32-bit platforms might allow remote X servers to gain privileges via a length value of INT_MAX, which triggers the client to stop reading data and get out of sync.


XFixesFetchRegionAndBounds (Display	    *dpy,
			    XserverRegion   region,
			    int		    *nrectanglesRet,
			    XRectangle	    *bounds)
{
    XFixesExtDisplayInfo	*info = XFixesFindDisplay (dpy);
    xXFixesFetchRegionReq	*req;
    xXFixesFetchRegionReply	rep;
    XRectangle			*rects;
    int    			nrects;
    long    			nbytes;
    long			nread;

    XFixesCheckExtension (dpy, info, NULL);
    LockDisplay (dpy);
    GetReq (XFixesFetchRegion, req);
    req->reqType = info->codes->major_opcode;
    req->xfixesReqType = X_XFixesFetchRegion;
    req->region = region;
    *nrectanglesRet = 0;
    if (!_XReply (dpy, (xReply *) &rep, 0, xFalse))
    {
	UnlockDisplay (dpy);
	SyncHandle ();
	return NULL;
    }
    bounds->x = rep.x;
    bounds->y = rep.y;
     bounds->y = rep.y;
     bounds->width = rep.width;
     bounds->height = rep.height;

    if (rep.length < (INT_MAX >> 2)) {
	nbytes = (long) rep.length << 2;
	nrects = rep.length >> 1;
	rects = Xmalloc (nrects * sizeof (XRectangle));
    } else {
	nbytes = 0;
	nrects = 0;
	rects = NULL;
    }

     if (!rects)
     {
 	_XEatDataWords(dpy, rep.length);
	_XEatData (dpy, (unsigned long) (nbytes - nread));
    }
    UnlockDisplay (dpy);
    SyncHandle();
    *nrectanglesRet = nrects;
    return rects;
}


mpegvideo_motion: Handle edge emulation even without unrestricted_mv

Fix out of bounds read.

Bug-Id: 962
Found by: F4B3CD@STARLAB and Agostino Sarubbo
Signed-off-by: Vittorio Giovara <vittorio.giovara@gmail.com>

static inline int hpel_motion(MpegEncContext *s,
                              uint8_t *dest, uint8_t *src,
                              int src_x, int src_y,
                              op_pixels_func *pix_op,
                              int motion_x, int motion_y)
{
    int dxy = 0;
    int emu = 0;

    src_x += motion_x >> 1;
    src_y += motion_y >> 1;

    /* WARNING: do no forget half pels */
    src_x = av_clip(src_x, -16, s->width); // FIXME unneeded for emu?
    if (src_x != s->width)
        dxy |= motion_x & 1;
    src_y = av_clip(src_y, -16, s->height);
    if (src_y != s->height)
         dxy |= (motion_y & 1) << 1;
     src += src_y * s->linesize + src_x;

    if (s->unrestricted_mv) {
        if ((unsigned)src_x > FFMAX(s->h_edge_pos - (motion_x & 1) - 8, 0) ||
            (unsigned)src_y > FFMAX(s->v_edge_pos - (motion_y & 1) - 8, 0)) {
            s->vdsp.emulated_edge_mc(s->sc.edge_emu_buffer, src,
                                     s->linesize, s->linesize,
                                     9, 9,
                                     src_x, src_y, s->h_edge_pos,
                                     s->v_edge_pos);
            src = s->sc.edge_emu_buffer;
            emu = 1;
        }
     }
     pix_op[dxy](dest, src, s->linesize, 8);
     return emu;
    }

The put_no_rnd_pixels8_xy2_mmx function in x86/rnd_template.c in libav 11.7 and earlier allows remote attackers to cause a denial of service (NULL pointer dereference and crash) via a crafted MP3 file.


static inline int hpel_motion(MpegEncContext *s,
                              uint8_t *dest, uint8_t *src,
                              int src_x, int src_y,
                              op_pixels_func *pix_op,
                              int motion_x, int motion_y)
{
    int dxy = 0;
    int emu = 0;

    src_x += motion_x >> 1;
    src_y += motion_y >> 1;

    /* WARNING: do no forget half pels */
    src_x = av_clip(src_x, -16, s->width); // FIXME unneeded for emu?
    if (src_x != s->width)
        dxy |= motion_x & 1;
    src_y = av_clip(src_y, -16, s->height);
    if (src_y != s->height)
         dxy |= (motion_y & 1) << 1;
     src += src_y * s->linesize + src_x;

    if ((unsigned)src_x > FFMAX(s->h_edge_pos - (motion_x & 1) - 8, 0) ||
        (unsigned)src_y > FFMAX(s->v_edge_pos - (motion_y & 1) - 8, 0)) {
        s->vdsp.emulated_edge_mc(s->sc.edge_emu_buffer, src,
                                 s->linesize, s->linesize,
                                 9, 9, src_x, src_y,
                                 s->h_edge_pos, s->v_edge_pos);
        src = s->sc.edge_emu_buffer;
        emu = 1;
     }
     pix_op[dxy](dest, src, s->linesize, 8);
     return emu;
    }