Commit message: Avoid uint overflow in case the length + index is over UINT_MAX
CVE: CVE-2012-1584
CWE: CWE-189
Vulnerability description: Integer overflow in the mid function in toolkit/tbytevector.cpp in TagLib 1.7 and earlier allows context-dependent attackers to cause a denial of service (application crash) via a crafted file header field in a media file, which triggers a large memory allocation.

Project: taglib
File: tbytevector.cpp

    char operator[](int index) const
    {
      return v[v.size() - index - 1];
    }

Project: taglib
File: tbytevector.cpp

bool ByteVector::operator!=(const ByteVector &v) const
{
  return !operator==(v);
}

Project: taglib
File: tbytevector.cpp

ByteVector ByteVector::fromUInt(uint value, bool mostSignificantByteFirst)
{
  return fromNumber<uint>(value, mostSignificantByteFirst);
}

Project: taglib
File: tbytevector.cpp

  ByteVector fromNumber(T value, bool mostSignificantByteFirst)
  {
    int size = sizeof(T);

    ByteVector v(size, 0);

    for(int i = 0; i < size; i++)
      v[i] = uchar(value >> ((mostSignificantByteFirst ? size - 1 - i : i) * 8) & 0xff);

    return v;
  }

Project: taglib
File: tbytevector.cpp

ByteVector ByteVector::fromLongLong(long long value, bool mostSignificantByteFirst)
{
  return fromNumber<long long>(value, mostSignificantByteFirst);
}

Project: taglib
File: tbytevector.cpp

ByteVector ByteVector::operator+(const ByteVector &v) const
{
  ByteVector sum(*this);
  sum.append(v);
  return sum;
}

Project: taglib
File: tbytevector.cpp

    char at(int index) const
    {
      return v.at(v.size() - index - 1);
    }

Project: taglib
File: tbytevector.cpp

char &ByteVector::operator[](int index)
{
  detach();

  return d->data[index];
}

Project: taglib
File: tbytevector.cpp

bool ByteVector::operator==(const ByteVector &v) const
{
  if(d->size != v.d->size)
    return false;

  return ::memcmp(data(), v.data(), size()) == 0;
}

Project: taglib
File: tbytevector.cpp

std::ostream &operator<<(std::ostream &s, const ByteVector &v)
{
  for(TagLib::uint i = 0; i < v.size(); i++)
    s << v[i];
  return s;
}

Project: taglib
File: tbytevector.cpp

unsigned short ByteVector::toUShort(bool mostSignificantByteFirst) const
{
  return toNumber<unsigned short>(d->data, mostSignificantByteFirst);
}

Project: taglib
File: tbytevector.cpp

ByteVector::ByteVector(char c)
{
  d = new ByteVectorPrivate;
  d->data.push_back(c);
  d->size = 1;
}

Project: taglib
File: tbytevector.cpp

ByteVector::ByteVector(const char *data)
{
  d = new ByteVectorPrivate;
  setData(data);
}

Project: taglib
File: tbytevector.cpp

const char &ByteVector::operator[](int index) const
{
  return d->data[index];
}

Project: taglib
File: tbytevector.cpp

ByteVector::~ByteVector()
{
  if(d->deref())
    delete d;
}

Project: taglib
File: tbytevector.cpp

ByteVector::ConstIterator ByteVector::end() const
{
  return d->data.end();
}

Project: taglib
File: tbytevector.cpp

ByteVector &ByteVector::replace(const ByteVector &pattern, const ByteVector &with)
{
  if(pattern.size() == 0 || pattern.size() > size())
    return *this;

  const uint withSize = with.size();
  const uint patternSize = pattern.size();
  int offset = 0;

  if(withSize == patternSize) {
    // I think this case might be common enough to optimize it
    detach();
    offset = find(pattern);
    while(offset >= 0) {
      ::memcpy(data() + offset, with.data(), withSize);
      offset = find(pattern, offset + withSize);
    }
    return *this;
  }

  // calculate new size:
  uint newSize = 0;
  for(;;) {
    int next = find(pattern, offset);
    if(next < 0) {
      if(offset == 0)
        // pattern not found, do nothing:
        return *this;
      newSize += size() - offset;
      break;
    }
    newSize += (next - offset) + withSize;
    offset = next + patternSize;
  }

  // new private data of appropriate size:
  ByteVectorPrivate *newData = new ByteVectorPrivate(newSize, 0);
  char *target = DATA(newData);
  const char *source = data();

  // copy modified data into new private data:
  offset = 0;
  for(;;) {
    int next = find(pattern, offset);
    if(next < 0) {
      ::memcpy(target, source + offset, size() - offset);
      break;
    }
    int chunkSize = next - offset;
    ::memcpy(target, source + offset, chunkSize);
    target += chunkSize;
    ::memcpy(target, with.data(), withSize);
    target += withSize;
    offset += chunkSize + patternSize;
  }
  
  // replace private data:
  if(d->deref())
    delete d;

  d = newData;

  return *this;
}

