Files
frameworks_base/libs/androidfw/include/androidfw/CursorWindow.h
Jeff Sharkey 256da5a361 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
2020-10-19 16:07:16 -06:00

198 lines
6.4 KiB
C++

/*
* Copyright (C) 2006 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.
*/
#ifndef _ANDROID__DATABASE_WINDOW_H
#define _ANDROID__DATABASE_WINDOW_H
#include <inttypes.h>
#include <stddef.h>
#include <stdint.h>
#include <string>
#include "android-base/stringprintf.h"
#include "binder/Parcel.h"
#include "utils/String8.h"
#define LOG_WINDOW(...)
namespace android {
/**
* This class stores a set of rows from a database in a buffer. Internally
* data is structured as a "heap" of string/blob allocations at the bottom
* of the memory region, and a "stack" of FieldSlot allocations at the top
* of the memory region. Here's an example visual representation:
*
* +----------------------------------------------------------------+
* |heap\0of\0strings\0 222211110000| ...
* +-------------------+--------------------------------+-------+---+
* ^ ^ ^ ^ ^ ^
* | | | | | |
* | +- mAllocOffset mSlotsOffset -+ | | |
* +- mData mSlotsStart -+ | |
* mSize -+ |
* mInflatedSize -+
*
* Strings are stored in UTF-8.
*/
class CursorWindow {
CursorWindow();
public:
/* Field types. */
enum {
FIELD_TYPE_NULL = 0,
FIELD_TYPE_INTEGER = 1,
FIELD_TYPE_FLOAT = 2,
FIELD_TYPE_STRING = 3,
FIELD_TYPE_BLOB = 4,
};
/* Opaque type that describes a field slot. */
struct FieldSlot {
private:
int32_t type;
union {
double d;
int64_t l;
struct {
uint32_t offset;
uint32_t size;
} buffer;
} data;
friend class CursorWindow;
} __attribute((packed));
~CursorWindow();
static status_t create(const String8& name, size_t size, CursorWindow** outCursorWindow);
static status_t createFromParcel(Parcel* parcel, CursorWindow** outCursorWindow);
status_t writeToParcel(Parcel* parcel);
inline String8 name() { return mName; }
inline size_t size() { return mSize; }
inline size_t freeSpace() { return mSlotsOffset - mAllocOffset; }
inline uint32_t getNumRows() { return mNumRows; }
inline uint32_t getNumColumns() { return mNumColumns; }
status_t clear();
status_t setNumColumns(uint32_t numColumns);
/**
* Allocate a row slot and its directory.
* The row is initialized will null entries for each field.
*/
status_t allocRow();
status_t freeLastRow();
status_t putBlob(uint32_t row, uint32_t column, const void* value, size_t size);
status_t putString(uint32_t row, uint32_t column, const char* value, size_t sizeIncludingNull);
status_t putLong(uint32_t row, uint32_t column, int64_t value);
status_t putDouble(uint32_t row, uint32_t column, double value);
status_t putNull(uint32_t row, uint32_t column);
/**
* Gets the field slot at the specified row and column.
* Returns null if the requested row or column is not in the window.
*/
FieldSlot* getFieldSlot(uint32_t row, uint32_t column);
inline int32_t getFieldSlotType(FieldSlot* fieldSlot) {
return fieldSlot->type;
}
inline int64_t getFieldSlotValueLong(FieldSlot* fieldSlot) {
return fieldSlot->data.l;
}
inline double getFieldSlotValueDouble(FieldSlot* fieldSlot) {
return fieldSlot->data.d;
}
inline const char* getFieldSlotValueString(FieldSlot* fieldSlot,
size_t* outSizeIncludingNull) {
*outSizeIncludingNull = fieldSlot->data.buffer.size;
return static_cast<char*>(offsetToPtr(
fieldSlot->data.buffer.offset, fieldSlot->data.buffer.size));
}
inline const void* getFieldSlotValueBlob(FieldSlot* fieldSlot, size_t* outSize) {
*outSize = fieldSlot->data.buffer.size;
return offsetToPtr(fieldSlot->data.buffer.offset, fieldSlot->data.buffer.size);
}
inline std::string toString() const {
return android::base::StringPrintf("CursorWindow{name=%s, fd=%d, size=%d, inflatedSize=%d, "
"allocOffset=%d, slotsOffset=%d, numRows=%d, numColumns=%d}", mName.c_str(),
mAshmemFd, mSize, mInflatedSize, mAllocOffset, mSlotsOffset, mNumRows, mNumColumns);
}
private:
String8 mName;
int mAshmemFd = -1;
void* mData = nullptr;
/**
* Pointer to the first FieldSlot, used to optimize the extremely
* hot code path of getFieldSlot().
*/
void* mSlotsStart = nullptr;
void* mSlotsEnd = nullptr;
uint32_t mSize = 0;
/**
* When a window starts as lightweight inline allocation, this value
* holds the "full" size to be created after ashmem inflation.
*/
uint32_t mInflatedSize = 0;
/**
* Offset to the top of the "heap" of string/blob allocations. By
* storing these allocations at the bottom of our memory region we
* avoid having to rewrite offsets when inflating.
*/
uint32_t mAllocOffset = 0;
/**
* Offset to the bottom of the "stack" of FieldSlot allocations.
*/
uint32_t mSlotsOffset = 0;
uint32_t mNumRows = 0;
uint32_t mNumColumns = 0;
bool mReadOnly = false;
void updateSlotsData();
void* offsetToPtr(uint32_t offset, uint32_t bufferSize);
uint32_t offsetFromPtr(void* ptr);
/**
* By default windows are lightweight inline allocations; this method
* inflates the window into a larger ashmem region.
*/
status_t maybeInflate();
/**
* Allocate a portion of the window.
*/
status_t alloc(size_t size, uint32_t* outOffset);
status_t putBlobOrString(uint32_t row, uint32_t column,
const void* value, size_t size, int32_t type);
};
}; // namespace android
#endif