Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 2db2c27d authored by Treehugger Robot's avatar Treehugger Robot Committed by Automerger Merge Worker
Browse files

Merge "Refactor imagedecoder_fuzzer" into main am: 3ce4fb8f am: 190937a3

parents 1dba6d8e 190937a3
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -111,6 +111,7 @@ cc_defaults {
            "allocator_may_return_null = 1",
        ],
    },
    dictionary: "fuzz/imagedecoder_fuzzer.dict",
    host_supported: true,
}

+91 −50
Original line number Diff line number Diff line
@@ -15,32 +15,15 @@
 */

#include <android/imagedecoder.h>

#include <binder/IPCThreadState.h>
#include <stddef.h>
#include <stdint.h>
#include <cstdlib>
#include <memory>
#include <fuzzer/FuzzedDataProvider.h>

#ifdef PNG_MUTATOR_DEFINE_LIBFUZZER_CUSTOM_MUTATOR
#include <fuzz/png_mutator.h>
#endif

struct DecoderDeleter {
    void operator()(AImageDecoder* decoder) const { AImageDecoder_delete(decoder); }
};

using DecoderPointer = std::unique_ptr<AImageDecoder, DecoderDeleter>;

static DecoderPointer makeDecoder(const uint8_t* data, size_t size) {
    AImageDecoder* decoder = nullptr;
    int result = AImageDecoder_createFromBuffer(data, size, &decoder);
    if (result != ANDROID_IMAGE_DECODER_SUCCESS) {
        // This was not a valid image.
        return nullptr;
    }
    return DecoderPointer(decoder);
}
constexpr int32_t kMaxDimension = 5000;
constexpr int32_t kMinDimension = 0;

