Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/media/mp4/Box.cpp
Line
Count
Source (jump to first uncovered line)
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
#include "ByteStream.h"
9
#include "mozilla/EndianUtils.h"
10
#include "mozilla/Unused.h"
11
#include <algorithm>
12
13
namespace mozilla {
14
15
// Limit reads to 32MiB max.
16
// static
17
const uint64_t Box::kMAX_BOX_READ = 32 * 1024 * 1024;
18
19
// Returns the offset from the start of the body of a box of type |aType|
20
// to the start of its first child.
21
static uint32_t
22
BoxOffset(AtomType aType)
23
0
{
24
0
  const uint32_t FULLBOX_OFFSET = 4;
25
0
26
0
  if (aType == AtomType("mp4a") || aType == AtomType("enca")) {
27
0
    // AudioSampleEntry; ISO 14496-12, section 8.16
28
0
    return 28;
29
0
  } else if (aType == AtomType("mp4v") || aType == AtomType("encv")) {
30
0
    // VideoSampleEntry; ISO 14496-12, section 8.16
31
0
    return 78;
32
0
  } else if (aType == AtomType("stsd")) {
33
0
    // SampleDescriptionBox; ISO 14496-12, section 8.16
34
0
    // This is a FullBox, and contains a |count| member before its child
35
0
    // boxes.
36
0
    return FULLBOX_OFFSET + 4;
37
0
  }
38
0
39
0
  return 0;
40
0
}
41
42
Box::Box(BoxContext* aContext, uint64_t aOffset, const Box* aParent)
43
  : mContext(aContext), mParent(aParent)
44
0
{
45
0
  uint8_t header[8];
46
0
47
0
  if (aOffset > INT64_MAX - sizeof(header)) {
48
0
    return;
49
0
  }
50
0
51
0
  MediaByteRange headerRange(aOffset, aOffset + sizeof(header));
52
0
  if (mParent && !mParent->mRange.Contains(headerRange)) {
53
0
    return;
54
0
  }
55
0
56
0
  const MediaByteRange* byteRange;
57
0
  for (int i = 0; ; i++) {
58
0
    if (i == mContext->mByteRanges.Length()) {
59
0
      return;
60
0
    }
61
0
62
0
    byteRange = static_cast<const MediaByteRange*>(&mContext->mByteRanges[i]);
63
0
    if (byteRange->Contains(headerRange)) {
64
0
      break;
65
0
    }
66
0
  }
67
0
68
0
  size_t bytes;
69
0
  if (!mContext->mSource->CachedReadAt(aOffset, header, sizeof(header),
70
0
                                       &bytes) ||
71
0
      bytes != sizeof(header)) {
72
0
    return;
73
0
  }
74
0
  mHeader.AppendElements(header, sizeof(header));
75
0
76
0
  uint64_t size = BigEndian::readUint32(header);
77
0
  if (size == 1) {
78
0
    uint8_t bigLength[8];
79
0
    if (aOffset > INT64_MAX - sizeof(header) - sizeof(bigLength)) {
80
0
      return;
81
0
    }
82
0
    MediaByteRange bigLengthRange(headerRange.mEnd,
83
0
                                  headerRange.mEnd + sizeof(bigLength));
84
0
    if ((mParent && !mParent->mRange.Contains(bigLengthRange)) ||
85
0
        !byteRange->Contains(bigLengthRange) ||
86
0
        !mContext->mSource->CachedReadAt(aOffset + sizeof(header), bigLength,
87
0
                                         sizeof(bigLength), &bytes) ||
88
0
        bytes != sizeof(bigLength)) {
89
0
      return;
90
0
    }
91
0
    size = BigEndian::readUint64(bigLength);
92
0
    mBodyOffset = bigLengthRange.mEnd;
93
0
    mHeader.AppendElements(bigLength, sizeof(bigLength));
94
0
  } else if (size == 0) {
95
0
    // box extends to end of file.
96
0
    size = mContext->mByteRanges.LastInterval().mEnd - aOffset;
97
0
    mBodyOffset = headerRange.mEnd;
98
0
  } else {
99
0
    mBodyOffset = headerRange.mEnd;
100
0
  }
101
0
102
0
  if (size > INT64_MAX) {
103
0
    return;
104
0
  }
105
0
  int64_t end = static_cast<int64_t>(aOffset) + static_cast<int64_t>(size);
106
0
  if (end < static_cast<int64_t>(aOffset)) {
107
0
    // Overflowed.
108
0
    return;
109
0
  }
110
0
111
0
  mType = BigEndian::readUint32(&header[4]);
112
0
  mChildOffset = mBodyOffset + BoxOffset(mType);
113
0
114
0
  MediaByteRange boxRange(aOffset, end);
115
0
  if (mChildOffset > boxRange.mEnd ||
116
0
      (mParent && !mParent->mRange.Contains(boxRange)) ||
117
0
      !byteRange->Contains(boxRange)) {
118
0
    return;
119
0
  }
120
0
121
0
  mRange = boxRange;
122
0
}
123
124
Box::Box()
125
  : mContext(nullptr)
126
  , mBodyOffset(0)
127
  , mChildOffset(0)
128
  , mParent(nullptr)
129
0
{}
130
131
Box
132
Box::Next() const
133
0
{
134
0
  MOZ_ASSERT(IsAvailable());
135
0
  return Box(mContext, mRange.mEnd, mParent);
136
0
}
137
138
Box
139
Box::FirstChild() const
140
0
{
141
0
  MOZ_ASSERT(IsAvailable());
142
0
  if (mChildOffset == mRange.mEnd) {
143
0
    return Box();
144
0
  }
145
0
  return Box(mContext, mChildOffset, this);
146
0
}
147
148
nsTArray<uint8_t>
149
Box::Read() const
150
0
{
151
0
  nsTArray<uint8_t> out;
152
0
  Unused << Read(&out, mRange);
153
0
  return out;
154
0
}
155
156
bool
157
Box::Read(nsTArray<uint8_t>* aDest, const MediaByteRange& aRange) const
158
0
{
159
0
  int64_t length;
160
0
  if (!mContext->mSource->Length(&length)) {
161
0
    // The HTTP server didn't give us a length to work with.
162
0
    // Limit the read to kMAX_BOX_READ max.
163
0
    length = std::min(aRange.mEnd - mChildOffset, kMAX_BOX_READ);
164
0
  } else {
165
0
    length = aRange.mEnd - mChildOffset;
166
0
  }
167
0
  aDest->SetLength(length);
168
0
  size_t bytes;
169
0
  if (!mContext->mSource->CachedReadAt(mChildOffset, aDest->Elements(),
170
0
                                       aDest->Length(), &bytes) ||
171
0
      bytes != aDest->Length()) {
172
0
    // Byte ranges are being reported incorrectly
173
0
    NS_WARNING("Read failed in mozilla::Box::Read()");
174
0
    aDest->Clear();
175
0
    return false;
176
0
  }
177
0
  return true;
178
0
}
179
}