Skip to content

Commit

Permalink
pe-zboot: Truncate the trailing zero if Image is signed
Browse files Browse the repository at this point in the history
*** Issue ***
In the linux kernel drivers/firmware/efi/libstub/Makefile.zboot, the
original Image is padded with zero, using the following instruction:
	truncate -s $$(hexdump -s16 -n4 -e '"%u"' $<) $@

Hence pe-zboot.c decompress and gets Image plus trailing zeroes.

These trailing zeroes don't affect loading the original PE file. But
they do raise an issue during the signature verification. The root cause is
that the kernel function:
	static int pefile_digest_pe_contents(const void *pebuf, unsigned int pelen,
					     struct pefile_context *ctx,
					     struct shash_desc *desc)
treats [pebuf, pebuf+pelen] as valid payload, which includes the
trailing zeroes. But that is not the truth.

*** Solution ***
In practice, the table of attribute certificates come at the end of a
PE file. This patch utilizes that fact and truncates at the boundary of the
certificate table to get the original Image.

Signed-off-by: Pingfan Liu <[email protected]>
To: [email protected]
  • Loading branch information
Pingfan Liu authored and horms committed Dec 13, 2024
1 parent 455f55c commit 6aecc32
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 3 deletions.
30 changes: 30 additions & 0 deletions include/pe.h
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,30 @@ struct pe32plus_opt_hdr {
uint32_t data_dirs; /* number of data dir entries */
};

struct data_dirent {
uint32_t virtual_address; /* relative to load address */
uint32_t size;
};

struct data_directory {
struct data_dirent exports; /* .edata */
struct data_dirent imports; /* .idata */
struct data_dirent resources; /* .rsrc */
struct data_dirent exceptions; /* .pdata */
struct data_dirent certs; /* certs */
struct data_dirent base_relocations; /* .reloc */
struct data_dirent debug; /* .debug */
struct data_dirent arch; /* reservered */
struct data_dirent global_ptr; /* global pointer reg. Size=0 */
struct data_dirent tls; /* .tls */
struct data_dirent load_config; /* load configuration structure */
struct data_dirent bound_imports; /* no idea */
struct data_dirent import_addrs; /* import address table */
struct data_dirent delay_imports; /* delay-load import table */
struct data_dirent clr_runtime_hdr; /* .cor (object only) */
struct data_dirent reserved;
};

struct section_header {
char name[8]; /* name or "/12\0" string tbl offset */
uint32_t virtual_size; /* size of loaded section in ram */
Expand All @@ -101,6 +125,12 @@ struct section_header {
uint32_t flags;
};

struct win_certificate {
uint32_t length;
uint16_t revision;
uint16_t cert_type;
};

/*
* Return -1 if not PE, else offset of the PE header
*/
Expand Down
25 changes: 22 additions & 3 deletions kexec/kexec-pe-zboot.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
#include <unistd.h>
#include <fcntl.h>
#include "kexec.h"
#include <pe.h>
#include <kexec-pe-zboot.h>

#define FILENAME_IMAGE "/tmp/ImageXXXXXX"
Expand All @@ -44,8 +45,11 @@ int pez_prepare(const char *crude_buf, off_t buf_sz, int *kernel_fd,
int fd = 0;
char *fname = NULL;
char *kernel_uncompressed_buf = NULL;
off_t decompressed_size = 0;
char *parse;
off_t original_file_sz, decompressed_size = 0;
const struct linux_pe_zboot_header *z;
struct pe32plus_opt_hdr *opt_hdr;
struct data_directory *dir;

z = (const struct linux_pe_zboot_header *)(crude_buf);

Expand Down Expand Up @@ -91,12 +95,27 @@ int pez_prepare(const char *crude_buf, off_t buf_sz, int *kernel_fd,
kernel_uncompressed_buf = slurp_decompress_file(fname,
&decompressed_size);

original_file_sz = decompressed_size;
dbgprintf("%s: decompressed size %ld\n", __func__, decompressed_size);

/* Makefile.zboot pads Image with zero, but the trailing zero is not part of PE file */
parse = kernel_uncompressed_buf + get_pehdr_offset(kernel_uncompressed_buf);
parse += sizeof(struct pe_hdr);
opt_hdr = (struct pe32plus_opt_hdr*)parse;
parse += sizeof(struct pe32plus_opt_hdr);
dir = (struct data_directory *)parse;
if (opt_hdr->data_dirs > ((char *)&dir->certs - (char *)dir)/sizeof(struct data_dirent)) {
/* If signed, the Attribute Certificate Table is always at the end of the PE file */
if (dir->certs.virtual_address != 0 && dir->certs.size != 0) {
original_file_sz = dir->certs.virtual_address + dir->certs.size;
ftruncate(fd, 0);
}
}

lseek(fd, 0, SEEK_SET);

if (write(fd, kernel_uncompressed_buf,
decompressed_size) != decompressed_size) {
original_file_sz) != original_file_sz) {
dbgprintf("%s: Can't write the decompressed file %s\n",
__func__, fname);
ret = -1;
Expand All @@ -111,7 +130,7 @@ int pez_prepare(const char *crude_buf, off_t buf_sz, int *kernel_fd,
goto fail_bad_header;
}

*kernel_size = decompressed_size;
*kernel_size = original_file_sz;
dbgprintf("%s: done\n", __func__);

ret = 0;
Expand Down

0 comments on commit 6aecc32

Please sign in to comment.