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
diff --git a/native/graphics/jni/Android.bp b/native/graphics/jni/Android.bp
index 15b473c..aaaaa01 100644
--- a/native/graphics/jni/Android.bp
+++ b/native/graphics/jni/Android.bp
@@ -58,3 +58,18 @@
     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"],
+    },
+}
diff --git a/native/graphics/jni/fuzz_imagedecoder.cpp b/native/graphics/jni/fuzz_imagedecoder.cpp
new file mode 100644
index 0000000..f2cd1a8
--- /dev/null
+++ b/native/graphics/jni/fuzz_imagedecoder.cpp
@@ -0,0 +1,78 @@
+/*
+ * 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;
+}