Re: [PATCH v2 6/9] x86/tools: Add relative relocs for randomized functions

From: Kees Cook
Date: Thu May 21 2020 - 15:54:31 EST


On Thu, May 21, 2020 at 09:56:37AM -0700, Kristen Carlson Accardi wrote:
> When reordering functions, the relative offsets for relocs that
> are either in the randomized sections, or refer to the randomized
> sections will need to be adjusted. Add code to detect whether a
> reloc satisifies these cases, and if so, add them to the appropriate
> reloc list.
>
> Signed-off-by: Kristen Carlson Accardi <kristen@xxxxxxxxxxxxxxx>
> Reviewed-by: Tony Luck <tony.luck@xxxxxxxxx>
> Tested-by: Tony Luck <tony.luck@xxxxxxxxx>

I'm just down to bikeshedding here (see below).

> ---
> arch/x86/boot/compressed/Makefile | 7 +++-
> arch/x86/tools/relocs.c | 55 ++++++++++++++++++++++++-------
> arch/x86/tools/relocs.h | 4 +--
> arch/x86/tools/relocs_common.c | 15 ++++++---
> 4 files changed, 62 insertions(+), 19 deletions(-)
>
> diff --git a/arch/x86/boot/compressed/Makefile b/arch/x86/boot/compressed/Makefile
> index 5f7c262bcc99..3a5a004498de 100644
> --- a/arch/x86/boot/compressed/Makefile
> +++ b/arch/x86/boot/compressed/Makefile
> @@ -117,6 +117,11 @@ $(obj)/vmlinux: $(vmlinux-objs-y) FORCE
> $(call if_changed,check-and-link-vmlinux)
>
> OBJCOPYFLAGS_vmlinux.bin := -R .comment -S
> +
> +ifdef CONFIG_FG_KASLR
> + RELOCS_ARGS += --fg-kaslr
> +endif
> +
> $(obj)/vmlinux.bin: vmlinux FORCE
> $(call if_changed,objcopy)
>
> @@ -124,7 +129,7 @@ targets += $(patsubst $(obj)/%,%,$(vmlinux-objs-y)) vmlinux.bin.all vmlinux.relo
>
> CMD_RELOCS = arch/x86/tools/relocs
> quiet_cmd_relocs = RELOCS $@
> - cmd_relocs = $(CMD_RELOCS) $< > $@;$(CMD_RELOCS) --abs-relocs $<
> + cmd_relocs = $(CMD_RELOCS) $(RELOCS_ARGS) $< > $@;$(CMD_RELOCS) $(RELOCS_ARGS) --abs-relocs $<
> $(obj)/vmlinux.relocs: vmlinux FORCE
> $(call if_changed,relocs)
>
> diff --git a/arch/x86/tools/relocs.c b/arch/x86/tools/relocs.c
> index a00dc133f109..bf51ff1854ff 100644
> --- a/arch/x86/tools/relocs.c
> +++ b/arch/x86/tools/relocs.c
> @@ -42,6 +42,8 @@ struct section {
> };
> static struct section *secs;
>
> +static int fg_kaslr;

I find the shifting name for fg_kaslr, fgkaslr, and global fg_kaslr
confusing. I think it would call this one "fgkaslr_mode" to indicate
that it does control the mode of how the later functions behave.

