2013-04-12 20:15:35

by Kees Cook

[permalink] [raw]
Subject: [PATCH v2 0/6] kernel ASLR

This splits up the relocs tool changes into the separate logical pieces,
which should be easier to review. I could use some suggestions on a
better way to build it in the 4th patch. What I have now seems ugly,
but Kbuild would not give me its secrets.

RO IDT was sent separately is is living in the tip/kaslr tree at the
moment. The main logic for KASLR remains split into two pieces: offset
selection and offset usage.

Thanks,

-Kees


2013-04-12 20:14:29

by Kees Cook

[permalink] [raw]
Subject: [PATCH 5/6] x86: kaslr: routines to choose random base offset

This provides routines for selecting a randomized kernel base offset,
bounded by the e820 entries. It tries to use RDRAND and falls back to
RDTSC. If "noaslr" is on the kernel command line, no offset will be used.

Heavily based on work by Dan Rosenberg and Neill Clift.

Signed-off-by: Kees Cook <[email protected]>
Cc: Eric Northup <[email protected]>
---
arch/x86/boot/compressed/Makefile | 2 +-
arch/x86/boot/compressed/aslr.S | 228 +++++++++++++++++++++++++++++++++++++
2 files changed, 229 insertions(+), 1 deletion(-)
create mode 100644 arch/x86/boot/compressed/aslr.S

diff --git a/arch/x86/boot/compressed/Makefile b/arch/x86/boot/compressed/Makefile
index 0dac175..feaf203 100644
--- a/arch/x86/boot/compressed/Makefile
+++ b/arch/x86/boot/compressed/Makefile
@@ -26,7 +26,7 @@ HOST_EXTRACFLAGS += -I$(srctree)/tools/include

VMLINUX_OBJS = $(obj)/vmlinux.lds $(obj)/head_$(BITS).o $(obj)/misc.o \
$(obj)/string.o $(obj)/cmdline.o $(obj)/early_serial_console.o \
- $(obj)/piggy.o
+ $(obj)/piggy.o $(obj)/aslr.o

$(obj)/eboot.o: KBUILD_CFLAGS += -fshort-wchar -mno-red-zone

diff --git a/arch/x86/boot/compressed/aslr.S b/arch/x86/boot/compressed/aslr.S
new file mode 100644
index 0000000..37cdef4
--- /dev/null
+++ b/arch/x86/boot/compressed/aslr.S
@@ -0,0 +1,228 @@
+/*
+ * arch/x86/boot/compressed/aslr.S
+ *
+ * Support routine for Kernel Address Space Layout Randomization used by both
+ * the 32 and 64 bit boot code.
+ *
+ */
+ .text
+
+#include <asm/boot.h>
+#include <asm/asm-offsets.h>
+#include <asm/cpufeature.h>
+#include <asm/processor-flags.h>
+#include <asm/e820.h>
+
+#ifdef CONFIG_RANDOMIZE_BASE
+
+ .globl select_aslr_address
+ .code32
+
+/*
+ * Get the physical memory limit for the run from the physical load position of
+ * the kernel. The kernel loads at LOAD_PHYSICAL_ADDR and we need to know how
+ * much physical memory is available for use after that point to make sure the
+ * relocated kernel will fit. Returns the limit in eax.
+ */
+get_physical_run_end:
+ pushl %edi
+ pushl %esi
+ pushl %ebx
+ pushl %edx
+ pushl %ecx
+ movzbl BP_e820_entries(%esi), %edi
+ leal BP_e820_map(%esi), %esi
+ testl %edi, %edi
+ jz 5f
+1: cmpl $E820_RAM, E820_type(%esi)
+ jnz 4f
+ movl E820_addr(%esi), %eax
+ movl E820_addr+4(%esi), %edx
+ testl %edx, %edx /* Start address is too big for 32 bit */
+ jnz 4f
+ cmpl $LOAD_PHYSICAL_ADDR, %eax
+ ja 4f
+ movl E820_size(%esi), %ecx
+ movl E820_size+4(%esi), %ebx
+ addl %eax, %ecx
+ adcl %edx, %ebx
+ jz 2f /* end address not beyond 32bit*/
+/* For a large run set the limit as 2^32-1 */
+ xorl %ecx, %ecx
+ decl %ecx
+ jmp 3f
+2: cmpl $LOAD_PHYSICAL_ADDR, %ecx
+ jb 4f
+3:
+ movl %ecx, %eax
+ jmp 6f
+
+4: addl $E820_entry_size, %esi
+ decl %edi
+ jnz 1b
+5: xorl %eax, %eax /* Fail */
+6: popl %ecx
+ popl %edx
+ popl %ebx
+ popl %esi
+ popl %edi
+ ret
+
+/*
+ * Get a random value to be used for the ASLR kernel offset.
+ * Returns the value in eax.
+ */
+get_aslr_offset:
+ pushl %ebx
+ pushl %edx
+ pushl %ecx
+ call find_cmdline_option
+ testl %eax, %eax
+ jne 4f
+ /* Standard check for cpuid */
+ pushfl /* Push original flags */
+ pushfl
+ popl %eax
+ movl %eax, %ebx
+ xorl $X86_EFLAGS_ID, %eax
+ pushl %eax
+ popfl
+ pushfl
+ popl %eax
+ popfl /* Pop original flags */
+ cmpl %eax, %ebx
+ /* Say zero offset if we can't change the flag */
+ movl $0, %eax
+ je 4f
+
+ /* Check for cpuid 1 */
+ cpuid
+ cmpl $0x1, %eax
+ jb 4f
+
+ movl $0x1, %eax
+ cpuid
+ xor %eax, %eax
+
+ /* RDRAND is bit 30 */
+ btl $(X86_FEATURE_RDRAND & 31), %ecx
+ jc 1f
+
+ /* RDTSC is bit 4 */
+ btl $(X86_FEATURE_TSC & 31), %edx
+ jc 3f
+
+ /* Nothing is supported */
+ jmp 4f
+1:
+ /*
+ * RDRAND sets carry bit on success, otherwise we should try
+ * again up to 16 times.
+ */
+ movl $0x10, %ecx
+2:
+ /* rdrand %eax */
+ .byte 0x0f, 0xc7, 0xf0
+ jc 4f
+ loop 2b
+
+ /* Fall through: if RDRAND is supported but fails, use RDTSC,
+ * which is guaranteed to be supported.
+ */
+3:
+ rdtsc
+ /*
+ * Since this is time related get some of the least significant bits
+ * past the alignment mask
+ */
+ shll $0x0c, %eax
+ /* Fix the maximal offset allowed */
+4: andl $CONFIG_RANDOMIZE_BASE_MAX_OFFSET-1, %eax
+ popl %ecx
+ popl %edx
+ popl %ebx
+ ret
+
+/*
+ * Select the ASLR address to use. We can get called once either in 32
+ * or 64 bit mode. The latter if we have a 64 bit loader.
+ * Uses ebp as the input base and returns the result in eax.
+ */
+select_aslr_address:
+ pushl %edx
+ pushl %ebx
+ pushl %ecx
+ pushl %edi
+ call get_aslr_offset
+ pushl %eax
+ call get_physical_run_end
+ movl %eax, %edx
+ popl %eax
+1: movl %ebp, %ebx
+ addl %eax, %ebx
+ movl BP_kernel_alignment(%esi), %edi
+ decl %edi
+ addl %edi, %ebx
+ notl %edi
+ andl %edi, %ebx
+ /* Make sure we don't copy beyond run */
+ leal boot_stack_end(%ebx), %ecx
+ leal z_extract_offset(%ecx), %ecx
+ cmpl %edx, %ecx
+ jb 2f
+ shrl $1, %eax /* Shink offset */
+ jne 1b /* Move on if offset zero */
+ mov %ebp, %ebx
+2: movl %ebx, %eax
+ popl %edi
+ popl %ecx
+ popl %ebx
+ popl %edx
+ ret
+
+/*
+ * Find the "noaslr" option if present on the command line.
+ */
+find_cmdline_option:
+
+#define ASLR_STRLEN 6
+
+ pushl %ecx
+ pushl %edi
+ xorl %eax, %eax /* Assume we fail */
+ movl BP_cmd_line_ptr(%esi), %edi
+ testl %edi, %edi
+ je 6f
+ /* Calculate string length */
+ leal -1(%edi), %ecx
+1: incl %ecx
+ cmpb $0, (%ecx)
+ jne 1b
+ subl %edi, %ecx
+2: cmpl $ASLR_STRLEN, %ecx
+ jb 6f
+ cmpl $0x73616f6e, (%edi) /* noas */
+ jne 4f
+ cmpb $0x6c, 4(%edi) /* l */
+ jne 4f
+ cmpb $0x72, 5(%edi) /* r */
+ jne 4f
+ /* If at the start then no beginning separator required */
+ cmpl %edi, BP_cmd_line_ptr(%esi)
+ je 3f
+ cmpb $0x20, -1(%edi)
+ ja 4f
+ /* If at the end then no end separator required */
+3: cmpl $ASLR_STRLEN, %ecx
+ je 5f
+ cmpb $0x20, ASLR_STRLEN(%edi)
+ jbe 5f
+4: incl %edi
+ decl %ecx
+ jmp 2b
+5: incl %eax /* Sucess */
+6: popl %edi
+ popl %ecx
+ ret
+
+#endif /* CONFIG_RANDOMIZE_BASE */
--
1.7.9.5

2013-04-12 20:14:36

by Kees Cook

[permalink] [raw]
Subject: [PATCH 3/6] x86: relocs: add 64-bit ELF support to relocs tool

This adds the ability to process relocations from the 64-bit kernel ELF,
if built with ELF_BITS=64 defined. The special case for the percpu area is
handled, along with some other symbols specific to the 64-bit kernel.

Based on work by Neill Clift and Michael Davidson.

Signed-off-by: Kees Cook <[email protected]>
---
arch/x86/tools/relocs.c | 267 +++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 261 insertions(+), 6 deletions(-)

diff --git a/arch/x86/tools/relocs.c b/arch/x86/tools/relocs.c
index bdc5930..1f7ff3d 100644
--- a/arch/x86/tools/relocs.c
+++ b/arch/x86/tools/relocs.c
@@ -17,19 +17,39 @@
#define _ElfW(bits, type) __ElfW(bits, type)
#define __ElfW(bits, type) Elf##bits##_##type

+#ifndef ELF_BITS
#define ELF_BITS 32
+#endif
+
+#if (ELF_BITS == 64)
+#define ELF_MACHINE EM_X86_64
+#define ELF_MACHINE_NAME "x86_64"
+#define SHT_REL_TYPE SHT_RELA
+#define Elf_Rel Elf64_Rela
+#else
#define ELF_MACHINE EM_386
#define ELF_MACHINE_NAME "i386"
#define SHT_REL_TYPE SHT_REL
+#define Elf_Rel ElfW(Rel)
+#endif

+#if (ELF_BITS == 64)
+#define ELF_CLASS ELFCLASS64
+#define ELF_R_SYM(val) ELF64_R_SYM(val)
+#define ELF_R_TYPE(val) ELF64_R_TYPE(val)
+#define ELF_ST_TYPE(o) ELF64_ST_TYPE(o)
+#define ELF_ST_BIND(o) ELF64_ST_BIND(o)
+#define ELF_ST_VISIBILITY(o) ELF64_ST_VISIBILITY(o)
+#else
#define ELF_CLASS ELFCLASS32
#define ELF_R_SYM(val) ELF32_R_SYM(val)
#define ELF_R_TYPE(val) ELF32_R_TYPE(val)
#define ELF_ST_TYPE(o) ELF32_ST_TYPE(o)
#define ELF_ST_BIND(o) ELF32_ST_BIND(o)
#define ELF_ST_VISIBILITY(o) ELF32_ST_VISIBILITY(o)
+#endif

