Commit message: Add a bounds check to ByteArray::Get().
CVE: CVE-2016-1709
CWE: CWE-703
Vulnerability description: Heap-based buffer overflow in the ByteArray::Get method in data/byte_array.cc in Google sfntly before 2016-06-10, as used in Google Chrome before 52.0.2743.82, allows remote attackers to cause a denial of service or possibly have unspecified other impact via a crafted SFNT font.

Project: sfntly
File: byte_array.cc

ByteArray::ByteArray(int32_t filled_length, int32_t storage_length) {
  Init(filled_length, storage_length, false);
}

Project: sfntly
File: byte_array.cc

int32_t ByteArray::CopyTo(ByteArray* array) {
  return CopyTo(array, 0, Length());
}

Project: sfntly
File: byte_array.cc

int32_t ByteArray::Get(int32_t index, ByteVector* b) {
  assert(b);
  return Get(index, &((*b)[0]), 0, b->size());
}

Project: sfntly
File: byte_array.cc

ByteArray::ByteArray(int32_t filled_length,
                     int32_t storage_length,
                     bool growable) {
  Init(filled_length, storage_length, growable);
}

Project: sfntly
File: byte_array.cc

bool ByteArray::CopyFrom(InputStream* is, int32_t length) {
  ByteVector b(COPY_BUFFER_SIZE);
  int32_t bytes_read = 0;
  int32_t index = 0;
  int32_t buffer_length = std::min<int32_t>(COPY_BUFFER_SIZE, length);
  while ((bytes_read = is->Read(&b, 0, buffer_length)) > 0) {
    if (Put(index, &(b[0]), 0, bytes_read) != bytes_read) {
#if defined (SFNTLY_NO_EXCEPTION)
      return 0;
#else
      throw IOException("Error writing bytes.");
#endif
    }
    index += bytes_read;
    length -= bytes_read;
    buffer_length = std::min<int32_t>(b.size(), length);
  }
  return true;
}

Project: sfntly
File: byte_array.cc

int32_t ByteArray::SetFilledLength(int32_t filled_length) {
  filled_length_ = std::min<int32_t>(filled_length, storage_length_);
  return filled_length_;
}

Project: sfntly
File: byte_array.cc

int32_t ByteArray::CopyTo(OutputStream* os, int32_t offset, int32_t length) {
  ByteVector b(COPY_BUFFER_SIZE);
  int32_t bytes_read = 0;
  int32_t index = 0;
  int32_t buffer_length = std::min<int32_t>(COPY_BUFFER_SIZE, length);
  while ((bytes_read = Get(index + offset, &(b[0]), 0, buffer_length)) > 0) {
    os->Write(&b, 0, bytes_read);
    index += bytes_read;
    buffer_length = std::min<int32_t>(b.size(), length - index);
  }
  return index;
}

Project: sfntly
File: byte_array.cc

int32_t ByteArray::Get(int32_t index) {
  if (index < 0 || index >= Length())
    return -1;
  return InternalGet(index) & 0xff;
}

Project: sfntly
File: byte_array.cc

int32_t ByteArray::CopyTo(OutputStream* os) {
    return CopyTo(os, 0, Length());
}

Project: sfntly
File: byte_array.cc

ByteArray::~ByteArray() {}

Project: sfntly
File: byte_array.cc

int32_t ByteArray::CopyTo(int32_t dst_offset, ByteArray* array,
                          int32_t src_offset, int32_t length) {
  assert(array);
  if (array->Size() < dst_offset + length) {  // insufficient space
    return -1;
  }

  ByteVector b(COPY_BUFFER_SIZE);
  int32_t bytes_read = 0;
  int32_t index = 0;
  int32_t remaining_length = length;
  int32_t buffer_length = std::min<int32_t>(COPY_BUFFER_SIZE, length);
  while ((bytes_read =
              Get(index + src_offset, &(b[0]), 0, buffer_length)) > 0) {
    int bytes_written = array->Put(index + dst_offset, &(b[0]), 0, bytes_read);
    UNREFERENCED_PARAMETER(bytes_written);
    index += bytes_read;
    remaining_length -= bytes_read;
    buffer_length = std::min<int32_t>(b.size(), remaining_length);
  }
  return index;
}

Project: sfntly
File: byte_array.cc

int32_t ByteArray::Size() { return storage_length_; }

Project: sfntly
File: byte_array.cc

int32_t ByteArray::CopyTo(ByteArray* array, int32_t offset, int32_t length) {
  return CopyTo(0, array, offset, length);
}

Project: sfntly
File: byte_array.cc

int32_t ByteArray::Put(int32_t index,
                       byte_t* b,
                       int32_t offset,
                       int32_t length) {
  assert(b);
  if (index < 0 || index >= Size()) {
#if defined (SFNTLY_NO_EXCEPTION)
    return 0;
#else
    throw IndexOutOfBoundException(
        "Attempt to write outside the bounds of the data");
#endif
  }
  int32_t actual_length = std::min<int32_t>(length, Size() - index);
  int32_t bytes_written = InternalPut(index, b, offset, actual_length);
  filled_length_ = std::max<int32_t>(filled_length_, index + bytes_written);
  return bytes_written;
}

Project: sfntly
File: byte_array.cc

bool ByteArray::CopyFrom(InputStream* is) {
  ByteVector b(COPY_BUFFER_SIZE);
  int32_t bytes_read = 0;
  int32_t index = 0;
  int32_t buffer_length = COPY_BUFFER_SIZE;
  while ((bytes_read = is->Read(&b, 0, buffer_length)) > 0) {
    if (Put(index, &b[0], 0, bytes_read) != bytes_read) {
#if defined (SFNTLY_NO_EXCEPTION)
      return 0;
#else
      throw IOException("Error writing bytes.");
#endif
    }
    index += bytes_read;
  }
  return true;
}

Project: sfntly
File: byte_array.cc

int32_t ByteArray::Length() { return filled_length_; }

Project: sfntly
File: byte_array.cc

void ByteArray::Put(int32_t index, byte_t b) {
  if (index < 0 || index >= Size()) {
#if defined (SFNTLY_NO_EXCEPTION)
    return;
#else
    throw IndexOutOfBoundException(
        "Attempt to write outside the bounds of the data");
#endif
  }
  InternalPut(index, b);
  filled_length_ = std::max<int32_t>(filled_length_, index + 1);
}

Project: sfntly
File: byte_array.cc

void ByteArray::Init(int32_t filled_length,
                     int32_t storage_length,
                     bool growable) {
  storage_length_ = storage_length;
  growable_ = growable;
  SetFilledLength(filled_length);
}

Project: sfntly
File: byte_array.cc

int32_t ByteArray::Put(int index, ByteVector* b) {
  assert(b);
  return Put(index, &((*b)[0]), 0, b->size());
}

Project: sfntly
File: byte_array.cc

int32_t ByteArray::Get(int32_t index,
                       byte_t* b,
                       int32_t offset,
                       int32_t length) {
  assert(b);
  if (index < 0 || index >= filled_length_) {
    return 0;
  }
  int32_t actual_length = std::min<int32_t>(length, filled_length_ - index);
  return InternalGet(index, b, offset, actual_length);
}