blob: 97d11600d8bd5fb654c96b8cbe0b73a4f217eeb8 [file] [log] [blame]
/*
* Copyright 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// Authors: corbin.souffrant@leviathansecurity.com
// dylan.katz@leviathansecurity.com
#include <android-base/file.h>
#include <android/content/AttributionSourceState.h>
#include <ctype.h>
#include <media/mediarecorder.h>
#include <media/stagefright/MPEG4Writer.h>
#include <media/stagefright/MediaDefs.h>
#include <stdlib.h>
#include <utils/StrongPointer.h>
#include <utils/Vector.h>
#include <functional>
#include <string>
#include "FuzzerMediaUtility.h"
#include "fuzzer/FuzzedDataProvider.h"
static constexpr uint16_t kMaxOperations = 5000;
static constexpr uint8_t kMaxPackageNameLen = 50;
// For other strings in mpeg we want a higher limit.
static constexpr uint16_t kMaxMPEGStrLen = 1000;
static constexpr uint16_t kMaxMediaBlobSize = 1000;
namespace android {
using android::content::AttributionSourceState;
std::string getFourCC(FuzzedDataProvider *fdp) {
std::string fourCC = fdp->ConsumeRandomLengthString(4);
// Replace any existing nulls
for (size_t pos = 0; pos < fourCC.length(); pos++) {
if (fourCC.at(pos) == '\0') {
fourCC.replace(pos, 1, "a");
}
}
// If our string is too short, fill the remainder with "a"s.
while (fourCC.length() < 4) {
fourCC += 'a';
}
return fourCC;
}
typedef std::vector<std::function<void(FuzzedDataProvider*,
sp<MediaWriter>, sp<MetaData>, int tmpFileFd)>> OperationVec;
typedef std::vector<std::function<void(FuzzedDataProvider*, MPEG4Writer*)>> MPEG4OperationVec;
static const OperationVec operations = {
[](FuzzedDataProvider*, sp<MediaWriter> mediaWriter, sp<MetaData>, int) {
mediaWriter->pause();
},
[](FuzzedDataProvider *dataProvider, sp<MediaWriter> mediaWriter, sp<MetaData>, int tmpFd) {
bool valid_fd = dataProvider->ConsumeBool();
int fd = -1;
if (valid_fd) {
fd = tmpFd;
}
// Args don't seem to be used
Vector<String16> args;
mediaWriter->dump(fd, args);
},
[](FuzzedDataProvider *dataProvider, sp<MediaWriter> mediaWriter, sp<MetaData>, int tmpFd) {
bool valid_fd = dataProvider->ConsumeBool();
int fd = -1;
if (valid_fd) {
fd = tmpFd;
}
mediaWriter->setNextFd(fd);
},
[](FuzzedDataProvider *dataProvider, sp<MediaWriter> mediaWriter, sp<MetaData>, int) {
mediaWriter->setCaptureRate(dataProvider->ConsumeFloatingPoint<float>());
},
[](FuzzedDataProvider *dataProvider, sp<MediaWriter> mediaWriter, sp<MetaData>, int) {
mediaWriter->setMaxFileDuration(dataProvider->ConsumeIntegral<int64_t>());
},
[](FuzzedDataProvider *dataProvider, sp<MediaWriter> mediaWriter, sp<MetaData>, int) {
mediaWriter->setStartTimeOffsetMs(dataProvider->ConsumeIntegral<int>());
// Likely won't do much, but might as well as do a quick check
// while we're here.
mediaWriter->getStartTimeOffsetMs();
},
[](FuzzedDataProvider *dataProvider, sp<MediaWriter> mediaWriter, sp<MetaData>, int) {
mediaWriter->setMaxFileDuration(dataProvider->ConsumeIntegral<int64_t>());
},
[](FuzzedDataProvider *dataProvider, sp<MediaWriter> mediaWriter, sp<MetaData>, int) {
mediaWriter->setMaxFileDuration(dataProvider->ConsumeIntegral<int64_t>());
},
};
static const MPEG4OperationVec mpeg4Operations = {
[](FuzzedDataProvider*, MPEG4Writer *mediaWriter) { mediaWriter->notifyApproachingLimit(); },
// Lower level write methods.
// High-level startBox/endBox/etc are all called elsewhere,
[](FuzzedDataProvider *dataProvider, MPEG4Writer *mediaWriter) {
uint8_t val = dataProvider->ConsumeIntegral<uint8_t>();
mediaWriter->writeInt8(val);
},
[](FuzzedDataProvider *dataProvider, MPEG4Writer *mediaWriter) {
uint16_t val = dataProvider->ConsumeIntegral<uint16_t>();
mediaWriter->writeInt16(val);
},
[](FuzzedDataProvider *dataProvider, MPEG4Writer *mediaWriter) {
uint32_t val = dataProvider->ConsumeIntegral<uint32_t>();
mediaWriter->writeInt32(val);
},
[](FuzzedDataProvider *dataProvider, MPEG4Writer *mediaWriter) {
uint64_t val = dataProvider->ConsumeIntegral<uint64_t>();
mediaWriter->writeInt64(val);
},
[](FuzzedDataProvider *dataProvider, MPEG4Writer *mediaWriter) {
std::string strVal = dataProvider->ConsumeRandomLengthString(kMaxMPEGStrLen);
mediaWriter->writeCString(strVal.c_str());
},
[](FuzzedDataProvider *dataProvider, MPEG4Writer *mediaWriter) {
std::string fourCC = getFourCC(dataProvider);
mediaWriter->writeFourcc(fourCC.c_str());
},
// Misc setters
[](FuzzedDataProvider *dataProvider, MPEG4Writer *mediaWriter) {
uint32_t layers = dataProvider->ConsumeIntegral<uint32_t>();
mediaWriter->setTemporalLayerCount(layers);
},
[](FuzzedDataProvider *dataProvider, MPEG4Writer *mediaWriter) {
uint32_t duration = dataProvider->ConsumeIntegral<uint32_t>();
mediaWriter->setInterleaveDuration(duration);
},
[](FuzzedDataProvider *dataProvider, MPEG4Writer *mediaWriter) {
int lat = dataProvider->ConsumeIntegral<int>();
int lon = dataProvider->ConsumeIntegral<int>();
mediaWriter->setGeoData(lat, lon);
},
};
// Not all writers can always add new sources, so we'll need additional checks.
void addSource(FuzzedDataProvider *dataProvider, sp<MediaWriter> mediaWriter) {
sp<MediaSource> mediaSource = genMediaSource(dataProvider, kMaxMediaBlobSize);
if (mediaSource == NULL) {
// There's a static check preventing NULLs in addSource.
return;
}
mediaWriter->addSource(mediaSource);
}
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
FuzzedDataProvider dataProvider(data, size);
TemporaryFile tf;
sp<MetaData> fileMeta = new MetaData;
StandardWriters writerType = dataProvider.ConsumeEnum<StandardWriters>();
sp<MediaWriter> writer = createWriter(tf.fd, writerType, fileMeta);
AttributionSourceState attributionSource;
attributionSource.packageName = dataProvider.ConsumeRandomLengthString(kMaxPackageNameLen);
attributionSource.uid = dataProvider.ConsumeIntegral<int32_t>();
attributionSource.pid = dataProvider.ConsumeIntegral<int32_t>();
attributionSource.token = sp<BBinder>::make();
sp<MediaRecorder> mr = new MediaRecorder(attributionSource);
writer->setListener(mr);
uint8_t baseOpLen = operations.size();
uint8_t totalLen = baseOpLen;
uint8_t maxSources;
// Different writers support different amounts of sources.
switch (writerType) {
case StandardWriters::AAC:
case StandardWriters::AAC_ADTS:
case StandardWriters::AMR_NB:
case StandardWriters::AMR_WB:
case StandardWriters::OGG:
maxSources = 1;
break;
case StandardWriters::WEBM:
maxSources = 2;
break;
default:
maxSources = UINT8_MAX;
break;
}
// Initialize some number of sources and add them to our writer.
uint8_t sourceCount = dataProvider.ConsumeIntegralInRange<uint8_t>(0, maxSources);
for (uint8_t i = 0; i < sourceCount; i++) {
addSource(&dataProvider, writer);
}
// Increase our range if additional operations are implemented.
// Currently only MPEG4 has additiona public operations on their writer.
if (writerType == StandardWriters::MPEG4) {
totalLen += mpeg4Operations.size();
}
// Many operations require the writer to be started.
writer->start(fileMeta.get());
for (size_t ops_run = 0; dataProvider.remaining_bytes() > 0 && ops_run < kMaxOperations - 1;
ops_run++) {
uint8_t op = dataProvider.ConsumeIntegralInRange<uint8_t>(0, totalLen - 1);
if (op < baseOpLen) {
operations[op](&dataProvider, writer, fileMeta, tf.fd);
} else if (writerType == StandardWriters::MPEG4) {
mpeg4Operations[op - baseOpLen](&dataProvider, (MPEG4Writer*)writer.get());
} else {
// Here just in case, will error out.
operations[op](&dataProvider, writer, fileMeta, tf.fd);
}
}
writer->stop();
writer.clear();
writer = nullptr;
return 0;
}
} // namespace android