Commit message: FontInfoScanner::scanFonts Fix infinite loop in broken files

Fixes #752
CVE: CVE-2019-11026
CWE: CWE-674
Vulnerability description: FontInfoScanner::scanFonts in FontInfo.cc in Poppler 0.75.0 has infinite recursion, leading to a call to the error function in Error.cc.

Project: poppler
File: FontInfo.cc

FontInfoScanner::FontInfoScanner(PDFDoc *docA, int firstPage) {
  doc = docA;
  currentPage = firstPage + 1;
}

Project: poppler
File: FontInfo.cc

void FontInfoScanner::scanFonts(XRef *xrefA, Dict *resDict, std::vector<FontInfo*> *fontsList) {
  GfxFontDict *gfxFontDict;
  GfxFont *font;

  // scan the fonts in this resource dictionary
  gfxFontDict = nullptr;
  const Object &fontObj = resDict->lookupNF("Font");
  if (fontObj.isRef()) {
    Object obj2 = fontObj.fetch(xrefA);
    if (obj2.isDict()) {
      Ref r = fontObj.getRef();
      gfxFontDict = new GfxFontDict(xrefA, &r, obj2.getDict());
    }
  } else if (fontObj.isDict()) {
    gfxFontDict = new GfxFontDict(xrefA, nullptr, fontObj.getDict());
  }
  if (gfxFontDict) {
    for (int i = 0; i < gfxFontDict->getNumFonts(); ++i) {
      if ((font = gfxFontDict->getFont(i))) {
        Ref fontRef = *font->getID();

        // add this font to the list if not already found
        if (fonts.find(fontRef.num) == fonts.end()) {
	  fontsList->push_back(new FontInfo(font, xrefA));
          fonts.insert(fontRef.num);
        }
      }
    }
    delete gfxFontDict;
  }

  // recursively scan any resource dictionaries in objects in this
  // resource dictionary
  const char *resTypes[] = { "XObject", "Pattern" };
  for (unsigned int resType = 0; resType < sizeof(resTypes) / sizeof(resTypes[0]); ++resType) {
    Object objDict = resDict->lookup(resTypes[resType]);
    if (objDict.isDict()) {
      for (int i = 0; i < objDict.dictGetLength(); ++i) {
        const Object &dictObjI = objDict.dictGetValNF(i);
        if (dictObjI.isRef()) {
          // check for an already-seen object
          const Ref r = dictObjI.getRef();
          if (visitedObjects.find(r.num) != visitedObjects.end()) {
            continue;
          }

          visitedObjects.insert(r.num);
        }

        Object obj2 = dictObjI.fetch(xrefA);
        if (obj2.isStream()) {
          Ref resourcesRef;
          const Object resObj = obj2.streamGetDict()->lookup("Resources", &resourcesRef);

          if (resourcesRef != Ref::INVALID()) {
            if (visitedObjects.find(resourcesRef.num) != visitedObjects.end()) {
              continue;
            }

            visitedObjects.insert(resourcesRef.num);
          }

          if (resObj.isDict() && resObj.getDict() != resDict) {
            scanFonts(xrefA, resObj.getDict(), fontsList);
          }
        }
      }
    }
  }
}

Project: poppler
File: FontInfo.cc

FontInfo::FontInfo(GfxFont *font, XRef *xref) {
  const GooString *origName;

  fontRef = *font->getID();

  // font name
  origName = font->getName();
  if (origName != nullptr) {
    name = font->getName()->copy();
  } else {
    name = nullptr;
  }

  // font type
  type = (FontInfo::Type)font->getType();

  // check for an embedded font
  if (font->getType() == fontType3) {
    emb = true;
  } else {
    emb = font->getEmbeddedFontID(&embRef);
  }

  file = nullptr;
  substituteName = nullptr;
  if (!emb)
  {
    SysFontType dummy;
    int dummy2;
    GooString substituteNameAux;
    file = globalParams->findSystemFontFile(font, &dummy, &dummy2, &substituteNameAux);
    if (substituteNameAux.getLength() > 0)
	substituteName = substituteNameAux.copy();
  }
  encoding = font->getEncodingName()->copy();

  // look for a ToUnicode map
  hasToUnicode = false;
  Object fontObj = xref->fetch(fontRef);
  if (fontObj.isDict()) {
    hasToUnicode = fontObj.dictLookup("ToUnicode").isStream();
  }

  // check for a font subset name: capital letters followed by a '+'
  // sign
  subset = false;
  if (name) {
    int i;
    for (i = 0; i < name->getLength(); ++i) {
      if (name->getChar(i) < 'A' || name->getChar(i) > 'Z') {
	break;
      }
    }
    subset = i > 0 && i < name->getLength() && name->getChar(i) == '+';
  }
}

Project: poppler
File: FontInfo.cc

FontInfoScanner::~FontInfoScanner() {
}

Project: poppler
File: FontInfo.cc

FontInfo::FontInfo(FontInfo& f) {
  name = f.name ? f.name->copy() : nullptr;
  file = f.file ? f.file->copy() : nullptr;
  encoding = f.encoding ? f.encoding->copy() : nullptr;
  substituteName = f.substituteName ? f.substituteName->copy() : nullptr;
  type = f.type;
  emb = f.emb;
  subset = f.subset;
  hasToUnicode = f.hasToUnicode;
  fontRef = f.fontRef;
  embRef = f.embRef;
}

Project: poppler
File: FontInfo.cc

FontInfo::~FontInfo() {
  delete name;
  delete file;
  delete encoding;
  if (substituteName)
    delete substituteName;
}

Project: poppler
File: FontInfo.cc

std::vector<FontInfo*> *FontInfoScanner::scan(int nPages) {
  Page *page;
  Dict *resDict;
  Annots *annots;
  int lastPage;

  if (currentPage > doc->getNumPages()) {
    return nullptr;
  }
 
  auto result = new std::vector<FontInfo*>();

  lastPage = currentPage + nPages;
  if (lastPage > doc->getNumPages() + 1) {
    lastPage = doc->getNumPages() + 1;
  }

  XRef *xrefA = doc->getXRef()->copy();
  for (int pg = currentPage; pg < lastPage; ++pg) {
    page = doc->getPage(pg);
    if (!page) continue;

    if ((resDict = page->getResourceDictCopy(xrefA))) {
      scanFonts(xrefA, resDict, result);
      delete resDict;
    }
    annots = page->getAnnots();
    for (int i = 0; i < annots->getNumAnnots(); ++i) {
      Object obj1 = annots->getAnnot(i)->getAppearanceResDict();
      if (obj1.isDict()) {
        scanFonts(xrefA, obj1.getDict(), result);
      }
    }
  }

  currentPage = lastPage;

  delete xrefA;
  return result;
}