From d31ec7b1167470cec8a327ca9a34e3f06030bdcb Mon Sep 17 00:00:00 2001 From: Pete Batard Date: Mon, 5 Sep 2022 00:07:59 +0100 Subject: [PATCH] work around Linux kernel efistub limitation with GetInfo() * The Linux kernel's EFI stub uses a fixed size GetInfo() buffer and has no provision whatsoever for retrying with a larger buffer, as it should, on getting a valid EFI_BUFFER_TOO_SMALL response. * Because the default buffer size we request is larger than what the kernel expects, this leads to the kernel bailing out on EFI_BUFFER_TOO_SMALL. * We therefore reduce the size of the default EFI_FILE_INFO buffer we use to the size that the kernel expects, and use this opportunity to replace the sizeof(EFI_FILE_INFO) constants with the more appropriate SIZE_OF_EFI_FILE_INFO. * Closes #26. * Closes #27. * Closes #38. --- src/driver.h | 8 ++++---- src/file.c | 34 ++++++++++++++++------------------ src/utf8.c | 13 +++++++------ 3 files changed, 27 insertions(+), 28 deletions(-) diff --git a/src/driver.h b/src/driver.h index 765dae6..575de56 100644 --- a/src/driver.h +++ b/src/driver.h @@ -103,7 +103,7 @@ /* Driver version */ #define FS_DRIVER_VERSION_MAJOR 1 -#define FS_DRIVER_VERSION_MINOR 7 +#define FS_DRIVER_VERSION_MINOR 9 #ifndef ARRAYSIZE #define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0])) @@ -119,9 +119,9 @@ #define _WIDEN(s) L ## s #define WIDEN(s) _WIDEN(s) -#define MAX_PATH 256 -#define MINIMUM_INFO_LENGTH (sizeof(EFI_FILE_INFO) + MAX_PATH * sizeof(CHAR16)) -#define MINIMUM_FS_INFO_LENGTH (sizeof(EFI_FILE_SYSTEM_INFO) + MAX_PATH * sizeof(CHAR16)) +#define MAX_FILE_NAME_LEN 256 +#define MINIMUM_INFO_LENGTH (SIZE_OF_EFI_FILE_INFO + MAX_FILE_NAME_LEN * sizeof(CHAR16)) +#define MINIMUM_FS_INFO_LENGTH (SIZE_OF_EFI_FILE_SYSTEM_INFO + MAX_FILE_NAME_LEN * sizeof(CHAR16)) #define IS_ROOT(File) (File == File->FileSystem->RootFile) /* Logging */ diff --git a/src/file.c b/src/file.c index a510d14..fcb79fc 100644 --- a/src/file.c +++ b/src/file.c @@ -35,7 +35,7 @@ static const CHAR16 * FileName(EFI_GRUB_FILE *File) { EFI_STATUS Status; - static CHAR16 Path[MAX_PATH]; + static CHAR16 Path[MAX_FILE_NAME_LEN]; Status = Utf8ToUtf16NoAlloc(File->path, Path, sizeof(Path)); if (EFI_ERROR(Status)) { @@ -82,7 +82,7 @@ FileOpen(EFI_FILE_HANDLE This, EFI_FILE_HANDLE *New, EFI_GRUB_FILE *NewFile; // TODO: Use dynamic buffers? - char path[MAX_PATH], clean_path[MAX_PATH], *dirname; + char path[MAX_FILE_NAME_LEN], clean_path[MAX_FILE_NAME_LEN], *dirname; INTN i, len; BOOLEAN AbsolutePath = (*Name == L'\\'); @@ -136,7 +136,7 @@ FileOpen(EFI_FILE_HANDLE This, EFI_FILE_HANDLE *New, /* We only want to handle absolute paths */ clean_path[0] = '/'; /* Find out if we're dealing with root by removing the junk */ - CopyPathRelative(&clean_path[1], path, MAX_PATH - 1); + CopyPathRelative(&clean_path[1], path, MAX_FILE_NAME_LEN - 1); if (clean_path[1] == 0) { /* We're dealing with the root */ PrintInfo(L" Reopening \n"); @@ -275,6 +275,7 @@ DirHook(const CHAR8 *name, const GRUB_DIRHOOK_INFO *DirInfo, VOID *Data) EFI_STATUS Status; EFI_FILE_INFO *Info = (EFI_FILE_INFO *) Data; INT64 *Index = (INT64 *) &Info->FileSize; + UINTN tmpLen; CHAR8 *filename = (CHAR8 *) (UINTN) Info->PhysicalSize; EFI_TIME Time = { 1970, 01, 01, 00, 00, 00, 0, 0, 0, 0, 0}; @@ -288,14 +289,14 @@ DirHook(const CHAR8 *name, const GRUB_DIRHOOK_INFO *DirInfo, VOID *Data) strcpya(filename, name); - Status = Utf8ToUtf16NoAlloc(filename, Info->FileName, (INTN)(Info->Size - sizeof(EFI_FILE_INFO))); + tmpLen = (UINTN)(Info->Size - SIZE_OF_EFI_FILE_INFO); + Status = Utf8ToUtf16NoAllocUpdateLen(filename, Info->FileName, &tmpLen); + Info->Size = SIZE_OF_EFI_FILE_INFO + tmpLen; if (EFI_ERROR(Status)) { if (Status != EFI_BUFFER_TOO_SMALL) PrintStatusError(Status, L"Could not convert directory entry to UTF-8"); return (INT32) Status; } - /* The Info struct size already accounts for the extra NUL */ - Info->Size = sizeof(*Info) + StrLen(Info->FileName) * sizeof(CHAR16); // Oh, and of course GRUB uses a 32 bit signed mtime value (seriously, wtf guys?!?) if (DirInfo->MtimeSet) @@ -328,7 +329,7 @@ FileReadDir(EFI_GRUB_FILE *File, UINTN *Len, VOID *Data) INT64 *Index = (INT64 *) &Info->FileSize; /* And PhysicalSize as a pointer to our filename */ CHAR8 **basename = (CHAR8 **) &Info->PhysicalSize; - CHAR8 path[MAX_PATH]; + CHAR8 path[MAX_FILE_NAME_LEN]; EFI_GRUB_FILE *TmpFile = NULL; INTN len; @@ -552,7 +553,7 @@ FileGetInfo(EFI_FILE_HANDLE This, EFI_GUID *Type, UINTN *Len, VOID *Data) return EFI_BUFFER_TOO_SMALL; } - ZeroMem(Data, sizeof(EFI_FILE_INFO)); + ZeroMem(Data, SIZE_OF_EFI_FILE_INFO); Info->Size = *Len; Info->Attribute = EFI_FILE_READ_ONLY; GrubTimeToEfiTime(File->Mtime, &Time); @@ -567,8 +568,7 @@ FileGetInfo(EFI_FILE_HANDLE This, EFI_GUID *Type, UINTN *Len, VOID *Data) Info->PhysicalSize = GrubGetFileSize(File); } - /* The Info struct size accounts for the NUL string terminator */ - tmpLen = (UINTN)(Info->Size - sizeof(EFI_FILE_INFO)); + tmpLen = (UINTN)(Info->Size - SIZE_OF_EFI_FILE_INFO); Status = Utf8ToUtf16NoAllocUpdateLen(File->basename, Info->FileName, &tmpLen); if (EFI_ERROR(Status)) { if (Status != EFI_BUFFER_TOO_SMALL) @@ -576,7 +576,7 @@ FileGetInfo(EFI_FILE_HANDLE This, EFI_GUID *Type, UINTN *Len, VOID *Data) return Status; } - Info->Size = sizeof(EFI_FILE_INFO) + tmpLen; + Info->Size = SIZE_OF_EFI_FILE_INFO + tmpLen; *Len = (INTN)Info->Size; return EFI_SUCCESS; @@ -589,7 +589,7 @@ FileGetInfo(EFI_FILE_HANDLE This, EFI_GUID *Type, UINTN *Len, VOID *Data) return EFI_BUFFER_TOO_SMALL; } - ZeroMem(Data, sizeof(EFI_FILE_INFO)); + ZeroMem(Data, SIZE_OF_EFI_FILE_SYSTEM_INFO); FSInfo->Size = *Len; FSInfo->ReadOnly = 1; /* NB: This should really be cluster size, but we don't have access to that */ @@ -616,17 +616,16 @@ FileGetInfo(EFI_FILE_HANDLE This, EFI_GUID *Type, UINTN *Len, VOID *Data) if (EFI_ERROR(Status)) { PrintStatusError(Status, L"Could not read disk label"); FSInfo->VolumeLabel[0] = 0; - *Len = sizeof(EFI_FILE_SYSTEM_INFO); + *Len = SIZE_OF_EFI_FILE_SYSTEM_INFO + sizeof(CHAR16); } else { - /* The Info struct size accounts for the NUL string terminator */ - tmpLen = (INTN)(FSInfo->Size - sizeof(EFI_FILE_SYSTEM_INFO)); + tmpLen = (INTN)(FSInfo->Size - SIZE_OF_EFI_FILE_SYSTEM_INFO); Status = Utf8ToUtf16NoAllocUpdateLen(label, FSInfo->VolumeLabel, &tmpLen); if (EFI_ERROR(Status)) { if (Status != EFI_BUFFER_TOO_SMALL) PrintStatusError(Status, L"Could not convert label to UTF-16"); return Status; } - FSInfo->Size = sizeof(EFI_FILE_SYSTEM_INFO) + tmpLen; + FSInfo->Size = SIZE_OF_EFI_FILE_SYSTEM_INFO + tmpLen; *Len = (INTN)FSInfo->Size; } return EFI_SUCCESS; @@ -637,8 +636,7 @@ FileGetInfo(EFI_FILE_HANDLE This, EFI_GUID *Type, UINTN *Len, VOID *Data) Status = GrubLabel(File, &label); if (EFI_ERROR(Status)) { PrintStatusError(Status, L"Could not read disk label"); - } - else { + } else { Status = Utf8ToUtf16NoAllocUpdateLen(label, VLInfo->VolumeLabel, Len); if (EFI_ERROR(Status)) { if (Status != EFI_BUFFER_TOO_SMALL) diff --git a/src/utf8.c b/src/utf8.c index dd610b3..554f73f 100644 --- a/src/utf8.c +++ b/src/utf8.c @@ -631,7 +631,7 @@ CHAR16 } /** - * Convert an UTF-8 string to UTF-16, using an user supplied buffer + * Convert an UTF-8 string to UTF-16, using a user supplied buffer * * @v Src A NUL terminated UTF-8 input string * @v Dst A pointer an UTF-16 string buffer @@ -658,11 +658,12 @@ Utf8ToUtf16NoAllocUpdateLen(CHAR8 *Src, CHAR16 *Dst, UINTN *Len) SrcLen++; /* +1 for NUL terminator */ - if (!ConvertUcs2Utf8(TRUE, (UINT8 *)Src, SrcLen, (UINT8 *)Dst, *Len, Len)) - return EFI_NO_MAPPING; - - if (*Len > OrgLen) - return EFI_BUFFER_TOO_SMALL; + if (!ConvertUcs2Utf8(TRUE, (UINT8*)Src, SrcLen, (UINT8*)Dst, *Len, Len)) { + if (*Len > OrgLen) + return EFI_BUFFER_TOO_SMALL; + else + return EFI_NO_MAPPING; + } return EFI_SUCCESS; }