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

Commit 140f5086 authored by Leon Scroggins III's avatar Leon Scroggins III
Browse files

AImageDecoder: Add a fuzz target

Bug: 142252770
Bug: 169137236
Bug: 169139756
Test: This (fuzzer)

Use the bytes to create an image file and decode it.

To run:

$ SANITIZE_TARGET=hwaddress make imagedecoder_fuzzer
$ adb root
$ adb sync data
$ adb shell /data/fuzz/arm64/imagedecoder_fuzzer/imagedecoder_fuzzer

Call startThreadPool() to support HEIF. Otherwise HEIF decodes may time
out on binder IPC calls. This is similar to a fix for skia_dm in
https://skia-review.googlesource.com/c/skia/+/108141.

Change-Id: I434730a74c5ff97437526065c41af3f54fae3335
parent c2ebc2bc
Loading
Loading
Loading
Loading
+15 −0
Original line number Diff line number Diff line
@@ -58,3 +58,18 @@ ndk_library {
    first_version: "9",
    unversioned_until: "current",
}

cc_fuzz {
    name: "imagedecoder_fuzzer",
    srcs: ["fuzz_imagedecoder.cpp"],
    header_libs: ["jni_headers"],
    shared_libs: [
        "libbinder",
        "libjnigraphics",
        "libutils",
    ],
    static_libs: ["libarect"],
    fuzz_config: {
        cc: ["scroggo@google.com"],
    },
}
+78 −0
Original line number Diff line number Diff line
/*
 * 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.
 */

#include <android/imagedecoder.h>

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

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);
}

struct PixelFreer {
    void operator()(void* pixels) const { std::free(pixels); }
};

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);
    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;
    }

    size_t stride = AImageDecoder_getMinimumStride(decoder.get());
    size_t pixelSize = height * stride;
    auto pixels = PixelPointer(std::malloc(pixelSize));
    if (!pixels.get()) {
        return 0;
    }

    AImageDecoder_decodeImage(decoder.get(), pixels.get(), stride, pixelSize);
    return 0;
}