-#define Elf_Rel ElfW(Rel)
+#define Elf_Addr ElfW(Addr)
#define Elf_Ehdr ElfW(Ehdr)
#define Elf_Phdr ElfW(Phdr)
#define Elf_Shdr ElfW(Shdr)
@@ -48,6 +68,7 @@ struct relocs {

static struct relocs relocs16;
static struct relocs relocs32;
+static struct relocs relocs64;

struct section {
Elf_Shdr shdr;
@@ -77,6 +98,9 @@ static const char * const sym_regex_kernel[S_NSYMTYPES] = {
"^(xen_irq_disable_direct_reloc$|"
"xen_save_fl_direct_reloc$|"
"VDSO|"
+#if (ELF_BITS == 64)
+ "__vvar_page|"
+#endif
"__crc_)",

/*
@@ -100,6 +124,11 @@ static const char * const sym_regex_kernel[S_NSYMTYPES] = {
"__end_rodata|"
"__initramfs_start|"
"(jiffies|jiffies_64)|"
+#if (ELF_BITS == 64)
+ "__per_cpu_load|"
+ "init_per_cpu__.*|"
+ "__end_rodata_hpage_align|"
+#endif
"_end)$"
};

@@ -226,6 +255,24 @@ static const char *rel_type(unsigned type)
{
static const char *type_name[] = {
#define REL_TYPE(X) [X] = #X
+#if (ELF_BITS == 64)
+ REL_TYPE(R_X86_64_NONE),
+ REL_TYPE(R_X86_64_64),
+ REL_TYPE(R_X86_64_PC32),
+ REL_TYPE(R_X86_64_GOT32),
+ REL_TYPE(R_X86_64_PLT32),
+ REL_TYPE(R_X86_64_COPY),
+ REL_TYPE(R_X86_64_GLOB_DAT),
+ REL_TYPE(R_X86_64_JUMP_SLOT),
+ REL_TYPE(R_X86_64_RELATIVE),
+ REL_TYPE(R_X86_64_GOTPCREL),
+ REL_TYPE(R_X86_64_32),
+ REL_TYPE(R_X86_64_32S),
+ REL_TYPE(R_X86_64_16),
+ REL_TYPE(R_X86_64_PC16),
+ REL_TYPE(R_X86_64_8),
+ REL_TYPE(R_X86_64_PC8),
+#else
REL_TYPE(R_386_NONE),
REL_TYPE(R_386_32),
REL_TYPE(R_386_PC32),
@@ -241,6 +288,7 @@ static const char *rel_type(unsigned type)
REL_TYPE(R_386_PC8),
REL_TYPE(R_386_16),
REL_TYPE(R_386_PC16),
+#endif
#undef REL_TYPE
};
const char *name = "unknown type rel type name";
@@ -281,15 +329,42 @@ static const char *sym_name(const char *sym_strtab, Elf_Sym *sym)
return name;
}

+static Elf_Sym *sym_lookup(const char *symname)
+{
+ int i;
+ for (i = 0; i < ehdr.e_shnum; i++) {
+ struct section *sec = &secs[i];
+ long nsyms;
+ char *strtab;
+ Elf_Sym *symtab;
+ Elf_Sym *sym;

+ if (sec->shdr.sh_type != SHT_SYMTAB)
+ continue;
+
+ nsyms = sec->shdr.sh_size/sizeof(Elf_Sym);
+ symtab = sec->symtab;
+ strtab = sec->link->strtab;
+
+ for (sym = symtab; --nsyms >= 0; sym++) {
+ if (!sym->st_name)
+ continue;
+ if (strcmp(symname, strtab + sym->st_name) == 0)
+ return sym;
+ }
+ }
+ return 0;
+}

#if BYTE_ORDER == LITTLE_ENDIAN
#define le16_to_cpu(val) (val)
#define le32_to_cpu(val) (val)
+#define le64_to_cpu(val) (val)
#endif
#if BYTE_ORDER == BIG_ENDIAN
#define le16_to_cpu(val) bswap_16(val)
#define le32_to_cpu(val) bswap_32(val)
+#define le64_to_cpu(val) bswap_64(val)
#endif

static uint16_t elf16_to_cpu(uint16_t val)
@@ -304,9 +379,20 @@ static uint32_t elf32_to_cpu(uint32_t val)

#define elf_half_to_cpu(x) elf16_to_cpu(x)
#define elf_word_to_cpu(x) elf32_to_cpu(x)
+
+#if (ELF_BITS == 64)
+static uint64_t elf64_to_cpu(uint64_t val)
+{
+ return le64_to_cpu(val);
+}
+#define elf_addr_to_cpu(x) elf64_to_cpu(x)
+#define elf_off_to_cpu(x) elf64_to_cpu(x)
+#define elf_xword_to_cpu(x) elf64_to_cpu(x)
+#else
#define elf_addr_to_cpu(x) elf32_to_cpu(x)
#define elf_off_to_cpu(x) elf32_to_cpu(x)
#define elf_xword_to_cpu(x) elf32_to_cpu(x)
+#endif

static void read_ehdr(FILE *fp)
{
@@ -483,6 +569,9 @@ static void read_relocs(FILE *fp)
Elf_Rel *rel = &sec->reltab[j];
rel->r_offset = elf_addr_to_cpu(rel->r_offset);
rel->r_info = elf_xword_to_cpu(rel->r_info);
+#if (SHT_REL_TYPE == SHT_RELA)
+ rel->r_addend = elf_xword_to_cpu(rel->r_addend);
+#endif
}
}
}
@@ -491,6 +580,13 @@ static void read_relocs(FILE *fp)
static void print_absolute_symbols(void)
{
int i;
+ const char *format;
+
+ if (ehdr.e_ident[EI_CLASS] == ELFCLASS64)
+ format = "%5d %016"PRIx64" %5"PRId64" %10s %10s %12s %s\n";
+ else
+ format = "%5d %08"PRIx32" %5"PRId32" %10s %10s %12s %s\n";
+
printf("Absolute symbols\n");
printf(" Num: Value Size Type Bind Visibility Name\n");
for (i = 0; i < ehdr.e_shnum; i++) {
@@ -510,7 +606,7 @@ static void print_absolute_symbols(void)
if (sym->st_shndx != SHN_ABS) {
continue;
}
- printf("%5d %08x %5d %10s %10s %12s %s\n",
+ printf(format,
j, sym->st_value, sym->st_size,
sym_type(ELF_ST_TYPE(sym->st_info)),
sym_bind(ELF_ST_BIND(sym->st_info)),
@@ -524,6 +620,12 @@ static void print_absolute_symbols(void)
static void print_absolute_relocs(void)
{
int i, printed = 0;
+ const char *format;
+
+ if (ehdr.e_ident[EI_CLASS] == ELFCLASS64)
+ format = "%016"PRIx64" %016"PRIx64" %10s %016"PRIx64" %s\n";
+ else
+ format = "%08"PRIx32" %08"PRIx32" %10s %08"PRIx32" %s\n";

for (i = 0; i < ehdr.e_shnum; i++) {
struct section *sec = &secs[i];
@@ -576,7 +678,7 @@ static void print_absolute_relocs(void)
printed = 1;
}

- printf("%08x %08x %10s %08x %s\n",
+ printf(format,
rel->r_offset,
rel->r_info,
rel_type(ELF_R_TYPE(rel->r_info)),
@@ -636,8 +738,140 @@ static void walk_relocs(int (*process)(struct section *sec, Elf_Rel *rel,
}
}

-static int do_reloc(struct section *sec, Elf_Rel *rel, Elf_Sym *sym,
- const char *symname)
+/*
+ * The .data..percpu section is a special case for x86_64 SMP kernels.
+ * It is used to initialize the actual per_cpu areas and to provide
+ * definitions for the per_cpu variables that correspond to their offsets
+ * within the percpu area. Since the values of all of the symbols need
+ * to be offsets from the start of the per_cpu area the virtual address
+ * (sh_addr) of .data..percpu is 0 in SMP kernels.
+ *
+ * This means that:
+ *
+ * Relocations that reference symbols in the per_cpu area do not
+ * need further relocation (since the value is an offset relative
+ * to the start of the per_cpu area that does not change).
+ *
+ * Relocations that apply to the per_cpu area need to have their
+ * offset adjusted by by the value of __per_cpu_load to make them
+ * point to the correct place in the loaded image (because the
+ * virtual address of .data..percpu is 0).
+ *
+ * For non SMP kernels .data..percpu is linked as part of the normal
+ * kernel data and does not require special treatment.
+ *
+ */
+static int per_cpu_shndx = -1;
+Elf_Addr per_cpu_load_addr;
+
+static void percpu_init(void)
+{
+ int i;
+ for (i = 0; i < ehdr.e_shnum; i++) {
+ ElfW(Sym) *sym;
+ if (strcmp(sec_name(i), ".data..percpu"))
+ continue;
+
+ if (secs[i].shdr.sh_addr != 0) /* non SMP kernel */
+ return;
+
+ sym = sym_lookup("__per_cpu_load");
+ if (!sym)
+ die("can't find __per_cpu_load\n");
+
+ per_cpu_shndx = i;
+ per_cpu_load_addr = sym->st_value;
+ return;
+ }
+}
+
+/*
+ * Check to see if a symbol lies in the .data..percpu section.
+ * For some as yet not understood reason the "__init_begin"
+ * symbol which immediately preceeds the .data..percpu section
+ * also shows up as it it were part of it so we do an explict
+ * check for that symbol name and ignore it.
+ */
+static int is_percpu_sym(ElfW(Sym) *sym, const char *symname)
+{
+ return (sym->st_shndx == per_cpu_shndx) &&
+ strcmp(symname, "__init_begin");
+}
+
+static int do_reloc64(struct section *sec, Elf_Rel *rel, ElfW(Sym) *sym,
+ const char *symname)
+{
+ unsigned r_type = ELF64_R_TYPE(rel->r_info);
+ ElfW(Addr) offset = rel->r_offset;
+ int shn_abs = (sym->st_shndx == SHN_ABS) && !is_reloc(S_REL, symname);
+
+ if (sym->st_shndx == SHN_UNDEF)
+ return 0;
+
+ /*
+ * Adjust the offset if this reloc applies to the percpu section.
+ */
+ if (sec->shdr.sh_info == per_cpu_shndx)
+ offset += per_cpu_load_addr;
+
+ switch (r_type) {
+ case R_X86_64_NONE:
+ case R_X86_64_PC32:
+ /*
+ * NONE can be ignored and PC relative relocations don't
+ * need to be adjusted.
+ */
+ break;
+
+ case R_X86_64_32:
+ case R_X86_64_32S:
+ case R_X86_64_64:
+ /*
+ * References to the percpu area don't need to be adjusted.
+ */
+ if (is_percpu_sym(sym, symname))
+ break;
+
+ if (shn_abs) {
+ /*
+ * Whitelisted absolute symbols do not require
+ * relocation.
+ */
+ if (is_reloc(S_ABS, symname))
+ break;
+
+ die("Invalid absolute %s relocation: %s\n",
+ rel_type(r_type), symname);
+ break;
+ }
+
+ /*
+ * Relocation offsets for 64 bit kernels are output
+ * as 32 bits and sign extended back to 64 bits when
+ * the relocations are processed.
+ * Make sure that the offset will fit.
+ */
+ if ((int32_t)offset != (int64_t)offset)
+ die("Relocation offset doesn't fit in 32 bits\n");
+
+ if (r_type == R_X86_64_64)
+ add_reloc(&relocs64, offset);
+ else
+ add_reloc(&relocs32, offset);
+ break;
+
+ default:
+ die("Unsupported relocation type: %s (%d)\n",
+ rel_type(r_type), r_type);
+ break;
+ }
+
+ return 0;
+}
+
+
+static int do_reloc32(struct section *sec, Elf_Rel *rel, Elf_Sym *sym,
+ const char *symname)
{
unsigned r_type = ELF32_R_TYPE(rel->r_info);
int shn_abs = (sym->st_shndx == SHN_ABS) && !is_reloc(S_REL, symname);
@@ -779,9 +1013,18 @@ static void emit_relocs(int as_text, int use_real_mode)
{
int i;
int (*write_reloc)(uint32_t, FILE *) = write32;
+ int (*do_reloc)(struct section *sec, Elf_Rel *rel, Elf_Sym *sym,
+ const char *symname);
+
+ if (ehdr.e_ident[EI_CLASS] == ELFCLASS64)
+ do_reloc = do_reloc64;
+ else if (!use_real_mode)
+ do_reloc = do_reloc32;
+ else
+ do_reloc = do_reloc_real;

/* Collect up the relocations */
- walk_relocs(use_real_mode ? do_reloc_real : do_reloc);
+ walk_relocs(do_reloc);

if (relocs16.count && !use_real_mode)
die("Segment relocations found but --realmode not specified\n");
@@ -789,6 +1032,7 @@ static void emit_relocs(int as_text, int use_real_mode)
/* Order the relocations for more efficient processing */
sort_relocs(&relocs16);
sort_relocs(&relocs32);
+ sort_relocs(&relocs64);

/* Print the relocations */
if (as_text) {
@@ -809,6 +1053,15 @@ static void emit_relocs(int as_text, int use_real_mode)
for (i = 0; i < relocs32.count; i++)
write_reloc(relocs32.offset[i], stdout);
} else {
+ if (ehdr.e_ident[EI_CLASS] == ELFCLASS64) {
+ /* Print a stop */
+ write_reloc(0, stdout);
+
+ /* Now print each relocation */
+ for (i = 0; i < relocs64.count; i++)
+ write_reloc(relocs64.offset[i], stdout);
+ }
+
/* Print a stop */
write_reloc(0, stdout);

@@ -876,6 +1129,8 @@ int main(int argc, char **argv)
read_strtabs(fp);
read_symtabs(fp);
read_relocs(fp);
+ if (ehdr.e_ident[EI_CLASS] == ELFCLASS64)
+ percpu_init();
if (show_absolute_syms) {
print_absolute_symbols();
goto out;
--
1.7.9.5

2013-04-12 20:14:35

by Kees Cook

[permalink] [raw]
Subject: [PATCH 2/6] x86: relocs: consolidate processing logic

Instead of counting and then processing relocations, do it in a single
pass. This splits the processing logic into separate functions for
realmode and 32-bit (and paves the way for 64-bit). Also extracts helper
functions when emitting relocations.

Based on work by Neill Clift and Michael Davidson.

Signed-off-by: Kees Cook <[email protected]>
---
arch/x86/tools/relocs.c | 304 ++++++++++++++++++++++++++---------------------
1 file changed, 170 insertions(+), 134 deletions(-)

diff --git a/arch/x86/tools/relocs.c b/arch/x86/tools/relocs.c
index fd28ef7..bdc5930 100644
--- a/arch/x86/tools/relocs.c
+++ b/arch/x86/tools/relocs.c
@@ -2,6 +2,7 @@
#include <stdarg.h>
#include <stdlib.h>
#include <stdint.h>
+#include <inttypes.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
@@ -38,10 +39,15 @@ static void die(char *fmt, ...);

#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
static Elf_Ehdr ehdr;
-static unsigned long reloc_count, reloc_idx;
-static unsigned long *relocs;
-static unsigned long reloc16_count, reloc16_idx;
-static unsigned long *relocs16;
+
+struct relocs {
+ uint32_t *offset;
+ unsigned long count;
+ unsigned long size;
+};
+
+static struct relocs relocs16;
+static struct relocs relocs32;

struct section {
Elf_Shdr shdr;
@@ -583,8 +589,23 @@ static void print_absolute_relocs(void)
printf("\n");
}

-static void walk_relocs(void (*visit)(Elf_Rel *rel, Elf_Sym *sym),
- int use_real_mode)
+static void add_reloc(struct relocs *r, uint32_t offset)
+{
+ if (r->count == r->size) {
+ unsigned long newsize = r->size + 50000;
+ void *mem = realloc(r->offset, newsize * sizeof(r->offset[0]));
+
+ if (!mem)
+ die("realloc of %ld entries for relocs failed\n",
+ newsize);
+ r->offset = mem;
+ r->size = newsize;
+ }
+ r->offset[r->count++] = offset;
+}
+
+static void walk_relocs(int (*process)(struct section *sec, Elf_Rel *rel,
+ Elf_Sym *sym, const char *symname))
{
int i;
/* Walk through the relocations */
@@ -606,100 +627,142 @@ static void walk_relocs(void (*visit)(Elf_Rel *rel, Elf_Sym *sym),
sh_symtab = sec_symtab->symtab;
sym_strtab = sec_symtab->link->strtab;
for (j = 0; j < sec->shdr.sh_size/sizeof(Elf_Rel); j++) {
- Elf_Rel *rel;
- Elf_Sym *sym;
- unsigned r_type;
- const char *symname;
- int shn_abs;
+ Elf_Rel *rel = &sec->reltab[j];
+ Elf_Sym *sym = &sh_symtab[ELF_R_SYM(rel->r_info)];
+ const char *symname = sym_name(sym_strtab, sym);

- rel = &sec->reltab[j];
- sym = &sh_symtab[ELF_R_SYM(rel->r_info)];
- r_type = ELF_R_TYPE(rel->r_info);
-
- shn_abs = sym->st_shndx == SHN_ABS;
-
- switch (r_type) {
- case R_386_NONE:
- case R_386_PC32:
- case R_386_PC16:
- case R_386_PC8:
- /*
- * NONE can be ignored and and PC relative
- * relocations don't need to be adjusted.
- */
+ process(sec, rel, sym, symname);
+ }
+ }
+}
+
+static int do_reloc(struct section *sec, Elf_Rel *rel, Elf_Sym *sym,
+ const char *symname)
+{
+ unsigned r_type = ELF32_R_TYPE(rel->r_info);
+ int shn_abs = (sym->st_shndx == SHN_ABS) && !is_reloc(S_REL, symname);
+
+ switch (r_type) {
+ case R_386_NONE:
+ case R_386_PC32:
+ case R_386_PC16:
+ case R_386_PC8:
+ /*
+ * NONE can be ignored and PC relative relocations don't
+ * need to be adjusted.
+ */
+ break;
+
+ case R_386_32:
+ if (shn_abs) {
+ /*
+ * Whitelisted absolute symbols do not require
+ * relocation.
+ */
+ if (is_reloc(S_ABS, symname))
break;

- case R_386_16:
- symname = sym_name(sym_strtab, sym);
- if (!use_real_mode)
- goto bad;
- if (shn_abs) {
- if (is_reloc(S_ABS, symname))
- break;
- else if (!is_reloc(S_SEG, symname))
- goto bad;
- } else {
- if (is_reloc(S_LIN, symname))
- goto bad;
- else
- break;
- }
- visit(rel, sym);
+ die("Invalid absolute %s relocation: %s\n",
+ rel_type(r_type), symname);
+ break;
+ }
+
+ add_reloc(&relocs32, rel->r_offset);
+ break;
+
+ default:
+ die("Unsupported relocation type: %s (%d)\n",
+ rel_type(r_type), r_type);
+ break;
+ }
+
+ return 0;
+}
+
+static int do_reloc_real(struct section *sec, Elf_Rel *rel, Elf_Sym *sym,
+ const char *symname)
+{
+ unsigned r_type = ELF32_R_TYPE(rel->r_info);
+ int shn_abs = (sym->st_shndx == SHN_ABS) && !is_reloc(S_REL, symname);
+
+ switch (r_type) {
+ case R_386_NONE:
+ case R_386_PC32:
+ case R_386_PC16:
+ case R_386_PC8:
+ /*
+ * NONE can be ignored and PC relative relocations don't
+ * need to be adjusted.
+ */
+ break;
+
+ case R_386_16:
+ if (shn_abs) {
+ /*
+ * Whitelisted absolute symbols do not require
+ * relocation.
+ */
+ if (is_reloc(S_ABS, symname))
break;

- case R_386_32:
- symname = sym_name(sym_strtab, sym);
- if (shn_abs) {
- if (is_reloc(S_ABS, symname))
- break;
- else if (!is_reloc(S_REL, symname))
- goto bad;
- } else {
- if (use_real_mode &&
- !is_reloc(S_LIN, symname))
- break;
- }
- visit(rel, sym);
+ if (is_reloc(S_SEG, symname)) {
+ add_reloc(&relocs16, rel->r_offset);
+ break;
+ }
+ } else {
+ if (!is_reloc(S_LIN, symname))
break;
- default:
- die("Unsupported relocation type: %s (%d)\n",
- rel_type(r_type), r_type);
+ }
+ die("Invalid %s %s relocation: %s\n",
+ shn_abs ? "absolute" : "relative",
+ rel_type(r_type), symname);
+ break;
+
+ case R_386_32:
+ if (shn_abs) {
+ /*
+ * Whitelisted absolute symbols do not require
+ * relocation.
+ */
+ if (is_reloc(S_ABS, symname))
+ break;
+
+ if (is_reloc(S_REL, symname)) {
+ add_reloc(&relocs32, rel->r_offset);
break;
- bad:
- symname = sym_name(sym_strtab, sym);
- die("Invalid %s %s relocation: %s\n",
- shn_abs ? "absolute" : "relative",
- rel_type(r_type), symname);
}
+ } else {
+ if (is_reloc(S_LIN, symname))
+ add_reloc(&relocs32, rel->r_offset);
+ break;
}
- }
-}
+ die("Invalid %s %s relocation: %s\n",
+ shn_abs ? "absolute" : "relative",
+ rel_type(r_type), symname);
+ break;

-static void count_reloc(Elf_Rel *rel, Elf_Sym *sym)
-{
- if (ELF_R_TYPE(rel->r_info) == R_386_16)
- reloc16_count++;
- else
- reloc_count++;
-}
+ default:
+ die("Unsupported relocation type: %s (%d)\n",
+ rel_type(r_type), r_type);
+ break;
+ }

-static void collect_reloc(Elf_Rel *rel, Elf_Sym *sym)
-{
- /* Remember the address that needs to be adjusted. */
- if (ELF_R_TYPE(rel->r_info) == R_386_16)
- relocs16[reloc16_idx++] = rel->r_offset;
- else
- relocs[reloc_idx++] = rel->r_offset;
+ return 0;
}

static int cmp_relocs(const void *va, const void *vb)
{
- const unsigned long *a, *b;
+ const uint32_t *a, *b;
a = va; b = vb;
return (*a == *b)? 0 : (*a > *b)? 1 : -1;
}

-static int write32(unsigned int v, FILE *f)
+static void sort_relocs(struct relocs *r)
+{
+ qsort(r->offset, r->count, sizeof(r->offset[0]), cmp_relocs);
+}
+
+static int write32(uint32_t v, FILE *f)
{
unsigned char buf[4];

@@ -707,33 +770,25 @@ static int write32(unsigned int v, FILE *f)
return fwrite(buf, 1, 4, f) == 4 ? 0 : -1;
}

+static int write32_as_text(uint32_t v, FILE *f)
+{
+ return fprintf(f, "\t.long 0x%08"PRIx32"\n", v) > 0 ? 0 : -1;
+}
+
static void emit_relocs(int as_text, int use_real_mode)
{
int i;
- /* Count how many relocations I have and allocate space for them. */
- reloc_count = 0;
- walk_relocs(count_reloc, use_real_mode);
- relocs = malloc(reloc_count * sizeof(relocs[0]));
- if (!relocs) {
- die("malloc of %d entries for relocs failed\n",
- reloc_count);
- }
+ int (*write_reloc)(uint32_t, FILE *) = write32;

- relocs16 = malloc(reloc16_count * sizeof(relocs[0]));
- if (!relocs16) {
- die("malloc of %d entries for relocs16 failed\n",
- reloc16_count);
- }
/* Collect up the relocations */
- reloc_idx = 0;
- walk_relocs(collect_reloc, use_real_mode);
+ walk_relocs(use_real_mode ? do_reloc_real : do_reloc);

- if (reloc16_count && !use_real_mode)
+ if (relocs16.count && !use_real_mode)
die("Segment relocations found but --realmode not specified\n");

/* Order the relocations for more efficient processing */
- qsort(relocs, reloc_count, sizeof(relocs[0]), cmp_relocs);
- qsort(relocs16, reloc16_count, sizeof(relocs16[0]), cmp_relocs);
+ sort_relocs(&relocs16);
+ sort_relocs(&relocs32);

/* Print the relocations */
if (as_text) {
@@ -742,43 +797,24 @@ static void emit_relocs(int as_text, int use_real_mode)
*/
printf(".section \".data.reloc\",\"a\"\n");
printf(".balign 4\n");
- if (use_real_mode) {
- printf("\t.long %lu\n", reloc16_count);
- for (i = 0; i < reloc16_count; i++)
- printf("\t.long 0x%08lx\n", relocs16[i]);
- printf("\t.long %lu\n", reloc_count);
- for (i = 0; i < reloc_count; i++) {
- printf("\t.long 0x%08lx\n", relocs[i]);
- }
- } else {
- /* Print a stop */
- printf("\t.long 0x%08lx\n", (unsigned long)0);
- for (i = 0; i < reloc_count; i++) {
- printf("\t.long 0x%08lx\n", relocs[i]);
- }
- }
-
- printf("\n");
+ write_reloc = write32_as_text;
}
- else {
- if (use_real_mode) {
- write32(reloc16_count, stdout);
- for (i = 0; i < reloc16_count; i++)
- write32(relocs16[i], stdout);
- write32(reloc_count, stdout);
-
- /* Now print each relocation */
- for (i = 0; i < reloc_count; i++)
- write32(relocs[i], stdout);
- } else {
- /* Print a stop */
- write32(0, stdout);

- /* Now print each relocation */
- for (i = 0; i < reloc_count; i++) {
- write32(relocs[i], stdout);
- }
- }
+ if (use_real_mode) {
+ write_reloc(relocs16.count, stdout);
+ for (i = 0; i < relocs16.count; i++)
+ write_reloc(relocs16.offset[i], stdout);
+
+ write_reloc(relocs32.count, stdout);
+ for (i = 0; i < relocs32.count; i++)
+ write_reloc(relocs32.offset[i], stdout);
+ } else {
+ /* Print a stop */
+ write_reloc(0, stdout);
+
+ /* Now print each relocation */
+ for (i = 0; i < relocs32.count; i++)
+ write_reloc(relocs32.offset[i], stdout);
}
}

--
1.7.9.5

2013-04-12 20:14:34

by Kees Cook

[permalink] [raw]
Subject: [PATCH 1/6] x86: relocs: generalize Elf structure names

In preparation for making the reloc tool operate on 64-bit relocations,
generalize the structure names for easy recompilation via #defines.

Based on work by Neill Clift and Michael Davidson.

Signed-off-by: Kees Cook <[email protected]>
---
arch/x86/tools/relocs.c | 170 +++++++++++++++++++++++++++--------------------
1 file changed, 99 insertions(+), 71 deletions(-)

diff --git a/arch/x86/tools/relocs.c b/arch/x86/tools/relocs.c
index 79d67bd..fd28ef7 100644
--- a/arch/x86/tools/relocs.c
+++ b/arch/x86/tools/relocs.c
@@ -12,20 +12,42 @@
#include <regex.h>
#include <tools/le_byteshift.h>

+#define ElfW(type) _ElfW(ELF_BITS, type)
+#define _ElfW(bits, type) __ElfW(bits, type)
+#define __ElfW(bits, type) Elf##bits##_##type
+
+#define ELF_BITS 32
+#define ELF_MACHINE EM_386
+#define ELF_MACHINE_NAME "i386"
+#define SHT_REL_TYPE SHT_REL
+
+#define ELF_CLASS ELFCLASS32
+#define ELF_R_SYM(val) ELF32_R_SYM(val)
+#define ELF_R_TYPE(val) ELF32_R_TYPE(val)
+#define ELF_ST_TYPE(o) ELF32_ST_TYPE(o)
+#define ELF_ST_BIND(o) ELF32_ST_BIND(o)
+#define ELF_ST_VISIBILITY(o) ELF32_ST_VISIBILITY(o)
+
+#define Elf_Rel ElfW(Rel)
+#define Elf_Ehdr ElfW(Ehdr)
+#define Elf_Phdr ElfW(Phdr)
+#define Elf_Shdr ElfW(Shdr)
+#define Elf_Sym ElfW(Sym)
+
static void die(char *fmt, ...);

#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
-static Elf32_Ehdr ehdr;
+static Elf_Ehdr ehdr;
static unsigned long reloc_count, reloc_idx;
static unsigned long *relocs;
static unsigned long reloc16_count, reloc16_idx;
static unsigned long *relocs16;

struct section {
- Elf32_Shdr shdr;
+ Elf_Shdr shdr;
struct section *link;
- Elf32_Sym *symtab;
- Elf32_Rel *reltab;
+ Elf_Sym *symtab;
+ Elf_Rel *reltab;
char *strtab;
};
static struct section *secs;
@@ -240,7 +262,7 @@ static const char *sec_name(unsigned shndx)
return name;
}

-static const char *sym_name(const char *sym_strtab, Elf32_Sym *sym)
+static const char *sym_name(const char *sym_strtab, Elf_Sym *sym)
{
const char *name;
name = "<noname>";
@@ -274,6 +296,12 @@ static uint32_t elf32_to_cpu(uint32_t val)
return le32_to_cpu(val);
}

+#define elf_half_to_cpu(x) elf16_to_cpu(x)
+#define elf_word_to_cpu(x) elf32_to_cpu(x)
+#define elf_addr_to_cpu(x) elf32_to_cpu(x)
+#define elf_off_to_cpu(x) elf32_to_cpu(x)
+#define elf_xword_to_cpu(x) elf32_to_cpu(x)
+
static void read_ehdr(FILE *fp)
{
if (fread(&ehdr, sizeof(ehdr), 1, fp) != 1) {
@@ -283,8 +311,8 @@ static void read_ehdr(FILE *fp)
if (memcmp(ehdr.e_ident, ELFMAG, SELFMAG) != 0) {
die("No ELF magic\n");
}
- if (ehdr.e_ident[EI_CLASS] != ELFCLASS32) {
- die("Not a 32 bit executable\n");
+ if (ehdr.e_ident[EI_CLASS] != ELF_CLASS) {
+ die("Not a %d bit executable\n", ELF_BITS);
}
if (ehdr.e_ident[EI_DATA] != ELFDATA2LSB) {
die("Not a LSB ELF executable\n");
@@ -293,36 +321,36 @@ static void read_ehdr(FILE *fp)
die("Unknown ELF version\n");
}
/* Convert the fields to native endian */
- ehdr.e_type = elf16_to_cpu(ehdr.e_type);
- ehdr.e_machine = elf16_to_cpu(ehdr.e_machine);
- ehdr.e_version = elf32_to_cpu(ehdr.e_version);
- ehdr.e_entry = elf32_to_cpu(ehdr.e_entry);
- ehdr.e_phoff = elf32_to_cpu(ehdr.e_phoff);
- ehdr.e_shoff = elf32_to_cpu(ehdr.e_shoff);
- ehdr.e_flags = elf32_to_cpu(ehdr.e_flags);
- ehdr.e_ehsize = elf16_to_cpu(ehdr.e_ehsize);
- ehdr.e_phentsize = elf16_to_cpu(ehdr.e_phentsize);
- ehdr.e_phnum = elf16_to_cpu(ehdr.e_phnum);
- ehdr.e_shentsize = elf16_to_cpu(ehdr.e_shentsize);
- ehdr.e_shnum = elf16_to_cpu(ehdr.e_shnum);
- ehdr.e_shstrndx = elf16_to_cpu(ehdr.e_shstrndx);
+ ehdr.e_type = elf_half_to_cpu(ehdr.e_type);
+ ehdr.e_machine = elf_half_to_cpu(ehdr.e_machine);
+ ehdr.e_version = elf_word_to_cpu(ehdr.e_version);
+ ehdr.e_entry = elf_addr_to_cpu(ehdr.e_entry);
+ ehdr.e_phoff = elf_off_to_cpu(ehdr.e_phoff);
+ ehdr.e_shoff = elf_off_to_cpu(ehdr.e_shoff);
+ ehdr.e_flags = elf_word_to_cpu(ehdr.e_flags);
+ ehdr.e_ehsize = elf_half_to_cpu(ehdr.e_ehsize);
+ ehdr.e_phentsize = elf_half_to_cpu(ehdr.e_phentsize);
+ ehdr.e_phnum = elf_half_to_cpu(ehdr.e_phnum);
+ ehdr.e_shentsize = elf_half_to_cpu(ehdr.e_shentsize);
+ ehdr.e_shnum = elf_half_to_cpu(ehdr.e_shnum);
+ ehdr.e_shstrndx = elf_half_to_cpu(ehdr.e_shstrndx);

if ((ehdr.e_type != ET_EXEC) && (ehdr.e_type != ET_DYN)) {
die("Unsupported ELF header type\n");
}
- if (ehdr.e_machine != EM_386) {
- die("Not for x86\n");
+ if (ehdr.e_machine != ELF_MACHINE) {
+ die("Not for %s\n", ELF_MACHINE_NAME);
}
if (ehdr.e_version != EV_CURRENT) {
die("Unknown ELF version\n");
}
- if (ehdr.e_ehsize != sizeof(Elf32_Ehdr)) {
+ if (ehdr.e_ehsize != sizeof(Elf_Ehdr)) {
die("Bad Elf header size\n");
}
- if (ehdr.e_phentsize != sizeof(Elf32_Phdr)) {
+ if (ehdr.e_phentsize != sizeof(Elf_Phdr)) {
die("Bad program header entry\n");
}
- if (ehdr.e_shentsize != sizeof(Elf32_Shdr)) {
+ if (ehdr.e_shentsize != sizeof(Elf_Shdr)) {
die("Bad section header entry\n");
}
if (ehdr.e_shstrndx >= ehdr.e_shnum) {
@@ -333,7 +361,7 @@ static void read_ehdr(FILE *fp)
static void read_shdrs(FILE *fp)
{
int i;
- Elf32_Shdr shdr;
+ Elf_Shdr shdr;

secs = calloc(ehdr.e_shnum, sizeof(struct section));
if (!secs) {
@@ -349,16 +377,16 @@ static void read_shdrs(FILE *fp)
if (fread(&shdr, sizeof shdr, 1, fp) != 1)
die("Cannot read ELF section headers %d/%d: %s\n",
i, ehdr.e_shnum, strerror(errno));
- sec->shdr.sh_name = elf32_to_cpu(shdr.sh_name);
- sec->shdr.sh_type = elf32_to_cpu(shdr.sh_type);
- sec->shdr.sh_flags = elf32_to_cpu(shdr.sh_flags);
- sec->shdr.sh_addr = elf32_to_cpu(shdr.sh_addr);
- sec->shdr.sh_offset = elf32_to_cpu(shdr.sh_offset);
- sec->shdr.sh_size = elf32_to_cpu(shdr.sh_size);
- sec->shdr.sh_link = elf32_to_cpu(shdr.sh_link);
- sec->shdr.sh_info = elf32_to_cpu(shdr.sh_info);
- sec->shdr.sh_addralign = elf32_to_cpu(shdr.sh_addralign);
- sec->shdr.sh_entsize = elf32_to_cpu(shdr.sh_entsize);
+ sec->shdr.sh_name = elf_word_to_cpu(shdr.sh_name);
+ sec->shdr.sh_type = elf_word_to_cpu(shdr.sh_type);
+ sec->shdr.sh_flags = elf_xword_to_cpu(shdr.sh_flags);
+ sec->shdr.sh_addr = elf_addr_to_cpu(shdr.sh_addr);
+ sec->shdr.sh_offset = elf_off_to_cpu(shdr.sh_offset);
+ sec->shdr.sh_size = elf_xword_to_cpu(shdr.sh_size);
+ sec->shdr.sh_link = elf_word_to_cpu(shdr.sh_link);
+ sec->shdr.sh_info = elf_word_to_cpu(shdr.sh_info);
+ sec->shdr.sh_addralign = elf_xword_to_cpu(shdr.sh_addralign);
+ sec->shdr.sh_entsize = elf_xword_to_cpu(shdr.sh_entsize);
if (sec->shdr.sh_link < ehdr.e_shnum)
sec->link = &secs[sec->shdr.sh_link];
}
@@ -412,12 +440,12 @@ static void read_symtabs(FILE *fp)
die("Cannot read symbol table: %s\n",
strerror(errno));
}
- for (j = 0; j < sec->shdr.sh_size/sizeof(Elf32_Sym); j++) {
- Elf32_Sym *sym = &sec->symtab[j];
- sym->st_name = elf32_to_cpu(sym->st_name);
- sym->st_value = elf32_to_cpu(sym->st_value);
- sym->st_size = elf32_to_cpu(sym->st_size);
- sym->st_shndx = elf16_to_cpu(sym->st_shndx);
+ for (j = 0; j < sec->shdr.sh_size/sizeof(Elf_Sym); j++) {
+ Elf_Sym *sym = &sec->symtab[j];
+ sym->st_name = elf_word_to_cpu(sym->st_name);
+ sym->st_value = elf_addr_to_cpu(sym->st_value);
+ sym->st_size = elf_xword_to_cpu(sym->st_size);
+ sym->st_shndx = elf_half_to_cpu(sym->st_shndx);
}
}
}
@@ -428,7 +456,7 @@ static void read_relocs(FILE *fp)
int i,j;
for (i = 0; i < ehdr.e_shnum; i++) {
struct section *sec = &secs[i];
- if (sec->shdr.sh_type != SHT_REL) {
+ if (sec->shdr.sh_type != SHT_REL_TYPE) {
continue;
}
sec->reltab = malloc(sec->shdr.sh_size);
@@ -445,10 +473,10 @@ static void read_relocs(FILE *fp)
die("Cannot read symbol table: %s\n",
strerror(errno));
}
- for (j = 0; j < sec->shdr.sh_size/sizeof(Elf32_Rel); j++) {
- Elf32_Rel *rel = &sec->reltab[j];
- rel->r_offset = elf32_to_cpu(rel->r_offset);
- rel->r_info = elf32_to_cpu(rel->r_info);
+ for (j = 0; j < sec->shdr.sh_size/sizeof(Elf_Rel); j++) {
+ Elf_Rel *rel = &sec->reltab[j];
+ rel->r_offset = elf_addr_to_cpu(rel->r_offset);
+ rel->r_info = elf_xword_to_cpu(rel->r_info);
}
}
}
@@ -468,8 +496,8 @@ static void print_absolute_symbols(void)
continue;
}
sym_strtab = sec->link->strtab;
- for (j = 0; j < sec->shdr.sh_size/sizeof(Elf32_Sym); j++) {
- Elf32_Sym *sym;
+ for (j = 0; j < sec->shdr.sh_size/sizeof(Elf_Sym); j++) {
+ Elf_Sym *sym;
const char *name;
sym = &sec->symtab[j];
name = sym_name(sym_strtab, sym);
@@ -478,9 +506,9 @@ static void print_absolute_symbols(void)
}
printf("%5d %08x %5d %10s %10s %12s %s\n",
j, sym->st_value, sym->st_size,
- sym_type(ELF32_ST_TYPE(sym->st_info)),
- sym_bind(ELF32_ST_BIND(sym->st_info)),
- sym_visibility(ELF32_ST_VISIBILITY(sym->st_other)),
+ sym_type(ELF_ST_TYPE(sym->st_info)),
+ sym_bind(ELF_ST_BIND(sym->st_info)),
+ sym_visibility(ELF_ST_VISIBILITY(sym->st_other)),
name);
}
}
@@ -495,9 +523,9 @@ static void print_absolute_relocs(void)
struct section *sec = &secs[i];
struct section *sec_applies, *sec_symtab;
char *sym_strtab;
- Elf32_Sym *sh_symtab;
+ Elf_Sym *sh_symtab;
int j;
- if (sec->shdr.sh_type != SHT_REL) {
+ if (sec->shdr.sh_type != SHT_REL_TYPE) {
continue;
}
sec_symtab = sec->link;
@@ -507,12 +535,12 @@ static void print_absolute_relocs(void)
}
sh_symtab = sec_symtab->symtab;
sym_strtab = sec_symtab->link->strtab;
- for (j = 0; j < sec->shdr.sh_size/sizeof(Elf32_Rel); j++) {
- Elf32_Rel *rel;
- Elf32_Sym *sym;
+ for (j = 0; j < sec->shdr.sh_size/sizeof(Elf_Rel); j++) {
+ Elf_Rel *rel;
+ Elf_Sym *sym;
const char *name;
rel = &sec->reltab[j];
- sym = &sh_symtab[ELF32_R_SYM(rel->r_info)];
+ sym = &sh_symtab[ELF_R_SYM(rel->r_info)];
name = sym_name(sym_strtab, sym);
if (sym->st_shndx != SHN_ABS) {
continue;
@@ -545,7 +573,7 @@ static void print_absolute_relocs(void)
printf("%08x %08x %10s %08x %s\n",
rel->r_offset,
rel->r_info,
- rel_type(ELF32_R_TYPE(rel->r_info)),
+ rel_type(ELF_R_TYPE(rel->r_info)),
sym->st_value,
name);
}
@@ -555,19 +583,19 @@ static void print_absolute_relocs(void)
printf("\n");
}

-static void walk_relocs(void (*visit)(Elf32_Rel *rel, Elf32_Sym *sym),
+static void walk_relocs(void (*visit)(Elf_Rel *rel, Elf_Sym *sym),
int use_real_mode)
{
int i;
/* Walk through the relocations */
for (i = 0; i < ehdr.e_shnum; i++) {
char *sym_strtab;
- Elf32_Sym *sh_symtab;
+ Elf_Sym *sh_symtab;
struct section *sec_applies, *sec_symtab;
int j;
struct section *sec = &secs[i];

- if (sec->shdr.sh_type != SHT_REL) {
+ if (sec->shdr.sh_type != SHT_REL_TYPE) {
continue;
}
sec_symtab = sec->link;
@@ -577,16 +605,16 @@ static void walk_relocs(void (*visit)(Elf32_Rel *rel, Elf32_Sym *sym),
}
sh_symtab = sec_symtab->symtab;
sym_strtab = sec_symtab->link->strtab;
- for (j = 0; j < sec->shdr.sh_size/sizeof(Elf32_Rel); j++) {
- Elf32_Rel *rel;
- Elf32_Sym *sym;
+ for (j = 0; j < sec->shdr.sh_size/sizeof(Elf_Rel); j++) {
+ Elf_Rel *rel;
+ Elf_Sym *sym;
unsigned r_type;
const char *symname;
int shn_abs;

rel = &sec->reltab[j];
- sym = &sh_symtab[ELF32_R_SYM(rel->r_info)];
- r_type = ELF32_R_TYPE(rel->r_info);
+ sym = &sh_symtab[ELF_R_SYM(rel->r_info)];
+ r_type = ELF_R_TYPE(rel->r_info);

shn_abs = sym->st_shndx == SHN_ABS;

@@ -647,18 +675,18 @@ static void walk_relocs(void (*visit)(Elf32_Rel *rel, Elf32_Sym *sym),
}
}

-static void count_reloc(Elf32_Rel *rel, Elf32_Sym *sym)
+static void count_reloc(Elf_Rel *rel, Elf_Sym *sym)
{
- if (ELF32_R_TYPE(rel->r_info) == R_386_16)
+ if (ELF_R_TYPE(rel->r_info) == R_386_16)
reloc16_count++;
else
reloc_count++;
}

-static void collect_reloc(Elf32_Rel *rel, Elf32_Sym *sym)
+static void collect_reloc(Elf_Rel *rel, Elf_Sym *sym)
{
/* Remember the address that needs to be adjusted. */
- if (ELF32_R_TYPE(rel->r_info) == R_386_16)
+ if (ELF_R_TYPE(rel->r_info) == R_386_16)
relocs16[reloc16_idx++] = rel->r_offset;
else
relocs[reloc_idx++] = rel->r_offset;
--
1.7.9.5

2013-04-12 20:15:37

by Kees Cook

[permalink] [raw]
Subject: [PATCH 6/6] x86: kaslr: relocate base offset at boot

This creates CONFIG_RANDOMIZE_BASE, so that the base offset of the kernel
can be randomized at boot.

This makes kernel vulnerabilities harder to reliably exploit, especially
from remote attacks and local processes in seccomp containers. Keeping the
location of kernel addresses secret becomes very important when using this
feature, so enabling kptr_restrict and dmesg_restrict is recommended.
Besides direct address leaks, several other attacks are possible to bypass
this on local systems, including cache timing[1]. However, the benefits of
this feature in certain environments (e.g. remote services, heavily
confined local processes) exceed the perceived weaknesses[2].

Current entropy is low, since the kernel has basically a minimum 2MB
alignment and has been built with -2G memory addressing. As a result,
available entropy will be 8 bits in the best case. The e820 entries on a
given system may further limit the available memory. This is still be
enough for attacks that must guess the base address to fail 99% of the
time.

This feature is presently incompatible with hibernation.

When built into the kernel, the "noaslr" kernel command line option will
disable the feature.

Heavily based on work by Dan Rosenberg[3] and Neill Clift.

[1] http://www.internetsociety.org/sites/default/files/Practical%20Timing%20Side%20Channel%20Attacks%20Against%20Kernel%20Space%20ASLR.pdf
[2] http://forums.grsecurity.net/viewtopic.php?f=7&t=3367
[3] http://lkml.indiana.edu/hypermail/linux/kernel/1105.3/index.html#00520

Signed-off-by: Kees Cook <[email protected]>
Cc: Eric Northup <[email protected]>
---
Documentation/kernel-parameters.txt | 4 +
arch/x86/Kconfig | 51 +++++++++++--
arch/x86/Makefile | 3 +
arch/x86/boot/compressed/head_32.S | 20 ++++-
arch/x86/boot/compressed/head_64.S | 135 ++++++++++++++++++++++++++++++++--
arch/x86/include/asm/page_32_types.h | 2 +
arch/x86/include/asm/page_64_types.h | 4 -
arch/x86/include/asm/page_types.h | 4 +
arch/x86/kernel/asm-offsets.c | 14 ++++
arch/x86/kernel/setup.c | 24 ++++++
10 files changed, 240 insertions(+), 21 deletions(-)

diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt
index 4609e81..e1b8993 100644
--- a/Documentation/kernel-parameters.txt
+++ b/Documentation/kernel-parameters.txt
@@ -1839,6 +1839,10 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
noapic [SMP,APIC] Tells the kernel to not make use of any
IOAPICs that may be present in the system.

+ noaslr [X86]
+ Disable kernel base offset ASLR (Address Space
+ Layout Randomization) if built into the kernel.
+
noautogroup Disable scheduler automatic task group creation.

nobats [PPC] Do not use BATs for mapping kernel lowmem
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index 70c0f3d..6fe1a3b 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -1649,8 +1649,8 @@ config PHYSICAL_START
If kernel is a not relocatable (CONFIG_RELOCATABLE=n) then
bzImage will decompress itself to above physical address and
run from there. Otherwise, bzImage will run from the address where
- it has been loaded by the boot loader and will ignore above physical
- address.
+ it has been loaded by the boot loader, using the above physical
+ address as a lower bound.

In normal kdump cases one does not have to set/change this option
as now bzImage can be compiled as a completely relocatable image
@@ -1696,15 +1696,49 @@ config RELOCATABLE

Note: If CONFIG_RELOCATABLE=y, then the kernel runs from the address
it has been loaded at and the compile time physical address
- (CONFIG_PHYSICAL_START) is ignored.
-
-# Relocation on x86-32 needs some additional build support
+ (CONFIG_PHYSICAL_START) is solely used as a lower bound.
+
+config RANDOMIZE_BASE
+ bool "Randomize the address of the kernel image"
+ depends on RELOCATABLE
+ depends on !HIBERNATION
+ default n
+ ---help---
+ Randomizes the phyiscal and virtual address at which the
+ kernel image is decompressed, as a security feature that
+ deters exploit attempts relying on knowledge of the location
+ of kernel internals.
+
+ This feature also uses a fixed mapping to move the IDT
+ (if not already done as a fix for the F00F bug), to avoid
+ exposing the location of kernel internals relative to the
+ original IDT. This has the additional security benefit of
+ marking the new virtual address of the IDT read-only.
+
+ Entropy is generated using the RDRAND instruction if it
+ is supported. If not, then RDTSC is used, if supported. If
+ neither RDRAND nor RDTSC are supported, then no randomness
+ is introduced. Support for the CPUID instruction is required
+ to check for the availability of these two instructions.
+
+config RANDOMIZE_BASE_MAX_OFFSET
+ hex "Maximum ASLR offset allowed"
+ depends on RANDOMIZE_BASE
+ default "0x10000000"
+ range 0x0 0x10000000
+ ---help---
+ Determines the maximal offset in bytes that will be applied to the
+ kernel when Address Space Layout Randomization (ASLR) is active.
+ Physical memory layout and kernel size may limit this further.
+ This must be a power of two.
+
+# Relocation on x86-32/64 needs some additional build support
config X86_NEED_RELOCS
def_bool y
- depends on X86_32 && RELOCATABLE
+ depends on RELOCATABLE

config PHYSICAL_ALIGN
- hex "Alignment value to which kernel should be aligned" if X86_32
+ hex "Alignment value to which kernel should be aligned"
default "0x1000000"
range 0x2000 0x1000000
---help---
@@ -1724,6 +1758,9 @@ config PHYSICAL_ALIGN
end result is that kernel runs from a physical address meeting
above alignment restrictions.

+ Generally when using CONFIG_RANDOMIZE_BASE, this is safe to
+ lower to 0x200000.
+
Don't change this unless you know what you are doing.

config HOTPLUG_CPU
diff --git a/arch/x86/Makefile b/arch/x86/Makefile
index 5c47726..4f280bd 100644
--- a/arch/x86/Makefile
+++ b/arch/x86/Makefile
@@ -60,6 +60,9 @@ else
# Use -mpreferred-stack-boundary=3 if supported.
KBUILD_CFLAGS += $(call cc-option,-mno-sse -mpreferred-stack-boundary=3)

+ ifdef CONFIG_RANDOMIZE_BASE
+ LDFLAGS_vmlinux := --emit-relocs
+ endif
# FIXME - should be integrated in Makefile.cpu (Makefile_32.cpu)
cflags-$(CONFIG_MK8) += $(call cc-option,-march=k8)
cflags-$(CONFIG_MPSC) += $(call cc-option,-march=nocona)
diff --git a/arch/x86/boot/compressed/head_32.S b/arch/x86/boot/compressed/head_32.S
index 1e3184f..957b1c7 100644
--- a/arch/x86/boot/compressed/head_32.S
+++ b/arch/x86/boot/compressed/head_32.S
@@ -29,6 +29,7 @@
#include <asm/page_types.h>
#include <asm/boot.h>
#include <asm/asm-offsets.h>
+#include <asm/cpufeature.h>

__HEAD
ENTRY(startup_32)
@@ -111,15 +112,29 @@ preferred_addr:
*/

#ifdef CONFIG_RELOCATABLE
+#ifdef CONFIG_RANDOMIZE_BASE
+ /* Setup boot stack for calls */
+ leal boot_stack_end(%ebp), %esp
+ call select_aslr_address /* Select ASLR address */
+ movl %eax, %ebx
+ /* LOAD_PHSYICAL_ADDR is the minimum safe address we can
+ * decompress at */
+ cmpl $LOAD_PHYSICAL_ADDR, %ebx
+ jae 1f
+ movl $LOAD_PHYSICAL_ADDR, %ebx
+1:
+#else /* CONFIG_RANDOMIZE_BASE */
movl %ebp, %ebx
movl BP_kernel_alignment(%esi), %eax
decl %eax
addl %eax, %ebx
notl %eax
andl %eax, %ebx
-#else
+#endif /* CONFIG_RANDOMIZE_BASE */
+
+#else /* CONFIG_RELOCATABLE */
movl $LOAD_PHYSICAL_ADDR, %ebx
-#endif
+#endif /* CONFIG_RELOCATABLE */

/* Target address to relocate to for decompression */
addl $z_extract_offset, %ebx
@@ -235,3 +250,4 @@ boot_heap:
boot_stack:
.fill BOOT_STACK_SIZE, 1, 0
boot_stack_end:
+ .globl boot_stack_end
diff --git a/arch/x86/boot/compressed/head_64.S b/arch/x86/boot/compressed/head_64.S
index c1d383d..fc37910 100644
--- a/arch/x86/boot/compressed/head_64.S
+++ b/arch/x86/boot/compressed/head_64.S
@@ -59,7 +59,7 @@ ENTRY(startup_32)
1:

/*
- * Calculate the delta between where we were compiled to run
+ * Calculate the delta between where we were linked to load
* at and where we were actually loaded at. This can only be done
* with a short local call on x86. Nothing else will tell us what
* address we are running at. The reserved chunk of the real-mode
@@ -78,10 +78,10 @@ ENTRY(startup_32)

call verify_cpu
testl %eax, %eax
- jnz no_longmode
+ jnz hang

/*
- * Compute the delta between where we were compiled to run at
+ * Compute the delta between where we were linked to load at
* and where the code will actually run at.
*
* %ebp contains the address we are loaded at by the boot loader and %ebx
@@ -90,15 +90,32 @@ ENTRY(startup_32)
*/

#ifdef CONFIG_RELOCATABLE
+#ifdef CONFIG_RANDOMIZE_BASE
+ call select_aslr_address /* Select ASLR offset */
+ movl %eax, %ebx
+ /* LOAD_PHYSICAL_ADDR is the minimum safe address we can
+ * decompress at */
+ cmpl $LOAD_PHYSICAL_ADDR, %ebx
+ jae 1f
+ movl $LOAD_PHYSICAL_ADDR, %ebx
+#else /* CONFIG_RANDOMIZE_BASE */
movl %ebp, %ebx
movl BP_kernel_alignment(%esi), %eax
decl %eax
addl %eax, %ebx
notl %eax
andl %eax, %ebx
-#else
+#endif /* CONFIG_RANDOMIZE_BASE */
+
+#ifdef CONFIG_RANDOMIZE_BASE
+1: movl %ebx, %eax
+ subl $LOAD_PHYSICAL_ADDR, %eax
+ movl %eax, aslr_offset(%ebp)
+ incl aslr_in_32bit(%ebp) /* say 32 bit code ran */
+#endif /* CONFIG_RANDOMIZE_BASE */
+#else /* CONFIG_RELOCATABLE */
movl $LOAD_PHYSICAL_ADDR, %ebx
-#endif
+#endif /* CONFIG_RELOCATABLE */

/* Target address to relocate to for decompression */
addl $z_extract_offset, %ebx
@@ -266,14 +283,30 @@ preferred_addr:
/* Start with the delta to where the kernel will run at. */
#ifdef CONFIG_RELOCATABLE
leaq startup_32(%rip) /* - $startup_32 */, %rbp
+#ifdef CONFIG_RANDOMIZE_BASE
+ leaq boot_stack_end(%rip), %rsp
+ testl $1, aslr_in_32bit(%rip)
+ jne 1f
+ call select_aslr_address
+ movq %rax, %rbp
+ jmp 2f
+1: movl aslr_offset(%rip), %eax
+ addq %rax, %rbp
+ /* LOAD_PHYSICAL_ADDR is the minimum safe address we can
+ * decompress at. */
+ cmpq $LOAD_PHYSICAL_ADDR, %rbp
+ jae 2f
+ movq $LOAD_PHYSICAL_ADDR, %rbp
+2:
+#endif /* CONFIG_RANDOMIZE_BASE */
movl BP_kernel_alignment(%rsi), %eax
decl %eax
addq %rax, %rbp
notq %rax
andq %rax, %rbp
-#else
+#else /* CONFIG_RELOCATABLE */
movq $LOAD_PHYSICAL_ADDR, %rbp
-#endif
+#endif /* CONFIG_RELOCATABLE */

/* Target address to relocate to for decompression */
leaq z_extract_offset(%rbp), %rbx
@@ -343,13 +376,85 @@ relocated:
call decompress_kernel
popq %rsi

+#ifdef CONFIG_RANDOMIZE_BASE
+/*
+ * Find the address of the relocations.
+ */
+ leaq z_output_len(%rbp), %rdi
+
+/*
+ * Calculate the delta between where vmlinux was linked to load
+ * and where it was actually loaded.
+ */
+ movq %rbp, %rbx
+ subq $LOAD_PHYSICAL_ADDR, %rbx
+ je 3f /* Nothing to be done if loaded at linked addr. */
+/*
+ * The kernel contains a table of relocation addresses. Those addresses
+ * have the final load address of the kernel in virtual memory.
+ * We are currently working in the self map. So we need to create an
+ * adjustment for kernel memory addresses to the self map. This will
+ * involve subtracting out the base address of the kernel.
+ */
+ movq $-__START_KERNEL_map, %rdx /* Literal is too big for add etc */
+ addq %rbx, %rdx
+/*
+ * Process relocations. 32 bit relocations first then 64 bit after.
+ * Two sets of binary relocations are added to the end of the
+ * kernel before compression. Each relocation table entry is the kernel
+ * address of the location which needs to be updated stored as a 32 bit
+ * value which is sign extended to 64 bits.
+ *
+ * Format is:
+ *
+ * kernel bits...
+ * 0 - zero terminator for 64 bit relocations
+ * 64 bit relocation repeated
+ * 0 - zero terminator for 32 bit relocations
+ * 32 bit relocation repeated
+ *
+ * So we work backwards from the end of the decompressed image.
+ */
+1: subq $4, %rdi
+ movslq (%rdi), %rcx
+ testq %rcx, %rcx
+ je 2f
+ addq %rdx, %rcx
+/*
+ * Relocation can't be before the image or
+ * after the current position of the current relocation.
+ * This is a cheap bounds check. It could be more exact
+ * and limit to the end of the image prior to the relocations
+ * but allowing relocations themselves to be fixed up will not
+ * do any harm.
+ */
+ cmpq %rbp, %rcx
+ jb hang
+ cmpq %rdi, %rcx
+ jae hang
+ addl %ebx, (%rcx) /* 32 bit relocation */
+ jmp 1b
+2: subq $4, %rdi
+ movslq (%rdi), %rcx
+ testq %rcx, %rcx
+ je 3f
+ addq %rdx, %rcx
+ cmpq %rbp, %rcx
+ jb hang
+ cmpq %rdi, %rcx
+ jae hang
+ addq %rbx, (%rcx) /* 64 bit relocation */
+ jmp 2b
+3:
+#endif /* CONFIG_RANDOMIZE_BASE */
+
/*
* Jump to the decompressed kernel.
*/
jmp *%rbp

.code32
-no_longmode:
+hang:
/* This isn't an x86-64 CPU so hang */
1:
hlt
@@ -369,6 +474,19 @@ gdt:
.quad 0x0000000000000000 /* TS continued */
gdt_end:

+#ifdef CONFIG_RANDOMIZE_BASE
+aslr_offset:
+ .long 0 /* Offset selected for ASLR */
+/*
+ * Set if ASLR ran in 32 bit mode. For 64 bit loaders the 32 bit code
+ * doesn't run and we need to do the offset calculation there for the
+ * first time.
+ */
+aslr_in_32bit:
+ .long 0
+
+#endif /* CONFIG_RANDOMIZE_BASE */
+
/*
* Stack and heap for uncompression
*/
@@ -379,6 +497,7 @@ boot_heap:
boot_stack:
.fill BOOT_STACK_SIZE, 1, 0
boot_stack_end:
+ .globl boot_stack_end

/*
* Space for page tables (not in .bss so not zeroed)
diff --git a/arch/x86/include/asm/page_32_types.h b/arch/x86/include/asm/page_32_types.h
index ef17af0..996582c 100644
--- a/arch/x86/include/asm/page_32_types.h
+++ b/arch/x86/include/asm/page_32_types.h
@@ -15,6 +15,8 @@
*/
#define __PAGE_OFFSET _AC(CONFIG_PAGE_OFFSET, UL)

+#define __START_KERNEL (__PAGE_OFFSET + __PHYSICAL_START)
+
#define THREAD_SIZE_ORDER 1
#define THREAD_SIZE (PAGE_SIZE << THREAD_SIZE_ORDER)

diff --git a/arch/x86/include/asm/page_64_types.h b/arch/x86/include/asm/page_64_types.h
index 8b491e6..c0dfe38 100644
--- a/arch/x86/include/asm/page_64_types.h
+++ b/arch/x86/include/asm/page_64_types.h
@@ -32,10 +32,6 @@
*/
#define __PAGE_OFFSET _AC(0xffff880000000000, UL)

-#define __PHYSICAL_START ((CONFIG_PHYSICAL_START + \
- (CONFIG_PHYSICAL_ALIGN - 1)) & \
- ~(CONFIG_PHYSICAL_ALIGN - 1))
-
#define __START_KERNEL (__START_KERNEL_map + __PHYSICAL_START)
#define __START_KERNEL_map _AC(0xffffffff80000000, UL)

diff --git a/arch/x86/include/asm/page_types.h b/arch/x86/include/asm/page_types.h
index 54c9787..b6f9b49 100644
--- a/arch/x86/include/asm/page_types.h
+++ b/arch/x86/include/asm/page_types.h
@@ -33,6 +33,10 @@
(((current->personality & READ_IMPLIES_EXEC) ? VM_EXEC : 0 ) | \
VM_READ | VM_WRITE | VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC)

+#define __PHYSICAL_START ((CONFIG_PHYSICAL_START + \
+ (CONFIG_PHYSICAL_ALIGN - 1)) & \
+ ~(CONFIG_PHYSICAL_ALIGN - 1))
+
#ifdef CONFIG_X86_64
#include <asm/page_64_types.h>
#else
diff --git a/arch/x86/kernel/asm-offsets.c b/arch/x86/kernel/asm-offsets.c
index 2861082..7e014b7 100644
--- a/arch/x86/kernel/asm-offsets.c
+++ b/arch/x86/kernel/asm-offsets.c
@@ -70,6 +70,20 @@ void common(void) {
OFFSET(BP_pref_address, boot_params, hdr.pref_address);
OFFSET(BP_code32_start, boot_params, hdr.code32_start);

+ OFFSET(BP_scratch, boot_params, scratch);
+ OFFSET(BP_loadflags, boot_params, hdr.loadflags);
+ OFFSET(BP_hardware_subarch, boot_params, hdr.hardware_subarch);
+ OFFSET(BP_version, boot_params, hdr.version);
+ OFFSET(BP_kernel_alignment, boot_params, hdr.kernel_alignment);
+ OFFSET(BP_e820_map, boot_params, e820_map);
+ OFFSET(BP_e820_entries, boot_params, e820_entries);
+ OFFSET(BP_cmd_line_ptr, boot_params, hdr.cmd_line_ptr);
+
+ OFFSET(E820_addr, e820entry, addr);
+ OFFSET(E820_size, e820entry, size);
+ OFFSET(E820_type, e820entry, type);
+ DEFINE(E820_entry_size, sizeof(struct e820entry));
+
BLANK();
DEFINE(PTREGS_SIZE, sizeof(struct pt_regs));
}
diff --git a/arch/x86/kernel/setup.c b/arch/x86/kernel/setup.c
index 90d8cc9..fd9e68f 100644
--- a/arch/x86/kernel/setup.c
+++ b/arch/x86/kernel/setup.c
@@ -801,6 +801,18 @@ static void __init trim_low_memory_range(void)
}

/*
+ * Dump out kernel offset information on panic.
+ */
+static int
+dump_kernel_offset(struct notifier_block *self, unsigned long v, void *p)
+{
+ pr_emerg("Kernel Offset: 0x%lx\n",
+ (unsigned long)&_text - __START_KERNEL);
+
+ return 0;
+}
+
+/*
* Determine if we were loaded by an EFI loader. If so, then we have also been
* passed the efi memmap, systab, etc., so we should use these data structures
* for initialization. Note, the efi init code path is determined by the
@@ -1220,3 +1232,15 @@ void __init i386_reserve_resources(void)
}

#endif /* CONFIG_X86_32 */
+
+static struct notifier_block kernel_offset_notifier = {
+ .notifier_call = dump_kernel_offset
+};
+
+static int __init register_kernel_offset_dumper(void)
+{
+ atomic_notifier_chain_register(&panic_notifier_list,
+ &kernel_offset_notifier);
+ return 0;
+}
+__initcall(register_kernel_offset_dumper);
--
1.7.9.5

2013-04-12 20:14:27

by Kees Cook

[permalink] [raw]
Subject: [PATCH 4/6] x86: relocs: build separate 32/64-bit tools

Since the ELF structures and access macros change size based on 32 vs
64 bits, build a separate 32-bit relocs tool (for handling realmode
and 32-bit relocations), and a 64-bit relocs tool (for handling 64-bit
kernel relocations).

Signed-off-by: Kees Cook <[email protected]>
--
This is ugly with the "cp". Is there some other cleaner way to trigger
two builds with different defines from the same source file?
---
arch/x86/boot/compressed/Makefile | 2 +-
arch/x86/realmode/rm/Makefile | 2 +-
arch/x86/tools/.gitignore | 3 ++-
arch/x86/tools/Makefile | 20 ++++++++++++++++++--
4 files changed, 22 insertions(+), 5 deletions(-)

diff --git a/arch/x86/boot/compressed/Makefile b/arch/x86/boot/compressed/Makefile
index 5ef205c..0dac175 100644
--- a/arch/x86/boot/compressed/Makefile
+++ b/arch/x86/boot/compressed/Makefile
@@ -44,7 +44,7 @@ $(obj)/vmlinux.bin: vmlinux FORCE

targets += $(patsubst $(obj)/%,%,$(VMLINUX_OBJS)) vmlinux.bin.all vmlinux.relocs

-CMD_RELOCS = arch/x86/tools/relocs
+CMD_RELOCS = arch/x86/tools/relocs_$(BITS)
quiet_cmd_relocs = RELOCS $@
cmd_relocs = $(CMD_RELOCS) $< > $@;$(CMD_RELOCS) --abs-relocs $<
$(obj)/vmlinux.relocs: vmlinux FORCE
diff --git a/arch/x86/realmode/rm/Makefile b/arch/x86/realmode/rm/Makefile
index 8869287..2b1e429 100644
--- a/arch/x86/realmode/rm/Makefile
+++ b/arch/x86/realmode/rm/Makefile
@@ -56,7 +56,7 @@ $(obj)/realmode.bin: $(obj)/realmode.elf $(obj)/realmode.relocs
$(call if_changed,objcopy)

quiet_cmd_relocs = RELOCS $@
- cmd_relocs = arch/x86/tools/relocs --realmode $< > $@
+ cmd_relocs = arch/x86/tools/relocs_32 --realmode $< > $@

targets += realmode.relocs
$(obj)/realmode.relocs: $(obj)/realmode.elf FORCE
diff --git a/arch/x86/tools/.gitignore b/arch/x86/tools/.gitignore
index be0ed06..2b45d5f 100644
--- a/arch/x86/tools/.gitignore
+++ b/arch/x86/tools/.gitignore
@@ -1 +1,2 @@
-relocs
+relocs_32*
+relocs_64*
diff --git a/arch/x86/tools/Makefile b/arch/x86/tools/Makefile
index bae601f..a8cb70c 100644
--- a/arch/x86/tools/Makefile
+++ b/arch/x86/tools/Makefile
@@ -37,6 +37,22 @@ $(obj)/test_get_len.o: $(srctree)/arch/x86/lib/insn.c $(srctree)/arch/x86/lib/in

$(obj)/insn_sanity.o: $(srctree)/arch/x86/lib/insn.c $(srctree)/arch/x86/lib/inat.c $(srctree)/arch/x86/include/asm/inat_types.h $(srctree)/arch/x86/include/asm/inat.h $(srctree)/arch/x86/include/asm/insn.h $(objtree)/arch/x86/lib/inat-tables.c

+HOSTCFLAGS_relocs_32.o += -DELF_BITS=32
+HOSTCFLAGS_relocs_64.o += -DELF_BITS=64
+
+quiet_cmd_cp_reloc = GEN $@
+ cmd_cp_reloc = cp $< $@
+
+$(obj)/relocs_%.c: $(srctree)/arch/x86/tools/relocs.c
+ $(call cmd,cp_reloc)
+
HOST_EXTRACFLAGS += -I$(srctree)/tools/include
-hostprogs-y += relocs
-relocs: $(obj)/relocs
+hostprogs-y += relocs_$(BITS)
+relocs_binaries = relocs_$(BITS)
+ifeq ($(CONFIG_64BIT),y)
+ hostprogs-y += relocs_32
+ relocs_binaries += relocs_32
+endif
+relocs: $(relocs_binaries)
+relocs_32: $(obj)/relocs_32
+relocs_64: $(obj)/relocs_64
--
1.7.9.5

2013-04-14 00:11:58

by Yinghai Lu

[permalink] [raw]
Subject: Re: [PATCH 5/6] x86: kaslr: routines to choose random base offset

On Fri, Apr 12, 2013 at 1:13 PM, Kees Cook <[email protected]> wrote:
> This provides routines for selecting a randomized kernel base offset,
> bounded by the e820 entries. It tries to use RDRAND and falls back to
> RDTSC. If "noaslr" is on the kernel command line, no offset will be used.
>
> Heavily based on work by Dan Rosenberg and Neill Clift.
>
> Signed-off-by: Kees Cook <[email protected]>
> Cc: Eric Northup <[email protected]>
> ---
> arch/x86/boot/compressed/Makefile | 2 +-
> arch/x86/boot/compressed/aslr.S | 228 +++++++++++++++++++++++++++++++++++++
> 2 files changed, 229 insertions(+), 1 deletion(-)
> create mode 100644 arch/x86/boot/compressed/aslr.S
>
> diff --git a/arch/x86/boot/compressed/Makefile b/arch/x86/boot/compressed/Makefile
> index 0dac175..feaf203 100644
> --- a/arch/x86/boot/compressed/Makefile
> +++ b/arch/x86/boot/compressed/Makefile
> @@ -26,7 +26,7 @@ HOST_EXTRACFLAGS += -I$(srctree)/tools/include
>
> VMLINUX_OBJS = $(obj)/vmlinux.lds $(obj)/head_$(BITS).o $(obj)/misc.o \
> $(obj)/string.o $(obj)/cmdline.o $(obj)/early_serial_console.o \
> - $(obj)/piggy.o
> + $(obj)/piggy.o $(obj)/aslr.o
>
> $(obj)/eboot.o: KBUILD_CFLAGS += -fshort-wchar -mno-red-zone
>
> diff --git a/arch/x86/boot/compressed/aslr.S b/arch/x86/boot/compressed/aslr.S
> new file mode 100644
> index 0000000..37cdef4
> --- /dev/null
> +++ b/arch/x86/boot/compressed/aslr.S
> @@ -0,0 +1,228 @@
> +/*
> + * arch/x86/boot/compressed/aslr.S
> + *
> + * Support routine for Kernel Address Space Layout Randomization used by both
> + * the 32 and 64 bit boot code.
> + *
> + */
> + .text
> +
> +#include <asm/boot.h>
> +#include <asm/asm-offsets.h>
> +#include <asm/cpufeature.h>
> +#include <asm/processor-flags.h>
> +#include <asm/e820.h>
> +
> +#ifdef CONFIG_RANDOMIZE_BASE
> +
> + .globl select_aslr_address
> + .code32
> +
> +/*
> + * Get the physical memory limit for the run from the physical load position of
> + * the kernel. The kernel loads at LOAD_PHYSICAL_ADDR and we need to know how
> + * much physical memory is available for use after that point to make sure the
> + * relocated kernel will fit. Returns the limit in eax.
> + */
> +get_physical_run_end:
> + pushl %edi
> + pushl %esi
> + pushl %ebx
> + pushl %edx
> + pushl %ecx
> + movzbl BP_e820_entries(%esi), %edi
> + leal BP_e820_map(%esi), %esi
> + testl %edi, %edi
> + jz 5f
> +1: cmpl $E820_RAM, E820_type(%esi)
> + jnz 4f
> + movl E820_addr(%esi), %eax
> + movl E820_addr+4(%esi), %edx
> + testl %edx, %edx /* Start address is too big for 32 bit */
> + jnz 4f
> + cmpl $LOAD_PHYSICAL_ADDR, %eax
> + ja 4f
> + movl E820_size(%esi), %ecx
> + movl E820_size+4(%esi), %ebx
> + addl %eax, %ecx
> + adcl %edx, %ebx
> + jz 2f /* end address not beyond 32bit*/
> +/* For a large run set the limit as 2^32-1 */
> + xorl %ecx, %ecx
> + decl %ecx
> + jmp 3f
> +2: cmpl $LOAD_PHYSICAL_ADDR, %ecx
> + jb 4f
> +3:
> + movl %ecx, %eax
> + jmp 6f
> +
> +4: addl $E820_entry_size, %esi
> + decl %edi
> + jnz 1b
> +5: xorl %eax, %eax /* Fail */
> +6: popl %ecx
> + popl %edx
> + popl %ebx
> + popl %esi
> + popl %edi
> + ret
> +
> +/*
> + * Get a random value to be used for the ASLR kernel offset.
> + * Returns the value in eax.
> + */
> +get_aslr_offset:
> + pushl %ebx
> + pushl %edx
> + pushl %ecx
> + call find_cmdline_option
> + testl %eax, %eax
> + jne 4f
> + /* Standard check for cpuid */
> + pushfl /* Push original flags */
> + pushfl
> + popl %eax
> + movl %eax, %ebx
> + xorl $X86_EFLAGS_ID, %eax
> + pushl %eax
> + popfl
> + pushfl
> + popl %eax
> + popfl /* Pop original flags */
> + cmpl %eax, %ebx
> + /* Say zero offset if we can't change the flag */
> + movl $0, %eax
> + je 4f
> +
> + /* Check for cpuid 1 */
> + cpuid
> + cmpl $0x1, %eax
> + jb 4f
> +
> + movl $0x1, %eax
> + cpuid
> + xor %eax, %eax
> +
> + /* RDRAND is bit 30 */
> + btl $(X86_FEATURE_RDRAND & 31), %ecx
> + jc 1f
> +
> + /* RDTSC is bit 4 */
> + btl $(X86_FEATURE_TSC & 31), %edx
> + jc 3f
> +
> + /* Nothing is supported */
> + jmp 4f
> +1:
> + /*
> + * RDRAND sets carry bit on success, otherwise we should try
> + * again up to 16 times.
> + */
> + movl $0x10, %ecx
> +2:
> + /* rdrand %eax */
> + .byte 0x0f, 0xc7, 0xf0
> + jc 4f
> + loop 2b
> +
> + /* Fall through: if RDRAND is supported but fails, use RDTSC,
> + * which is guaranteed to be supported.
> + */
> +3:
> + rdtsc
> + /*
> + * Since this is time related get some of the least significant bits
> + * past the alignment mask
> + */
> + shll $0x0c, %eax
> + /* Fix the maximal offset allowed */
> +4: andl $CONFIG_RANDOMIZE_BASE_MAX_OFFSET-1, %eax
> + popl %ecx
> + popl %edx
> + popl %ebx
> + ret
> +
> +/*
> + * Select the ASLR address to use. We can get called once either in 32
> + * or 64 bit mode. The latter if we have a 64 bit loader.
> + * Uses ebp as the input base and returns the result in eax.
> + */
> +select_aslr_address:
> + pushl %edx
> + pushl %ebx
> + pushl %ecx
> + pushl %edi
> + call get_aslr_offset
> + pushl %eax
> + call get_physical_run_end
> + movl %eax, %edx
> + popl %eax
> +1: movl %ebp, %ebx
> + addl %eax, %ebx
> + movl BP_kernel_alignment(%esi), %edi
> + decl %edi
> + addl %edi, %ebx
> + notl %edi
> + andl %edi, %ebx
> + /* Make sure we don't copy beyond run */
> + leal boot_stack_end(%ebx), %ecx
> + leal z_extract_offset(%ecx), %ecx
> + cmpl %edx, %ecx
> + jb 2f
> + shrl $1, %eax /* Shink offset */
> + jne 1b /* Move on if offset zero */
> + mov %ebp, %ebx
> +2: movl %ebx, %eax
> + popl %edi
> + popl %ecx
> + popl %ebx
> + popl %edx
> + ret

So the code could run when it is 64bit and bzImage64 is loaded above 4G?

Thanks

Yinghai

2013-04-14 00:37:22

by Yinghai Lu

[permalink] [raw]
Subject: Re: [PATCH 6/6] x86: kaslr: relocate base offset at boot

On Fri, Apr 12, 2013 at 1:13 PM, Kees Cook <[email protected]> wrote:
[...]
> diff --git a/arch/x86/boot/compressed/head_64.S b/arch/x86/boot/compressed/head_64.S
> index c1d383d..fc37910 100644
> --- a/arch/x86/boot/compressed/head_64.S
> +++ b/arch/x86/boot/compressed/head_64.S
> @@ -59,7 +59,7 @@ ENTRY(startup_32)
> 1:
>
> /*
> - * Calculate the delta between where we were compiled to run
> + * Calculate the delta between where we were linked to load
> * at and where we were actually loaded at. This can only be done
> * with a short local call on x86. Nothing else will tell us what
> * address we are running at. The reserved chunk of the real-mode
> @@ -78,10 +78,10 @@ ENTRY(startup_32)
>
> call verify_cpu
> testl %eax, %eax
> - jnz no_longmode
> + jnz hang
>
> /*
> - * Compute the delta between where we were compiled to run at
> + * Compute the delta between where we were linked to load at
> * and where the code will actually run at.
> *
> * %ebp contains the address we are loaded at by the boot loader and %ebx
> @@ -90,15 +90,32 @@ ENTRY(startup_32)
> */
>
> #ifdef CONFIG_RELOCATABLE
> +#ifdef CONFIG_RANDOMIZE_BASE
> + call select_aslr_address /* Select ASLR offset */
> + movl %eax, %ebx
> + /* LOAD_PHYSICAL_ADDR is the minimum safe address we can
> + * decompress at */
> + cmpl $LOAD_PHYSICAL_ADDR, %ebx
> + jae 1f
> + movl $LOAD_PHYSICAL_ADDR, %ebx
> +#else /* CONFIG_RANDOMIZE_BASE */
> movl %ebp, %ebx
> movl BP_kernel_alignment(%esi), %eax
> decl %eax
> addl %eax, %ebx
> notl %eax
> andl %eax, %ebx
> -#else
> +#endif /* CONFIG_RANDOMIZE_BASE */
> +
> +#ifdef CONFIG_RANDOMIZE_BASE
> +1: movl %ebx, %eax
> + subl $LOAD_PHYSICAL_ADDR, %eax
> + movl %eax, aslr_offset(%ebp)
> + incl aslr_in_32bit(%ebp) /* say 32 bit code ran */
> +#endif /* CONFIG_RANDOMIZE_BASE */
> +#else /* CONFIG_RELOCATABLE */
> movl $LOAD_PHYSICAL_ADDR, %ebx
> -#endif
> +#endif /* CONFIG_RELOCATABLE */
>
> /* Target address to relocate to for decompression */
> addl $z_extract_offset, %ebx
> @@ -266,14 +283,30 @@ preferred_addr:
> /* Start with the delta to where the kernel will run at. */
> #ifdef CONFIG_RELOCATABLE
> leaq startup_32(%rip) /* - $startup_32 */, %rbp
> +#ifdef CONFIG_RANDOMIZE_BASE
> + leaq boot_stack_end(%rip), %rsp
> + testl $1, aslr_in_32bit(%rip)
> + jne 1f
> + call select_aslr_address
> + movq %rax, %rbp

select_aslr_address only play %ebp, so you assume bzImage is loaded under 4G?

can you just run slect_aslr_address in 64bit only?

> + jmp 2f
> +1: movl aslr_offset(%rip), %eax
> + addq %rax, %rbp
> + /* LOAD_PHYSICAL_ADDR is the minimum safe address we can
> + * decompress at. */
> + cmpq $LOAD_PHYSICAL_ADDR, %rbp
> + jae 2f
> + movq $LOAD_PHYSICAL_ADDR, %rbp

should use old value before select_alsr_addr?

> +2:
> +#endif /* CONFIG_RANDOMIZE_BASE */
> movl BP_kernel_alignment(%rsi), %eax
> decl %eax
> addq %rax, %rbp
> notq %rax
> andq %rax, %rbp
> -#else
> +#else /* CONFIG_RELOCATABLE */
> movq $LOAD_PHYSICAL_ADDR, %rbp
> -#endif
> +#endif /* CONFIG_RELOCATABLE */
>
> /* Target address to relocate to for decompression */
> leaq z_extract_offset(%rbp), %rbx
> @@ -343,13 +376,85 @@ relocated:
> call decompress_kernel
> popq %rsi
>
> +#ifdef CONFIG_RANDOMIZE_BASE
> +/*
> + * Find the address of the relocations.
> + */
> + leaq z_output_len(%rbp), %rdi
> +
> +/*
> + * Calculate the delta between where vmlinux was linked to load
> + * and where it was actually loaded.
> + */
> + movq %rbp, %rbx
> + subq $LOAD_PHYSICAL_ADDR, %rbx
> + je 3f /* Nothing to be done if loaded at linked addr. */
> +/*
> + * The kernel contains a table of relocation addresses. Those addresses
> + * have the final load address of the kernel in virtual memory.
> + * We are currently working in the self map. So we need to create an
> + * adjustment for kernel memory addresses to the self map. This will
> + * involve subtracting out the base address of the kernel.
> + */
> + movq $-__START_KERNEL_map, %rdx /* Literal is too big for add etc */
> + addq %rbx, %rdx
> +/*
> + * Process relocations. 32 bit relocations first then 64 bit after.
> + * Two sets of binary relocations are added to the end of the
> + * kernel before compression. Each relocation table entry is the kernel
> + * address of the location which needs to be updated stored as a 32 bit
> + * value which is sign extended to 64 bits.
> + *
> + * Format is:
> + *
> + * kernel bits...
> + * 0 - zero terminator for 64 bit relocations
> + * 64 bit relocation repeated
> + * 0 - zero terminator for 32 bit relocations
> + * 32 bit relocation repeated
> + *
> + * So we work backwards from the end of the decompressed image.
> + */
> +1: subq $4, %rdi
> + movslq (%rdi), %rcx
> + testq %rcx, %rcx
> + je 2f
> + addq %rdx, %rcx
> +/*
> + * Relocation can't be before the image or
> + * after the current position of the current relocation.
> + * This is a cheap bounds check. It could be more exact
> + * and limit to the end of the image prior to the relocations
> + * but allowing relocations themselves to be fixed up will not
> + * do any harm.
> + */
> + cmpq %rbp, %rcx
> + jb hang
> + cmpq %rdi, %rcx
> + jae hang
> + addl %ebx, (%rcx) /* 32 bit relocation */
> + jmp 1b
> +2: subq $4, %rdi
> + movslq (%rdi), %rcx
> + testq %rcx, %rcx
> + je 3f
> + addq %rdx, %rcx
> + cmpq %rbp, %rcx
> + jb hang
> + cmpq %rdi, %rcx
> + jae hang
> + addq %rbx, (%rcx) /* 64 bit relocation */
> + jmp 2b
> +3:
> +#endif /* CONFIG_RANDOMIZE_BASE */

so decompress code position is changed?

You may push out bss and other data area of run-time kernel of limit
that boot loader
chose according to setup_header.init_size.
aka that make those area overlap with ram hole or other area like
boot command line or initrd....


Thanks

Yinghai

2013-04-14 03:07:26

by H. Peter Anvin

[permalink] [raw]
Subject: Re: [PATCH 6/6] x86: kaslr: relocate base offset at boot

On 04/13/2013 05:37 PM, Yinghai Lu wrote:
>
> so decompress code position is changed?
>
> You may push out bss and other data area of run-time kernel of limit
> that boot loader
> chose according to setup_header.init_size.
> aka that make those area overlap with ram hole or other area like
> boot command line or initrd....
>

Is there a strong reason to randomize the physical address on 64 bits
(and if so, shouldn't we do it right?)

-hpa


--
H. Peter Anvin, Intel Open Source Technology Center
I work for Intel. I don't speak on their behalf.

2013-04-15 21:06:38

by Eric Northup

[permalink] [raw]
Subject: Re: [PATCH 6/6] x86: kaslr: relocate base offset at boot

On Sat, Apr 13, 2013 at 8:06 PM, H. Peter Anvin <[email protected]> wrote:
> On 04/13/2013 05:37 PM, Yinghai Lu wrote:
>>
>> so decompress code position is changed?
>>
>> You may push out bss and other data area of run-time kernel of limit
>> that boot loader
>> chose according to setup_header.init_size.
>> aka that make those area overlap with ram hole or other area like
>> boot command line or initrd....
>>
>
> Is there a strong reason to randomize the physical address on 64 bits
> (and if so, shouldn't we do it right?)

The reason to randomize the physical address is because of the kernel
direct mapping range -- a predictable-to-attackers physical address
implies a predictable-to-attackers virtual address.

It had seemed to me like changing the virtual base of the direct
mapping would be much more involved than physically relocating the
kernel, but better suggestions would be most welcome :-)

2013-04-15 21:25:42

by H. Peter Anvin

[permalink] [raw]
Subject: Re: [PATCH 6/6] x86: kaslr: relocate base offset at boot

On 04/15/2013 02:06 PM, Eric Northup wrote:
> On Sat, Apr 13, 2013 at 8:06 PM, H. Peter Anvin <[email protected]> wrote:
>> On 04/13/2013 05:37 PM, Yinghai Lu wrote:
>>>
>>> so decompress code position is changed?
>>>
>>> You may push out bss and other data area of run-time kernel of limit
>>> that boot loader
>>> chose according to setup_header.init_size.
>>> aka that make those area overlap with ram hole or other area like
>>> boot command line or initrd....
>>>
>>
>> Is there a strong reason to randomize the physical address on 64 bits
>> (and if so, shouldn't we do it right?)
>
> The reason to randomize the physical address is because of the kernel
> direct mapping range -- a predictable-to-attackers physical address
> implies a predictable-to-attackers virtual address.
>
> It had seemed to me like changing the virtual base of the direct
> mapping would be much more involved than physically relocating the
> kernel, but better suggestions would be most welcome :-)
>

You seem to be missing something here...

There are *two* mappings in 64-bit mode. Physically, if you're going to
randomize you might as well randomize over the entire range... except
not too far down (on either 32 or 64 bit mode)... in particular, you
don't want to drop below 16 MiB if you can avoid it.

On 64 bits, there is no reason the virtual address has to be randomized
the same way.

-hpa


--
H. Peter Anvin, Intel Open Source Technology Center
I work for Intel. I don't speak on their behalf.

2013-04-15 21:41:45

by Kees Cook

[permalink] [raw]
Subject: Re: [PATCH 6/6] x86: kaslr: relocate base offset at boot

On Mon, Apr 15, 2013 at 2:25 PM, H. Peter Anvin <[email protected]> wrote:
> On 04/15/2013 02:06 PM, Eric Northup wrote:
>> On Sat, Apr 13, 2013 at 8:06 PM, H. Peter Anvin <[email protected]> wrote:
>>> On 04/13/2013 05:37 PM, Yinghai Lu wrote:
>>>>
>>>> so decompress code position is changed?
>>>>
>>>> You may push out bss and other data area of run-time kernel of limit
>>>> that boot loader
>>>> chose according to setup_header.init_size.
>>>> aka that make those area overlap with ram hole or other area like
>>>> boot command line or initrd....
>>>>
>>>
>>> Is there a strong reason to randomize the physical address on 64 bits
>>> (and if so, shouldn't we do it right?)
>>
>> The reason to randomize the physical address is because of the kernel
>> direct mapping range -- a predictable-to-attackers physical address
>> implies a predictable-to-attackers virtual address.
>>
>> It had seemed to me like changing the virtual base of the direct
>> mapping would be much more involved than physically relocating the
>> kernel, but better suggestions would be most welcome :-)
>>
>
> You seem to be missing something here...
>
> There are *two* mappings in 64-bit mode. Physically, if you're going to
> randomize you might as well randomize over the entire range... except
> not too far down (on either 32 or 64 bit mode)... in particular, you
> don't want to drop below 16 MiB if you can avoid it.
>
> On 64 bits, there is no reason the virtual address has to be randomized
> the same way.

Aren't we bound by the negative 2GB addressing due to -mcmodel=kernel?

-Kees

--
Kees Cook
Chrome OS Security

2013-04-15 21:44:56

by Eric Northup

[permalink] [raw]
Subject: Re: [PATCH 6/6] x86: kaslr: relocate base offset at boot

W/the relocation information, we can pick the virtual address to load
at independent from the physical load address.

On Mon, Apr 15, 2013 at 2:41 PM, Kees Cook <[email protected]> wrote:
> On Mon, Apr 15, 2013 at 2:25 PM, H. Peter Anvin <[email protected]> wrote:
>> On 04/15/2013 02:06 PM, Eric Northup wrote:
>>> On Sat, Apr 13, 2013 at 8:06 PM, H. Peter Anvin <[email protected]> wrote:
>>>> On 04/13/2013 05:37 PM, Yinghai Lu wrote:
>>>>>
>>>>> so decompress code position is changed?
>>>>>
>>>>> You may push out bss and other data area of run-time kernel of limit
>>>>> that boot loader
>>>>> chose according to setup_header.init_size.
>>>>> aka that make those area overlap with ram hole or other area like
>>>>> boot command line or initrd....
>>>>>
>>>>
>>>> Is there a strong reason to randomize the physical address on 64 bits
>>>> (and if so, shouldn't we do it right?)
>>>
>>> The reason to randomize the physical address is because of the kernel
>>> direct mapping range -- a predictable-to-attackers physical address
>>> implies a predictable-to-attackers virtual address.
>>>
>>> It had seemed to me like changing the virtual base of the direct
>>> mapping would be much more involved than physically relocating the
>>> kernel, but better suggestions would be most welcome :-)
>>>
>>
>> You seem to be missing something here...
>>
>> There are *two* mappings in 64-bit mode. Physically, if you're going to
>> randomize you might as well randomize over the entire range... except
>> not too far down (on either 32 or 64 bit mode)... in particular, you
>> don't want to drop below 16 MiB if you can avoid it.
>>
>> On 64 bits, there is no reason the virtual address has to be randomized
>> the same way.
>
> Aren't we bound by the negative 2GB addressing due to -mcmodel=kernel?
>
> -Kees
>
> --
> Kees Cook
> Chrome OS Security

2013-04-15 21:46:29

by H. Peter Anvin

[permalink] [raw]
Subject: Re: [PATCH 6/6] x86: kaslr: relocate base offset at boot

On 04/15/2013 02:41 PM, Kees Cook wrote:
>>
>> You seem to be missing something here...
>>
>> There are *two* mappings in 64-bit mode. Physically, if you're going to
>> randomize you might as well randomize over the entire range... except
>> not too far down (on either 32 or 64 bit mode)... in particular, you
>> don't want to drop below 16 MiB if you can avoid it.
>>
>> On 64 bits, there is no reason the virtual address has to be randomized
>> the same way.
>
> Aren't we bound by the negative 2GB addressing due to -mcmodel=kernel?
>

Guys,

Please read what I wrote.

The 2 GB limit is for the *virtual* mapping.

The *physical* mapping, where it lands in RAM, is completely
independent, and if you're going to randomize the latter, there is no
reason it has to match the former. Instead, randomize it freely.

That is different from the i386 kernel which runs at its
physical-mapping address.

Incidentally, for performance reasons please avoid locating the kernel
below CONFIG_PHYSICAL_ADDRESS if possible.

Also make sure your code works with more than 128 e820 entries.

-hpa


--
H. Peter Anvin, Intel Open Source Technology Center
I work for Intel. I don't speak on their behalf.

2013-04-15 21:59:12

by Kees Cook

[permalink] [raw]
Subject: Re: [PATCH 6/6] x86: kaslr: relocate base offset at boot

On Mon, Apr 15, 2013 at 2:46 PM, H. Peter Anvin <[email protected]> wrote:
> On 04/15/2013 02:41 PM, Kees Cook wrote:
>>>
>>> You seem to be missing something here...
>>>
>>> There are *two* mappings in 64-bit mode. Physically, if you're going to
>>> randomize you might as well randomize over the entire range... except
>>> not too far down (on either 32 or 64 bit mode)... in particular, you
>>> don't want to drop below 16 MiB if you can avoid it.
>>>
>>> On 64 bits, there is no reason the virtual address has to be randomized
>>> the same way.
>>
>> Aren't we bound by the negative 2GB addressing due to -mcmodel=kernel?
>>
>
> Guys,
>
> Please read what I wrote.
>
> The 2 GB limit is for the *virtual* mapping.
>
> The *physical* mapping, where it lands in RAM, is completely
> independent, and if you're going to randomize the latter, there is no
> reason it has to match the former. Instead, randomize it freely.

Ah, gotcha. I don't see much benefit in doing this as it would make
the 32-bit and 64-bit logic pretty different without much real-world
gain, IMO.

> That is different from the i386 kernel which runs at its
> physical-mapping address.
>
> Incidentally, for performance reasons please avoid locating the kernel
> below CONFIG_PHYSICAL_ADDRESS if possible.

You mean CONFIG_PHYSICAL_START? This is already done in aslr.S via
LOAD_PHYSICAL_ADDR which is calculated from the
CONFIG_PHYSICAL_ALIGN-masked CONFIG_PHYSICAL_START.

> Also make sure your code works with more than 128 e820 entries.

There should be no problem here; we're using edi to count them.

-Kees

--
Kees Cook
Chrome OS Security

2013-04-15 22:00:07

by Yinghai Lu

[permalink] [raw]
Subject: Re: [PATCH 6/6] x86: kaslr: relocate base offset at boot

On Mon, Apr 15, 2013 at 2:46 PM, H. Peter Anvin <[email protected]> wrote:
> On 04/15/2013 02:41 PM, Kees Cook wrote:

> Please read what I wrote.
>
> The 2 GB limit is for the *virtual* mapping.
>
> The *physical* mapping, where it lands in RAM, is completely
> independent, and if you're going to randomize the latter, there is no
> reason it has to match the former. Instead, randomize it freely.
>
> That is different from the i386 kernel which runs at its
> physical-mapping address.
>
> Incidentally, for performance reasons please avoid locating the kernel
> below CONFIG_PHYSICAL_ADDRESS if possible.
>
> Also make sure your code works with more than 128 e820 entries.

also do not overlap with boot_param, command_line, and initrd.

and need to double check setup_header.init_size to make sure bss and
etc will not
fall into memory hole or reserved area in e820.

also may need to setup page table for target position as bootloader may only
has ident mapping only for loaded bzImage 64 areas.

looks you are trying redo the work for bootloader to pick loaded phys addr.

Thanks

Yinghai

2013-04-15 22:07:20

by Kees Cook

[permalink] [raw]
Subject: Re: [PATCH 6/6] x86: kaslr: relocate base offset at boot

On Mon, Apr 15, 2013 at 3:00 PM, Yinghai Lu <[email protected]> wrote:
> On Mon, Apr 15, 2013 at 2:46 PM, H. Peter Anvin <[email protected]> wrote:
>> On 04/15/2013 02:41 PM, Kees Cook wrote:
>
>> Please read what I wrote.
>>
>> The 2 GB limit is for the *virtual* mapping.
>>
>> The *physical* mapping, where it lands in RAM, is completely
>> independent, and if you're going to randomize the latter, there is no
>> reason it has to match the former. Instead, randomize it freely.
>>
>> That is different from the i386 kernel which runs at its
>> physical-mapping address.
>>
>> Incidentally, for performance reasons please avoid locating the kernel
>> below CONFIG_PHYSICAL_ADDRESS if possible.
>>
>> Also make sure your code works with more than 128 e820 entries.
>
> also do not overlap with boot_param, command_line, and initrd.
>
> and need to double check setup_header.init_size to make sure bss and
> etc will not
> fall into memory hole or reserved area in e820.
>
> also may need to setup page table for target position as bootloader may only
> has ident mapping only for loaded bzImage 64 areas.
>
> looks you are trying redo the work for bootloader to pick loaded phys addr.

aslr.S's select_aslr_address uses z_extract_offset as the upper bound.

-Kees

--
Kees Cook
Chrome OS Security

2013-04-15 22:38:22

by Yinghai Lu

[permalink] [raw]
Subject: Re: [PATCH 6/6] x86: kaslr: relocate base offset at boot

On Mon, Apr 15, 2013 at 3:07 PM, Kees Cook <[email protected]> wrote:
> On Mon, Apr 15, 2013 at 3:00 PM, Yinghai Lu <[email protected]> wrote:
>> also do not overlap with boot_param, command_line, and initrd.
>>
>> and need to double check setup_header.init_size to make sure bss and
>> etc will not
>> fall into memory hole or reserved area in e820.
>>
>> also may need to setup page table for target position as bootloader may only
>> has ident mapping only for loaded bzImage 64 areas.
>>
>> looks you are trying redo the work for bootloader to pick loaded phys addr.
>
> aslr.S's select_aslr_address uses z_extract_offset as the upper bound.
>

so the decompressed image is not moved high?

2013-04-15 22:43:01

by Kees Cook

[permalink] [raw]
Subject: Re: [PATCH 6/6] x86: kaslr: relocate base offset at boot

On Mon, Apr 15, 2013 at 3:38 PM, Yinghai Lu <[email protected]> wrote:
> On Mon, Apr 15, 2013 at 3:07 PM, Kees Cook <[email protected]> wrote:
>> On Mon, Apr 15, 2013 at 3:00 PM, Yinghai Lu <[email protected]> wrote:
>>> also do not overlap with boot_param, command_line, and initrd.
>>>
>>> and need to double check setup_header.init_size to make sure bss and
>>> etc will not
>>> fall into memory hole or reserved area in e820.
>>>
>>> also may need to setup page table for target position as bootloader may only
>>> has ident mapping only for loaded bzImage 64 areas.
>>>
>>> looks you are trying redo the work for bootloader to pick loaded phys addr.
>>
>> aslr.S's select_aslr_address uses z_extract_offset as the upper bound.
>
> so the decompressed image is not moved high?

No, I mean the size of the relocated image is using z_extract_offset
as the end size of the relocated memory. See select_aslr_address. It
takes the chosen aslr offset plus the location of z_extract_offset and
keeps reducing the offset until it's inside the physical end of
memory.

-Kees

--
Kees Cook
Chrome OS Security

2013-04-15 22:57:11

by Yinghai Lu

[permalink] [raw]
Subject: Re: [PATCH 6/6] x86: kaslr: relocate base offset at boot

On Mon, Apr 15, 2013 at 3:42 PM, Kees Cook <[email protected]> wrote:
> On Mon, Apr 15, 2013 at 3:38 PM, Yinghai Lu <[email protected]> wrote:
>
>> so the decompressed image is not moved high?
>
> No, I mean the size of the relocated image is using z_extract_offset
> as the end size of the relocated memory. See select_aslr_address. It
> takes the chosen aslr offset plus the location of z_extract_offset and
> keeps reducing the offset until it's inside the physical end of
> memory.

My point is:
If you move the decompressed high, for example, before aslr uncompressed
vmlinx is on [1024g+16M, 1024g+256M).
after aslr, it is on [1024g+16M + 32M, 1024g+256M + 32M).

You will need to make sure that 32M is still RAM in e820 memmap.
also those range should not be with boot_param, command line, and initrd.
as bootloader could put boot_param, commandline and initrd in that 32M range.

Yinghai

2013-04-16 02:31:47

by H. Peter Anvin

[permalink] [raw]
Subject: Re: [PATCH 6/6] x86: kaslr: relocate base offset at boot

On 04/15/2013 02:59 PM, Kees Cook wrote:
>>
>> The *physical* mapping, where it lands in RAM, is completely
>> independent, and if you're going to randomize the latter, there is no
>> reason it has to match the former. Instead, randomize it freely.
>
> Ah, gotcha. I don't see much benefit in doing this as it would make
> the 32-bit and 64-bit logic pretty different without much real-world
> gain, IMO.
>

You *have* to make them different anyway. Otherwise you aren't really
doing anything useful on x86-64.

Seriously, do it right if anything.

I also am starting to think that this really would be done better being
integrated with the decompressor code, since that code already ends up
moving the code around... no reason to do this again.

>> That is different from the i386 kernel which runs at its
>> physical-mapping address.
>>
>> Incidentally, for performance reasons please avoid locating the kernel
>> below CONFIG_PHYSICAL_ADDRESS if possible.
>
> You mean CONFIG_PHYSICAL_START? This is already done in aslr.S via
> LOAD_PHYSICAL_ADDR which is calculated from the
> CONFIG_PHYSICAL_ALIGN-masked CONFIG_PHYSICAL_START.
>
>> Also make sure your code works with more than 128 e820 entries.
>
> There should be no problem here; we're using edi to count them.

More than 128 entries are fed via a different protocol.

-hpa

--
H. Peter Anvin, Intel Open Source Technology Center
I work for Intel. I don't speak on their behalf.

2013-04-16 02:34:41

by H. Peter Anvin

[permalink] [raw]
Subject: Re: [PATCH 6/6] x86: kaslr: relocate base offset at boot

On 04/15/2013 03:38 PM, Yinghai Lu wrote:
> On Mon, Apr 15, 2013 at 3:07 PM, Kees Cook <[email protected]> wrote:
>> On Mon, Apr 15, 2013 at 3:00 PM, Yinghai Lu <[email protected]> wrote:
>>> also do not overlap with boot_param, command_line, and initrd.
>>>
>>> and need to double check setup_header.init_size to make sure bss and
>>> etc will not
>>> fall into memory hole or reserved area in e820.
>>>
>>> also may need to setup page table for target position as bootloader may only
>>> has ident mapping only for loaded bzImage 64 areas.
>>>
>>> looks you are trying redo the work for bootloader to pick loaded phys addr.
>>
>> aslr.S's select_aslr_address uses z_extract_offset as the upper bound.

That seems really, really odd.

> so the decompressed image is not moved high?

This really seems wrong in so many ways.

If you relocate pre-decompression the amount of memory you need is given
by init_size:

#define ZO_INIT_SIZE (ZO__end - ZO_startup_32 + ZO_z_extract_offset)
#define VO_INIT_SIZE (VO__end - VO__text)
#if ZO_INIT_SIZE > VO_INIT_SIZE
#define INIT_SIZE ZO_INIT_SIZE
#else
#define INIT_SIZE VO_INIT_SIZE
#endif
init_size: .long INIT_SIZE # kernel initialization size

In real life, VO_INIT_SIZE dominates almost every time.

-hpa

--
H. Peter Anvin, Intel Open Source Technology Center
I work for Intel. I don't speak on their behalf.

2013-04-16 02:37:07

by H. Peter Anvin

[permalink] [raw]
Subject: Re: [PATCH 6/6] x86: kaslr: relocate base offset at boot

On 04/15/2013 03:00 PM, Yinghai Lu wrote:
>
> looks you are trying redo the work for bootloader to pick loaded phys addr.
>

Well, that is exactly what they are doing. On top of that they also
need to randomize the 64-bit virtual mapping.

I wonder if we need a bootloader bit to inhibit kaslr in addition to the
command line...

-hpa


--
H. Peter Anvin, Intel Open Source Technology Center
I work for Intel. I don't speak on their behalf.

2013-04-16 02:40:54

by H. Peter Anvin

[permalink] [raw]
Subject: Re: [PATCH 6/6] x86: kaslr: relocate base offset at boot

On 04/15/2013 07:31 PM, H. Peter Anvin wrote:
>
> I also am starting to think that this really would be done better being
> integrated with the decompressor code, since that code already ends up
> moving the code around... no reason to do this again.
>

Another good reason to do this in the decompressor wrapper: it can be
written in C that way.

-hpa

--
H. Peter Anvin, Intel Open Source Technology Center
I work for Intel. I don't speak on their behalf.

2013-04-16 02:56:23

by Yinghai Lu

[permalink] [raw]
Subject: Re: [PATCH 6/6] x86: kaslr: relocate base offset at boot

On Mon, Apr 15, 2013 at 7:36 PM, H. Peter Anvin <[email protected]> wrote:
> On 04/15/2013 03:00 PM, Yinghai Lu wrote:
>>
>> looks you are trying redo the work for bootloader to pick loaded phys addr.
>>
>
> Well, that is exactly what they are doing. On top of that they also
> need to randomize the 64-bit virtual mapping.
>
> I wonder if we need a bootloader bit to inhibit kaslr in addition to the
> command line...

so let the bootloader to parse kaslr and pick ramdom address,
and kernel arch/x86/boot/compressed/head_64.S to pick random virtual address?

Yinghai

2013-04-16 11:59:08

by H. Peter Anvin

[permalink] [raw]
Subject: Re: [PATCH 6/6] x86: kaslr: relocate base offset at boot

No. Fixing one bootloader is almost impossible. Fixing them all is a Sisiphyean task.

Yinghai Lu <[email protected]> wrote:

>On Mon, Apr 15, 2013 at 7:36 PM, H. Peter Anvin <[email protected]> wrote:
>> On 04/15/2013 03:00 PM, Yinghai Lu wrote:
>>>
>>> looks you are trying redo the work for bootloader to pick loaded
>phys addr.
>>>
>>
>> Well, that is exactly what they are doing. On top of that they also
>> need to randomize the 64-bit virtual mapping.
>>
>> I wonder if we need a bootloader bit to inhibit kaslr in addition to
>the
>> command line...
>
>so let the bootloader to parse kaslr and pick ramdom address,
>and kernel arch/x86/boot/compressed/head_64.S to pick random virtual
>address?
>
>Yinghai

--
Sent from my mobile phone. Please excuse brevity and lack of formatting.

2013-04-16 13:09:00

by Ingo Molnar

[permalink] [raw]
Subject: Re: [PATCH 6/6] x86: kaslr: relocate base offset at boot


* H. Peter Anvin <[email protected]> wrote:

> No. Fixing one bootloader is almost impossible. Fixing them all is a
> Sisiphyean task.

It's a self inflicted wound really: if only we had a reference bootloader in the
kernel tree, which we could fix. The effort that went into fixing various
bootloader interactions would have been enough to write two new bootloaders from
scratch.

Thanks,

Ingo

2013-04-16 13:27:40

by H. Peter Anvin

[permalink] [raw]
Subject: Re: [PATCH 6/6] x86: kaslr: relocate base offset at boot

Well... G**** is it's own problem (doing actively broken thinks for political reasons), but the real issue mostly is that there are a lot of them simply because there are a lot of ways one may want to load the kernel.

This is why things like decompression and the BIOS and EFI stubs are part of the kernel and not the bootloader. In this case moving the randomization into the decompressor send like the right thing to do - C code, and no additional copy.

Ingo Molnar <[email protected]> wrote:

>
>* H. Peter Anvin <[email protected]> wrote:
>
>> No. Fixing one bootloader is almost impossible. Fixing them all is
>a
>> Sisiphyean task.
>
>It's a self inflicted wound really: if only we had a reference
>bootloader in the
>kernel tree, which we could fix. The effort that went into fixing
>various
>bootloader interactions would have been enough to write two new
>bootloaders from
>scratch.
>
>Thanks,
>
> Ingo

--
Sent from my mobile phone. Please excuse brevity and lack of formatting.

2013-04-16 16:08:58

by Kees Cook

[permalink] [raw]
Subject: Re: [PATCH 6/6] x86: kaslr: relocate base offset at boot

On Mon, Apr 15, 2013 at 7:40 PM, H. Peter Anvin <[email protected]> wrote:
> On 04/15/2013 07:31 PM, H. Peter Anvin wrote:
>>
>> I also am starting to think that this really would be done better being
>> integrated with the decompressor code, since that code already ends up
>> moving the code around... no reason to do this again.
>>
>
> Another good reason to do this in the decompressor wrapper: it can be
> written in C that way.

This would make the code much more readable, for sure. And given that
the 64-bit relocation work happens after decompression, this seems
only right. Good idea! I'll start working on it.

-Kees

--
Kees Cook
Chrome OS Security

2013-04-16 18:08:48

by Kees Cook

[permalink] [raw]
Subject: Re: [PATCH v2 0/6] kernel ASLR

On Fri, Apr 12, 2013 at 1:13 PM, Kees Cook <[email protected]> wrote:
> This splits up the relocs tool changes into the separate logical pieces,
> which should be easier to review. I could use some suggestions on a
> better way to build it in the 4th patch. What I have now seems ugly,
> but Kbuild would not give me its secrets.

How does the relocation portion of this series look? It's separate
from the ASLR logic, but required for it. Do those changes look good
for the tip/kaslr tree?

Thanks,

-Kees

--
Kees Cook
Chrome OS Security

2013-04-16 18:15:47

by H. Peter Anvin

[permalink] [raw]
Subject: Re: [PATCH v2 0/6] kernel ASLR

On 04/16/2013 11:08 AM, Kees Cook wrote:
> On Fri, Apr 12, 2013 at 1:13 PM, Kees Cook <[email protected]> wrote:
>> This splits up the relocs tool changes into the separate logical pieces,
>> which should be easier to review. I could use some suggestions on a
>> better way to build it in the 4th patch. What I have now seems ugly,
>> but Kbuild would not give me its secrets.
>
> How does the relocation portion of this series look? It's separate
> from the ASLR logic, but required for it. Do those changes look good
> for the tip/kaslr tree?
>

They are probably okay... I should take a closer look.

I might prioritize it down since (a) I am sick, and (b) it is the week
before the merge window.

-hpa

--
H. Peter Anvin, Intel Open Source Technology Center
I work for Intel. I don't speak on their behalf.

2013-04-16 22:22:25

by H. Peter Anvin

[permalink] [raw]
Subject: Re: [PATCH 4/6] x86: relocs: build separate 32/64-bit tools

On 04/12/2013 01:13 PM, Kees Cook wrote:
> Since the ELF structures and access macros change size based on 32 vs
> 64 bits, build a separate 32-bit relocs tool (for handling realmode
> and 32-bit relocations), and a 64-bit relocs tool (for handling 64-bit
> kernel relocations).
>
> Signed-off-by: Kees Cook <[email protected]>
> --
> This is ugly with the "cp". Is there some other cleaner way to trigger
> two builds with different defines from the same source file?

There definitely is.

Have simple wrapper files which do:

/* relocs_32.c */
#define ELF_BITS 32
#include "relocs.c"

/* relocs_64.c */
#define ELF_BITS 64
#include "relocs.c"

-hpa

2013-04-16 22:38:36

by Kees Cook

[permalink] [raw]
Subject: Re: [PATCH 4/6] x86: relocs: build separate 32/64-bit tools

On Tue, Apr 16, 2013 at 3:21 PM, H. Peter Anvin <[email protected]> wrote:
> On 04/12/2013 01:13 PM, Kees Cook wrote:
>> Since the ELF structures and access macros change size based on 32 vs
>> 64 bits, build a separate 32-bit relocs tool (for handling realmode
>> and 32-bit relocations), and a 64-bit relocs tool (for handling 64-bit
>> kernel relocations).
>>
>> Signed-off-by: Kees Cook <[email protected]>
>> --
>> This is ugly with the "cp". Is there some other cleaner way to trigger
>> two builds with different defines from the same source file?
>
> There definitely is.
>
> Have simple wrapper files which do:
>
> /* relocs_32.c */
> #define ELF_BITS 32
> #include "relocs.c"
>
> /* relocs_64.c */
> #define ELF_BITS 64
> #include "relocs.c"

That's what I did in my first pass, but it seemed even worse to me. I
will go back to this.

Thanks!

-Kees

--
Kees Cook
Chrome OS Security

2013-04-16 22:39:59

by H. Peter Anvin

[permalink] [raw]
Subject: Re: [PATCH 4/6] x86: relocs: build separate 32/64-bit tools

On 04/16/2013 03:38 PM, Kees Cook wrote:
>>
>> Have simple wrapper files which do:
>>
>> /* relocs_32.c */
>> #define ELF_BITS 32
>> #include "relocs.c"
>>
>> /* relocs_64.c */
>> #define ELF_BITS 64
>> #include "relocs.c"
>
> That's what I did in my first pass, but it seemed even worse to me. I
> will go back to this.
>

Actually, I'm trying to see if I can refactor this into one binary.
Hang on a few.

-hpa

Subject: [tip:x86/kaslr] x86, relocs: Generalize ELF structure names

Commit-ID: bf11655cf2ecdcfaacbc8324da4a3edfe276ba9d
Gitweb: http://git.kernel.org/tip/bf11655cf2ecdcfaacbc8324da4a3edfe276ba9d
Author: Kees Cook <[email protected]>
AuthorDate: Fri, 12 Apr 2013 13:13:42 -0700
Committer: H. Peter Anvin <[email protected]>
CommitDate: Tue, 16 Apr 2013 15:19:06 -0700

x86, relocs: Generalize ELF structure names

In preparation for making the reloc tool operate on 64-bit relocations,
generalize the structure names for easy recompilation via #defines.

Based on work by Neill Clift and Michael Davidson.

Signed-off-by: Kees Cook <[email protected]>
Link: http://lkml.kernel.org/r/[email protected]
Signed-off-by: H. Peter Anvin <[email protected]>
---
arch/x86/tools/relocs.c | 170 ++++++++++++++++++++++++++++--------------------
1 file changed, 99 insertions(+), 71 deletions(-)

diff --git a/arch/x86/tools/relocs.c b/arch/x86/tools/relocs.c
index 79d67bd..fd28ef7 100644
--- a/arch/x86/tools/relocs.c
+++ b/arch/x86/tools/relocs.c
@@ -12,20 +12,42 @@
#include <regex.h>
#include <tools/le_byteshift.h>

+#define ElfW(type) _ElfW(ELF_BITS, type)
+#define _ElfW(bits, type) __ElfW(bits, type)
+#define __ElfW(bits, type) Elf##bits##_##type
+
+#define ELF_BITS 32
+#define ELF_MACHINE EM_386
+#define ELF_MACHINE_NAME "i386"
+#define SHT_REL_TYPE SHT_REL
+
+#define ELF_CLASS ELFCLASS32
+#define ELF_R_SYM(val) ELF32_R_SYM(val)
+#define ELF_R_TYPE(val) ELF32_R_TYPE(val)
+#define ELF_ST_TYPE(o) ELF32_ST_TYPE(o)
+#define ELF_ST_BIND(o) ELF32_ST_BIND(o)
+#define ELF_ST_VISIBILITY(o) ELF32_ST_VISIBILITY(o)
+
+#define Elf_Rel ElfW(Rel)
+#define Elf_Ehdr ElfW(Ehdr)
+#define Elf_Phdr ElfW(Phdr)
+#define Elf_Shdr ElfW(Shdr)
+#define Elf_Sym ElfW(Sym)
+
static void die(char *fmt, ...);

#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
-static Elf32_Ehdr ehdr;
+static Elf_Ehdr ehdr;
static unsigned long reloc_count, reloc_idx;
static unsigned long *relocs;
static unsigned long reloc16_count, reloc16_idx;
static unsigned long *relocs16;

struct section {
- Elf32_Shdr shdr;
+ Elf_Shdr shdr;
struct section *link;
- Elf32_Sym *symtab;
- Elf32_Rel *reltab;
+ Elf_Sym *symtab;
+ Elf_Rel *reltab;
char *strtab;
};
static struct section *secs;
@@ -240,7 +262,7 @@ static const char *sec_name(unsigned shndx)
return name;
}

-static const char *sym_name(const char *sym_strtab, Elf32_Sym *sym)
+static const char *sym_name(const char *sym_strtab, Elf_Sym *sym)
{
const char *name;
name = "<noname>";
@@ -274,6 +296,12 @@ static uint32_t elf32_to_cpu(uint32_t val)
return le32_to_cpu(val);
}

+#define elf_half_to_cpu(x) elf16_to_cpu(x)
+#define elf_word_to_cpu(x) elf32_to_cpu(x)
+#define elf_addr_to_cpu(x) elf32_to_cpu(x)
+#define elf_off_to_cpu(x) elf32_to_cpu(x)
+#define elf_xword_to_cpu(x) elf32_to_cpu(x)
+
static void read_ehdr(FILE *fp)
{
if (fread(&ehdr, sizeof(ehdr), 1, fp) != 1) {
@@ -283,8 +311,8 @@ static void read_ehdr(FILE *fp)
if (memcmp(ehdr.e_ident, ELFMAG, SELFMAG) != 0) {
die("No ELF magic\n");
}
- if (ehdr.e_ident[EI_CLASS] != ELFCLASS32) {
- die("Not a 32 bit executable\n");
+ if (ehdr.e_ident[EI_CLASS] != ELF_CLASS) {
+ die("Not a %d bit executable\n", ELF_BITS);
}
if (ehdr.e_ident[EI_DATA] != ELFDATA2LSB) {
die("Not a LSB ELF executable\n");
@@ -293,36 +321,36 @@ static void read_ehdr(FILE *fp)
die("Unknown ELF version\n");
}
/* Convert the fields to native endian */
- ehdr.e_type = elf16_to_cpu(ehdr.e_type);
- ehdr.e_machine = elf16_to_cpu(ehdr.e_machine);
- ehdr.e_version = elf32_to_cpu(ehdr.e_version);
- ehdr.e_entry = elf32_to_cpu(ehdr.e_entry);
- ehdr.e_phoff = elf32_to_cpu(ehdr.e_phoff);
- ehdr.e_shoff = elf32_to_cpu(ehdr.e_shoff);
- ehdr.e_flags = elf32_to_cpu(ehdr.e_flags);
- ehdr.e_ehsize = elf16_to_cpu(ehdr.e_ehsize);
- ehdr.e_phentsize = elf16_to_cpu(ehdr.e_phentsize);
- ehdr.e_phnum = elf16_to_cpu(ehdr.e_phnum);
- ehdr.e_shentsize = elf16_to_cpu(ehdr.e_shentsize);
- ehdr.e_shnum = elf16_to_cpu(ehdr.e_shnum);
- ehdr.e_shstrndx = elf16_to_cpu(ehdr.e_shstrndx);
+ ehdr.e_type = elf_half_to_cpu(ehdr.e_type);
+ ehdr.e_machine = elf_half_to_cpu(ehdr.e_machine);
+ ehdr.e_version = elf_word_to_cpu(ehdr.e_version);
+ ehdr.e_entry = elf_addr_to_cpu(ehdr.e_entry);
+ ehdr.e_phoff = elf_off_to_cpu(ehdr.e_phoff);
+ ehdr.e_shoff = elf_off_to_cpu(ehdr.e_shoff);
+ ehdr.e_flags = elf_word_to_cpu(ehdr.e_flags);
+ ehdr.e_ehsize = elf_half_to_cpu(ehdr.e_ehsize);
+ ehdr.e_phentsize = elf_half_to_cpu(ehdr.e_phentsize);
+ ehdr.e_phnum = elf_half_to_cpu(ehdr.e_phnum);
+ ehdr.e_shentsize = elf_half_to_cpu(ehdr.e_shentsize);
+ ehdr.e_shnum = elf_half_to_cpu(ehdr.e_shnum);
+ ehdr.e_shstrndx = elf_half_to_cpu(ehdr.e_shstrndx);

if ((ehdr.e_type != ET_EXEC) && (ehdr.e_type != ET_DYN)) {
die("Unsupported ELF header type\n");
}
- if (ehdr.e_machine != EM_386) {
- die("Not for x86\n");
+ if (ehdr.e_machine != ELF_MACHINE) {
+ die("Not for %s\n", ELF_MACHINE_NAME);
}
if (ehdr.e_version != EV_CURRENT) {
die("Unknown ELF version\n");
}
- if (ehdr.e_ehsize != sizeof(Elf32_Ehdr)) {
+ if (ehdr.e_ehsize != sizeof(Elf_Ehdr)) {
die("Bad Elf header size\n");
}
- if (ehdr.e_phentsize != sizeof(Elf32_Phdr)) {
+ if (ehdr.e_phentsize != sizeof(Elf_Phdr)) {
die("Bad program header entry\n");
}
- if (ehdr.e_shentsize != sizeof(Elf32_Shdr)) {
+ if (ehdr.e_shentsize != sizeof(Elf_Shdr)) {
die("Bad section header entry\n");
}
if (ehdr.e_shstrndx >= ehdr.e_shnum) {
@@ -333,7 +361,7 @@ static void read_ehdr(FILE *fp)
static void read_shdrs(FILE *fp)
{
int i;
- Elf32_Shdr shdr;
+ Elf_Shdr shdr;

secs = calloc(ehdr.e_shnum, sizeof(struct section));
if (!secs) {
@@ -349,16 +377,16 @@ static void read_shdrs(FILE *fp)
if (fread(&shdr, sizeof shdr, 1, fp) != 1)
die("Cannot read ELF section headers %d/%d: %s\n",
i, ehdr.e_shnum, strerror(errno));
- sec->shdr.sh_name = elf32_to_cpu(shdr.sh_name);
- sec->shdr.sh_type = elf32_to_cpu(shdr.sh_type);
- sec->shdr.sh_flags = elf32_to_cpu(shdr.sh_flags);
- sec->shdr.sh_addr = elf32_to_cpu(shdr.sh_addr);
- sec->shdr.sh_offset = elf32_to_cpu(shdr.sh_offset);
- sec->shdr.sh_size = elf32_to_cpu(shdr.sh_size);
- sec->shdr.sh_link = elf32_to_cpu(shdr.sh_link);
- sec->shdr.sh_info = elf32_to_cpu(shdr.sh_info);
- sec->shdr.sh_addralign = elf32_to_cpu(shdr.sh_addralign);
- sec->shdr.sh_entsize = elf32_to_cpu(shdr.sh_entsize);
+ sec->shdr.sh_name = elf_word_to_cpu(shdr.sh_name);
+ sec->shdr.sh_type = elf_word_to_cpu(shdr.sh_type);
+ sec->shdr.sh_flags = elf_xword_to_cpu(shdr.sh_flags);
+ sec->shdr.sh_addr = elf_addr_to_cpu(shdr.sh_addr);
+ sec->shdr.sh_offset = elf_off_to_cpu(shdr.sh_offset);
+ sec->shdr.sh_size = elf_xword_to_cpu(shdr.sh_size);
+ sec->shdr.sh_link = elf_word_to_cpu(shdr.sh_link);
+ sec->shdr.sh_info = elf_word_to_cpu(shdr.sh_info);
+ sec->shdr.sh_addralign = elf_xword_to_cpu(shdr.sh_addralign);
+ sec->shdr.sh_entsize = elf_xword_to_cpu(shdr.sh_entsize);
if (sec->shdr.sh_link < ehdr.e_shnum)
sec->link = &secs[sec->shdr.sh_link];
}
@@ -412,12 +440,12 @@ static void read_symtabs(FILE *fp)
die("Cannot read symbol table: %s\n",
strerror(errno));
}
- for (j = 0; j < sec->shdr.sh_size/sizeof(Elf32_Sym); j++) {
- Elf32_Sym *sym = &sec->symtab[j];
- sym->st_name = elf32_to_cpu(sym->st_name);
- sym->st_value = elf32_to_cpu(sym->st_value);
- sym->st_size = elf32_to_cpu(sym->st_size);
- sym->st_shndx = elf16_to_cpu(sym->st_shndx);
+ for (j = 0; j < sec->shdr.sh_size/sizeof(Elf_Sym); j++) {
+ Elf_Sym *sym = &sec->symtab[j];
+ sym->st_name = elf_word_to_cpu(sym->st_name);
+ sym->st_value = elf_addr_to_cpu(sym->st_value);
+ sym->st_size = elf_xword_to_cpu(sym->st_size);
+ sym->st_shndx = elf_half_to_cpu(sym->st_shndx);
}
}
}
@@ -428,7 +456,7 @@ static void read_relocs(FILE *fp)
int i,j;
for (i = 0; i < ehdr.e_shnum; i++) {
struct section *sec = &secs[i];
- if (sec->shdr.sh_type != SHT_REL) {
+ if (sec->shdr.sh_type != SHT_REL_TYPE) {
continue;
}
sec->reltab = malloc(sec->shdr.sh_size);
@@ -445,10 +473,10 @@ static void read_relocs(FILE *fp)
die("Cannot read symbol table: %s\n",
strerror(errno));
}
- for (j = 0; j < sec->shdr.sh_size/sizeof(Elf32_Rel); j++) {
- Elf32_Rel *rel = &sec->reltab[j];
- rel->r_offset = elf32_to_cpu(rel->r_offset);
- rel->r_info = elf32_to_cpu(rel->r_info);
+ for (j = 0; j < sec->shdr.sh_size/sizeof(Elf_Rel); j++) {
+ Elf_Rel *rel = &sec->reltab[j];
+ rel->r_offset = elf_addr_to_cpu(rel->r_offset);
+ rel->r_info = elf_xword_to_cpu(rel->r_info);
}
}
}
@@ -468,8 +496,8 @@ static void print_absolute_symbols(void)
continue;
}
sym_strtab = sec->link->strtab;
- for (j = 0; j < sec->shdr.sh_size/sizeof(Elf32_Sym); j++) {
- Elf32_Sym *sym;
+ for (j = 0; j < sec->shdr.sh_size/sizeof(Elf_Sym); j++) {
+ Elf_Sym *sym;
const char *name;
sym = &sec->symtab[j];
name = sym_name(sym_strtab, sym);
@@ -478,9 +506,9 @@ static void print_absolute_symbols(void)
}
printf("%5d %08x %5d %10s %10s %12s %s\n",
j, sym->st_value, sym->st_size,
- sym_type(ELF32_ST_TYPE(sym->st_info)),
- sym_bind(ELF32_ST_BIND(sym->st_info)),
- sym_visibility(ELF32_ST_VISIBILITY(sym->st_other)),
+ sym_type(ELF_ST_TYPE(sym->st_info)),
+ sym_bind(ELF_ST_BIND(sym->st_info)),
+ sym_visibility(ELF_ST_VISIBILITY(sym->st_other)),
name);
}
}
@@ -495,9 +523,9 @@ static void print_absolute_relocs(void)
struct section *sec = &secs[i];
struct section *sec_applies, *sec_symtab;
char *sym_strtab;
- Elf32_Sym *sh_symtab;
+ Elf_Sym *sh_symtab;
int j;
- if (sec->shdr.sh_type != SHT_REL) {
+ if (sec->shdr.sh_type != SHT_REL_TYPE) {
continue;
}
sec_symtab = sec->link;
@@ -507,12 +535,12 @@ static void print_absolute_relocs(void)
}
sh_symtab = sec_symtab->symtab;
sym_strtab = sec_symtab->link->strtab;
- for (j = 0; j < sec->shdr.sh_size/sizeof(Elf32_Rel); j++) {
- Elf32_Rel *rel;
- Elf32_Sym *sym;
+ for (j = 0; j < sec->shdr.sh_size/sizeof(Elf_Rel); j++) {
+ Elf_Rel *rel;
+ Elf_Sym *sym;
const char *name;
rel = &sec->reltab[j];
- sym = &sh_symtab[ELF32_R_SYM(rel->r_info)];
+ sym = &sh_symtab[ELF_R_SYM(rel->r_info)];
name = sym_name(sym_strtab, sym);
if (sym->st_shndx != SHN_ABS) {
continue;
@@ -545,7 +573,7 @@ static void print_absolute_relocs(void)
printf("%08x %08x %10s %08x %s\n",
rel->r_offset,
rel->r_info,
- rel_type(ELF32_R_TYPE(rel->r_info)),
+ rel_type(ELF_R_TYPE(rel->r_info)),
sym->st_value,
name);
}
@@ -555,19 +583,19 @@ static void print_absolute_relocs(void)
printf("\n");
}

-static void walk_relocs(void (*visit)(Elf32_Rel *rel, Elf32_Sym *sym),
+static void walk_relocs(void (*visit)(Elf_Rel *rel, Elf_Sym *sym),
int use_real_mode)
{
int i;
/* Walk through the relocations */
for (i = 0; i < ehdr.e_shnum; i++) {
char *sym_strtab;
- Elf32_Sym *sh_symtab;
+ Elf_Sym *sh_symtab;
struct section *sec_applies, *sec_symtab;
int j;
struct section *sec = &secs[i];

- if (sec->shdr.sh_type != SHT_REL) {
+ if (sec->shdr.sh_type != SHT_REL_TYPE) {
continue;
}
sec_symtab = sec->link;
@@ -577,16 +605,16 @@ static void walk_relocs(void (*visit)(Elf32_Rel *rel, Elf32_Sym *sym),
}
sh_symtab = sec_symtab->symtab;
sym_strtab = sec_symtab->link->strtab;
- for (j = 0; j < sec->shdr.sh_size/sizeof(Elf32_Rel); j++) {
- Elf32_Rel *rel;
- Elf32_Sym *sym;
+ for (j = 0; j < sec->shdr.sh_size/sizeof(Elf_Rel); j++) {
+ Elf_Rel *rel;
+ Elf_Sym *sym;
unsigned r_type;
const char *symname;
int shn_abs;

rel = &sec->reltab[j];
- sym = &sh_symtab[ELF32_R_SYM(rel->r_info)];
- r_type = ELF32_R_TYPE(rel->r_info);
+ sym = &sh_symtab[ELF_R_SYM(rel->r_info)];
+ r_type = ELF_R_TYPE(rel->r_info);

shn_abs = sym->st_shndx == SHN_ABS;

@@ -647,18 +675,18 @@ static void walk_relocs(void (*visit)(Elf32_Rel *rel, Elf32_Sym *sym),
}
}

-static void count_reloc(Elf32_Rel *rel, Elf32_Sym *sym)
+static void count_reloc(Elf_Rel *rel, Elf_Sym *sym)
{
- if (ELF32_R_TYPE(rel->r_info) == R_386_16)
+ if (ELF_R_TYPE(rel->r_info) == R_386_16)
reloc16_count++;
else
reloc_count++;
}

-static void collect_reloc(Elf32_Rel *rel, Elf32_Sym *sym)
+static void collect_reloc(Elf_Rel *rel, Elf_Sym *sym)
{
/* Remember the address that needs to be adjusted. */
- if (ELF32_R_TYPE(rel->r_info) == R_386_16)
+ if (ELF_R_TYPE(rel->r_info) == R_386_16)
relocs16[reloc16_idx++] = rel->r_offset;
else
relocs[reloc_idx++] = rel->r_offset;

Subject: [tip:x86/kaslr] x86, relocs: Consolidate processing logic

Commit-ID: 5d442e63d6a1b5736fd48a907bd7d2d87e411816
Gitweb: http://git.kernel.org/tip/5d442e63d6a1b5736fd48a907bd7d2d87e411816
Author: Kees Cook <[email protected]>
AuthorDate: Fri, 12 Apr 2013 13:13:43 -0700
Committer: H. Peter Anvin <[email protected]>
CommitDate: Tue, 16 Apr 2013 15:19:13 -0700

x86, relocs: Consolidate processing logic

Instead of counting and then processing relocations, do it in a single
pass. This splits the processing logic into separate functions for
realmode and 32-bit (and paves the way for 64-bit). Also extracts helper
functions when emitting relocations.

Based on work by Neill Clift and Michael Davidson.

Signed-off-by: Kees Cook <[email protected]>
Link: http://lkml.kernel.org/r/[email protected]
Signed-off-by: H. Peter Anvin <[email protected]>
---
arch/x86/tools/relocs.c | 304 +++++++++++++++++++++++++++---------------------
1 file changed, 170 insertions(+), 134 deletions(-)

diff --git a/arch/x86/tools/relocs.c b/arch/x86/tools/relocs.c
index fd28ef7..bdc5930 100644
--- a/arch/x86/tools/relocs.c
+++ b/arch/x86/tools/relocs.c
@@ -2,6 +2,7 @@
#include <stdarg.h>
#include <stdlib.h>
#include <stdint.h>
+#include <inttypes.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
@@ -38,10 +39,15 @@ static void die(char *fmt, ...);

#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
static Elf_Ehdr ehdr;
-static unsigned long reloc_count, reloc_idx;
-static unsigned long *relocs;
-static unsigned long reloc16_count, reloc16_idx;
-static unsigned long *relocs16;
+
+struct relocs {
+ uint32_t *offset;
+ unsigned long count;
+ unsigned long size;
+};
+
+static struct relocs relocs16;
+static struct relocs relocs32;

struct section {
Elf_Shdr shdr;
@@ -583,8 +589,23 @@ static void print_absolute_relocs(void)
printf("\n");
}

-static void walk_relocs(void (*visit)(Elf_Rel *rel, Elf_Sym *sym),
- int use_real_mode)
+static void add_reloc(struct relocs *r, uint32_t offset)
+{
+ if (r->count == r->size) {
+ unsigned long newsize = r->size + 50000;
+ void *mem = realloc(r->offset, newsize * sizeof(r->offset[0]));
+
+ if (!mem)
+ die("realloc of %ld entries for relocs failed\n",
+ newsize);
+ r->offset = mem;
+ r->size = newsize;
+ }
+ r->offset[r->count++] = offset;
+}
+
+static void walk_relocs(int (*process)(struct section *sec, Elf_Rel *rel,
+ Elf_Sym *sym, const char *symname))
{
int i;
/* Walk through the relocations */
@@ -606,100 +627,142 @@ static void walk_relocs(void (*visit)(Elf_Rel *rel, Elf_Sym *sym),
sh_symtab = sec_symtab->symtab;
sym_strtab = sec_symtab->link->strtab;
for (j = 0; j < sec->shdr.sh_size/sizeof(Elf_Rel); j++) {
- Elf_Rel *rel;
- Elf_Sym *sym;
- unsigned r_type;
- const char *symname;
- int shn_abs;
+ Elf_Rel *rel = &sec->reltab[j];
+ Elf_Sym *sym = &sh_symtab[ELF_R_SYM(rel->r_info)];
+ const char *symname = sym_name(sym_strtab, sym);

- rel = &sec->reltab[j];
- sym = &sh_symtab[ELF_R_SYM(rel->r_info)];
- r_type = ELF_R_TYPE(rel->r_info);
-
- shn_abs = sym->st_shndx == SHN_ABS;
-
- switch (r_type) {
- case R_386_NONE:
- case R_386_PC32:
- case R_386_PC16:
- case R_386_PC8:
- /*
- * NONE can be ignored and and PC relative
- * relocations don't need to be adjusted.
- */
+ process(sec, rel, sym, symname);
+ }
+ }
+}
+
+static int do_reloc(struct section *sec, Elf_Rel *rel, Elf_Sym *sym,
+ const char *symname)
+{
+ unsigned r_type = ELF32_R_TYPE(rel->r_info);
+ int shn_abs = (sym->st_shndx == SHN_ABS) && !is_reloc(S_REL, symname);
+
+ switch (r_type) {
+ case R_386_NONE:
+ case R_386_PC32:
+ case R_386_PC16:
+ case R_386_PC8:
+ /*
+ * NONE can be ignored and PC relative relocations don't
+ * need to be adjusted.
+ */
+ break;
+
+ case R_386_32:
+ if (shn_abs) {
+ /*
+ * Whitelisted absolute symbols do not require
+ * relocation.
+ */
+ if (is_reloc(S_ABS, symname))
break;

- case R_386_16:
- symname = sym_name(sym_strtab, sym);
- if (!use_real_mode)
- goto bad;
- if (shn_abs) {
- if (is_reloc(S_ABS, symname))
- break;
- else if (!is_reloc(S_SEG, symname))
- goto bad;
- } else {
- if (is_reloc(S_LIN, symname))
- goto bad;
- else
- break;
- }
- visit(rel, sym);
+ die("Invalid absolute %s relocation: %s\n",
+ rel_type(r_type), symname);
+ break;
+ }
+
+ add_reloc(&relocs32, rel->r_offset);
+ break;
+
+ default:
+ die("Unsupported relocation type: %s (%d)\n",
+ rel_type(r_type), r_type);
+ break;
+ }
+
+ return 0;
+}
+
+static int do_reloc_real(struct section *sec, Elf_Rel *rel, Elf_Sym *sym,
+ const char *symname)
+{
+ unsigned r_type = ELF32_R_TYPE(rel->r_info);
+ int shn_abs = (sym->st_shndx == SHN_ABS) && !is_reloc(S_REL, symname);
+
+ switch (r_type) {
+ case R_386_NONE:
+ case R_386_PC32:
+ case R_386_PC16:
+ case R_386_PC8:
+ /*
+ * NONE can be ignored and PC relative relocations don't
+ * need to be adjusted.
+ */
+ break;
+
+ case R_386_16:
+ if (shn_abs) {
+ /*
+ * Whitelisted absolute symbols do not require
+ * relocation.
+ */
+ if (is_reloc(S_ABS, symname))
break;

- case R_386_32:
- symname = sym_name(sym_strtab, sym);
- if (shn_abs) {
- if (is_reloc(S_ABS, symname))
- break;
- else if (!is_reloc(S_REL, symname))
- goto bad;
- } else {
- if (use_real_mode &&
- !is_reloc(S_LIN, symname))
- break;
- }
- visit(rel, sym);
+ if (is_reloc(S_SEG, symname)) {
+ add_reloc(&relocs16, rel->r_offset);
+ break;
+ }
+ } else {
+ if (!is_reloc(S_LIN, symname))
break;
- default:
- die("Unsupported relocation type: %s (%d)\n",
- rel_type(r_type), r_type);
+ }
+ die("Invalid %s %s relocation: %s\n",
+ shn_abs ? "absolute" : "relative",
+ rel_type(r_type), symname);
+ break;
+
+ case R_386_32:
+ if (shn_abs) {
+ /*
+ * Whitelisted absolute symbols do not require
+ * relocation.
+ */
+ if (is_reloc(S_ABS, symname))
+ break;
+
+ if (is_reloc(S_REL, symname)) {
+ add_reloc(&relocs32, rel->r_offset);
break;
- bad:
- symname = sym_name(sym_strtab, sym);
- die("Invalid %s %s relocation: %s\n",
- shn_abs ? "absolute" : "relative",
- rel_type(r_type), symname);
}
+ } else {
+ if (is_reloc(S_LIN, symname))
+ add_reloc(&relocs32, rel->r_offset);
+ break;
}
- }
-}
+ die("Invalid %s %s relocation: %s\n",
+ shn_abs ? "absolute" : "relative",
+ rel_type(r_type), symname);
+ break;

-static void count_reloc(Elf_Rel *rel, Elf_Sym *sym)
-{
- if (ELF_R_TYPE(rel->r_info) == R_386_16)
- reloc16_count++;
- else
- reloc_count++;
-}
+ default:
+ die("Unsupported relocation type: %s (%d)\n",
+ rel_type(r_type), r_type);
+ break;
+ }

-static void collect_reloc(Elf_Rel *rel, Elf_Sym *sym)
-{
- /* Remember the address that needs to be adjusted. */
- if (ELF_R_TYPE(rel->r_info) == R_386_16)
- relocs16[reloc16_idx++] = rel->r_offset;
- else
- relocs[reloc_idx++] = rel->r_offset;
+ return 0;
}

static int cmp_relocs(const void *va, const void *vb)
{
- const unsigned long *a, *b;
+ const uint32_t *a, *b;
a = va; b = vb;
return (*a == *b)? 0 : (*a > *b)? 1 : -1;
}

-static int write32(unsigned int v, FILE *f)
+static void sort_relocs(struct relocs *r)
+{
+ qsort(r->offset, r->count, sizeof(r->offset[0]), cmp_relocs);
+}
+
+static int write32(uint32_t v, FILE *f)
{
unsigned char buf[4];

@@ -707,33 +770,25 @@ static int write32(unsigned int v, FILE *f)
return fwrite(buf, 1, 4, f) == 4 ? 0 : -1;
}

+static int write32_as_text(uint32_t v, FILE *f)
+{
+ return fprintf(f, "\t.long 0x%08"PRIx32"\n", v) > 0 ? 0 : -1;
+}
+
static void emit_relocs(int as_text, int use_real_mode)
{
int i;
- /* Count how many relocations I have and allocate space for them. */
- reloc_count = 0;
- walk_relocs(count_reloc, use_real_mode);
- relocs = malloc(reloc_count * sizeof(relocs[0]));
- if (!relocs) {
- die("malloc of %d entries for relocs failed\n",
- reloc_count);
- }
+ int (*write_reloc)(uint32_t, FILE *) = write32;

- relocs16 = malloc(reloc16_count * sizeof(relocs[0]));
- if (!relocs16) {
- die("malloc of %d entries for relocs16 failed\n",
- reloc16_count);
- }
/* Collect up the relocations */
- reloc_idx = 0;
- walk_relocs(collect_reloc, use_real_mode);
+ walk_relocs(use_real_mode ? do_reloc_real : do_reloc);

- if (reloc16_count && !use_real_mode)
+ if (relocs16.count && !use_real_mode)
die("Segment relocations found but --realmode not specified\n");

/* Order the relocations for more efficient processing */
- qsort(relocs, reloc_count, sizeof(relocs[0]), cmp_relocs);
- qsort(relocs16, reloc16_count, sizeof(relocs16[0]), cmp_relocs);
+ sort_relocs(&relocs16);
+ sort_relocs(&relocs32);

/* Print the relocations */
if (as_text) {
@@ -742,43 +797,24 @@ static void emit_relocs(int as_text, int use_real_mode)
*/
printf(".section \".data.reloc\",\"a\"\n");
printf(".balign 4\n");
- if (use_real_mode) {
- printf("\t.long %lu\n", reloc16_count);
- for (i = 0; i < reloc16_count; i++)
- printf("\t.long 0x%08lx\n", relocs16[i]);
- printf("\t.long %lu\n", reloc_count);
- for (i = 0; i < reloc_count; i++) {
- printf("\t.long 0x%08lx\n", relocs[i]);
- }
- } else {
- /* Print a stop */
- printf("\t.long 0x%08lx\n", (unsigned long)0);
- for (i = 0; i < reloc_count; i++) {
- printf("\t.long 0x%08lx\n", relocs[i]);
- }
- }
-
- printf("\n");
+ write_reloc = write32_as_text;
}
- else {
- if (use_real_mode) {
- write32(reloc16_count, stdout);
- for (i = 0; i < reloc16_count; i++)
- write32(relocs16[i], stdout);
- write32(reloc_count, stdout);
-
- /* Now print each relocation */
- for (i = 0; i < reloc_count; i++)
- write32(relocs[i], stdout);
- } else {
- /* Print a stop */
- write32(0, stdout);

- /* Now print each relocation */
- for (i = 0; i < reloc_count; i++) {
- write32(relocs[i], stdout);
- }
- }
+ if (use_real_mode) {
+ write_reloc(relocs16.count, stdout);
+ for (i = 0; i < relocs16.count; i++)
+ write_reloc(relocs16.offset[i], stdout);
+
+ write_reloc(relocs32.count, stdout);
+ for (i = 0; i < relocs32.count; i++)
+ write_reloc(relocs32.offset[i], stdout);
+ } else {
+ /* Print a stop */
+ write_reloc(0, stdout);
+
+ /* Now print each relocation */
+ for (i = 0; i < relocs32.count; i++)
+ write_reloc(relocs32.offset[i], stdout);
}
}

Subject: [tip:x86/kaslr] x86, relocs: Add 64-bit ELF support to relocs tool

Commit-ID: 946166af95d1defacfbc21e7c902d0556a2a7660
Gitweb: http://git.kernel.org/tip/946166af95d1defacfbc21e7c902d0556a2a7660
Author: Kees Cook <[email protected]>
AuthorDate: Fri, 12 Apr 2013 13:13:44 -0700
Committer: H. Peter Anvin <[email protected]>
CommitDate: Tue, 16 Apr 2013 15:19:22 -0700

x86, relocs: Add 64-bit ELF support to relocs tool

This adds the ability to process relocations from the 64-bit kernel ELF,
if built with ELF_BITS=64 defined. The special case for the percpu area is
handled, along with some other symbols specific to the 64-bit kernel.

Based on work by Neill Clift and Michael Davidson.

Signed-off-by: Kees Cook <[email protected]>
Link: http://lkml.kernel.org/r/[email protected]
Signed-off-by: H. Peter Anvin <[email protected]>
---
arch/x86/tools/relocs.c | 267 ++++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 261 insertions(+), 6 deletions(-)

diff --git a/arch/x86/tools/relocs.c b/arch/x86/tools/relocs.c
index bdc5930..1f7ff3d 100644
--- a/arch/x86/tools/relocs.c
+++ b/arch/x86/tools/relocs.c
@@ -17,19 +17,39 @@
#define _ElfW(bits, type) __ElfW(bits, type)
#define __ElfW(bits, type) Elf##bits##_##type

+#ifndef ELF_BITS
#define ELF_BITS 32
+#endif
+
+#if (ELF_BITS == 64)
+#define ELF_MACHINE EM_X86_64
+#define ELF_MACHINE_NAME "x86_64"
+#define SHT_REL_TYPE SHT_RELA
+#define Elf_Rel Elf64_Rela
+#else
#define ELF_MACHINE EM_386
#define ELF_MACHINE_NAME "i386"
#define SHT_REL_TYPE SHT_REL
+#define Elf_Rel ElfW(Rel)
+#endif

+#if (ELF_BITS == 64)
+#define ELF_CLASS ELFCLASS64
+#define ELF_R_SYM(val) ELF64_R_SYM(val)
+#define ELF_R_TYPE(val) ELF64_R_TYPE(val)
+#define ELF_ST_TYPE(o) ELF64_ST_TYPE(o)
+#define ELF_ST_BIND(o) ELF64_ST_BIND(o)
+#define ELF_ST_VISIBILITY(o) ELF64_ST_VISIBILITY(o)
+#else
#define ELF_CLASS ELFCLASS32
#define ELF_R_SYM(val) ELF32_R_SYM(val)
#define ELF_R_TYPE(val) ELF32_R_TYPE(val)
#define ELF_ST_TYPE(o) ELF32_ST_TYPE(o)
#define ELF_ST_BIND(o) ELF32_ST_BIND(o)
#define ELF_ST_VISIBILITY(o) ELF32_ST_VISIBILITY(o)
+#endif

-#define Elf_Rel ElfW(Rel)
+#define Elf_Addr ElfW(Addr)
#define Elf_Ehdr ElfW(Ehdr)
#define Elf_Phdr ElfW(Phdr)
#define Elf_Shdr ElfW(Shdr)
@@ -48,6 +68,7 @@ struct relocs {

static struct relocs relocs16;
static struct relocs relocs32;
+static struct relocs relocs64;

struct section {
Elf_Shdr shdr;
@@ -77,6 +98,9 @@ static const char * const sym_regex_kernel[S_NSYMTYPES] = {
"^(xen_irq_disable_direct_reloc$|"
"xen_save_fl_direct_reloc$|"
"VDSO|"
+#if (ELF_BITS == 64)
+ "__vvar_page|"
+#endif
"__crc_)",

/*
@@ -100,6 +124,11 @@ static const char * const sym_regex_kernel[S_NSYMTYPES] = {
"__end_rodata|"
"__initramfs_start|"
"(jiffies|jiffies_64)|"
+#if (ELF_BITS == 64)
+ "__per_cpu_load|"
+ "init_per_cpu__.*|"
+ "__end_rodata_hpage_align|"
+#endif
"_end)$"
};

@@ -226,6 +255,24 @@ static const char *rel_type(unsigned type)
{
static const char *type_name[] = {
#define REL_TYPE(X) [X] = #X
+#if (ELF_BITS == 64)
+ REL_TYPE(R_X86_64_NONE),
+ REL_TYPE(R_X86_64_64),
+ REL_TYPE(R_X86_64_PC32),
+ REL_TYPE(R_X86_64_GOT32),
+ REL_TYPE(R_X86_64_PLT32),
+ REL_TYPE(R_X86_64_COPY),
+ REL_TYPE(R_X86_64_GLOB_DAT),
+ REL_TYPE(R_X86_64_JUMP_SLOT),
+ REL_TYPE(R_X86_64_RELATIVE),
+ REL_TYPE(R_X86_64_GOTPCREL),
+ REL_TYPE(R_X86_64_32),
+ REL_TYPE(R_X86_64_32S),
+ REL_TYPE(R_X86_64_16),
+ REL_TYPE(R_X86_64_PC16),
+ REL_TYPE(R_X86_64_8),
+ REL_TYPE(R_X86_64_PC8),
+#else
REL_TYPE(R_386_NONE),
REL_TYPE(R_386_32),
REL_TYPE(R_386_PC32),
@@ -241,6 +288,7 @@ static const char *rel_type(unsigned type)
REL_TYPE(R_386_PC8),
REL_TYPE(R_386_16),
REL_TYPE(R_386_PC16),
+#endif
#undef REL_TYPE
};
const char *name = "unknown type rel type name";
@@ -281,15 +329,42 @@ static const char *sym_name(const char *sym_strtab, Elf_Sym *sym)
return name;
}

+static Elf_Sym *sym_lookup(const char *symname)
+{
+ int i;
+ for (i = 0; i < ehdr.e_shnum; i++) {
+ struct section *sec = &secs[i];
+ long nsyms;
+ char *strtab;
+ Elf_Sym *symtab;
+ Elf_Sym *sym;

+ if (sec->shdr.sh_type != SHT_SYMTAB)
+ continue;
+
+ nsyms = sec->shdr.sh_size/sizeof(Elf_Sym);
+ symtab = sec->symtab;
+ strtab = sec->link->strtab;
+
+ for (sym = symtab; --nsyms >= 0; sym++) {
+ if (!sym->st_name)
+ continue;
+ if (strcmp(symname, strtab + sym->st_name) == 0)
+ return sym;
+ }
+ }
+ return 0;
+}

#if BYTE_ORDER == LITTLE_ENDIAN
#define le16_to_cpu(val) (val)
#define le32_to_cpu(val) (val)
+#define le64_to_cpu(val) (val)
#endif
#if BYTE_ORDER == BIG_ENDIAN
#define le16_to_cpu(val) bswap_16(val)
#define le32_to_cpu(val) bswap_32(val)
+#define le64_to_cpu(val) bswap_64(val)
#endif

static uint16_t elf16_to_cpu(uint16_t val)
@@ -304,9 +379,20 @@ static uint32_t elf32_to_cpu(uint32_t val)

#define elf_half_to_cpu(x) elf16_to_cpu(x)
#define elf_word_to_cpu(x) elf32_to_cpu(x)
+
+#if (ELF_BITS == 64)
+static uint64_t elf64_to_cpu(uint64_t val)
+{
+ return le64_to_cpu(val);
+}
+#define elf_addr_to_cpu(x) elf64_to_cpu(x)
+#define elf_off_to_cpu(x) elf64_to_cpu(x)
+#define elf_xword_to_cpu(x) elf64_to_cpu(x)
+#else
#define elf_addr_to_cpu(x) elf32_to_cpu(x)
#define elf_off_to_cpu(x) elf32_to_cpu(x)
#define elf_xword_to_cpu(x) elf32_to_cpu(x)
+#endif

static void read_ehdr(FILE *fp)
{
@@ -483,6 +569,9 @@ static void read_relocs(FILE *fp)
Elf_Rel *rel = &sec->reltab[j];
rel->r_offset = elf_addr_to_cpu(rel->r_offset);
rel->r_info = elf_xword_to_cpu(rel->r_info);
+#if (SHT_REL_TYPE == SHT_RELA)
+ rel->r_addend = elf_xword_to_cpu(rel->r_addend);
+#endif
}
}
}
@@ -491,6 +580,13 @@ static void read_relocs(FILE *fp)
static void print_absolute_symbols(void)
{
int i;
+ const char *format;
+
+ if (ehdr.e_ident[EI_CLASS] == ELFCLASS64)
+ format = "%5d %016"PRIx64" %5"PRId64" %10s %10s %12s %s\n";
+ else
+ format = "%5d %08"PRIx32" %5"PRId32" %10s %10s %12s %s\n";
+
printf("Absolute symbols\n");
printf(" Num: Value Size Type Bind Visibility Name\n");
for (i = 0; i < ehdr.e_shnum; i++) {
@@ -510,7 +606,7 @@ static void print_absolute_symbols(void)
if (sym->st_shndx != SHN_ABS) {
continue;
}
- printf("%5d %08x %5d %10s %10s %12s %s\n",
+ printf(format,
j, sym->st_value, sym->st_size,
sym_type(ELF_ST_TYPE(sym->st_info)),
sym_bind(ELF_ST_BIND(sym->st_info)),
@@ -524,6 +620,12 @@ static void print_absolute_symbols(void)
static void print_absolute_relocs(void)
{
int i, printed = 0;
+ const char *format;
+
+ if (ehdr.e_ident[EI_CLASS] == ELFCLASS64)
+ format = "%016"PRIx64" %016"PRIx64" %10s %016"PRIx64" %s\n";
+ else
+ format = "%08"PRIx32" %08"PRIx32" %10s %08"PRIx32" %s\n";

for (i = 0; i < ehdr.e_shnum; i++) {
struct section *sec = &secs[i];
@@ -576,7 +678,7 @@ static void print_absolute_relocs(void)
printed = 1;
}

- printf("%08x %08x %10s %08x %s\n",
+ printf(format,
rel->r_offset,
rel->r_info,
rel_type(ELF_R_TYPE(rel->r_info)),
@@ -636,8 +738,140 @@ static void walk_relocs(int (*process)(struct section *sec, Elf_Rel *rel,
}
}

-static int do_reloc(struct section *sec, Elf_Rel *rel, Elf_Sym *sym,
- const char *symname)
+/*
+ * The .data..percpu section is a special case for x86_64 SMP kernels.
+ * It is used to initialize the actual per_cpu areas and to provide
+ * definitions for the per_cpu variables that correspond to their offsets
+ * within the percpu area. Since the values of all of the symbols need
+ * to be offsets from the start of the per_cpu area the virtual address
+ * (sh_addr) of .data..percpu is 0 in SMP kernels.
+ *
+ * This means that:
+ *
+ * Relocations that reference symbols in the per_cpu area do not
+ * need further relocation (since the value is an offset relative
+ * to the start of the per_cpu area that does not change).
+ *
+ * Relocations that apply to the per_cpu area need to have their
+ * offset adjusted by by the value of __per_cpu_load to make them
+ * point to the correct place in the loaded image (because the
+ * virtual address of .data..percpu is 0).
+ *
+ * For non SMP kernels .data..percpu is linked as part of the normal
+ * kernel data and does not require special treatment.
+ *
+ */
+static int per_cpu_shndx = -1;
+Elf_Addr per_cpu_load_addr;
+
+static void percpu_init(void)
+{
+ int i;
+ for (i = 0; i < ehdr.e_shnum; i++) {
+ ElfW(Sym) *sym;
+ if (strcmp(sec_name(i), ".data..percpu"))
+ continue;
+
+ if (secs[i].shdr.sh_addr != 0) /* non SMP kernel */
+ return;
+
+ sym = sym_lookup("__per_cpu_load");
+ if (!sym)
+ die("can't find __per_cpu_load\n");
+
+ per_cpu_shndx = i;
+ per_cpu_load_addr = sym->st_value;
+ return;
+ }
+}
+
+/*
+ * Check to see if a symbol lies in the .data..percpu section.
+ * For some as yet not understood reason the "__init_begin"
+ * symbol which immediately preceeds the .data..percpu section
+ * also shows up as it it were part of it so we do an explict
+ * check for that symbol name and ignore it.
+ */
+static int is_percpu_sym(ElfW(Sym) *sym, const char *symname)
+{
+ return (sym->st_shndx == per_cpu_shndx) &&
+ strcmp(symname, "__init_begin");
+}
+
+static int do_reloc64(struct section *sec, Elf_Rel *rel, ElfW(Sym) *sym,
+ const char *symname)
+{
+ unsigned r_type = ELF64_R_TYPE(rel->r_info);
+ ElfW(Addr) offset = rel->r_offset;
+ int shn_abs = (sym->st_shndx == SHN_ABS) && !is_reloc(S_REL, symname);
+
+ if (sym->st_shndx == SHN_UNDEF)
+ return 0;
+
+ /*
+ * Adjust the offset if this reloc applies to the percpu section.
+ */
+ if (sec->shdr.sh_info == per_cpu_shndx)
+ offset += per_cpu_load_addr;
+
+ switch (r_type) {
+ case R_X86_64_NONE:
+ case R_X86_64_PC32:
+ /*
+ * NONE can be ignored and PC relative relocations don't
+ * need to be adjusted.
+ */
+ break;
+
+ case R_X86_64_32:
+ case R_X86_64_32S:
+ case R_X86_64_64:
+ /*
+ * References to the percpu area don't need to be adjusted.
+ */
+ if (is_percpu_sym(sym, symname))
+ break;
+
+ if (shn_abs) {
+ /*
+ * Whitelisted absolute symbols do not require
+ * relocation.
+ */
+ if (is_reloc(S_ABS, symname))
+ break;
+
+ die("Invalid absolute %s relocation: %s\n",
+ rel_type(r_type), symname);
+ break;
+ }
+
+ /*
+ * Relocation offsets for 64 bit kernels are output
+ * as 32 bits and sign extended back to 64 bits when
+ * the relocations are processed.
+ * Make sure that the offset will fit.
+ */
+ if ((int32_t)offset != (int64_t)offset)
+ die("Relocation offset doesn't fit in 32 bits\n");
+
+ if (r_type == R_X86_64_64)
+ add_reloc(&relocs64, offset);
+ else
+ add_reloc(&relocs32, offset);
+ break;
+
+ default:
+ die("Unsupported relocation type: %s (%d)\n",
+ rel_type(r_type), r_type);
+ break;
+ }
+
+ return 0;
+}
+
+
+static int do_reloc32(struct section *sec, Elf_Rel *rel, Elf_Sym *sym,
+ const char *symname)
{
unsigned r_type = ELF32_R_TYPE(rel->r_info);
int shn_abs = (sym->st_shndx == SHN_ABS) && !is_reloc(S_REL, symname);
@@ -779,9 +1013,18 @@ static void emit_relocs(int as_text, int use_real_mode)
{
int i;
int (*write_reloc)(uint32_t, FILE *) = write32;
+ int (*do_reloc)(struct section *sec, Elf_Rel *rel, Elf_Sym *sym,
+ const char *symname);
+
+ if (ehdr.e_ident[EI_CLASS] == ELFCLASS64)
+ do_reloc = do_reloc64;
+ else if (!use_real_mode)
+ do_reloc = do_reloc32;
+ else
+ do_reloc = do_reloc_real;

/* Collect up the relocations */
- walk_relocs(use_real_mode ? do_reloc_real : do_reloc);
+ walk_relocs(do_reloc);

if (relocs16.count && !use_real_mode)
die("Segment relocations found but --realmode not specified\n");
@@ -789,6 +1032,7 @@ static void emit_relocs(int as_text, int use_real_mode)
/* Order the relocations for more efficient processing */
sort_relocs(&relocs16);
sort_relocs(&relocs32);
+ sort_relocs(&relocs64);

/* Print the relocations */
if (as_text) {
@@ -809,6 +1053,15 @@ static void emit_relocs(int as_text, int use_real_mode)
for (i = 0; i < relocs32.count; i++)
write_reloc(relocs32.offset[i], stdout);
} else {
+ if (ehdr.e_ident[EI_CLASS] == ELFCLASS64) {
+ /* Print a stop */
+ write_reloc(0, stdout);
+
+ /* Now print each relocation */
+ for (i = 0; i < relocs64.count; i++)
+ write_reloc(relocs64.offset[i], stdout);
+ }
+
/* Print a stop */
write_reloc(0, stdout);

@@ -876,6 +1129,8 @@ int main(int argc, char **argv)
read_strtabs(fp);
read_symtabs(fp);
read_relocs(fp);
+ if (ehdr.e_ident[EI_CLASS] == ELFCLASS64)
+ percpu_init();
if (show_absolute_syms) {
print_absolute_symbols();
goto out;

Subject: [tip:x86/kaslr] x86, relocs: Build separate 32/64-bit tools

Commit-ID: 17c961f7702ff6037b66bb2e5f3ddd58de4ce7e5
Gitweb: http://git.kernel.org/tip/17c961f7702ff6037b66bb2e5f3ddd58de4ce7e5
Author: Kees Cook <[email protected]>
AuthorDate: Fri, 12 Apr 2013 13:13:45 -0700
Committer: H. Peter Anvin <[email protected]>
CommitDate: Tue, 16 Apr 2013 15:22:01 -0700

x86, relocs: Build separate 32/64-bit tools

Since the ELF structures and access macros change size based on 32 vs
64 bits, build a separate 32-bit relocs tool (for handling realmode
and 32-bit relocations), and a 64-bit relocs tool (for handling 64-bit
kernel relocations).

Signed-off-by: Kees Cook <[email protected]>
Link: http://lkml.kernel.org/r/[email protected]
Signed-off-by: H. Peter Anvin <[email protected]>
---
arch/x86/boot/compressed/Makefile | 2 +-
arch/x86/realmode/rm/Makefile | 2 +-
arch/x86/tools/.gitignore | 3 ++-
arch/x86/tools/Makefile | 20 ++++++++++++++++++--
4 files changed, 22 insertions(+), 5 deletions(-)

diff --git a/arch/x86/boot/compressed/Makefile b/arch/x86/boot/compressed/Makefile
index 5ef205c..0dac175 100644
--- a/arch/x86/boot/compressed/Makefile
+++ b/arch/x86/boot/compressed/Makefile
@@ -44,7 +44,7 @@ $(obj)/vmlinux.bin: vmlinux FORCE

targets += $(patsubst $(obj)/%,%,$(VMLINUX_OBJS)) vmlinux.bin.all vmlinux.relocs

-CMD_RELOCS = arch/x86/tools/relocs
+CMD_RELOCS = arch/x86/tools/relocs_$(BITS)
quiet_cmd_relocs = RELOCS $@
cmd_relocs = $(CMD_RELOCS) $< > $@;$(CMD_RELOCS) --abs-relocs $<
$(obj)/vmlinux.relocs: vmlinux FORCE
diff --git a/arch/x86/realmode/rm/Makefile b/arch/x86/realmode/rm/Makefile
index 8869287..2b1e429 100644
--- a/arch/x86/realmode/rm/Makefile
+++ b/arch/x86/realmode/rm/Makefile
@@ -56,7 +56,7 @@ $(obj)/realmode.bin: $(obj)/realmode.elf $(obj)/realmode.relocs
$(call if_changed,objcopy)

quiet_cmd_relocs = RELOCS $@
- cmd_relocs = arch/x86/tools/relocs --realmode $< > $@
+ cmd_relocs = arch/x86/tools/relocs_32 --realmode $< > $@

targets += realmode.relocs
$(obj)/realmode.relocs: $(obj)/realmode.elf FORCE
diff --git a/arch/x86/tools/.gitignore b/arch/x86/tools/.gitignore
index be0ed06..2b45d5f 100644
--- a/arch/x86/tools/.gitignore
+++ b/arch/x86/tools/.gitignore
@@ -1 +1,2 @@
-relocs
+relocs_32*
+relocs_64*
diff --git a/arch/x86/tools/Makefile b/arch/x86/tools/Makefile
index bae601f..a8cb70c 100644
--- a/arch/x86/tools/Makefile
+++ b/arch/x86/tools/Makefile
@@ -37,6 +37,22 @@ $(obj)/test_get_len.o: $(srctree)/arch/x86/lib/insn.c $(srctree)/arch/x86/lib/in

$(obj)/insn_sanity.o: $(srctree)/arch/x86/lib/insn.c $(srctree)/arch/x86/lib/inat.c $(srctree)/arch/x86/include/asm/inat_types.h $(srctree)/arch/x86/include/asm/inat.h $(srctree)/arch/x86/include/asm/insn.h $(objtree)/arch/x86/lib/inat-tables.c

+HOSTCFLAGS_relocs_32.o += -DELF_BITS=32
+HOSTCFLAGS_relocs_64.o += -DELF_BITS=64
+
+quiet_cmd_cp_reloc = GEN $@
+ cmd_cp_reloc = cp $< $@
+
+$(obj)/relocs_%.c: $(srctree)/arch/x86/tools/relocs.c
+ $(call cmd,cp_reloc)
+
HOST_EXTRACFLAGS += -I$(srctree)/tools/include
-hostprogs-y += relocs
-relocs: $(obj)/relocs
+hostprogs-y += relocs_$(BITS)
+relocs_binaries = relocs_$(BITS)
+ifeq ($(CONFIG_64BIT),y)
+ hostprogs-y += relocs_32
+ relocs_binaries += relocs_32
+endif
+relocs: $(relocs_binaries)
+relocs_32: $(obj)/relocs_32
+relocs_64: $(obj)/relocs_64

Subject: [tip:x86/kaslr] x86, relocs: Refactor the relocs tool to merge 32- and 64-bit ELF

Commit-ID: c889ba801dc3b3a0155fa77d567f2c3a6097de1c
Gitweb: http://git.kernel.org/tip/c889ba801dc3b3a0155fa77d567f2c3a6097de1c
Author: H. Peter Anvin <[email protected]>
AuthorDate: Tue, 16 Apr 2013 16:02:58 -0700
Committer: H. Peter Anvin <[email protected]>
CommitDate: Tue, 16 Apr 2013 16:02:58 -0700

x86, relocs: Refactor the relocs tool to merge 32- and 64-bit ELF

Refactor the relocs tool so that the same tool can handle 32- and
64-bit ELF.

Signed-off-by: H. Peter Anvin <[email protected]>
Cc: Kees Cook <[email protected]>
Link: http://lkml.kernel.org/r/[email protected]
---
arch/x86/boot/compressed/Makefile | 2 +-
arch/x86/realmode/rm/Makefile | 2 +-
arch/x86/tools/.gitignore | 3 +-
arch/x86/tools/Makefile | 21 +----
arch/x86/tools/relocs.c | 162 ++++++++------------------------------
arch/x86/tools/relocs.h | 36 +++++++++
arch/x86/tools/relocs_32.c | 17 ++++
arch/x86/tools/relocs_64.c | 17 ++++
arch/x86/tools/relocs_common.c | 76 ++++++++++++++++++
9 files changed, 183 insertions(+), 153 deletions(-)

diff --git a/arch/x86/boot/compressed/Makefile b/arch/x86/boot/compressed/Makefile
index 0dac175..5ef205c 100644
--- a/arch/x86/boot/compressed/Makefile
+++ b/arch/x86/boot/compressed/Makefile
@@ -44,7 +44,7 @@ $(obj)/vmlinux.bin: vmlinux FORCE

targets += $(patsubst $(obj)/%,%,$(VMLINUX_OBJS)) vmlinux.bin.all vmlinux.relocs

-CMD_RELOCS = arch/x86/tools/relocs_$(BITS)
+CMD_RELOCS = arch/x86/tools/relocs
quiet_cmd_relocs = RELOCS $@
cmd_relocs = $(CMD_RELOCS) $< > $@;$(CMD_RELOCS) --abs-relocs $<
$(obj)/vmlinux.relocs: vmlinux FORCE
diff --git a/arch/x86/realmode/rm/Makefile b/arch/x86/realmode/rm/Makefile
index 2b1e429..8869287 100644
--- a/arch/x86/realmode/rm/Makefile
+++ b/arch/x86/realmode/rm/Makefile
@@ -56,7 +56,7 @@ $(obj)/realmode.bin: $(obj)/realmode.elf $(obj)/realmode.relocs
$(call if_changed,objcopy)

quiet_cmd_relocs = RELOCS $@
- cmd_relocs = arch/x86/tools/relocs_32 --realmode $< > $@
+ cmd_relocs = arch/x86/tools/relocs --realmode $< > $@

targets += realmode.relocs
$(obj)/realmode.relocs: $(obj)/realmode.elf FORCE
diff --git a/arch/x86/tools/.gitignore b/arch/x86/tools/.gitignore
index 2b45d5f..be0ed06 100644
--- a/arch/x86/tools/.gitignore
+++ b/arch/x86/tools/.gitignore
@@ -1,2 +1 @@
-relocs_32*
-relocs_64*
+relocs
diff --git a/arch/x86/tools/Makefile b/arch/x86/tools/Makefile
index a8cb70c..e812034 100644
--- a/arch/x86/tools/Makefile
+++ b/arch/x86/tools/Makefile
@@ -37,22 +37,7 @@ $(obj)/test_get_len.o: $(srctree)/arch/x86/lib/insn.c $(srctree)/arch/x86/lib/in

$(obj)/insn_sanity.o: $(srctree)/arch/x86/lib/insn.c $(srctree)/arch/x86/lib/inat.c $(srctree)/arch/x86/include/asm/inat_types.h $(srctree)/arch/x86/include/asm/inat.h $(srctree)/arch/x86/include/asm/insn.h $(objtree)/arch/x86/lib/inat-tables.c

-HOSTCFLAGS_relocs_32.o += -DELF_BITS=32
-HOSTCFLAGS_relocs_64.o += -DELF_BITS=64
-
-quiet_cmd_cp_reloc = GEN $@
- cmd_cp_reloc = cp $< $@
-
-$(obj)/relocs_%.c: $(srctree)/arch/x86/tools/relocs.c
- $(call cmd,cp_reloc)
-
HOST_EXTRACFLAGS += -I$(srctree)/tools/include
-hostprogs-y += relocs_$(BITS)
-relocs_binaries = relocs_$(BITS)
-ifeq ($(CONFIG_64BIT),y)
- hostprogs-y += relocs_32
- relocs_binaries += relocs_32
-endif
-relocs: $(relocs_binaries)
-relocs_32: $(obj)/relocs_32
-relocs_64: $(obj)/relocs_64
+hostprogs-y += relocs
+relocs-objs := relocs_32.o relocs_64.o relocs_common.o
+relocs: $(obj)/relocs
diff --git a/arch/x86/tools/relocs.c b/arch/x86/tools/relocs.c
index 1f7ff3d..590be10 100644
--- a/arch/x86/tools/relocs.c
+++ b/arch/x86/tools/relocs.c
@@ -1,63 +1,15 @@
-#include <stdio.h>
-#include <stdarg.h>
-#include <stdlib.h>
-#include <stdint.h>
-#include <inttypes.h>
-#include <string.h>
-#include <errno.h>
-#include <unistd.h>
-#include <elf.h>
-#include <byteswap.h>
-#define USE_BSD
-#include <endian.h>
-#include <regex.h>
-#include <tools/le_byteshift.h>
+/* This is included from relocs_32/64.c */

#define ElfW(type) _ElfW(ELF_BITS, type)
#define _ElfW(bits, type) __ElfW(bits, type)
#define __ElfW(bits, type) Elf##bits##_##type

-#ifndef ELF_BITS
-#define ELF_BITS 32
-#endif
-
-#if (ELF_BITS == 64)
-#define ELF_MACHINE EM_X86_64
-#define ELF_MACHINE_NAME "x86_64"
-#define SHT_REL_TYPE SHT_RELA
-#define Elf_Rel Elf64_Rela
-#else
-#define ELF_MACHINE EM_386
-#define ELF_MACHINE_NAME "i386"
-#define SHT_REL_TYPE SHT_REL
-#define Elf_Rel ElfW(Rel)
-#endif
-
-#if (ELF_BITS == 64)
-#define ELF_CLASS ELFCLASS64
-#define ELF_R_SYM(val) ELF64_R_SYM(val)
-#define ELF_R_TYPE(val) ELF64_R_TYPE(val)
-#define ELF_ST_TYPE(o) ELF64_ST_TYPE(o)
-#define ELF_ST_BIND(o) ELF64_ST_BIND(o)
-#define ELF_ST_VISIBILITY(o) ELF64_ST_VISIBILITY(o)
-#else
-#define ELF_CLASS ELFCLASS32
-#define ELF_R_SYM(val) ELF32_R_SYM(val)
-#define ELF_R_TYPE(val) ELF32_R_TYPE(val)
-#define ELF_ST_TYPE(o) ELF32_ST_TYPE(o)
-#define ELF_ST_BIND(o) ELF32_ST_BIND(o)
-#define ELF_ST_VISIBILITY(o) ELF32_ST_VISIBILITY(o)
-#endif
-
#define Elf_Addr ElfW(Addr)
#define Elf_Ehdr ElfW(Ehdr)
#define Elf_Phdr ElfW(Phdr)
#define Elf_Shdr ElfW(Shdr)
#define Elf_Sym ElfW(Sym)

-static void die(char *fmt, ...);
-
-#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
static Elf_Ehdr ehdr;

struct relocs {
@@ -79,14 +31,6 @@ struct section {
};
static struct section *secs;

-enum symtype {
- S_ABS,
- S_REL,
- S_SEG,
- S_LIN,
- S_NSYMTYPES
-};
-
static const char * const sym_regex_kernel[S_NSYMTYPES] = {
/*
* Following symbols have been audited. There values are constant and do
@@ -98,7 +42,7 @@ static const char * const sym_regex_kernel[S_NSYMTYPES] = {
"^(xen_irq_disable_direct_reloc$|"
"xen_save_fl_direct_reloc$|"
"VDSO|"
-#if (ELF_BITS == 64)
+#if ELF_BITS == 64
"__vvar_page|"
#endif
"__crc_)",
@@ -124,7 +68,7 @@ static const char * const sym_regex_kernel[S_NSYMTYPES] = {
"__end_rodata|"
"__initramfs_start|"
"(jiffies|jiffies_64)|"
-#if (ELF_BITS == 64)
+#if ELF_BITS == 64
"__per_cpu_load|"
"init_per_cpu__.*|"
"__end_rodata_hpage_align|"
@@ -189,15 +133,6 @@ static void regex_init(int use_real_mode)
}
}

-static void die(char *fmt, ...)
-{
- va_list ap;
- va_start(ap, fmt);
- vfprintf(stderr, fmt, ap);
- va_end(ap);
- exit(1);
-}
-
static const char *sym_type(unsigned type)
{
static const char *type_name[] = {
@@ -255,7 +190,7 @@ static const char *rel_type(unsigned type)
{
static const char *type_name[] = {
#define REL_TYPE(X) [X] = #X
-#if (ELF_BITS == 64)
+#if ELF_BITS == 64
REL_TYPE(R_X86_64_NONE),
REL_TYPE(R_X86_64_64),
REL_TYPE(R_X86_64_PC32),
@@ -380,7 +315,7 @@ static uint32_t elf32_to_cpu(uint32_t val)
#define elf_half_to_cpu(x) elf16_to_cpu(x)
#define elf_word_to_cpu(x) elf32_to_cpu(x)

-#if (ELF_BITS == 64)
+#if ELF_BITS == 64
static uint64_t elf64_to_cpu(uint64_t val)
{
return le64_to_cpu(val);
@@ -582,7 +517,7 @@ static void print_absolute_symbols(void)
int i;
const char *format;

- if (ehdr.e_ident[EI_CLASS] == ELFCLASS64)
+ if (ELF_BITS == 64)
format = "%5d %016"PRIx64" %5"PRId64" %10s %10s %12s %s\n";
else
format = "%5d %08"PRIx32" %5"PRId32" %10s %10s %12s %s\n";
@@ -622,7 +557,7 @@ static void print_absolute_relocs(void)
int i, printed = 0;
const char *format;

- if (ehdr.e_ident[EI_CLASS] == ELFCLASS64)
+ if (ELF_BITS == 64)
format = "%016"PRIx64" %016"PRIx64" %10s %016"PRIx64" %s\n";
else
format = "%08"PRIx32" %08"PRIx32" %10s %08"PRIx32" %s\n";
@@ -785,6 +720,8 @@ static void percpu_init(void)
}
}

+#if ELF_BITS == 64
+
/*
* Check to see if a symbol lies in the .data..percpu section.
* For some as yet not understood reason the "__init_begin"
@@ -798,6 +735,7 @@ static int is_percpu_sym(ElfW(Sym) *sym, const char *symname)
strcmp(symname, "__init_begin");
}

+
static int do_reloc64(struct section *sec, Elf_Rel *rel, ElfW(Sym) *sym,
const char *symname)
{
@@ -869,6 +807,7 @@ static int do_reloc64(struct section *sec, Elf_Rel *rel, ElfW(Sym) *sym,
return 0;
}

+#else

static int do_reloc32(struct section *sec, Elf_Rel *rel, Elf_Sym *sym,
const char *symname)
@@ -984,6 +923,8 @@ static int do_reloc_real(struct section *sec, Elf_Rel *rel, Elf_Sym *sym,
return 0;
}

+#endif
+
static int cmp_relocs(const void *va, const void *vb)
{
const uint32_t *a, *b;
@@ -1016,12 +957,17 @@ static void emit_relocs(int as_text, int use_real_mode)
int (*do_reloc)(struct section *sec, Elf_Rel *rel, Elf_Sym *sym,
const char *symname);

- if (ehdr.e_ident[EI_CLASS] == ELFCLASS64)
+#if ELF_BITS == 64
+ if (!use_real_mode)
do_reloc = do_reloc64;
- else if (!use_real_mode)
+ else
+ die("--realmode not valid for a 64-bit ELF file");
+#else
+ if (!use_real_mode)
do_reloc = do_reloc32;
else
do_reloc = do_reloc_real;
+#endif

/* Collect up the relocations */
walk_relocs(do_reloc);
@@ -1053,7 +999,7 @@ static void emit_relocs(int as_text, int use_real_mode)
for (i = 0; i < relocs32.count; i++)
write_reloc(relocs32.offset[i], stdout);
} else {
- if (ehdr.e_ident[EI_CLASS] == ELFCLASS64) {
+ if (ELF_BITS == 64) {
/* Print a stop */
write_reloc(0, stdout);

@@ -1071,76 +1017,30 @@ static void emit_relocs(int as_text, int use_real_mode)
}
}

-static void usage(void)
-{
- die("relocs [--abs-syms|--abs-relocs|--text|--realmode] vmlinux\n");
-}
+#if ELF_BITS == 64
+# define process process_64
+#else
+# define process process_32
+#endif

-int main(int argc, char **argv)
+void process(FILE *fp, int use_real_mode, int as_text,
+ int show_absolute_syms, int show_absolute_relocs)
{
- int show_absolute_syms, show_absolute_relocs;
- int as_text, use_real_mode;
- const char *fname;
- FILE *fp;
- int i;
-
- show_absolute_syms = 0;
- show_absolute_relocs = 0;
- as_text = 0;
- use_real_mode = 0;
- fname = NULL;
- for (i = 1; i < argc; i++) {
- char *arg = argv[i];
- if (*arg == '-') {
- if (strcmp(arg, "--abs-syms") == 0) {
- show_absolute_syms = 1;
- continue;
- }
- if (strcmp(arg, "--abs-relocs") == 0) {
- show_absolute_relocs = 1;
- continue;
- }
- if (strcmp(arg, "--text") == 0) {
- as_text = 1;
- continue;
- }
- if (strcmp(arg, "--realmode") == 0) {
- use_real_mode = 1;
- continue;
- }
- }
- else if (!fname) {
- fname = arg;
- continue;
- }
- usage();
- }
- if (!fname) {
- usage();
- }
regex_init(use_real_mode);
- fp = fopen(fname, "r");
- if (!fp) {
- die("Cannot open %s: %s\n",
- fname, strerror(errno));
- }
read_ehdr(fp);
read_shdrs(fp);
read_strtabs(fp);
read_symtabs(fp);
read_relocs(fp);
- if (ehdr.e_ident[EI_CLASS] == ELFCLASS64)
+ if (ELF_BITS == 64)
percpu_init();
if (show_absolute_syms) {
print_absolute_symbols();
- goto out;
+ return;
}
if (show_absolute_relocs) {
print_absolute_relocs();
- goto out;
+ return;
}
emit_relocs(as_text, use_real_mode);
-out:
- fclose(fp);
- return 0;
}
diff --git a/arch/x86/tools/relocs.h b/arch/x86/tools/relocs.h
new file mode 100644
index 0000000..07cdb1e
--- /dev/null
+++ b/arch/x86/tools/relocs.h
@@ -0,0 +1,36 @@
+#ifndef RELOCS_H
+#define RELOCS_H
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <inttypes.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <elf.h>
+#include <byteswap.h>
+#define USE_BSD
+#include <endian.h>
+#include <regex.h>
+#include <tools/le_byteshift.h>
+
+void die(char *fmt, ...);
+
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
+
+enum symtype {
+ S_ABS,
+ S_REL,
+ S_SEG,
+ S_LIN,
+ S_NSYMTYPES
+};
+
+void process_32(FILE *fp, int use_real_mode, int as_text,
+ int show_absolute_syms, int show_absolute_relocs);
+void process_64(FILE *fp, int use_real_mode, int as_text,
+ int show_absolute_syms, int show_absolute_relocs);
+
+#endif /* RELOCS_H */
diff --git a/arch/x86/tools/relocs_32.c b/arch/x86/tools/relocs_32.c
new file mode 100644
index 0000000..b2ade2b
--- /dev/null
+++ b/arch/x86/tools/relocs_32.c
@@ -0,0 +1,17 @@
+#include "relocs.h"
+
+#define ELF_BITS 32
+
+#define ELF_MACHINE EM_386
+#define ELF_MACHINE_NAME "i386"
+#define SHT_REL_TYPE SHT_REL
+#define Elf_Rel ElfW(Rel)
+
+#define ELF_CLASS ELFCLASS32
+#define ELF_R_SYM(val) ELF32_R_SYM(val)
+#define ELF_R_TYPE(val) ELF32_R_TYPE(val)
+#define ELF_ST_TYPE(o) ELF32_ST_TYPE(o)
+#define ELF_ST_BIND(o) ELF32_ST_BIND(o)
+#define ELF_ST_VISIBILITY(o) ELF32_ST_VISIBILITY(o)
+
+#include "relocs.c"
diff --git a/arch/x86/tools/relocs_64.c b/arch/x86/tools/relocs_64.c
new file mode 100644
index 0000000..56b61b7
--- /dev/null
+++ b/arch/x86/tools/relocs_64.c
@@ -0,0 +1,17 @@
+#include "relocs.h"
+
+#define ELF_BITS 64
+
+#define ELF_MACHINE EM_X86_64
+#define ELF_MACHINE_NAME "x86_64"
+#define SHT_REL_TYPE SHT_RELA
+#define Elf_Rel Elf64_Rela
+
+#define ELF_CLASS ELFCLASS64
+#define ELF_R_SYM(val) ELF64_R_SYM(val)
+#define ELF_R_TYPE(val) ELF64_R_TYPE(val)
+#define ELF_ST_TYPE(o) ELF64_ST_TYPE(o)
+#define ELF_ST_BIND(o) ELF64_ST_BIND(o)
+#define ELF_ST_VISIBILITY(o) ELF64_ST_VISIBILITY(o)
+
+#include "relocs.c"
diff --git a/arch/x86/tools/relocs_common.c b/arch/x86/tools/relocs_common.c
new file mode 100644
index 0000000..44d3968
--- /dev/null
+++ b/arch/x86/tools/relocs_common.c
@@ -0,0 +1,76 @@
+#include "relocs.h"
+
+void die(char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ exit(1);
+}
+
+static void usage(void)
+{
+ die("relocs [--abs-syms|--abs-relocs|--text|--realmode] vmlinux\n");
+}
+
+int main(int argc, char **argv)
+{
+ int show_absolute_syms, show_absolute_relocs;
+ int as_text, use_real_mode;
+ const char *fname;
+ FILE *fp;
+ int i;
+ unsigned char e_ident[EI_NIDENT];
+
+ show_absolute_syms = 0;
+ show_absolute_relocs = 0;
+ as_text = 0;
+ use_real_mode = 0;
+ fname = NULL;
+ for (i = 1; i < argc; i++) {
+ char *arg = argv[i];
+ if (*arg == '-') {
+ if (strcmp(arg, "--abs-syms") == 0) {
+ show_absolute_syms = 1;
+ continue;
+ }
+ if (strcmp(arg, "--abs-relocs") == 0) {
+ show_absolute_relocs = 1;
+ continue;
+ }
+ if (strcmp(arg, "--text") == 0) {
+ as_text = 1;
+ continue;
+ }
+ if (strcmp(arg, "--realmode") == 0) {
+ use_real_mode = 1;
+ continue;
+ }
+ }
+ else if (!fname) {
+ fname = arg;
+ continue;
+ }
+ usage();
+ }
+ if (!fname) {
+ usage();
+ }
+ fp = fopen(fname, "r");
+ if (!fp) {
+ die("Cannot open %s: %s\n", fname, strerror(errno));
+ }
+ if (fread(&e_ident, 1, EI_NIDENT, fp) != EI_NIDENT) {
+ die("Cannot read %s: %s", fname, strerror(errno));
+ }
+ rewind(fp);
+ if (e_ident[EI_CLASS] == ELFCLASS64)
+ process_64(fp, use_real_mode, as_text,
+ show_absolute_syms, show_absolute_relocs);
+ else
+ process_32(fp, use_real_mode, as_text,
+ show_absolute_syms, show_absolute_relocs);
+ fclose(fp);
+ return 0;
+}