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:
95
Android.bp
95
Android.bp
@@ -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
86
diskio-heap.cc
Normal 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();
|
||||
}
|
||||
8
diskio.h
8
diskio.h
@@ -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
22
fuzzer/Android.bp
Normal 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
BIN
fuzzer/corpus/typical.bin
Normal file
Binary file not shown.
43
fuzzer/libgptf_fuzzer.cc
Normal file
43
fuzzer/libgptf_fuzzer.cc
Normal 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
5
gpt.cc
@@ -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
1
gpt.h
@@ -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);}
|
||||
|
||||
Reference in New Issue
Block a user