Project: taglib
File: tbytevector.cpp

bool ByteVector::operator<(const ByteVector &v) const
{
  int result = ::memcmp(data(), v.data(), d->size < v.d->size ? d->size : v.d->size);

  if(result != 0)
    return result < 0;
  else
    return size() < v.size();
}

Project: taglib
File: tbytevector.cpp

bool ByteVector::startsWith(const ByteVector &pattern) const
{
  return containsAt(pattern, 0);
}

Project: taglib
File: tbytevector.cpp

bool ByteVector::operator>(const ByteVector &v) const
{
  return v < *this;
}

Project: taglib
File: tbytevector.cpp

ByteVector &ByteVector::operator=(char c)
{
  *this = ByteVector(c);
  return *this;
}

Project: taglib
File: tbytevector.cpp

bool ByteVector::endsWith(const ByteVector &pattern) const
{
  return containsAt(pattern, size() - pattern.size());
}

Project: taglib
File: tbytevector.cpp

TagLib::uint ByteVector::size() const
{
  return d->size;
}

Project: taglib
File: tbytevector.cpp

    ByteVectorMirror mid(uint index, uint length = 0xffffffff) const
    {
      return length == 0xffffffff ? v.mid(0, index) : v.mid(index - length, length);
    }

Project: taglib
File: tbytevector.cpp

    uint size() const
    {
      return v.size();
    }

Project: taglib
File: tbytevector.cpp

ByteVector &ByteVector::append(const ByteVector &v)
{
  if(v.d->size == 0)
    return *this; // Simply return if appending nothing.

  detach();

  uint originalSize = d->size;
  resize(d->size + v.d->size);
  ::memcpy(DATA(d) + originalSize, DATA(v.d), v.size());

  return *this;
}

Project: taglib
File: tbytevector.cpp

  ByteVectorPrivate() : RefCounter(), size(0) {}

Project: taglib
File: tbytevector.cpp

ByteVector &ByteVector::operator=(const char *data)
{
  *this = ByteVector(data);
  return *this;
}

Project: taglib
File: tbytevector.cpp

  ByteVectorPrivate(const std::vector<char> &v) : RefCounter(), data(v), size(v.size()) {}

Project: taglib
File: tbytevector.cpp

int ByteVector::rfind(const ByteVector &pattern, uint offset, int byteAlign) const
{
  // Ok, this is a little goofy, but pretty cool after it sinks in.  Instead of
  // reversing the find method's Boyer-Moore search algorithm I created a "mirror"
  // for a ByteVector to reverse the behavior of the accessors.

  ByteVectorMirror v(*this);
  ByteVectorMirror p(pattern);

  return v.find(p, offset, byteAlign);
}

Project: taglib
File: tbytevector.cpp

ByteVector::Iterator ByteVector::begin()
{
  return d->data.begin();
}

Project: taglib
File: tbytevector.cpp

bool ByteVector::operator==(const char *s) const
{
  if(d->size != ::strlen(s))
    return false;

  return ::memcmp(data(), s, d->size) == 0;
}

Project: taglib
File: tbytevector.cpp

bool ByteVector::isNull() const
{
  return d == null.d;
}

Project: taglib
File: tbytevector.cpp

const char *ByteVector::data() const
{
  return size() > 0 ? DATA(d) : 0;
}

Project: taglib
File: tbytevector.cpp

bool ByteVector::isEmpty() const
{
  return d->data.size() == 0;
}

Project: taglib
File: tbytevector.cpp

ByteVector::ByteVector()
{
  d = new ByteVectorPrivate;
}

