Commit message: EbmlMaster: propagate upper level element after infinite sized one correctly

When the parser encountered a deeply nested element with an infinite
size then a following element of an upper level was not propagated
correctly. Instead the element with the infinite size was added into the
EBML element tree a second time resulting in memory access after freeing
it and multiple attempts to free the same memory address during
destruction.

Fixes the issue reported as Cisco TALOS-CAN-0037.
CVE: CVE-2015-8789
CWE: CWE-703
Vulnerability description: Use-after-free vulnerability in the EbmlMaster::Read function in libEBML before 1.3.3 allows context-dependent attackers to have unspecified impact via a "deeply nested element with infinite size" followed by another element of an upper level in an EBML document.

Project: libebml
File: EbmlMaster.cpp

void EbmlMaster::Read(EbmlStream & inDataStream, const EbmlSemanticContext & sContext, int & UpperEltFound, EbmlElement * & FoundElt, bool AllowDummyElt, ScopeMode ReadFully)
{
  if (ReadFully == SCOPE_NO_DATA)
    return;

  EbmlElement * ElementLevelA;
  // remove all existing elements, including the mandatory ones...
  size_t Index;
  for (Index=0; Index<ElementList.size(); Index++) {
    if (!(*ElementList[Index]).IsLocked()) {
      delete ElementList[Index];
    }
  }
  ElementList.clear();
  uint64 MaxSizeToRead;

  if (IsFiniteSize())
    MaxSizeToRead = GetSize();
  else
    MaxSizeToRead = 0x7FFFFFFF;

  // read blocks and discard the ones we don't care about
  if (MaxSizeToRead > 0)
  {
    inDataStream.I_O().setFilePointer(GetSizePosition() + GetSizeLength(), seek_beginning);
    ElementLevelA = inDataStream.FindNextElement(sContext, UpperEltFound, MaxSizeToRead, AllowDummyElt);
    while (ElementLevelA != NULL && UpperEltFound <= 0 && MaxSizeToRead > 0) {
      if (IsFiniteSize() && ElementLevelA->IsFiniteSize())
        MaxSizeToRead = GetEndPosition() - ElementLevelA->GetEndPosition(); // even if it's the default value
      if (!AllowDummyElt && ElementLevelA->IsDummy()) {
        if (ElementLevelA->IsFiniteSize()) {
          ElementLevelA->SkipData(inDataStream, sContext);
          delete ElementLevelA; // forget this unknown element
        } else {
          delete ElementLevelA; // forget this unknown element
          break;
        }
      } else {
        ElementLevelA->Read(inDataStream, EBML_CONTEXT(ElementLevelA), UpperEltFound, FoundElt, AllowDummyElt, ReadFully);

        // Discard elements that couldn't be read properly if
        // SCOPE_ALL_DATA has been requested. This can happen
        // e.g. if block data is defective.
        bool DeleteElement = true;

        if (ElementLevelA->ValueIsSet() || (ReadFully != SCOPE_ALL_DATA)) {
          ElementList.push_back(ElementLevelA);
          DeleteElement = false;
        }

        // just in case
        if (ElementLevelA->IsFiniteSize()) {
          ElementLevelA->SkipData(inDataStream, EBML_CONTEXT(ElementLevelA));
          if (DeleteElement)
            delete ElementLevelA;
        } else {
          if (DeleteElement)
            delete ElementLevelA;
          break;
        }
      }

      if (UpperEltFound > 0) {
        UpperEltFound--;
        if (UpperEltFound > 0 || MaxSizeToRead <= 0)
          goto processCrc;
        ElementLevelA = FoundElt;
        continue;
      }

      if (UpperEltFound < 0) {
        UpperEltFound++;
        if (UpperEltFound < 0)
          goto processCrc;
      }

      if (MaxSizeToRead <= 0)
        goto processCrc;// this level is finished

      ElementLevelA = inDataStream.FindNextElement(sContext, UpperEltFound, MaxSizeToRead, AllowDummyElt);
    }
    if (UpperEltFound > 0) {
      FoundElt = ElementLevelA;
    }
  }
processCrc:

  EBML_MASTER_ITERATOR Itr, CrcItr;
  for (Itr = ElementList.begin(); Itr != ElementList.end();) {
    if ((EbmlId)(*(*Itr)) == EBML_ID(EbmlCrc32)) {
      bChecksumUsed = true;
      // remove the element
      Checksum = *(static_cast<EbmlCrc32*>(*Itr));
      CrcItr = Itr;
      break;
    }
    ++Itr;
  }

  if (bChecksumUsed)
  {
    delete *CrcItr;
    Remove(CrcItr);
  }

  SetValueIsSet();
}