tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

Box.cpp (7630B)


      1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim:set ts=2 sw=2 sts=2 et cindent: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "Box.h"
      8 
      9 #include <algorithm>
     10 
     11 #include "ByteStream.h"
     12 #include "mozilla/EndianUtils.h"
     13 
     14 namespace mozilla {
     15 
     16 // Limit reads to 32MiB max.
     17 // static
     18 const uint64_t Box::kMAX_BOX_READ = 32 * 1024 * 1024;
     19 
     20 // Returns the offset from the start of the body of a box of type |aType|
     21 // to the start of its first child.
     22 static uint32_t BoxOffset(AtomType aType) {
     23  const uint32_t FULLBOX_OFFSET = 4;
     24 
     25  if (aType == AtomType("mp4a") || aType == AtomType("enca")) {
     26    // AudioSampleEntry; ISO 14496-12, section 8.16
     27    return 28;
     28  } else if (aType == AtomType("mp4v") || aType == AtomType("encv")) {
     29    // VideoSampleEntry; ISO 14496-12, section 8.16
     30    return 78;
     31  } else if (aType == AtomType("stsd")) {
     32    // SampleDescriptionBox; ISO 14496-12, section 8.16
     33    // This is a FullBox, and contains a |count| member before its child
     34    // boxes.
     35    return FULLBOX_OFFSET + 4;
     36  }
     37 
     38  return 0;
     39 }
     40 
     41 Box::Box(BoxContext* aContext, uint64_t aOffset, const Box* aParent)
     42    : mContext(aContext), mParent(aParent) {
     43  // Default error code for early returns if ranges are not as expected
     44  mInitStatus = NS_ERROR_DOM_MEDIA_RANGE_ERR;
     45  // Set start offset for Offset() even when the box is unavailable.
     46  // The empty range indicates !IsAvailable(), overwritten below on success.
     47  mRange = MediaByteRange(aOffset, aOffset);
     48 
     49  uint8_t header[8];
     50  if (aOffset > INT64_MAX - sizeof(header)) {
     51    return;
     52  }
     53 
     54  MediaByteRange headerRange(aOffset, aOffset + sizeof(header));
     55  if (mParent && !mParent->mRange.Contains(headerRange)) {
     56    return;
     57  }
     58 
     59  const MediaByteRange* byteRange;
     60  for (int i = 0;; i++) {
     61    if (i == mContext->mByteRanges.Length()) {
     62      return;
     63    }
     64 
     65    byteRange = static_cast<const MediaByteRange*>(&mContext->mByteRanges[i]);
     66    if (byteRange->Contains(headerRange)) {
     67      break;
     68    }
     69  }
     70 
     71  size_t bytes;
     72  nsresult rv =
     73      mContext->mSource->CachedReadAt(aOffset, header, sizeof(header), &bytes);
     74  if (NS_FAILED(rv)) {
     75    mInitStatus = rv;
     76    return;
     77  }
     78  if (bytes != sizeof(header)) {
     79    // CachedReadAt() would usually return an error if the read cannot
     80    // complete, but BlockingStream can return fewer bytes on end of stream or
     81    // after a network error has occurred.
     82    return;
     83  }
     84 
     85  uint64_t size = BigEndian::readUint32(header);
     86  if (size == 1) {
     87    uint8_t bigLength[8];
     88    if (aOffset > INT64_MAX - sizeof(header) - sizeof(bigLength)) {
     89      return;
     90    }
     91    MediaByteRange bigLengthRange(headerRange.mEnd,
     92                                  headerRange.mEnd + sizeof(bigLength));
     93    if ((mParent && !mParent->mRange.Contains(bigLengthRange)) ||
     94        !byteRange->Contains(bigLengthRange)) {
     95      return;
     96    }
     97    rv = mContext->mSource->CachedReadAt(aOffset + sizeof(header), bigLength,
     98                                         sizeof(bigLength), &bytes);
     99    if (NS_FAILED(rv)) {
    100      mInitStatus = rv;
    101      return;
    102    }
    103    if (bytes != sizeof(bigLength)) {
    104      return;
    105    }
    106    size = BigEndian::readUint64(bigLength);
    107    mBodyOffset = bigLengthRange.mEnd;
    108  } else if (size == 0) {
    109    // box extends to end of file.
    110    size = mContext->mByteRanges.LastInterval().mEnd - aOffset;
    111    mBodyOffset = headerRange.mEnd;
    112  } else {
    113    mBodyOffset = headerRange.mEnd;
    114  }
    115 
    116  if (size > INT64_MAX) {
    117    return;
    118  }
    119  int64_t end = static_cast<int64_t>(aOffset) + static_cast<int64_t>(size);
    120  if (end < static_cast<int64_t>(aOffset)) {
    121    // Overflowed.
    122    return;
    123  }
    124 
    125  mType = BigEndian::readUint32(&header[4]);
    126  mChildOffset = mBodyOffset + BoxOffset(mType);
    127 
    128  MediaByteRange boxRange(aOffset, end);
    129  if (mChildOffset > boxRange.mEnd ||
    130      (mParent && !mParent->mRange.Contains(boxRange)) ||
    131      !byteRange->Contains(boxRange)) {
    132    return;
    133  }
    134 
    135  mInitStatus = NS_OK;
    136  mRange = boxRange;
    137 }
    138 
    139 Box::Box()
    140    : mContext(nullptr), mBodyOffset(0), mChildOffset(0), mParent(nullptr) {}
    141 
    142 Box Box::Next() const {
    143  MOZ_ASSERT(IsAvailable());
    144  return Box(mContext, mRange.mEnd, mParent);
    145 }
    146 
    147 Box Box::FirstChild() const {
    148  MOZ_ASSERT(IsAvailable());
    149  if (mChildOffset == mRange.mEnd) {
    150    return Box();
    151  }
    152  return Box(mContext, mChildOffset, this);
    153 }
    154 
    155 nsTArray<uint8_t> Box::ReadCompleteBox() const {
    156  const size_t length = mRange.mEnd - mRange.mStart;
    157  nsTArray<uint8_t> out(length);
    158  out.SetLength(length);
    159  size_t bytesRead = 0;
    160  if (NS_FAILED(mContext->mSource->CachedReadAt(mRange.mStart, out.Elements(),
    161                                                length, &bytesRead)) ||
    162      bytesRead != length) {
    163    // Byte ranges are being reported incorrectly
    164    NS_WARNING("Read failed in mozilla::Box::ReadCompleteBox()");
    165    return nsTArray<uint8_t>(0);
    166  }
    167  return out;
    168 }
    169 
    170 nsTArray<uint8_t> Box::Read() const {
    171  nsTArray<uint8_t> out;
    172  (void)Read(&out, mRange);
    173  return out;
    174 }
    175 
    176 bool Box::Read(nsTArray<uint8_t>* aDest, const MediaByteRange& aRange) const {
    177  int64_t length;
    178  if (!mContext->mSource->Length(&length)) {
    179    // The HTTP server didn't give us a length to work with.
    180    // Limit the read to kMAX_BOX_READ max.
    181    length = std::min(aRange.mEnd - mChildOffset, kMAX_BOX_READ);
    182  } else {
    183    length = aRange.mEnd - mChildOffset;
    184  }
    185  aDest->SetLength(length);
    186  size_t bytes;
    187  if (NS_FAILED(mContext->mSource->CachedReadAt(mChildOffset, aDest->Elements(),
    188                                                aDest->Length(), &bytes)) ||
    189      bytes != aDest->Length()) {
    190    // Byte ranges are being reported incorrectly
    191    NS_WARNING("Read failed in mozilla::Box::Read()");
    192    aDest->Clear();
    193    return false;
    194  }
    195  return true;
    196 }
    197 
    198 ByteSlice Box::ReadAsSlice() const {
    199  if (!mContext || mRange.IsEmpty()) {
    200    return ByteSlice{nullptr, 0};
    201  }
    202 
    203  int64_t length;
    204  if (!mContext->mSource->Length(&length)) {
    205    // The HTTP server didn't give us a length to work with.
    206    // Limit the read to kMAX_BOX_READ max.
    207    length = std::min(mRange.mEnd - mChildOffset, kMAX_BOX_READ);
    208  } else {
    209    length = mRange.mEnd - mChildOffset;
    210  }
    211 
    212  const uint8_t* data =
    213      mContext->mSource->GetContiguousAccess(mChildOffset, length);
    214  if (data) {
    215    // We can direct access the underlying storage of the ByteStream.
    216    return ByteSlice{data, size_t(length)};
    217  }
    218 
    219  uint8_t* p = mContext->mAllocator.Allocate(size_t(length));
    220  size_t bytes;
    221  if (NS_FAILED(
    222          mContext->mSource->CachedReadAt(mChildOffset, p, length, &bytes)) ||
    223      bytes != length) {
    224    // Byte ranges are being reported incorrectly
    225    NS_WARNING("Read failed in mozilla::Box::ReadAsSlice()");
    226    return ByteSlice{nullptr, 0};
    227  }
    228  return ByteSlice{p, size_t(length)};
    229 }
    230 
    231 const size_t BLOCK_CAPACITY = 16 * 1024;
    232 
    233 uint8_t* BumpAllocator::Allocate(size_t aNumBytes) {
    234  if (aNumBytes > BLOCK_CAPACITY) {
    235    mBuffers.AppendElement(nsTArray<uint8_t>(aNumBytes));
    236    mBuffers.LastElement().SetLength(aNumBytes);
    237    return mBuffers.LastElement().Elements();
    238  }
    239  for (nsTArray<uint8_t>& buffer : mBuffers) {
    240    if (buffer.Length() + aNumBytes < BLOCK_CAPACITY) {
    241      size_t offset = buffer.Length();
    242      buffer.SetLength(buffer.Length() + aNumBytes);
    243      return buffer.Elements() + offset;
    244    }
    245  }
    246  mBuffers.AppendElement(nsTArray<uint8_t>(BLOCK_CAPACITY));
    247  mBuffers.LastElement().SetLength(aNumBytes);
    248  return mBuffers.LastElement().Elements();
    249 }
    250 
    251 }  // namespace mozilla