Add a fuzzer for gptfdisk.

Since gptfdisk is used to parse MBR and GPT partition tables from
untrusted USB and SD card storage devices, we should get a fuzzer
wired up to hunt for security issues.

To enable the fuzzer, we create a new "diskio-heap" implementation
for backing I/O operations, which allows us to treat the fuzzer
input as a fake block device.  These changes are as minimal as
possible to avoid future merge conflicts.

The single corpus input is a sample block device with a single
empty GPT partition created using these commands:

$ losetup /dev/loop0 typical.bin
$ gdisk /dev/loop0

And the final table is:

Number  Start (sector)    End (sector)  Size       Code  Name
   1              34              38   2.5 KiB     8300  Linux filesystem

Bug: 170783842
Test: SANITIZE_HOST=address make ${FUZZER_NAME} && ${ANDROID_HOST_OUT}/fuzz/$(get_build_var HOST_ARCH)/${FUZZER_NAME}/${FUZZER_NAME}
Change-Id: I21a2a5f7f1019365accf8fd74c958aaafe7f7ff7
This commit is contained in:
Jeff Sharkey
2020-10-13 19:50:30 -06:00
parent d0c42f389a
commit 83fdc99960
8 changed files with 218 additions and 42 deletions

View File

@@ -1,51 +1,11 @@
cc_binary {
name: "sgdisk",
host_supported: true,
srcs: [
"sgdisk.cc",
],
cflags: [
"-Wno-unused-parameter",
"-Wno-pragma-pack",
"-Werror",
],
shared_libs: ["libext2_uuid"],
static_libs: ["libgptf"],
}
cc_library_static {
name: "libgptf",
host_supported: true,
export_include_dirs:["."],
srcs: [
"gptcl.cc",
"crc32.cc",
"support.cc",
"guid.cc",
"gptpart.cc",
"mbrpart.cc",
"basicmbr.cc",
"mbr.cc",
"gpt.cc",
"bsd.cc",
"parttypes.cc",
"attributes.cc",
"diskio.cc",
"diskio-unix.cc",
"android_popt.cc",
],
cc_defaults {
name: "gptfdisk_default_flags",
cflags: [
"-Wno-unused-parameter",
"-Wno-pragma-pack",
"-Werror",
"-fPIC",
],
target: {
darwin: {
cflags: [
@@ -54,6 +14,57 @@ cc_library_static {
],
},
},
}
cc_binary {
name: "sgdisk",
defaults: ["gptfdisk_default_flags"],
host_supported: true,
srcs: [
"sgdisk.cc",
],
shared_libs: ["libext2_uuid"],
static_libs: ["libgptf"],
}
lib_common_srcs = [
"gptcl.cc",
"crc32.cc",
"support.cc",
"guid.cc",
"gptpart.cc",
"mbrpart.cc",
"basicmbr.cc",
"mbr.cc",
"gpt.cc",
"bsd.cc",
"parttypes.cc",
"attributes.cc",
"diskio.cc",
"android_popt.cc",
]
cc_library_static {
name: "libgptf",
defaults: ["gptfdisk_default_flags"],
host_supported: true,
export_include_dirs: ["."],
srcs: lib_common_srcs + ["diskio-unix.cc"],
shared_libs: ["libext2_uuid"],
}
cc_library_static {
name: "libgptf_fuzzer_lib",
defaults: ["gptfdisk_default_flags"],
host_supported: true,
export_include_dirs: ["."],
srcs: lib_common_srcs + ["diskio-heap.cc"],
cflags: ["-DENABLE_HEAP_DISKIO"],
shared_libs: ["libext2_uuid"],
}

86
diskio-heap.cc Normal file
View File

@@ -0,0 +1,86 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <algorithm>
#include "diskio.h"
using namespace std;
int DiskIO::OpenForRead(const unsigned char* data, size_t size) {
this->data = data;
this->size = size;
this->off = 0;
this->isOpen = 1;
this->openForWrite = 0;
return 1;
}
void DiskIO::MakeRealName(void) {
realFilename = userFilename;
}
int DiskIO::OpenForRead(void) {
return 1;
}
int DiskIO::OpenForWrite(void) {
return 1;
}
void DiskIO::Close(void) {
}
int DiskIO::GetBlockSize(void) {
return 512;
}
int DiskIO::GetPhysBlockSize(void) {
return 512;
}
uint32_t DiskIO::GetNumHeads(void) {
return 255;
}
uint32_t DiskIO::GetNumSecsPerTrack(void) {
return 63;
}
int DiskIO::DiskSync(void) {
return 1;
}
int DiskIO::Seek(uint64_t sector) {
off_t off = sector * GetBlockSize();
if (off >= this->size) {
return 0;
} else {
this->off = off;
return 1;
}
}
int DiskIO::Read(void* buffer, int numBytes) {
int actualBytes = std::min(static_cast<int>(this->size - this->off), numBytes);
memcpy(buffer, this->data + this->off, actualBytes);
return actualBytes;
}
int DiskIO::Write(void*, int) {
return 0;
}
uint64_t DiskIO::DiskSize(int *) {
return this->size / GetBlockSize();
}

View File

@@ -56,12 +56,20 @@ class DiskIO {
HANDLE fd;
#else
int fd;
#endif
#ifdef ENABLE_HEAP_DISKIO
const unsigned char* data;
size_t size;
off_t off;
#endif
public:
DiskIO(void);
~DiskIO(void);
void MakeRealName(void);
#ifdef ENABLE_HEAP_DISKIO
int OpenForRead(const unsigned char* data, size_t size);
#endif
int OpenForRead(const string & filename);
int OpenForRead(void);
int OpenForWrite(const string & filename);

22
fuzzer/Android.bp Normal file
View File

@@ -0,0 +1,22 @@
cc_fuzz {
name: "libgptf_fuzzer",
srcs: ["libgptf_fuzzer.cc"],
cflags: ["-DENABLE_HEAP_DISKIO"],
host_supported: true,
corpus: ["corpus/*"],
static_libs: ["libgmock"],
target: {
android: {
static_libs: [
"libgptf_fuzzer_lib",
"libext2_uuid",
],
},
host: {
static_libs: [
"libgptf_fuzzer_lib",
"libext2_uuid",
],
},
},
}

BIN
fuzzer/corpus/typical.bin Normal file

Binary file not shown.

43
fuzzer/libgptf_fuzzer.cc Normal file
View File

@@ -0,0 +1,43 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <fstream>
#include <iostream>
#include "diskio.h"
#include "mbr.h"
#include "gpt.h"
#include <fuzzer/FuzzedDataProvider.h>
std::ofstream silence("/dev/null");
extern "C" int LLVMFuzzerInitialize(int *, char ***) {
std::cout.rdbuf(silence.rdbuf());
std::cerr.rdbuf(silence.rdbuf());
return 0;
}
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
DiskIO disk;
disk.OpenForRead(static_cast<const unsigned char*>(data), size);
BasicMBRData mbrData;
mbrData.ReadMBRData(&disk);
GPTData gptData;
gptData.SetDisk(disk);
gptData.LoadPartitions("/dev/does_not_exist");
return 0;
}

5
gpt.cc
View File

@@ -780,6 +780,11 @@ int GPTData::SetDisk(const string & deviceFilename) {
return allOK;
} // GPTData::SetDisk()
int GPTData::SetDisk(const DiskIO & disk) {
myDisk = disk;
return 1;
} // GPTData::SetDisk()
// Scan for partition data. This function loads the MBR data (regular MBR or
// protective MBR) and loads BSD disklabel data (which is probably invalid).
// It also looks for APM data, forces a load of GPT data, and summarizes

1
gpt.h
View File

@@ -112,6 +112,7 @@ public:
// Load or save data from/to disk
int SetDisk(const string & deviceFilename);
int SetDisk(const DiskIO & disk);
DiskIO* GetDisk(void) {return &myDisk;}
int LoadMBR(const string & f) {return protectiveMBR.ReadMBRData(f);}
int WriteProtectiveMBR(void) {return protectiveMBR.WriteMBRData(&myDisk);}