struct PixelFreer {
    void operator()(void* pixels) const { std::free(pixels); }
@@ -49,40 +32,98 @@ struct PixelFreer {
using PixelPointer = std::unique_ptr<void, PixelFreer>;

extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
    // Without this call, decoding HEIF may time out on binder IPC calls.
    android::ProcessState::self()->startThreadPool();

    DecoderPointer decoder = makeDecoder(data, size);
    FuzzedDataProvider dataProvider = FuzzedDataProvider(data, size);
    /**
     * Use maximum of 80% of buffer for creating decoder and save at least
     * 20% buffer for fuzzing other APIs
     */
    const int32_t dataSize = dataProvider.ConsumeIntegralInRange<int32_t>(0, (size * 80) / 100);
    std::vector<uint8_t> inputBuffer = dataProvider.ConsumeBytes<uint8_t>(dataSize);
    AImageDecoder* decoder = nullptr;
    AImageDecoder_createFromBuffer(inputBuffer.data(), inputBuffer.size(), &decoder);
    if (!decoder) {
        return 0;
    }

    const AImageDecoderHeaderInfo* info = AImageDecoder_getHeaderInfo(decoder.get());
    int32_t width = AImageDecoderHeaderInfo_getWidth(info);
    int32_t height = AImageDecoderHeaderInfo_getHeight(info);

    // Set an arbitrary limit on the size of an image. The fuzzer runs with a
    // limited amount of memory, and keeping this allocation small allows the
    // fuzzer to continue running to try to find more serious problems. This
    // size is large enough to hold a photo taken by a current gen phone.
    constexpr int32_t kMaxDimension = 5000;
    if (width > kMaxDimension || height > kMaxDimension) {
        return 0;
    const AImageDecoderHeaderInfo* headerInfo = AImageDecoder_getHeaderInfo(decoder);
    AImageDecoderFrameInfo* frameInfo = AImageDecoderFrameInfo_create();
    int32_t height = AImageDecoderHeaderInfo_getHeight(headerInfo);
    int32_t width = AImageDecoderHeaderInfo_getWidth(headerInfo);
    while (dataProvider.remaining_bytes()) {
        auto invokeImageApi = dataProvider.PickValueInArray<const std::function<void()>>({
                [&]() {
                    int32_t testHeight =
                            dataProvider.ConsumeIntegralInRange<int32_t>(kMinDimension,
                                                                         kMaxDimension);
                    int32_t testWidth = dataProvider.ConsumeIntegralInRange<int32_t>(kMinDimension,
                                                                                     kMaxDimension);
                    int32_t result = AImageDecoder_setTargetSize(decoder, testHeight, testWidth);
                    if (result == ANDROID_IMAGE_DECODER_SUCCESS) {
                        height = testHeight;
                        width = testWidth;
                    }

    size_t stride = AImageDecoder_getMinimumStride(decoder.get());
                },
                [&]() {
                    const bool required = dataProvider.ConsumeBool();
                    AImageDecoder_setUnpremultipliedRequired(decoder, required);
                },
                [&]() {
                    AImageDecoder_setAndroidBitmapFormat(
                            decoder,
                            dataProvider.ConsumeIntegralInRange<
                                    int32_t>(ANDROID_BITMAP_FORMAT_NONE,
                                             ANDROID_BITMAP_FORMAT_RGBA_1010102) /* format */);
                },
                [&]() {
                    AImageDecoder_setDataSpace(decoder,
                                               dataProvider
                                                       .ConsumeIntegral<int32_t>() /* dataspace */);
                },
                [&]() {
                    ARect rect{dataProvider.ConsumeIntegral<int32_t>() /* left */,
                               dataProvider.ConsumeIntegral<int32_t>() /* top */,
                               dataProvider.ConsumeIntegral<int32_t>() /* right */,
                               dataProvider.ConsumeIntegral<int32_t>() /* bottom */};
                    AImageDecoder_setCrop(decoder, rect);
                },
                [&]() { AImageDecoderHeaderInfo_getWidth(headerInfo); },
                [&]() { AImageDecoderHeaderInfo_getMimeType(headerInfo); },
                [&]() { AImageDecoderHeaderInfo_getAlphaFlags(headerInfo); },
                [&]() { AImageDecoderHeaderInfo_getAndroidBitmapFormat(headerInfo); },
                [&]() {
                    int32_t tempHeight;
                    int32_t tempWidth;
                    AImageDecoder_computeSampledSize(decoder,
                                                     dataProvider.ConsumeIntegral<
                                                             int>() /* sampleSize */,
                                                     &tempWidth, &tempHeight);
                },
                [&]() { AImageDecoderHeaderInfo_getDataSpace(headerInfo); },
                [&]() { AImageDecoder_getRepeatCount(decoder); },
                [&]() { AImageDecoder_getFrameInfo(decoder, frameInfo); },
                [&]() { AImageDecoderFrameInfo_getDuration(frameInfo); },
                [&]() { AImageDecoderFrameInfo_hasAlphaWithinBounds(frameInfo); },
                [&]() { AImageDecoderFrameInfo_getDisposeOp(frameInfo); },
                [&]() { AImageDecoderFrameInfo_getBlendOp(frameInfo); },
                [&]() {
                    AImageDecoder_setInternallyHandleDisposePrevious(
                            decoder, dataProvider.ConsumeBool() /* handle */);
                },
                [&]() { AImageDecoder_rewind(decoder); },
                [&]() { AImageDecoder_advanceFrame(decoder); },
                [&]() {
                    size_t stride = AImageDecoder_getMinimumStride(decoder);
                    size_t pixelSize = height * stride;
                    auto pixels = PixelPointer(std::malloc(pixelSize));
                    if (!pixels.get()) {
        return 0;
                        return;
                    }

    while (true) {
        int result = AImageDecoder_decodeImage(decoder.get(), pixels.get(), stride, pixelSize);
        if (result != ANDROID_IMAGE_DECODER_SUCCESS) break;

        result = AImageDecoder_advanceFrame(decoder.get());
        if (result != ANDROID_IMAGE_DECODER_SUCCESS) break;
                    AImageDecoder_decodeImage(decoder, pixels.get(), stride, pixelSize);
                },
        });
        invokeImageApi();
    }

    AImageDecoderFrameInfo_delete(frameInfo);
    AImageDecoder_delete(decoder);
    return 0;
}
+7 −0
Original line number Diff line number Diff line
kw1="\x89\x50\x4E\x47"
kw2="\xff\xD8\xFF"
kw4="\x52\x49\x46\x46"
kw5="\x00\x00\x01\x00"
kw6="\x47\x49\x46\x08"
kw7="ftyp"
kw8="\x04\x00\x00\x00"