mirror of
https://github.com/acidanthera/OpenCorePkg.git
synced 2025-12-08 19:25:01 +00:00
We need to pass a non-empty buffer into the read routine because the return code EFI_BUFFER_TOO_SMALL is expected only for directories. Properly, the right way is to retrieve file size using FatGetInfo or FileGetInfo, but this will significantly slow down the fuzzing process, that's why we use 100-byte temporary buffer
504 lines
11 KiB
C
504 lines
11 KiB
C
/** @file
|
|
Copyright (c) 2023, Savva Mitrofanov. All rights reserved.
|
|
SPDX-License-Identifier: BSD-3-Clause
|
|
**/
|
|
|
|
#include <Fat.h>
|
|
|
|
#include <UserFile.h>
|
|
#include <UserGlobalVar.h>
|
|
#include <UserMemory.h>
|
|
#include <UserUnicodeCollation.h>
|
|
#include <string.h>
|
|
|
|
extern EFI_UNICODE_COLLATION_PROTOCOL *mUnicodeCollationInterface;
|
|
|
|
STATIC EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *mEfiSfsInterface;
|
|
|
|
UINTN mFuzzOffset;
|
|
UINTN mFuzzSize;
|
|
CONST UINT8 *mFuzzPointer;
|
|
|
|
EFI_STATUS
|
|
EFIAPI
|
|
FuzzBlockIoFlushBlocks (
|
|
IN EFI_BLOCK_IO_PROTOCOL *This
|
|
)
|
|
{
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
EFI_STATUS
|
|
EFIAPI
|
|
FuzzReadDisk (
|
|
IN EFI_DISK_IO_PROTOCOL *This,
|
|
IN UINT32 MediaId,
|
|
IN UINT64 Offset,
|
|
IN UINTN BufferSize,
|
|
OUT VOID *Buffer
|
|
)
|
|
{
|
|
if (Buffer == NULL) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
if ((mFuzzSize - mFuzzOffset) < BufferSize) {
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
|
|
CopyMem (Buffer, mFuzzPointer, BufferSize);
|
|
mFuzzPointer += BufferSize;
|
|
mFuzzOffset += BufferSize;
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
EFI_STATUS
|
|
EFIAPI
|
|
WrapInstallMultipleProtocolInterfaces (
|
|
IN OUT EFI_HANDLE *Handle,
|
|
...
|
|
)
|
|
{
|
|
VA_LIST Args;
|
|
EFI_STATUS Status;
|
|
EFI_GUID *Protocol;
|
|
VOID *Interface;
|
|
|
|
if (Handle == NULL) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
VA_START (Args, Handle);
|
|
for (Status = EFI_SUCCESS; !EFI_ERROR (Status);) {
|
|
//
|
|
// If protocol is NULL, then it's the end of the list
|
|
//
|
|
Protocol = VA_ARG (Args, EFI_GUID *);
|
|
if (Protocol == NULL) {
|
|
break;
|
|
}
|
|
|
|
Interface = VA_ARG (Args, VOID *);
|
|
|
|
//
|
|
// If this is Sfs protocol then save interface into global state
|
|
//
|
|
if (CompareGuid (Protocol, &gEfiSimpleFileSystemProtocolGuid)) {
|
|
mEfiSfsInterface = Interface;
|
|
}
|
|
}
|
|
|
|
VA_END (Args);
|
|
|
|
return Status;
|
|
}
|
|
|
|
VOID
|
|
FreeAll (
|
|
IN CHAR16 *FileName,
|
|
IN FAT_VOLUME *Volume,
|
|
IN EFI_FILE_PROTOCOL *RootDirInstance
|
|
)
|
|
{
|
|
FreePool (FileName);
|
|
|
|
//
|
|
// Closes root directory
|
|
//
|
|
if (RootDirInstance != NULL) {
|
|
FatClose (RootDirInstance);
|
|
}
|
|
|
|
//
|
|
// Abandon volume structure
|
|
//
|
|
if (Volume != NULL) {
|
|
if (Volume->DiskIo != NULL) {
|
|
FreePool (Volume->DiskIo);
|
|
}
|
|
|
|
if (Volume->BlockIo != NULL) {
|
|
if (Volume->BlockIo->Media != NULL) {
|
|
FreePool (Volume->BlockIo->Media);
|
|
}
|
|
|
|
FreePool (Volume->BlockIo);
|
|
}
|
|
|
|
if (Volume->Root != NULL) {
|
|
FatSetVolumeError (
|
|
Volume->Root,
|
|
EFI_NO_MEDIA
|
|
);
|
|
}
|
|
|
|
Volume->Valid = FALSE;
|
|
|
|
FatCleanupVolume (Volume, NULL, EFI_SUCCESS, NULL);
|
|
}
|
|
}
|
|
|
|
STATIC
|
|
INT32
|
|
TestFatDxe (
|
|
CONST UINT8 *FuzzData,
|
|
UINTN FuzzSize
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_FILE_PROTOCOL *VolumeRootDir;
|
|
UINTN BufferSize;
|
|
VOID *Buffer;
|
|
VOID *TmpBuffer;
|
|
EFI_FILE_PROTOCOL *NewHandle;
|
|
CHAR16 *FileName;
|
|
VOID *Info;
|
|
UINTN Len;
|
|
UINT64 Position;
|
|
EFI_DISK_IO_PROTOCOL *DiskIo;
|
|
EFI_BLOCK_IO_PROTOCOL *BlockIo;
|
|
FAT_VOLUME *Volume;
|
|
FAT_IFILE *FileInstance;
|
|
UINT64 FileSize;
|
|
|
|
EFI_HANDLE DeviceHandle;
|
|
|
|
BufferSize = 100;
|
|
|
|
mFuzzOffset = 0;
|
|
mFuzzSize = FuzzSize;
|
|
mFuzzPointer = FuzzData;
|
|
mEfiSfsInterface = NULL;
|
|
Volume = NULL;
|
|
VolumeRootDir = NULL;
|
|
Position = 0;
|
|
|
|
DeviceHandle = (EFI_HANDLE)0xDEADDEADULL;
|
|
|
|
//
|
|
// Construct File Name
|
|
//
|
|
FileName = AllocateZeroPool (BufferSize);
|
|
if (FileName == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
ASAN_CHECK_MEMORY_REGION (FileName, BufferSize);
|
|
|
|
if ((mFuzzSize - mFuzzOffset) < BufferSize) {
|
|
FreeAll (FileName, Volume, VolumeRootDir);
|
|
return 0;
|
|
}
|
|
|
|
CopyMem (FileName, mFuzzPointer, BufferSize - 2);
|
|
mFuzzPointer += BufferSize - 2;
|
|
mFuzzOffset += BufferSize - 2;
|
|
|
|
//
|
|
// Construct BlockIo and DiskIo interfaces
|
|
//
|
|
DiskIo = AllocateZeroPool (sizeof (EFI_DISK_IO_PROTOCOL));
|
|
if (DiskIo == NULL) {
|
|
FreeAll (FileName, Volume, VolumeRootDir);
|
|
return 0;
|
|
}
|
|
|
|
ASAN_CHECK_MEMORY_REGION (DiskIo, sizeof (EFI_DISK_IO_PROTOCOL));
|
|
|
|
DiskIo->ReadDisk = FuzzReadDisk;
|
|
|
|
BlockIo = AllocateZeroPool (sizeof (EFI_BLOCK_IO_PROTOCOL));
|
|
if (BlockIo == NULL) {
|
|
FreePool (DiskIo);
|
|
FreeAll (FileName, Volume, VolumeRootDir);
|
|
return 0;
|
|
}
|
|
|
|
ASAN_CHECK_MEMORY_REGION (BlockIo, sizeof (EFI_BLOCK_IO_PROTOCOL));
|
|
|
|
BlockIo->Media = AllocateZeroPool (sizeof (EFI_BLOCK_IO_MEDIA));
|
|
if (BlockIo->Media == NULL) {
|
|
FreePool (DiskIo);
|
|
FreePool (BlockIo);
|
|
FreeAll (FileName, Volume, VolumeRootDir);
|
|
return 0;
|
|
}
|
|
|
|
ASAN_CHECK_MEMORY_REGION (BlockIo->Media, sizeof (EFI_BLOCK_IO_MEDIA));
|
|
|
|
BlockIo->FlushBlocks = FuzzBlockIoFlushBlocks;
|
|
|
|
//
|
|
// Allocate volume
|
|
//
|
|
Status = FatAllocateVolume (DeviceHandle, DiskIo, NULL, BlockIo);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
FreePool (BlockIo->Media);
|
|
FreePool (BlockIo);
|
|
FreePool (DiskIo);
|
|
FreeAll (FileName, Volume, VolumeRootDir);
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// Open volume root dir
|
|
//
|
|
Status = FatOpenVolume (mEfiSfsInterface, &VolumeRootDir);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
FreeAll (FileName, Volume, VolumeRootDir);
|
|
return 0;
|
|
}
|
|
|
|
Volume = VOLUME_FROM_VOL_INTERFACE (mEfiSfsInterface);
|
|
|
|
//
|
|
// Open File
|
|
//
|
|
Status = FatOpen (VolumeRootDir, &NewHandle, FileName, EFI_FILE_MODE_READ, 0);
|
|
if (Status == EFI_SUCCESS) {
|
|
//
|
|
// Try to read 100 bytes
|
|
//
|
|
Buffer = AllocateZeroPool (100);
|
|
BufferSize = 100;
|
|
if (Buffer == NULL) {
|
|
FatClose (NewHandle);
|
|
FreeAll (FileName, Volume, VolumeRootDir);
|
|
return 0;
|
|
}
|
|
|
|
Status = FatRead (NewHandle, &BufferSize, Buffer);
|
|
if (Status == EFI_BUFFER_TOO_SMALL) {
|
|
TmpBuffer = ReallocatePool (100, BufferSize, Buffer);
|
|
if (TmpBuffer == NULL) {
|
|
FreePool (Buffer);
|
|
FatClose (NewHandle);
|
|
FreeAll (FileName, Volume, VolumeRootDir);
|
|
return 0;
|
|
}
|
|
|
|
Buffer = TmpBuffer;
|
|
|
|
ASAN_CHECK_MEMORY_REGION (Buffer, BufferSize);
|
|
|
|
FatRead (NewHandle, &BufferSize, Buffer);
|
|
}
|
|
|
|
FatWrite (NewHandle, &BufferSize, Buffer);
|
|
|
|
FatFlush (NewHandle);
|
|
|
|
FreePool (Buffer);
|
|
|
|
//
|
|
// Set/Get file info
|
|
//
|
|
Len = 0;
|
|
Info = NULL;
|
|
Status = FatGetInfo (NewHandle, &gEfiFileInfoGuid, &Len, Info);
|
|
if (Status == EFI_BUFFER_TOO_SMALL) {
|
|
Info = AllocateZeroPool (Len);
|
|
if (Info == NULL) {
|
|
FatClose (NewHandle);
|
|
FreeAll (FileName, Volume, VolumeRootDir);
|
|
return 0;
|
|
}
|
|
|
|
Status = FatGetInfo (NewHandle, &gEfiFileInfoGuid, &Len, Info);
|
|
if (!EFI_ERROR (Status)) {
|
|
//
|
|
// Try to set this info back
|
|
//
|
|
FatSetInfo (NewHandle, &gEfiFileInfoGuid, Len, Info);
|
|
FreePool (Info);
|
|
}
|
|
}
|
|
|
|
Len = 0;
|
|
Info = NULL;
|
|
Status = FatGetInfo (NewHandle, &gEfiFileSystemInfoGuid, &Len, Info);
|
|
if (Status == EFI_BUFFER_TOO_SMALL) {
|
|
Info = AllocateZeroPool (Len);
|
|
if (Info == NULL) {
|
|
FatClose (NewHandle);
|
|
FreeAll (FileName, Volume, VolumeRootDir);
|
|
return 0;
|
|
}
|
|
|
|
Status = FatGetInfo (NewHandle, &gEfiFileSystemInfoGuid, &Len, Info);
|
|
if (!EFI_ERROR (Status)) {
|
|
//
|
|
// Try to set this info back
|
|
//
|
|
FatSetInfo (NewHandle, &gEfiFileSystemInfoGuid, Len, Info);
|
|
FreePool (Info);
|
|
}
|
|
}
|
|
|
|
Len = 0;
|
|
Info = NULL;
|
|
Status = FatGetInfo (NewHandle, &gEfiFileSystemVolumeLabelInfoIdGuid, &Len, Info);
|
|
if (Status == EFI_BUFFER_TOO_SMALL) {
|
|
Info = AllocateZeroPool (Len);
|
|
if (Info == NULL) {
|
|
FatClose (NewHandle);
|
|
FreeAll (FileName, Volume, VolumeRootDir);
|
|
return 0;
|
|
}
|
|
|
|
Status = FatGetInfo (NewHandle, &gEfiFileSystemVolumeLabelInfoIdGuid, &Len, Info);
|
|
if (!EFI_ERROR (Status)) {
|
|
//
|
|
// Try to set this info back
|
|
//
|
|
FatSetInfo (NewHandle, &gEfiFileSystemVolumeLabelInfoIdGuid, Len, Info);
|
|
FreePool (Info);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Test position functions
|
|
//
|
|
FatGetPosition (NewHandle, &Position);
|
|
FatSetPosition (NewHandle, Position);
|
|
|
|
//
|
|
// Trying to reach the end of file and read/write it
|
|
//
|
|
Position = (UINT64)-1;
|
|
Status = FatSetPosition (NewHandle, Position);
|
|
if (!EFI_ERROR (Status)) {
|
|
Buffer = AllocateZeroPool (100);
|
|
BufferSize = 100;
|
|
|
|
if (Buffer == NULL) {
|
|
FatClose (NewHandle);
|
|
FreeAll (FileName, Volume, VolumeRootDir);
|
|
return 0;
|
|
}
|
|
|
|
Status = FatRead (NewHandle, &BufferSize, Buffer);
|
|
if (Status == EFI_BUFFER_TOO_SMALL) {
|
|
TmpBuffer = ReallocatePool (100, BufferSize, Buffer);
|
|
if (TmpBuffer == NULL) {
|
|
FreePool (Buffer);
|
|
FatClose (NewHandle);
|
|
FreeAll (FileName, Volume, VolumeRootDir);
|
|
return 0;
|
|
}
|
|
|
|
Buffer = TmpBuffer;
|
|
|
|
ASAN_CHECK_MEMORY_REGION (Buffer, BufferSize);
|
|
|
|
FatRead (NewHandle, &BufferSize, Buffer);
|
|
}
|
|
|
|
FatWrite (NewHandle, &BufferSize, Buffer);
|
|
|
|
FatFlush (NewHandle);
|
|
|
|
FreePool (Buffer);
|
|
}
|
|
|
|
//
|
|
// Trying to reach out of bound of FileSize
|
|
//
|
|
FileInstance = IFILE_FROM_FHAND (NewHandle);
|
|
FileSize = FileInstance->OFile->FileSize;
|
|
if (FileSize < (UINT64)-1 - 1) {
|
|
Position = FileSize + 1;
|
|
Status = FatSetPosition (NewHandle, Position);
|
|
if (!EFI_ERROR (Status)) {
|
|
Buffer = AllocateZeroPool (100);
|
|
BufferSize = 100;
|
|
if (Buffer == NULL) {
|
|
FatClose (NewHandle);
|
|
FreeAll (FileName, Volume, VolumeRootDir);
|
|
return 0;
|
|
}
|
|
|
|
Status = FatRead (NewHandle, &BufferSize, Buffer);
|
|
|
|
ASSERT (Status == EFI_DEVICE_ERROR);
|
|
FreePool (Buffer);
|
|
}
|
|
}
|
|
|
|
FatDelete (NewHandle);
|
|
}
|
|
|
|
FreeAll (FileName, Volume, VolumeRootDir);
|
|
|
|
return 0;
|
|
}
|
|
|
|
INT32
|
|
LLVMFuzzerTestOneInput (
|
|
CONST UINT8 *FuzzData,
|
|
UINTN FuzzSize
|
|
)
|
|
{
|
|
VOID *NewData;
|
|
|
|
#ifdef MEMORY_MUTATIONS
|
|
UINT32 ConfigSize;
|
|
#endif
|
|
|
|
if (FuzzSize == 0) {
|
|
return 0;
|
|
}
|
|
|
|
// Set up unicode collation protocol
|
|
UserUnicodeCollationInstallProtocol (&mUnicodeCollationInterface);
|
|
|
|
//
|
|
// Override InstallMultipleProtocolInterfaces with custom wrapper
|
|
//
|
|
gBS->InstallMultipleProtocolInterfaces = WrapInstallMultipleProtocolInterfaces;
|
|
|
|
#ifdef MEMORY_MUTATIONS
|
|
ConfigSize = 0;
|
|
ConfigureMemoryAllocations (FuzzData, FuzzSize, &ConfigSize);
|
|
#endif
|
|
SetPoolAllocationSizeLimit (BASE_1GB | BASE_2GB);
|
|
|
|
NewData = AllocatePool (FuzzSize);
|
|
if (NewData != NULL) {
|
|
CopyMem (NewData, FuzzData, FuzzSize);
|
|
TestFatDxe (NewData, FuzzSize);
|
|
FreePool (NewData);
|
|
}
|
|
|
|
DEBUG ((
|
|
DEBUG_POOL | DEBUG_PAGE,
|
|
"UMEM: Allocated %u pools %u pages\n",
|
|
(UINT32)mPoolAllocations,
|
|
(UINT32)mPageAllocations
|
|
));
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
ENTRY_POINT (
|
|
int argc,
|
|
char **argv
|
|
)
|
|
{
|
|
uint32_t f;
|
|
uint8_t *b;
|
|
|
|
if ((b = UserReadFile ((argc > 1) ? argv[1] : "in.bin", &f)) == NULL) {
|
|
DEBUG ((DEBUG_ERROR, "Read fail\n"));
|
|
return -1;
|
|
}
|
|
|
|
LLVMFuzzerTestOneInput (b, f);
|
|
FreePool (b);
|
|
return 0;
|
|
}
|