Blob Blame History Raw
From a846aedd0e9dfe26ca6afaf6a1db8a54c20363c1 Mon Sep 17 00:00:00 2001
From: Peter Jones <pjones@redhat.com>
Date: Tue, 30 Sep 2014 18:52:59 -0400
Subject: [PATCH 64/74] Actually find the relocations correctly and process
 them that way.

Find the relocations based on the *file* address in the old binary,
because it's only the same as the virtual address some of the time.

Also perform some extra validation before processing it, and don't bail
out in /error/ if both ReloceBase and RelocEnd are null - that condition
is fine.

Signed-off-by: Peter Jones <pjones@redhat.com>
---
 shim.c | 90 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------
 1 file changed, 77 insertions(+), 13 deletions(-)

diff --git a/shim.c b/shim.c
index 7cd4182..4baf8b1 100644
--- a/shim.c
+++ b/shim.c
@@ -222,6 +222,7 @@ image_is_loadable(EFI_IMAGE_OPTIONAL_HEADER_UNION *PEHdr)
  * Perform the actual relocation
  */
 static EFI_STATUS relocate_coff (PE_COFF_LOADER_IMAGE_CONTEXT *context,
+				 EFI_IMAGE_SECTION_HEADER *Section,
 				 void *orig, void *data)
 {
 	EFI_IMAGE_BASE_RELOCATION *RelocBase, *RelocBaseEnd;
@@ -233,14 +234,46 @@ static EFI_STATUS relocate_coff (PE_COFF_LOADER_IMAGE_CONTEXT *context,
 	UINT64 *Fixup64;
 	int size = context->ImageSize;
 	void *ImageEnd = (char *)orig + size;
+	int n = 0;
 
 	if (image_is_64_bit(context->PEHdr))
 		context->PEHdr->Pe32Plus.OptionalHeader.ImageBase = (UINT64)(unsigned long)data;
 	else
 		context->PEHdr->Pe32.OptionalHeader.ImageBase = (UINT32)(unsigned long)data;
 
-	RelocBase = ImageAddress(orig, size, context->RelocDir->VirtualAddress);
-	RelocBaseEnd = ImageAddress(orig, size, context->RelocDir->VirtualAddress + context->RelocDir->Size - 1);
+	/* Alright, so here's how this works:
+	 *
+	 * context->RelocDir gives us two things:
+	 * - the VA the table of base relocation blocks are (maybe) to be
+	 *   mapped at (RelocDir->VirtualAddress)
+	 * - the virtual size (RelocDir->Size)
+	 *
+	 * The .reloc section (Section here) gives us some other things:
+	 * - the name! kind of. (Section->Name)
+	 * - the virtual size (Section->VirtualSize), which should be the same
+	 *   as RelocDir->Size
+	 * - the virtual address (Section->VirtualAddress)
+	 * - the file section size (Section->SizeOfRawData), which is
+	 *   a multiple of OptHdr->FileAlignment.  Only useful for image
+	 *   validation, not really useful for iteration bounds.
+	 * - the file address (Section->PointerToRawData)
+	 * - a bunch of stuff we don't use that's 0 in our binaries usually
+	 * - Flags (Section->Characteristics)
+	 *
+	 * and then the thing that's actually at the file address is an array
+	 * of EFI_IMAGE_BASE_RELOCATION structs with some values packed behind
+	 * them.  The SizeOfBlock field of this structure includes the
+	 * structure itself, and adding it to that structure's address will
+	 * yield the next entry in the array.
+	 */
+	RelocBase = ImageAddress(orig, size, Section->PointerToRawData);
+	/* RelocBaseEnd here is the address of the first entry /past/ the
+	 * table.  */
+	RelocBaseEnd = ImageAddress(orig, size, Section->PointerToRawData +
+						Section->Misc.VirtualSize);
+
+	if (!RelocBase && !RelocBaseEnd)
+		return EFI_SUCCESS;
 
 	if (!RelocBase || !RelocBaseEnd) {
 		perror(L"Reloc table overflows binary\n");
@@ -256,19 +289,19 @@ static EFI_STATUS relocate_coff (PE_COFF_LOADER_IMAGE_CONTEXT *context,
 		Reloc = (UINT16 *) ((char *) RelocBase + sizeof (EFI_IMAGE_BASE_RELOCATION));
 
 		if ((RelocBase->SizeOfBlock == 0) || (RelocBase->SizeOfBlock > context->RelocDir->Size)) {
-			perror(L"Reloc block size %d is invalid\n", RelocBase->SizeOfBlock);
+			perror(L"Reloc %d block size %d is invalid\n", n, RelocBase->SizeOfBlock);
 			return EFI_UNSUPPORTED;
 		}
 
 		RelocEnd = (UINT16 *) ((char *) RelocBase + RelocBase->SizeOfBlock);
 		if ((void *)RelocEnd < orig || (void *)RelocEnd > ImageEnd) {
-			perror(L"Reloc entry overflows binary\n");
+			perror(L"Reloc %d entry overflows binary\n", n);
 			return EFI_UNSUPPORTED;
 		}
 
 		FixupBase = ImageAddress(data, size, RelocBase->VirtualAddress);
 		if (!FixupBase) {
-			perror(L"Invalid fixupbase\n");
+			perror(L"Reloc %d Invalid fixupbase\n", n);
 			return EFI_UNSUPPORTED;
 		}
 
@@ -317,12 +350,13 @@ static EFI_STATUS relocate_coff (PE_COFF_LOADER_IMAGE_CONTEXT *context,
 				break;
 
 			default:
-				perror(L"Unknown relocation\n");
+				perror(L"Reloc %d Unknown relocation\n", n);
 				return EFI_UNSUPPORTED;
 			}
 			Reloc += 1;
 		}
 		RelocBase = (EFI_IMAGE_BASE_RELOCATION *) RelocEnd;
+		n++;
 	}
 
 	return EFI_SUCCESS;
@@ -1102,15 +1136,21 @@ static EFI_STATUS handle_image (void *data, unsigned int datasize,
 
 	CopyMem(buffer, data, context.SizeOfHeaders);
 
+	char *RelocBase, *RelocBaseEnd;
+	RelocBase = ImageAddress(buffer, datasize,
+				 context.RelocDir->VirtualAddress);
+	/* RelocBaseEnd here is the address of the last byte of the table */
+	RelocBaseEnd = ImageAddress(buffer, datasize,
+				    context.RelocDir->VirtualAddress +
+				    context.RelocDir->Size - 1);
+
+	EFI_IMAGE_SECTION_HEADER *RelocSection = NULL;
+
 	/*
 	 * Copy the executable's sections to their desired offsets
 	 */
 	Section = context.FirstSection;
 	for (i = 0; i < context.NumberOfSections; i++, Section++) {
-		if (Section->Characteristics & 0x02000000)
-			/* section has EFI_IMAGE_SCN_MEM_DISCARDABLE attr set */
-			continue;
-
 		size = Section->Misc.VirtualSize;
 
 		if (size > Section->SizeOfRawData)
@@ -1118,7 +1158,6 @@ static EFI_STATUS handle_image (void *data, unsigned int datasize,
 
 		base = ImageAddress (buffer, context.ImageSize, Section->VirtualAddress);
 		end = ImageAddress (buffer, context.ImageSize, Section->VirtualAddress + size - 1);
-
 		if (!base || !end) {
 			perror(L"Invalid section size\n");
 			return EFI_UNSUPPORTED;
@@ -1130,6 +1169,30 @@ static EFI_STATUS handle_image (void *data, unsigned int datasize,
 			return EFI_UNSUPPORTED;
 		}
 
+		/* We do want to process .reloc, but it's often marked
+		 * discardable, so we don't want to memcpy it. */
+		if (CompareMem(Section->Name, ".reloc\0\0", 8) == 0) {
+			if (RelocSection) {
+				perror(L"Image has multiple relocation sections\n");
+				return EFI_UNSUPPORTED;
+			}
+			/* If it has nonzero sizes, and our bounds check
+			 * made sense, and the VA and size match RelocDir's
+			 * versions, then we believe in this section table. */
+			if (Section->SizeOfRawData &&
+					Section->Misc.VirtualSize &&
+					base && end &&
+					RelocBase == base &&
+					RelocBaseEnd == end) {
+				RelocSection = Section;
+			}
+		}
+
+		if (Section->Characteristics & 0x02000000) {
+			/* section has EFI_IMAGE_SCN_MEM_DISCARDABLE attr set */
+			continue;
+		}
+
 		if (Section->SizeOfRawData > 0)
 			CopyMem(base, data + Section->PointerToRawData, size);
 
@@ -1143,11 +1206,12 @@ static EFI_STATUS handle_image (void *data, unsigned int datasize,
 		return EFI_UNSUPPORTED;
 	}
 
-	if (context.RelocDir->Size) {
+	if (context.RelocDir->Size && RelocSection) {
 		/*
 		 * Run the relocation fixups
 		 */
-		efi_status = relocate_coff(&context, data, buffer);
+		efi_status = relocate_coff(&context, RelocSection, data,
+					   buffer);
 
 		if (efi_status != EFI_SUCCESS) {
 			perror(L"Relocation failed: %r\n", efi_status);
-- 
1.9.3