Project: taglib
File: tbytevector.cpp

    int find(const ByteVectorMirror &pattern, uint offset = 0, int byteAlign = 1) const
    {
      ByteVectorMirror v(*this);

      if(offset > 0) {
        offset = size() - offset - pattern.size();
        if(offset >= size())
          offset = 0;
      }

      const int pos = vectorFind<ByteVectorMirror>(v, pattern, offset, byteAlign);

      // If the offset is zero then we need to adjust the location in the search
      // to be appropriately reversed.  If not we need to account for the fact
      // that the recursive call (called from the above line) has already ajusted
      // for this but that the normal templatized find above will add the offset
      // to the returned value.
      //
      // This is a little confusing at first if you don't first stop to think
      // through the logic involved in the forward search.

      if(pos == -1)
        return -1;

      return size() - pos - pattern.size();
    }

Project: taglib
File: tbytevector.cpp

bool ByteVector::containsAt(const ByteVector &pattern, uint offset, uint patternOffset, uint patternLength) const
{
  if(pattern.size() < patternLength)
    patternLength = pattern.size();

  // do some sanity checking -- all of these things are needed for the search to be valid

  if(patternLength > size() || offset >= size() || patternOffset >= pattern.size() || patternLength == 0)
    return false;

  // loop through looking for a mismatch

  for(uint i = 0; i < patternLength - patternOffset; i++) {
    if(at(i + offset) != pattern[i + patternOffset])
      return false;
  }

  return true;
}

Project: taglib
File: tbytevector.cpp

ByteVector &ByteVector::clear()
{
  detach();
  d->data.clear();
  d->size = 0;

  return *this;
}

Project: taglib
File: tbytevector.cpp

ByteVector::Iterator ByteVector::end()
{
  return d->data.end();
}

Project: taglib
File: tbytevector.cpp

char *ByteVector::data()
{
  detach();
  return size() > 0 ? DATA(d) : 0;
}

Project: taglib
File: tbytevector.cpp

  int vectorFind(const Vector &v, const Vector &pattern, uint offset, int byteAlign)
  {
    if(pattern.size() > v.size() || offset > v.size() - 1)
      return -1;

    // Let's go ahead and special case a pattern of size one since that's common
    // and easy to make fast.

    if(pattern.size() == 1) {
      char p = pattern[0];
      for(uint i = offset; i < v.size(); i++) {
        if(v[i] == p && (i - offset) % byteAlign == 0)
          return i;
      }
      return -1;
    }

    uchar lastOccurrence[256];

    for(uint i = 0; i < 256; ++i)
      lastOccurrence[i] = uchar(pattern.size());

    for(uint i = 0; i < pattern.size() - 1; ++i)
      lastOccurrence[uchar(pattern[i])] = uchar(pattern.size() - i - 1);

    for(uint i = pattern.size() - 1 + offset; i < v.size(); i += lastOccurrence[uchar(v.at(i))]) {
      int iBuffer = i;
      int iPattern = pattern.size() - 1;

      while(iPattern >= 0 && v.at(iBuffer) == pattern[iPattern]) {
        --iBuffer;
        --iPattern;
      }

      if(-1 == iPattern && (iBuffer + 1 - offset) % byteAlign == 0)
        return iBuffer + 1;
    }

    return -1;
  }

Project: taglib
File: tbytevector.cpp

char ByteVector::at(uint index) const
{
  return index < size() ? d->data[index] : 0;
}

Project: taglib
File: tbytevector.cpp

ByteVector::ByteVector(uint size, char value)
{
  d = new ByteVectorPrivate(size, value);
}

Project: taglib
File: tbytevector.cpp

long long ByteVector::toLongLong(bool mostSignificantByteFirst) const
{
  return toNumber<unsigned long long>(d->data, mostSignificantByteFirst);
}

Project: taglib
File: tbytevector.cpp

ByteVector ByteVector::fromShort(short value, bool mostSignificantByteFirst)
{
  return fromNumber<short>(value, mostSignificantByteFirst);
}

Project: taglib
File: tbytevector.cpp

    ByteVectorMirror(const ByteVector &source) : v(source) {}

Project: taglib
File: tbytevector.cpp

  T toNumber(const std::vector<char> &data, bool mostSignificantByteFirst)
  {
    T sum = 0;

    if(data.size() <= 0) {
      debug("ByteVectorMirror::toNumber<T>() -- data is empty, returning 0");
      return sum;
    }

    uint size = sizeof(T);
    uint last = data.size() > size ? size - 1 : data.size() - 1;

    for(uint i = 0; i <= last; i++)
      sum |= (T) uchar(data[i]) << ((mostSignificantByteFirst ? last - i : i) * 8);

    return sum;
  }

Project: taglib
File: tbytevector.cpp

bool ByteVector::operator!=(const char *s) const
{
  return !operator==(s);
}

