Keep in mind i didnt find this myself, it was listed in an exploit database that i cant remember the name of.
this is something i found a while ago, it is a webkit exploit that depends on a bug inside of the mp4 playback, the mp4 file is available here https://www.dropbox.com/s/aer8bmojvfu5ar1/crash.mp4?dl=0
EDIT: the code in case anyone is wondering is javascript.
EDIT 2: I remember when I found the code, the version of webkit it applies to is the same as the webkit version inside of 4.01
this is something i found a while ago, it is a webkit exploit that depends on a bug inside of the mp4 playback, the mp4 file is available here https://www.dropbox.com/s/aer8bmojvfu5ar1/crash.mp4?dl=0
EDIT: the code in case anyone is wondering is javascript.
EDIT 2: I remember when I found the code, the version of webkit it applies to is the same as the webkit version inside of 4.01
Code:
};
Vector<AuxRange> mDeferredSaiz;
Vector<AuxRange> mDeferredSaio;
MPEG4Source(const MPEG4Source &);
MPEG4Source &operator=(const MPEG4Source &);
bool ensureSrcBufferAllocated(int32_t size);
+ // Ensure that we have enough data in mMediaBuffer to copy our data.
+ // Returns false if not and clear mMediaBuffer.
+ bool ensureMediaBufferAllocated(int32_t size);
};
// This custom data source wraps an existing one and satisfies requests
// falling entirely within a cached range from the cache while forwarding
// all remaining requests to the wrapped datasource.
// This is used to cache the full sampletable metadata for a single track,
// possibly wrapping multiple times to cover all tracks, i.e.
// Each MPEG4DataSource caches the sampletable metadata for a single track.
@@ -3265,22 +3268,35 @@ status_t MPEG4Source::lookForMoof() {
}
}
bool MPEG4Source::ensureSrcBufferAllocated(int32_t aSize) {
if (mSrcBackend.Length() >= aSize) {
return true;
}
if (!mSrcBackend.SetLength(aSize)) {
+ ALOGE("Error insufficient memory, requested %u bytes (had:%u)",
+ aSize, mSrcBackend.Length());
return false;
}
mSrcBuffer = mSrcBackend.Elements();
return true;
}
+bool MPEG4Source::ensureMediaBufferAllocated(int32_t aSize) {
+ if (mBuffer->size() < aSize) {
+ ALOGE("Error insufficient memory, requested %u bytes (had:%u)",
+ aSize, mBuffer->size());
+ mBuffer->release();
+ mBuffer = NULL;
+ return false;
+ }
+ return true;
+}
+
status_t MPEG4Source::read(
MediaBuffer **out, const ReadOptions *options) {
Mutex::Autolock autoLock(mLock);
CHECK(mStarted);
if (!mLookedForMoof) {
mLookedForMoof = lookForMoof() == OK;
@@ -3405,16 +3421,19 @@ status_t MPEG4Source::read(
return ERROR_MALFORMED;
}
mBuffer = new MediaBuffer(max_size);
assert(mBuffer);
}
if (!mIsAVC || mWantsNALFragments) {
if (newBuffer) {
+ if (!ensureMediaBufferAllocated(size)) {
+ return ERROR_MALFORMED;
+ }
ssize_t num_bytes_read =
mDataSource->readAt(offset, (uint8_t *)mBuffer->data(), size);
if (num_bytes_read < (ssize_t)size) {
mBuffer->release();
mBuffer = NULL;
return ERROR_IO;
@@ -3512,22 +3531,23 @@ status_t MPEG4Source::read(
return OK;
} else {
// Whole NAL units are returned but each fragment is prefixed by
// the NAL length, stored in four bytes.
ssize_t num_bytes_read = 0;
int32_t drm = 0;
bool usesDRM = (mFormat->findInt32(kKeyIsDRM, &drm) && drm != 0);
if (usesDRM) {
+ if (!ensureMediaBufferAllocated(size)) {
+ return ERROR_MALFORMED;
+ }
num_bytes_read =
mDataSource->readAt(offset, (uint8_t*)mBuffer->data(), size);
} else {
if (!ensureSrcBufferAllocated(size)) {
- ALOGE("Error insufficient memory, requested %u bytes (had:%u)",
- size, mSrcBackend.Length());
return ERROR_MALFORMED;
}
num_bytes_read = mDataSource->readAt(offset, mSrcBuffer, size);
}
if (num_bytes_read < (ssize_t)size) {
mBuffer->release();
mBuffer = NULL;
@@ -3559,17 +3579,19 @@ status_t MPEG4Source::read(
mBuffer = NULL;
return ERROR_MALFORMED;
}
if (nalLength == 0) {
continue;
}
- CHECK(dstOffset + 4 <= mBuffer->size());
+ if (!ensureMediaBufferAllocated(dstOffset + 4 + nalLength)) {
+ return ERROR_MALFORMED;
+ }
dstData[dstOffset++] = (uint8_t) (nalLength >> 24);
dstData[dstOffset++] = (uint8_t) (nalLength >> 16);
dstData[dstOffset++] = (uint8_t) (nalLength >> 8);
dstData[dstOffset++] = (uint8_t) nalLength;
memcpy(&dstData[dstOffset], &mSrcBuffer[srcOffset], nalLength);
srcOffset += nalLength;
dstOffset += nalLength;
}
@@ -3784,16 +3806,19 @@ status_t MPEG4Source::fragmentedRead(
bufmeta->setData(kKeyCryptoIV, 0, smpl->iv, 16); // use 16 or the actual size?
bufmeta->setInt32(kKeyCryptoDefaultIVSize, mDefaultIVSize);
bufmeta->setInt32(kKeyCryptoMode, mCryptoMode);
bufmeta->setData(kKeyCryptoKey, 0, mCryptoKey, 16);
}
if (!mIsAVC || mWantsNALFragments) {
if (newBuffer) {
+ if (!ensureMediaBufferAllocated(size)) {
+ return ERROR_MALFORMED;
+ }
ssize_t num_bytes_read =
mDataSource->readAt(offset, (uint8_t *)mBuffer->data(), size);
if (num_bytes_read < (ssize_t)size) {
mBuffer->release();
mBuffer = NULL;
ALOGV("i/o error");
@@ -3869,22 +3894,23 @@ status_t MPEG4Source::fragmentedRead(
} else {
ALOGV("whole NAL");
// Whole NAL units are returned but each fragment is prefixed by
// the NAL unit's length, stored in four bytes.
ssize_t num_bytes_read = 0;
int32_t drm = 0;
bool usesDRM = (mFormat->findInt32(kKeyIsDRM, &drm) && drm != 0);
if (usesDRM) {
+ if (!ensureMediaBufferAllocated(size)) {
+ return ERROR_MALFORMED;
+ }
num_bytes_read =
mDataSource->readAt(offset, (uint8_t*)mBuffer->data(), size);
} else {
if (!ensureSrcBufferAllocated(size)) {
- ALOGE("Error insufficient memory, requested %u bytes (had:%u)",
- size, mSrcBackend.Length());
return ERROR_MALFORMED;
}
num_bytes_read = mDataSource->readAt(offset, mSrcBuffer, size);
}
if (num_bytes_read < (ssize_t)size) {
mBuffer->release();
mBuffer = NULL;
@@ -3917,17 +3943,19 @@ status_t MPEG4Source::fragmentedRead(
mBuffer = NULL;
return ERROR_MALFORMED;
}
if (nalLength == 0) {
continue;
}
- CHECK(dstOffset + 4 <= mBuffer->size());
+ if (!ensureMediaBufferAllocated(dstOffset + 4 + nalLength)) {
+ return ERROR_MALFORMED;
+ }
dstData[dstOffset++] = (uint8_t) (nalLength >> 24);
dstData[dstOffset++] = (uint8_t) (nalLength >> 16);
dstData[dstOffset++] = (uint8_t) (nalLength >> 8);
dstData[dstOffset++] = (uint8_t) nalLength;
memcpy(&dstData[dstOffset], &mSrcBuffer[srcOffset], nalLength);
srcOffset += nalLength;
dstOffset += nalLength;
mBuffer = NULL;
return OK;
}
// Each NAL unit is split up into its constituent fragments and
// each one of them returned in its own buffer.
- CHECK(mBuffer->range_length() >= mNALLengthSize);
+ if (mBuffer->range_length() < mNALLengthSize) {
+ ALOGE("incomplete NAL unit.");
+
+ mBuffer->release();
+ mBuffer = NULL;
+
+ return ERROR_MALFORMED;
+ }
const uint8_t *src =
(const uint8_t *)mBuffer->data() + mBuffer->range_offset();
size_t nal_size = parseNALSize(src);
if (mBuffer->range_length() < mNALLengthSize + nal_size) {
ALOGE("incomplete NAL unit.");
@@ -3854,17 +3861,24 @@ status_t MPEG4Source::fragmentedRead(
mBuffer = NULL;
return OK;
}
// Each NAL unit is split up into its constituent fragments and
// each one of them returned in its own buffer.
- CHECK(mBuffer->range_length() >= mNALLengthSize);
+ if (mBuffer->range_length() < mNALLengthSize) {
+ ALOGE("incomplete NAL unit.");
+
+ mBuffer->release();
+ mBuffer = NULL;
+
+ return ERROR_MALFORMED;
+ }
const uint8_t *src =
(const uint8_t *)mBuffer->data() + mBuffer->range_offset();
size_t nal_size = parseNALSize(src);
if (mBuffer->range_length() < mNALLengthSize + nal_size) {
ALOGE("incomplete NAL unit.");
diff --git a/media/libstagefright/frameworks/av/include/media/stagefright/MediaBuffer.h b/media/libstagefright/frameworks/av/include/media/stagefright/MediaBuffer.h
--- a/media/libstagefright/frameworks/av/include/media/stagefright/MediaBuffer.h
+++ b/media/libstagefright/frameworks/av/include/media/stagefright/MediaBuffer.h
@@ -17,16 +17,17 @@
#ifndef MEDIA_BUFFER_H_
#define MEDIA_BUFFER_H_
#include <pthread.h>
#include <utils/Errors.h>
#include <utils/RefBase.h>
+#include "nsTArray.h"
namespace stagefright {
struct ABuffer;
class GraphicBuffer;
class MediaBuffer;
class MediaBufferObserver;
class MetaData;
@@ -80,16 +81,18 @@ public:
// Returns a clone of this MediaBuffer increasing its reference count.
// The clone references the same data but has its own range and
// MetaData.
MediaBuffer *clone();
int refcount() const;
+ bool ensuresize(size_t length);
+
protected:
virtual ~MediaBuffer();
private:
friend class MediaBufferGroup;
friend class OMXDecoder;
// For use by OMXDecoder, reference count must be 1, drop reference
@@ -111,13 +114,15 @@ private:
MediaBuffer *mOriginal;
void setNextBuffer(MediaBuffer *buffer);
MediaBuffer *nextBuffer();
MediaBuffer(const MediaBuffer &);
MediaBuffer &operator=(const MediaBuffer &);
+
+ FallibleTArray<uint8_t> mBufferBackend;
};
} // namespace stagefright
#endif // MEDIA_BUFFER_H_
diff --git a/media/libstagefright/frameworks/av/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/frameworks/av/media/libstagefright/MPEG4Extractor.cpp
--- a/media/libstagefright/frameworks/av/media/libstagefright/MPEG4Extractor.cpp
+++ b/media/libstagefright/frameworks/av/media/libstagefright/MPEG4Extractor.cpp
@@ -3277,24 +3277,24 @@ bool MPEG4Source::ensureSrcBufferAllocat
aSize, mSrcBackend.Length());
return false;
}
mSrcBuffer = mSrcBackend.Elements();
return true;
}
bool MPEG4Source::ensureMediaBufferAllocated(int32_t aSize) {
- if (mBuffer->size() < aSize) {
- ALOGE("Error insufficient memory, requested %u bytes (had:%u)",
- aSize, mBuffer->size());
- mBuffer->release();
- mBuffer = NULL;
- return false;
+ if (mBuffer->ensuresize(aSize)) {
+ return true;
}
- return true;
+ ALOGE("Error insufficient memory, requested %u bytes (had:%u)",
+ aSize, mBuffer->size());
+ mBuffer->release();
+ mBuffer = NULL;
+ return false;
}
status_t MPEG4Source::read(
MediaBuffer **out, const ReadOptions *options) {
Mutex::Autolock autoLock(mLock);
CHECK(mStarted);
@@ -3411,22 +3411,17 @@ status_t MPEG4Source::read(
&isSyncSample, &dts);
if (err != OK) {
return err;
}
int32_t max_size;
CHECK(mFormat->findInt32(kKeyMaxInputSize, &max_size));
- if (!ValidInputSize(max_size)) {
- ALOGE("Invalid max input size %d", max_size);
- return ERROR_MALFORMED;
- }
- mBuffer = new MediaBuffer(max_size);
- assert(mBuffer);
+ mBuffer = new MediaBuffer(max_size > 1024*1024 ? 1024*1024 : max_size);
}
if (!mIsAVC || mWantsNALFragments) {
if (newBuffer) {
if (!ensureMediaBufferAllocated(size)) {
return ERROR_MALFORMED;
}
ssize_t num_bytes_read =
@@ -3562,17 +3557,16 @@ status_t MPEG4Source::read(
return ERROR_IO;
}
if (usesDRM) {
CHECK(mBuffer != NULL);
mBuffer->set_range(0, size);
} else {
- uint8_t *dstData = (uint8_t *)mBuffer->data();
size_t srcOffset = 0;
size_t dstOffset = 0;
while (srcOffset < size) {
bool isMalFormed = (srcOffset + mNALLengthSize > size);
size_t nalLength = 0;
if (!isMalFormed) {
nalLength = parseNALSize(&mSrcBuffer[srcOffset]);
@@ -3589,16 +3583,17 @@ status_t MPEG4Source::read(
if (nalLength == 0) {
continue;
}
if (!ensureMediaBufferAllocated(dstOffset + 4 + nalLength)) {
return ERROR_MALFORMED;
}
+ uint8_t *dstData = (uint8_t *)mBuffer->data();
dstData[dstOffset++] = (uint8_t) (nalLength >> 24);
dstData[dstOffset++] = (uint8_t) (nalLength >> 16);
dstData[dstOffset++] = (uint8_t) (nalLength >> 8);
dstData[dstOffset++] = (uint8_t) nalLength;
memcpy(&dstData[dstOffset], &mSrcBuffer[srcOffset], nalLength);
srcOffset += nalLength;
dstOffset += nalLength;
}
@@ -3788,22 +3783,17 @@ status_t MPEG4Source::fragmentedRead(
dts = mCurrentTime;
cts = mCurrentTime + smpl->ctsOffset;
duration = smpl->duration;
mCurrentTime += smpl->duration;
isSyncSample = smpl->isSync();
int32_t max_size;
CHECK(mFormat->findInt32(kKeyMaxInputSize, &max_size));
- if (!ValidInputSize(max_size)) {
- ALOGE("Invalid max input size %d", max_size);
- return ERROR_MALFORMED;
- }
- mBuffer = new MediaBuffer(max_size);
- assert(mBuffer);
+ mBuffer = new MediaBuffer(max_size > 1024*1024 ? 1024*1024 : max_size);
}
const Sample *smpl = &mCurrentSamples[mCurrentSampleIndex];
const sp<MetaData> bufmeta = mBuffer->meta_data();
bufmeta->clear();
if (smpl->encryptedsizes.size()) {
// store clear/encrypted lengths in metadata
bufmeta->setData(kKeyPlainSizes, 0,
@@ -3933,17 +3923,16 @@ status_t MPEG4Source::fragmentedRead(
return ERROR_IO;
}
if (usesDRM) {
CHECK(mBuffer != NULL);
mBuffer->set_range(0, size);
} else {
- uint8_t *dstData = (uint8_t *)mBuffer->data();
size_t srcOffset = 0;
size_t dstOffset = 0;
while (srcOffset < size) {
bool isMalFormed = (srcOffset + mNALLengthSize > size);
size_t nalLength = 0;
if (!isMalFormed) {
nalLength = parseNALSize(&mSrcBuffer[srcOffset]);
@@ -3960,16 +3949,17 @@ status_t MPEG4Source::fragmentedRead(
if (nalLength == 0) {
continue;
}
if (!ensureMediaBufferAllocated(dstOffset + 4 + nalLength)) {
return ERROR_MALFORMED;
}
+ uint8_t *dstData = (uint8_t *)mBuffer->data();
dstData[dstOffset++] = (uint8_t) (nalLength >> 24);
dstData[dstOffset++] = (uint8_t) (nalLength >> 16);
dstData[dstOffset++] = (uint8_t) (nalLength >> 8);
dstData[dstOffset++] = (uint8_t) nalLength;
memcpy(&dstData[dstOffset], &mSrcBuffer[srcOffset], nalLength);
srcOffset += nalLength;
dstOffset += nalLength;
}
diff --git a/media/libstagefright/frameworks/av/media/libstagefright/MediaBuffer.cpp b/media/libstagefright/frameworks/av/media/libstagefright/MediaBuffer.cpp
--- a/media/libstagefright/frameworks/av/media/libstagefright/MediaBuffer.cpp
+++ b/media/libstagefright/frameworks/av/media/libstagefright/MediaBuffer.cpp
@@ -43,23 +43,24 @@ MediaBuffer::MediaBuffer(void *data, siz
mMetaData(new MetaData),
mOriginal(NULL) {
}
MediaBuffer::MediaBuffer(size_t size)
: mObserver(NULL),
mNextBuffer(NULL),
mRefCount(0),
- mData(malloc(size)),
+ mData(NULL),
mSize(size),
mRangeOffset(0),
mRangeLength(size),
mOwnsData(true),
mMetaData(new MetaData),
mOriginal(NULL) {
+ ensuresize(size);
}
MediaBuffer::MediaBuffer(const sp<GraphicBuffer>& graphicBuffer)
: mObserver(NULL),
mNextBuffer(NULL),
mRefCount(0),
mData(NULL),
mSize(1),
@@ -154,21 +155,16 @@ sp<MetaData> MediaBuffer::meta_data() {
void MediaBuffer::reset() {
mMetaData->clear();
set_range(0, mSize);
}
MediaBuffer::~MediaBuffer() {
CHECK(mObserver == NULL);
- if (mOwnsData && mData != NULL) {
- free(mData);
- mData = NULL;
- }
-
if (mOriginal != NULL) {
mOriginal->release();
mOriginal = NULL;
}
}
void MediaBuffer::setObserver(MediaBufferObserver *observer) {
CHECK(observer == NULL || mObserver == NULL);
@@ -195,9 +191,25 @@ MediaBuffer *MediaBuffer::clone() {
buffer->mMetaData = new MetaData(*mMetaData.get());
add_ref();
buffer->mOriginal = this;
return buffer;
}
+bool MediaBuffer::ensuresize(size_t length) {
+ if (mBufferBackend.Length() >= length) {
+ return true;
+ }
+ // Can't reallocate data we don't owned or shared with another.
+ if (!mOwnsData || refcount()) {
+ return false;
+ }
+ if (!mBufferBackend.SetLength(length)) {
+ return false;
+ }
+ mData = mBufferBackend.Elements();
+ mSize = length;
+ return true;
+}
+
} diff --git a/media/libstagefright/frameworks/av/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/frameworks/av/media/libstagefright/MPEG4Extractor.cpp
--- a/media/libstagefright/frameworks/av/media/libstagefright/MPEG4Extractor.cpp
+++ b/media/libstagefright/frameworks/av/media/libstagefright/MPEG4Extractor.cpp
@@ -32,16 +32,17 @@
#include <media/stagefright/foundation/ABuffer.h>
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/AMessage.h>
#include <media/stagefright/MediaBuffer.h>
#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/MediaSource.h>
#include <media/stagefright/MetaData.h>
#include <utils/String8.h>
+#include "nsTArray.h"
namespace stagefright {
class MPEG4Source : public MediaSource {
public:
// Caller retains ownership of both "dataSource" and "sampleTable".
MPEG4Source(const sp<MetaData> &format,
const sp<DataSource> &dataSource,
@@ -98,16 +99,17 @@ private:
bool mStarted;
MediaBuffer *mBuffer;
bool mWantsNALFragments;
uint8_t *mSrcBuffer;
+ FallibleTArray<uint8_t> mSrcBackend;
size_t parseNALSize(const uint8_t *data) const;
status_t parseChunk(off64_t *offset);
status_t parseTrackFragmentData(off64_t offset, off64_t size);
status_t parseTrackFragmentHeader(off64_t offset, off64_t size);
status_t parseTrackFragmentRun(off64_t offset, off64_t size);
status_t parseSampleAuxiliaryInformationSizes(off64_t offset, off64_t size);
status_t parseSampleAuxiliaryInformationOffsets(off64_t offset, off64_t size);
@@ -151,16 +153,21 @@ private:
Vector<uint16_t> clearsizes;
Vector<uint32_t> encryptedsizes;
};
Vector<Sample> mCurrentSamples;
MPEG4Extractor::TrackExtends mTrackExtends;
MPEG4Source(const MPEG4Source &);
MPEG4Source &operator=(const MPEG4Source &);
+
+ bool ensureSrcBufferAllocated(int32_t size);
+ // Ensure that we have enough data in mMediaBuffer to copy our data.
+ // Returns false if not and clear mMediaBuffer.
+ bool ensureMediaBufferAllocated(int32_t size);
};
// This custom data source wraps an existing one and satisfies requests
// falling entirely within a cached range from the cache while forwarding
// all remaining requests to the wrapped datasource.
// This is used to cache the full sampletable metadata for a single track,
// possibly wrapping multiple times to cover all tracks, i.e.
// Each MPEG4DataSource caches the sampletable metadata for a single track.
@@ -748,16 +755,23 @@ static void convertTimeToDate(int64_t ti
}
char tmp[32];
strftime(tmp, sizeof(tmp), "%Y%m%dT%H%M%S.000Z", gmtime(&time_1970));
s->setTo(tmp);
}
+static bool ValidInputSize(int32_t size) {
+ // Reject compressed samples larger than an uncompressed UHD
+ // frame. This is a reasonable cut-off for a lossy codec,
+ // combined with the current Firefox limit to 5k video.
+ return (size > 0 && size <= 4 * (1920 * 1080) * 3 / 2);
+}
+
status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) {
ALOGV("entering parseChunk %lld/%d", *offset, depth);
uint32_t hdr[2];
if (mDataSource->readAt(*offset, hdr, 8) < 8) {
return ERROR_IO;
}
uint64_t chunk_size = ntohl(hdr[0]);
uint32_t chunk_type = ntohl(hdr[1]);
@@ -2480,62 +2494,47 @@ MPEG4Source::MPEG4Source(
MPEG4Source::~MPEG4Source() {
if (mStarted) {
stop();
}
free(mCurrentSampleInfoSizes);
free(mCurrentSampleInfoOffsets);
}
-static bool ValidInputSize(int32_t size) {
- // Reject compressed samples larger than an uncompressed UHD
- // frame. This is a reasonable cut-off for a lossy codec,
- // combined with the current Firefox limit to 5k video.
- return (size > 0 && size < 4 * (1920 * 1080) * 3 / 2);
-}
-
status_t MPEG4Source::start(MetaData *params) {
Mutex::Autolock autoLock(mLock);
CHECK(!mStarted);
int32_t val;
if (params && params->findInt32(kKeyWantsNALFragments, &val)
&& val != 0) {
mWantsNALFragments = true;
} else {
mWantsNALFragments = false;
}
- int32_t max_size;
- CHECK(mFormat->findInt32(kKeyMaxInputSize, &max_size));
- if (!ValidInputSize(max_size)) {
- ALOGE("Invalid max input size %d", max_size);
- return ERROR_MALFORMED;
- }
-
- mSrcBuffer = new uint8_t[max_size];
+ mSrcBuffer = mSrcBackend.Elements();
mStarted = true;
return OK;
}
status_t MPEG4Source::stop() {
Mutex::Autolock autoLock(mLock);
CHECK(mStarted);
if (mBuffer != NULL) {
mBuffer->release();
mBuffer = NULL;
}
- delete[] mSrcBuffer;
- mSrcBuffer = NULL;
+ mSrcBackend.Clear();
mStarted = false;
mCurrentSampleIndex = 0;
return OK;
}
status_t MPEG4Source::parseChunk(off64_t *offset) {
@@ -3137,16 +3136,40 @@ size_t MPEG4Source::parseNALSize(const u
// This cannot happen, mNALLengthSize springs to life by adding 1 to
// a 2-bit integer.
CHECK(!"Should not be here.");
return 0;
}
+bool MPEG4Source::ensureSrcBufferAllocated(int32_t aSize) {
+ if (mSrcBackend.Length() >= aSize) {
+ return true;
+ }
+ if (!mSrcBackend.SetLength(aSize)) {
+ ALOGE("Error insufficient memory, requested %u bytes (had:%u)",
+ aSize, mSrcBackend.Length());
+ return false;
+ }
+ mSrcBuffer = mSrcBackend.Elements();
+ return true;
+}
+
+bool MPEG4Source::ensureMediaBufferAllocated(int32_t aSize) {
+ if (mBuffer->size() < aSize) {
+ ALOGE("Error insufficient memory, requested %u bytes (had:%u)",
+ aSize, mBuffer->size());
+ mBuffer->release();
+ mBuffer = NULL;
+ return false;
+ }
+ return true;
+}
+
status_t MPEG4Source::read(
MediaBuffer **out, const ReadOptions *options) {
Mutex::Autolock autoLock(mLock);
CHECK(mStarted);
if (mFirstMoofOffset > 0) {
return fragmentedRead(out, options);
@@ -3267,16 +3290,19 @@ status_t MPEG4Source::read(
return ERROR_MALFORMED;
}
mBuffer = new MediaBuffer(max_size);
assert(mBuffer);
}
if (!mIsAVC || mWantsNALFragments) {
if (newBuffer) {
+ if (!ensureMediaBufferAllocated(size)) {
+ return ERROR_MALFORMED;
+ }
ssize_t num_bytes_read =
mDataSource->readAt(offset, (uint8_t *)mBuffer->data(), size);
if (num_bytes_read < (ssize_t)size) {
mBuffer->release();
mBuffer = NULL;
return ERROR_IO;
@@ -3352,19 +3378,25 @@ status_t MPEG4Source::read(
return OK;
} else {
// Whole NAL units are returned but each fragment is prefixed by
// the NAL length, stored in four bytes.
ssize_t num_bytes_read = 0;
int32_t drm = 0;
bool usesDRM = (mFormat->findInt32(kKeyIsDRM, &drm) && drm != 0);
if (usesDRM) {
+ if (!ensureMediaBufferAllocated(size)) {
+ return ERROR_MALFORMED;
+ }
num_bytes_read =
mDataSource->readAt(offset, (uint8_t*)mBuffer->data(), size);
} else {
+ if (!ensureSrcBufferAllocated(size)) {
+ return ERROR_MALFORMED;
+ }
num_bytes_read = mDataSource->readAt(offset, mSrcBuffer, size);
}
if (num_bytes_read < (ssize_t)size) {
mBuffer->release();
mBuffer = NULL;
return ERROR_IO;
@@ -3394,17 +3426,19 @@ status_t MPEG4Source::read(
mBuffer = NULL;
return ERROR_MALFORMED;
}
if (nalLength == 0) {
continue;
}
- CHECK(dstOffset + 4 <= mBuffer->size());
+ if (!ensureMediaBufferAllocated(dstOffset + 4 + nalLength)) {
+ return ERROR_MALFORMED;
+ }
dstData[dstOffset++] = (uint8_t) (nalLength >> 24);
dstData[dstOffset++] = (uint8_t) (nalLength >> 16);
dstData[dstOffset++] = (uint8_t) (nalLength >> 8);
dstData[dstOffset++] = (uint8_t) nalLength;
memcpy(&dstData[dstOffset], &mSrcBuffer[srcOffset], nalLength);
srcOffset += nalLength;
dstOffset += nalLength;
}
@@ -3568,16 +3602,19 @@ status_t MPEG4Source::fragmentedRead(
bufmeta->setData(kKeyCryptoIV, 0, smpl->iv, 16); // use 16 or the actual size?
bufmeta->setInt32(kKeyCryptoDefaultIVSize, mDefaultIVSize);
bufmeta->setInt32(kKeyCryptoMode, mCryptoMode);
bufmeta->setData(kKeyCryptoKey, 0, mCryptoKey, 16);
}
if (!mIsAVC || mWantsNALFragments) {
if (newBuffer) {
+ if (!ensureMediaBufferAllocated(size)) {
+ return ERROR_MALFORMED;
+ }
ssize_t num_bytes_read =
mDataSource->readAt(offset, (uint8_t *)mBuffer->data(), size);
if (num_bytes_read < (ssize_t)size) {
mBuffer->release();
mBuffer = NULL;
ALOGV("i/o error");
@@ -3653,19 +3690,25 @@ status_t MPEG4Source::fragmentedRead(
} else {
ALOGV("whole NAL");
// Whole NAL units are returned but each fragment is prefixed by
// the NAL unit's length, stored in four bytes.
ssize_t num_bytes_read = 0;
int32_t drm = 0;
bool usesDRM = (mFormat->findInt32(kKeyIsDRM, &drm) && drm != 0);
if (usesDRM) {
+ if (!ensureMediaBufferAllocated(size)) {
+ return ERROR_MALFORMED;
+ }
num_bytes_read =
mDataSource->readAt(offset, (uint8_t*)mBuffer->data(), size);
} else {
+ if (!ensureSrcBufferAllocated(size)) {
+ return ERROR_MALFORMED;
+ }
num_bytes_read = mDataSource->readAt(offset, mSrcBuffer, size);
}
if (num_bytes_read < (ssize_t)size) {
mBuffer->release();
mBuffer = NULL;
ALOGV("i/o error");
@@ -3696,17 +3739,19 @@ status_t MPEG4Source::fragmentedRead(
mBuffer = NULL;
return ERROR_MALFORMED;
}
if (nalLength == 0) {
continue;
}
- CHECK(dstOffset + 4 <= mBuffer->size());
+ if (!ensureMediaBufferAllocated(dstOffset + 4 + nalLength)) {
+ return ERROR_MALFORMED;
+ }
dstData[dstOffset++] = (uint8_t) (nalLength >> 24);
dstData[dstOffset++] = (uint8_t) (nalLength >> 16);
dstData[dstOffset++] = (uint8_t) (nalLength >> 8);
dstData[dstOffset++] = (uint8_t) nalLength;
memcpy(&dstData[dstOffset], &mSrcBuffer[srcOffset], nalLength);
srcOffset += nalLength;
dstOffset += nalLength;
diff --git a/media/libstagefright/frameworks/av/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/frameworks/av/media/libstagefright/MPEG4Extractor.cpp
--- a/media/libstagefright/frameworks/av/media/libstagefright/MPEG4Extractor.cpp
+++ b/media/libstagefright/frameworks/av/media/libstagefright/MPEG4Extractor.cpp
@@ -33,16 +33,17 @@
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/AMessage.h>
#include <media/stagefright/MediaBuffer.h>
#include <media/stagefright/MediaBufferGroup.h>
#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/MediaSource.h>
#include <media/stagefright/MetaData.h>
#include <utils/String8.h>
+#include "nsTArray.h"
namespace stagefright {
class MPEG4Source : public MediaSource {
public:
// Caller retains ownership of both "dataSource" and "sampleTable".
MPEG4Source(const sp<MetaData> &format,
const sp<DataSource> &dataSource,
@@ -99,16 +100,17 @@ private:
MediaBufferGroup *mGroup;
MediaBuffer *mBuffer;
bool mWantsNALFragments;
uint8_t *mSrcBuffer;
+ FallibleTArray<uint8_t> mSrcBackend;
size_t parseNALSize(const uint8_t *data) const;
status_t parseChunk(off64_t *offset);
status_t parseTrackFragmentHeader(off64_t offset, off64_t size);
status_t parseTrackFragmentRun(off64_t offset, off64_t size);
status_t parseSampleAuxiliaryInformationSizes(off64_t offset, off64_t size);
status_t parseSampleAuxiliaryInformationOffsets(off64_t offset, off64_t size);
@@ -141,16 +143,21 @@ private:
uint8_t iv[16];
Vector<size_t> clearsizes;
Vector<size_t> encryptedsizes;
};
Vector<Sample> mCurrentSamples;
MPEG4Source(const MPEG4Source &);
MPEG4Source &operator=(const MPEG4Source &);
+
+ bool ensureSrcBufferAllocated(int32_t size);
+ // Ensure that we have enough data in mMediaBuffer to copy our data.
+ // Returns false if not and clear mMediaBuffer.
+ bool ensureMediaBufferAllocated(int32_t size);
};
// This custom data source wraps an existing one and satisfies requests
// falling entirely within a cached range from the cache while forwarding
// all remaining requests to the wrapped datasource.
// This is used to cache the full sampletable metadata for a single track,
// possibly wrapping multiple times to cover all tracks, i.e.
// Each MPEG4DataSource caches the sampletable metadata for a single track.
@@ -2449,35 +2456,34 @@ status_t MPEG4Source::start(MetaData *pa
mGroup = new MediaBufferGroup;
int32_t max_size;
CHECK(mFormat->findInt32(kKeyMaxInputSize, &max_size));
mGroup->add_buffer(new MediaBuffer(max_size));
- mSrcBuffer = new uint8_t[max_size];
+ mSrcBuffer = mSrcBackend.Elements();
mStarted = true;
return OK;
}
status_t MPEG4Source::stop() {
Mutex::Autolock autoLock(mLock);
CHECK(mStarted);
if (mBuffer != NULL) {
mBuffer->release();
mBuffer = NULL;
}
- delete[] mSrcBuffer;
- mSrcBuffer = NULL;
+ mSrcBackend.Clear();
delete mGroup;
mGroup = NULL;
mStarted = false;
mCurrentSampleIndex = 0;
return OK;
@@ -3026,16 +3032,40 @@ size_t MPEG4Source::parseNALSize(const u
// This cannot happen, mNALLengthSize springs to life by adding 1 to
// a 2-bit integer.
CHECK(!"Should not be here.");
return 0;
}
+bool MPEG4Source::ensureSrcBufferAllocated(int32_t aSize) {
+ if (mSrcBackend.Length() >= aSize) {
+ return true;
+ }
+ if (!mSrcBackend.SetLength(aSize)) {
+ ALOGE("Error insufficient memory, requested %u bytes (had:%u)",
+ aSize, mSrcBackend.Length());
+ return false;
+ }
+ mSrcBuffer = mSrcBackend.Elements();
+ return true;
+}
+
+bool MPEG4Source::ensureMediaBufferAllocated(int32_t aSize) {
+ if (mBuffer->size() < aSize) {
+ ALOGE("Error insufficient memory, requested %u bytes (had:%u)",
+ aSize, mBuffer->size());
+ mBuffer->release();
+ mBuffer = NULL;
+ return false;
+ }
+ return true;
+}
+
status_t MPEG4Source::read(
MediaBuffer **out, const ReadOptions *options) {
Mutex::Autolock autoLock(mLock);
CHECK(mStarted);
if (mFirstMoofOffset > 0) {
return fragmentedRead(out, options);
@@ -3148,16 +3178,19 @@ status_t MPEG4Source::read(
if (err != OK) {
CHECK(mBuffer == NULL);
return err;
}
}
if (!mIsAVC || mWantsNALFragments) {
if (newBuffer) {
+ if (!ensureMediaBufferAllocated(size)) {
+ return ERROR_MALFORMED;
+ }
ssize_t num_bytes_read =
mDataSource->readAt(offset, (uint8_t *)mBuffer->data(), size);
if (num_bytes_read < (ssize_t)size) {
mBuffer->release();
mBuffer = NULL;
return ERROR_IO;
@@ -3226,19 +3259,25 @@ status_t MPEG4Source::read(
return OK;
} else {
// Whole NAL units are returned but each fragment is prefixed by
// the start code (0x00 00 00 01).
ssize_t num_bytes_read = 0;
int32_t drm = 0;
bool usesDRM = (mFormat->findInt32(kKeyIsDRM, &drm) && drm != 0);
if (usesDRM) {
+ if (!ensureMediaBufferAllocated(size)) {
+ return ERROR_MALFORMED;
+ }
num_bytes_read =
mDataSource->readAt(offset, (uint8_t*)mBuffer->data(), size);
} else {
+ if (!ensureSrcBufferAllocated(size)) {
+ return ERROR_MALFORMED;
+ }
num_bytes_read = mDataSource->readAt(offset, mSrcBuffer, size);
}
if (num_bytes_read < (ssize_t)size) {
mBuffer->release();
mBuffer = NULL;
return ERROR_IO;
@@ -3268,17 +3307,19 @@ status_t MPEG4Source::read(
mBuffer = NULL;
return ERROR_MALFORMED;
}
if (nalLength == 0) {
continue;
}
- CHECK(dstOffset + 4 <= mBuffer->size());
+ if (!ensureMediaBufferAllocated(dstOffset + 4 + nalLength)) {
+ return ERROR_MALFORMED;
+ }
dstData[dstOffset++] = 0;
dstData[dstOffset++] = 0;
dstData[dstOffset++] = 0;
dstData[dstOffset++] = 1;
memcpy(&dstData[dstOffset], &mSrcBuffer[srcOffset], nalLength);
srcOffset += nalLength;
dstOffset += nalLength;
@@ -3423,16 +3464,19 @@ status_t MPEG4Source::fragmentedRead(
bufmeta->setData(kKeyCryptoIV, 0, smpl->iv, 16); // use 16 or the actual size?
bufmeta->setInt32(kKeyCryptoDefaultIVSize, mDefaultIVSize);
bufmeta->setInt32(kKeyCryptoMode, mCryptoMode);
bufmeta->setData(kKeyCryptoKey, 0, mCryptoKey, 16);
}
if (!mIsAVC || mWantsNALFragments) {
if (newBuffer) {
+ if (!ensureMediaBufferAllocated(size)) {
+ return ERROR_MALFORMED;
+ }
ssize_t num_bytes_read =
mDataSource->readAt(offset, (uint8_t *)mBuffer->data(), size);
if (num_bytes_read < (ssize_t)size) {
mBuffer->release();
mBuffer = NULL;
ALOGV("i/o error");
@@ -3501,19 +3545,25 @@ status_t MPEG4Source::fragmentedRead(
} else {
ALOGV("whole NAL");
// Whole NAL units are returned but each fragment is prefixed by
// the start code (0x00 00 00 01).
ssize_t num_bytes_read = 0;
int32_t drm = 0;
bool usesDRM = (mFormat->findInt32(kKeyIsDRM, &drm) && drm != 0);
if (usesDRM) {
+ if (!ensureMediaBufferAllocated(size)) {
+ return ERROR_MALFORMED;
+ }
num_bytes_read =
mDataSource->readAt(offset, (uint8_t*)mBuffer->data(), size);
} else {
+ if (!ensureSrcBufferAllocated(size)) {
+ return ERROR_MALFORMED;
+ }
num_bytes_read = mDataSource->readAt(offset, mSrcBuffer, size);
}
if (num_bytes_read < (ssize_t)size) {
mBuffer->release();
mBuffer = NULL;
ALOGV("i/o error");
@@ -3544,17 +3594,19 @@ status_t MPEG4Source::fragmentedRead(
mBuffer = NULL;
return ERROR_MALFORMED;
}
if (nalLength == 0) {
continue;
}
- CHECK(dstOffset + 4 <= mBuffer->size());
+ if (!ensureMediaBufferAllocated(dstOffset + 4 + nalLength)) {
+ return ERROR_MALFORMED;
+ }
dstData[dstOffset++] = 0;
dstData[dstOffset++] = 0;
dstData[dstOffset++] = 0;
dstData[dstOffset++] = 1;
memcpy(&dstData[dstOffset], &mSrcBuffer[srcOffset], nalLength);
srcOffset += nalLength;
dstOffset += nalLength;
}