summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJeff Sharkey <jsharkey@android.com>2020-10-13 09:40:52 -0600
committerJeff Sharkey <jsharkey@android.com>2020-10-19 16:07:16 -0600
commit256da5a361104276b01ad2834f9929bc8ed457f7 (patch)
tree46f2d9b200fe5447417209539db5843075c0a5e8
parentae2d88a65c69c27ea01478d5761fe385ac32a7f9 (diff)
Add fuzzer for rewritten CursorWindow.
We recently rewrote CursorWindow, so let's get a fuzzer wired up to see if it has any bugs. This change creates a separate "libandroidfw_fuzz" library, since we can't link to libbinder when building Windows host-side binaries; the fuzzer doesn't need Window support. And fix our first vulnerability where getFieldSlot() could be tricked into reading out of bounds data. The included corpus seed was generated using this example code: CursorWindow* w = nullptr; CursorWindow::create(android::String8("test"), 1 << 21, &w); w->setNumColumns(3); w->allocRow(); w->putLong(0,0,0xcafe); w->putLong(0,1,0xcafe); w->putLong(0,2,0xcafe); // Row purposefully left empty w->allocRow(); w->allocRow(); w->putNull(2,0); w->putNull(2,1); w->putNull(2,2); w->allocRow(); w->putString(3,0,"cafe",5); w->putString(3,1,"cafe",5); w->putString(3,2,"cafe",5); w->allocRow(); w->putDouble(4,0,3.14159f); w->putDouble(4,1,3.14159f); w->putDouble(4,2,3.14159f); Parcel p; w->writeToParcel(&p); Bug: 169251528 Test: atest libandroidfw_tests:CursorWindowTest Test: SANITIZE_HOST=address make ${FUZZER_NAME} && ${ANDROID_HOST_OUT}/fuzz/$(get_build_var HOST_ARCH)/${FUZZER_NAME}/${FUZZER_NAME} Change-Id: I405d377900943de0ad732d3f1a1a0970e17d5140
-rw-r--r--libs/androidfw/Android.bp18
-rw-r--r--libs/androidfw/CursorWindow.cpp21
-rw-r--r--libs/androidfw/fuzz/cursorwindow_fuzzer/Android.bp31
-rw-r--r--libs/androidfw/fuzz/cursorwindow_fuzzer/corpus/typical.binbin0 -> 292 bytes
-rw-r--r--libs/androidfw/fuzz/cursorwindow_fuzzer/cursorwindow_fuzzer.cpp78
-rw-r--r--libs/androidfw/include/androidfw/CursorWindow.h3
6 files changed, 141 insertions, 10 deletions
diff --git a/libs/androidfw/Android.bp b/libs/androidfw/Android.bp
index 45ad94fdb75a..903ca2aa0783 100644
--- a/libs/androidfw/Android.bp
+++ b/libs/androidfw/Android.bp
@@ -193,3 +193,21 @@ cc_benchmark {
shared_libs: common_test_libs,
data: ["tests/data/**/*.apk"],
}
+
+cc_library {
+ name: "libandroidfw_fuzzer_lib",
+ defaults: ["libandroidfw_defaults"],
+ host_supported: true,
+ srcs: [
+ "CursorWindow.cpp",
+ ],
+ export_include_dirs: ["include"],
+ target: {
+ android: {
+ shared_libs: common_test_libs + ["libbinder", "liblog"],
+ },
+ host: {
+ static_libs: common_test_libs + ["libbinder", "liblog"],
+ },
+ },
+}
diff --git a/libs/androidfw/CursorWindow.cpp b/libs/androidfw/CursorWindow.cpp
index 1e6de67a40ba..915c0d75a280 100644
--- a/libs/androidfw/CursorWindow.cpp
+++ b/libs/androidfw/CursorWindow.cpp
@@ -251,7 +251,8 @@ status_t CursorWindow::clear() {
}
void CursorWindow::updateSlotsData() {
- mSlotsData = static_cast<uint8_t*>(mData) + mSize - kSlotSizeBytes;
+ mSlotsStart = static_cast<uint8_t*>(mData) + mSize - kSlotSizeBytes;
+ mSlotsEnd = static_cast<uint8_t*>(mData) + mSlotsOffset;
}
void* CursorWindow::offsetToPtr(uint32_t offset, uint32_t bufferSize = 0) {
@@ -300,6 +301,7 @@ status_t CursorWindow::allocRow() {
}
memset(offsetToPtr(newOffset), 0, size);
mSlotsOffset = newOffset;
+ updateSlotsData();
mNumRows++;
return OK;
}
@@ -314,6 +316,7 @@ status_t CursorWindow::freeLastRow() {
return NO_MEMORY;
}
mSlotsOffset = newOffset;
+ updateSlotsData();
mNumRows--;
return OK;
}
@@ -337,18 +340,18 @@ status_t CursorWindow::alloc(size_t size, uint32_t* outOffset) {
}
CursorWindow::FieldSlot* CursorWindow::getFieldSlot(uint32_t row, uint32_t column) {
- if (row >= mNumRows || column >= mNumColumns) {
- LOG(ERROR) << "Failed to read row " << row << ", column " << column
- << " from a window with " << mNumRows << " rows, " << mNumColumns << " columns";
- return nullptr;
- }
-
// This is carefully tuned to use as few cycles as
// possible, since this is an extremely hot code path;
// see CursorWindow_bench.cpp for more details
- void *result = static_cast<uint8_t*>(mSlotsData)
+ void *result = static_cast<uint8_t*>(mSlotsStart)
- (((row * mNumColumns) + column) << kSlotShift);
- return static_cast<FieldSlot*>(result);
+ if (result < mSlotsEnd || column >= mNumColumns) {
+ LOG(ERROR) << "Failed to read row " << row << ", column " << column
+ << " from a window with " << mNumRows << " rows, " << mNumColumns << " columns";
+ return nullptr;
+ } else {
+ return static_cast<FieldSlot*>(result);
+ }
}
status_t CursorWindow::putBlob(uint32_t row, uint32_t column, const void* value, size_t size) {
diff --git a/libs/androidfw/fuzz/cursorwindow_fuzzer/Android.bp b/libs/androidfw/fuzz/cursorwindow_fuzzer/Android.bp
new file mode 100644
index 000000000000..2dac47b0dac6
--- /dev/null
+++ b/libs/androidfw/fuzz/cursorwindow_fuzzer/Android.bp
@@ -0,0 +1,31 @@
+cc_fuzz {
+ name: "cursorwindow_fuzzer",
+ srcs: [
+ "cursorwindow_fuzzer.cpp",
+ ],
+ host_supported: true,
+ corpus: ["corpus/*"],
+ static_libs: ["libgmock"],
+ target: {
+ android: {
+ shared_libs: [
+ "libandroidfw_fuzzer_lib",
+ "libbase",
+ "libbinder",
+ "libcutils",
+ "liblog",
+ "libutils",
+ ],
+ },
+ host: {
+ static_libs: [
+ "libandroidfw_fuzzer_lib",
+ "libbase",
+ "libbinder",
+ "libcutils",
+ "liblog",
+ "libutils",
+ ],
+ },
+ },
+}
diff --git a/libs/androidfw/fuzz/cursorwindow_fuzzer/corpus/typical.bin b/libs/androidfw/fuzz/cursorwindow_fuzzer/corpus/typical.bin
new file mode 100644
index 000000000000..c7e22dd26ea7
--- /dev/null
+++ b/libs/androidfw/fuzz/cursorwindow_fuzzer/corpus/typical.bin
Binary files differ
diff --git a/libs/androidfw/fuzz/cursorwindow_fuzzer/cursorwindow_fuzzer.cpp b/libs/androidfw/fuzz/cursorwindow_fuzzer/cursorwindow_fuzzer.cpp
new file mode 100644
index 000000000000..8dce21220199
--- /dev/null
+++ b/libs/androidfw/fuzz/cursorwindow_fuzzer/cursorwindow_fuzzer.cpp
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 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 <stddef.h>
+#include <stdint.h>
+#include <string.h>
+#include <string>
+#include <memory>
+
+#include "android-base/logging.h"
+#include "androidfw/CursorWindow.h"
+#include "binder/Parcel.h"
+
+#include <fuzzer/FuzzedDataProvider.h>
+
+using android::CursorWindow;
+using android::Parcel;
+
+extern "C" int LLVMFuzzerInitialize(int *, char ***) {
+ setenv("ANDROID_LOG_TAGS", "*:s", 1);
+ android::base::InitLogging(nullptr, &android::base::StderrLogger);
+ return 0;
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ Parcel p;
+ p.setData(data, size);
+
+ CursorWindow* w = nullptr;
+ if (!CursorWindow::createFromParcel(&p, &w)) {
+ LOG(WARNING) << "Valid cursor with " << w->getNumRows() << " rows, "
+ << w->getNumColumns() << " cols";
+
+ // Try obtaining heap allocations for most items; we trim the
+ // search space to speed things up
+ auto rows = std::min(w->getNumRows(), static_cast<uint32_t>(128));
+ auto cols = std::min(w->getNumColumns(), static_cast<uint32_t>(128));
+ for (auto row = 0; row < rows; row++) {
+ for (auto col = 0; col < cols; col++) {
+ auto field = w->getFieldSlot(row, col);
+ if (!field) continue;
+ switch (w->getFieldSlotType(field)) {
+ case CursorWindow::FIELD_TYPE_STRING: {
+ size_t size;
+ w->getFieldSlotValueString(field, &size);
+ break;
+ }
+ case CursorWindow::FIELD_TYPE_BLOB: {
+ size_t size;
+ w->getFieldSlotValueBlob(field, &size);
+ break;
+ }
+ }
+ }
+ }
+
+ // Finally, try obtaining the furthest valid field
+ if (rows > 0 && cols > 0) {
+ w->getFieldSlot(w->getNumRows() - 1, w->getNumColumns() - 1);
+ }
+ }
+ delete w;
+
+ return 0;
+}
diff --git a/libs/androidfw/include/androidfw/CursorWindow.h b/libs/androidfw/include/androidfw/CursorWindow.h
index 4c4f7335008d..6e55a9a0eb8b 100644
--- a/libs/androidfw/include/androidfw/CursorWindow.h
+++ b/libs/androidfw/include/androidfw/CursorWindow.h
@@ -150,7 +150,8 @@ private:
* Pointer to the first FieldSlot, used to optimize the extremely
* hot code path of getFieldSlot().
*/
- void* mSlotsData = nullptr;
+ void* mSlotsStart = nullptr;
+ void* mSlotsEnd = nullptr;
uint32_t mSize = 0;
/**
* When a window starts as lightweight inline allocation, this value