> +
> static const char * const sym_regex_kernel[S_NSYMTYPES] = {
> /*
> * Following symbols have been audited. There values are constant and do
> @@ -351,8 +353,8 @@ static int sym_index(Elf_Sym *sym)
> return sym->st_shndx;
>
> /* calculate offset of sym from head of table. */
> - offset = (unsigned long) sym - (unsigned long) symtab;
> - index = offset/sizeof(*sym);
> + offset = (unsigned long)sym - (unsigned long)symtab;
> + index = offset / sizeof(*sym);
>
> return elf32_to_cpu(xsymtab[index]);
> }
> @@ -500,22 +502,22 @@ static void read_symtabs(FILE *fp)
> sec->xsymtab = malloc(sec->shdr.sh_size);
> if (!sec->xsymtab) {
> die("malloc of %d bytes for xsymtab failed\n",
> - sec->shdr.sh_size);
> + sec->shdr.sh_size);
> }
> if (fseek(fp, sec->shdr.sh_offset, SEEK_SET) < 0) {
> die("Seek to %d failed: %s\n",
> - sec->shdr.sh_offset, strerror(errno));
> + sec->shdr.sh_offset, strerror(errno));
> }
> if (fread(sec->xsymtab, 1, sec->shdr.sh_size, fp)
> - != sec->shdr.sh_size) {
> + != sec->shdr.sh_size) {
> die("Cannot read extended symbol table: %s\n",
> - strerror(errno));
> + strerror(errno));
> }
> shxsymtabndx = i;
> continue;
>
> case SHT_SYMTAB:
> - num_syms = sec->shdr.sh_size/sizeof(Elf_Sym);
> + num_syms = sec->shdr.sh_size / sizeof(Elf_Sym);
>
> sec->symtab = malloc(sec->shdr.sh_size);
> if (!sec->symtab) {

I would mention these whitespace/readability cleanups in the commit log.

> @@ -818,6 +820,32 @@ static int is_percpu_sym(ElfW(Sym) *sym, const char *symname)
> strncmp(symname, "init_per_cpu_", 13);
> }
>
> +static int is_function_section(struct section *sec)
> +{
> + const char *name;
> +
> + if (!fg_kaslr)
> + return 0;
> +
> + name = sec_name(sec->shdr.sh_info);
> +
> + return(!strncmp(name, ".text.", 6));
> +}
> +
> +static int is_randomized_sym(ElfW(Sym) *sym)
> +{
> + const char *name;
> +
> + if (!fg_kaslr)
> + return 0;
> +
> + if (sym->st_shndx > shnum)
> + return 0;
> +
> + name = sec_name(sym_index(sym));
> + return(!strncmp(name, ".text.", 6));
> +}
> +
> static int do_reloc64(struct section *sec, Elf_Rel *rel, ElfW(Sym) *sym,
> const char *symname)
> {
> @@ -842,13 +870,17 @@ static int do_reloc64(struct section *sec, Elf_Rel *rel, ElfW(Sym) *sym,
> case R_X86_64_PC32:
> case R_X86_64_PLT32:
> /*
> - * PC relative relocations don't need to be adjusted unless
> - * referencing a percpu symbol.
> + * we need to keep pc relative relocations for sections which
> + * might be randomized, and for the percpu section.
> + * We also need to keep relocations for any offset which might
> + * reference an address in a section which has been randomized.
> *
> * NB: R_X86_64_PLT32 can be treated as R_X86_64_PC32.
> */
> - if (is_percpu_sym(sym, symname))
> + if (is_function_section(sec) || is_randomized_sym(sym) ||
> + is_percpu_sym(sym, symname))
> add_reloc(&relocs32neg, offset);
> +
> break;
>
> case R_X86_64_PC64:
> @@ -1158,8 +1190,9 @@ static void print_reloc_info(void)
>
> void process(FILE *fp, int use_real_mode, int as_text,
> int show_absolute_syms, int show_absolute_relocs,
> - int show_reloc_info)
> + int show_reloc_info, int fgkaslr)
> {
> + fg_kaslr = fgkaslr;

Then this becomes a bit more readable:

fgkaslr_mode = fgkaslr;

> regex_init(use_real_mode);
> read_ehdr(fp);
> read_shdrs(fp);
> diff --git a/arch/x86/tools/relocs.h b/arch/x86/tools/relocs.h
> index 43c83c0fd22c..05504052c846 100644
> --- a/arch/x86/tools/relocs.h
> +++ b/arch/x86/tools/relocs.h
> @@ -31,8 +31,8 @@ enum symtype {
>
> void process_32(FILE *fp, int use_real_mode, int as_text,
> int show_absolute_syms, int show_absolute_relocs,
> - int show_reloc_info);
> + int show_reloc_info, int fg_kaslr);
> void process_64(FILE *fp, int use_real_mode, int as_text,
> int show_absolute_syms, int show_absolute_relocs,
> - int show_reloc_info);
> + int show_reloc_info, int fg_kaslr);

I think the prototype and declaration should have matching names:
fgkaslr in "process" and fg_kaslr here. I suggest just calling it
fgkaslr in all.

> #endif /* RELOCS_H */
> diff --git a/arch/x86/tools/relocs_common.c b/arch/x86/tools/relocs_common.c
> index 6634352a20bc..1407db72367a 100644
> --- a/arch/x86/tools/relocs_common.c
> +++ b/arch/x86/tools/relocs_common.c
> @@ -12,14 +12,14 @@ void die(char *fmt, ...)
>
> static void usage(void)
> {
> - die("relocs [--abs-syms|--abs-relocs|--reloc-info|--text|--realmode]" \
> - " vmlinux\n");
> + die("relocs [--abs-syms|--abs-relocs|--reloc-info|--text|--realmode|" \
> + "--fg-kaslr] vmlinux\n");
> }
>
> int main(int argc, char **argv)
> {
> int show_absolute_syms, show_absolute_relocs, show_reloc_info;
> - int as_text, use_real_mode;
> + int as_text, use_real_mode, fg_kaslr;

And I think I'd call this one fgkaslr_opt to show it comes from the opt
parsing.

> const char *fname;
> FILE *fp;
> int i;
> @@ -30,6 +30,7 @@ int main(int argc, char **argv)
> show_reloc_info = 0;
> as_text = 0;
> use_real_mode = 0;
> + fg_kaslr = 0;
> fname = NULL;
> for (i = 1; i < argc; i++) {
> char *arg = argv[i];
> @@ -54,6 +55,10 @@ int main(int argc, char **argv)
> use_real_mode = 1;
> continue;
> }
> + if (strcmp(arg, "--fg-kaslr") == 0) {
> + fg_kaslr = 1;
> + continue;
> + }
> }
> else if (!fname) {
> fname = arg;
> @@ -75,11 +80,11 @@ int main(int argc, char **argv)
> if (e_ident[EI_CLASS] == ELFCLASS64)
> process_64(fp, use_real_mode, as_text,
> show_absolute_syms, show_absolute_relocs,
> - show_reloc_info);
> + show_reloc_info, fg_kaslr);
> else
> process_32(fp, use_real_mode, as_text,
> show_absolute_syms, show_absolute_relocs,
> - show_reloc_info);
> + show_reloc_info, fg_kaslr);
> fclose(fp);
> return 0;
> }
> --
> 2.20.1
>

With these changes, yeah, I think it's good to go.

Reviewed-by: Kees Cook <keescook@xxxxxxxxxxxx>

--
Kees Cook