Project: taglib
File: tbytevector.cpp

ByteVector ByteVector::toHex() const
{
  ByteVector encoded(size() * 2);

  uint j = 0;
  for(uint i = 0; i < size(); i++) {
    unsigned char c = d->data[i];
    encoded[j++] = hexTable[(c >> 4) & 0x0F];
    encoded[j++] = hexTable[(c     ) & 0x0F];
  }

  return encoded;
}

Project: taglib
File: tbytevector.cpp

int ByteVector::find(const ByteVector &pattern, uint offset, int byteAlign) const
{
  return vectorFind<ByteVector>(*this, pattern, offset, byteAlign);
}

Project: taglib
File: tbytevector.cpp

ByteVector::ConstIterator ByteVector::begin() const
{
  return d->data.begin();
}

Project: taglib
File: tbytevector.cpp

void ByteVector::detach()
{
  if(d->count() > 1) {
    d->deref();
    d = new ByteVectorPrivate(d->data);
  }
}

Project: taglib
File: tbytevector.cpp

int ByteVector::endsWithPartialMatch(const ByteVector &pattern) const
{
  if(pattern.size() > size())
    return -1;

  const int startIndex = size() - pattern.size();

  // try to match the last n-1 bytes from the vector (where n is the pattern
  // size) -- continue trying to match n-2, n-3...1 bytes

  for(uint i = 1; i < pattern.size(); i++) {
    if(containsAt(pattern, startIndex + i, 0, pattern.size() - i))
      return startIndex + i;
  }

  return -1;
}

Project: taglib
File: tbytevector.cpp

  ByteVectorPrivate(TagLib::uint len, char value) : RefCounter(), data(len, value), size(len) {}

Project: taglib
File: tbytevector.cpp

short ByteVector::toShort(bool mostSignificantByteFirst) const
{
  return toNumber<unsigned short>(d->data, mostSignificantByteFirst);
}

Project: taglib
File: tbytevector.cpp

ByteVector &ByteVector::setData(const char *data, uint length)
{
  detach();

  resize(length);

  if(length > 0)
    ::memcpy(DATA(d), data, length);

  return *this;
}

Project: taglib
File: tbytevector.cpp

TagLib::uint ByteVector::checksum() const
{
  uint sum = 0;
  for(ByteVector::ConstIterator it = begin(); it != end(); ++it)
    sum = (sum << 8) ^ crcTable[((sum >> 24) & 0xff) ^ uchar(*it)];
  return sum;
}

Project: taglib
File: tbytevector.cpp

ByteVector &ByteVector::resize(uint size, char padding)
{
  if(d->size < size) {
    d->data.reserve(size);
    d->data.insert(d->data.end(), size - d->size, padding);
  }
  else
    d->data.erase(d->data.begin() + size, d->data.end());

  d->size = size;

  return *this;
}

Project: taglib
File: tbytevector.cpp

TagLib::uint ByteVector::toUInt(bool mostSignificantByteFirst) const
{
  return toNumber<uint>(d->data, mostSignificantByteFirst);
}

Project: taglib
File: tbytevector.cpp

ByteVector &ByteVector::operator=(const ByteVector &v)
{
  if(&v == this)
    return *this;

  if(d->deref())
    delete d;

  d = v.d;
  d->ref();
  return *this;
}

Project: taglib
File: tbytevector.cpp

ByteVector ByteVector::fromCString(const char *s, uint length)
{
  ByteVector v;

  if(length == 0xffffffff)
    v.setData(s);
  else
    v.setData(s, length);

  return v;
}

Project: taglib
File: tbytevector.cpp

ByteVector::ByteVector(const ByteVector &v) : d(v.d)
{
  d->ref();
}

Project: taglib
File: tbytevector.cpp

ByteVector ByteVector::mid(uint index, uint length) const
{
  ByteVector v;

  if(index > size())
    return v;

  ConstIterator endIt;

  if(length < size() - index)
    endIt = d->data.begin() + index + length;
  else
    endIt = d->data.end();

  v.d->data.insert(v.d->data.begin(), ConstIterator(d->data.begin() + index), endIt);
  v.d->size = v.d->data.size();

  return v;
}

Project: taglib
File: tbytevector.cpp

ByteVector &ByteVector::setData(const char *data)
{
  return setData(data, ::strlen(data));
}

Project: taglib
File: tbytevector.cpp

ByteVector::ByteVector(const char *data, uint length)
{
  d = new ByteVectorPrivate;
  setData(data, length);
}