Re: [RFC PATCH 06/11] x86: make sure _etext includes function sections
From: Kees Cook
Date: Thu Feb 06 2020 - 07:26:29 EST
On Wed, Feb 05, 2020 at 02:39:45PM -0800, Kristen Carlson Accardi wrote:
> We will be using -ffunction-sections to place each function in
> it's own text section so it can be randomized at load time. The
> linker considers these .text.* sections "orphaned sections", and
> will place them after the first similar section (.text). However,
> we need to move _etext so that it is after both .text and .text.*
> We also need to calculate text size to include .text AND .text.*
The dependency on the linker's orphan section handling is, I feel,
rather fragile (during work on CFI and generally building kernels with
Clang's LLD linker, we keep tripping over difference between how BFD and
LLD handle orphans). However, this is currently no way to perform a
section "pass through" where input sections retain their name as an
output section. (If anyone knows a way to do this, I'm all ears).
Right now, you can only collect sections like this:
.text : AT(ADDR(.text) - LOAD_OFFSET) {
*(.text.*)
}
or let them be orphans, which then the linker attempts to find a
"similar" (code, data, etc) section to put them near:
https://sourceware.org/binutils/docs-2.33.1/ld/Orphan-Sections.html
So, basically, yes, this works, but I'd like to see BFD and LLD grow
some kind of /PASSTHRU/ special section (like /DISCARD/), that would let
a linker script specify _where_ these sections should roughly live.
Related thoughts:
I know x86_64 stack alignment is 16 bytes. I cannot find evidence for
what function start alignment should be. It seems the linker is 16 byte
aligning these functions, when I think no alignment is needed for
function starts, so we're wasting some memory (average 8 bytes per
function, at say 50,000 functions, so approaching 512KB) between
functions. If we can specify a 1 byte alignment for these orphan
sections, that would be nice, as mentioned in the cover letter: we lose
a 4 bits of entropy to this alignment, since all randomized function
addresses will have their low bits set to zero.
And we can't adjust function section alignment, or there is some
benefit to a larger alignment, I would like to have a way for the linker
to specify the inter-section padding (or fill) bytes. Right now, the
FILL(0xnn) (or =0xnn) can be used to specify the padding bytes _within_
a section, but not between sections. Right now, BFD appears to 0-pad. I'd
like that to be 0xCC so "guessing" addresses incorrectly will trigger
a trap.
-Kees
>
> Signed-off-by: Kristen Carlson Accardi <kristen@xxxxxxxxxxxxxxx>
> ---
> arch/x86/kernel/vmlinux.lds.S | 18 +++++++++++++++++-
> include/asm-generic/vmlinux.lds.h | 2 +-
> 2 files changed, 18 insertions(+), 2 deletions(-)
>
> diff --git a/arch/x86/kernel/vmlinux.lds.S b/arch/x86/kernel/vmlinux.lds.S
> index 3a1a819da137..e54e9ac5b429 100644
> --- a/arch/x86/kernel/vmlinux.lds.S
> +++ b/arch/x86/kernel/vmlinux.lds.S
> @@ -146,8 +146,24 @@ SECTIONS
> #endif
> } :text =0xcccc
>
> - /* End of text section, which should occupy whole number of pages */
> +#ifdef CONFIG_FG_KASLR
> + /*
> + * -ffunction-sections creates .text.* sections, which are considered
> + * "orphan sections" and added after the first similar section (.text).
> + * Adding this ALIGN statement causes the address of _etext
> + * to be below that of all the .text.* orphaned sections
> + */
> + . = ALIGN(PAGE_SIZE);
> +#endif
> _etext = .;
> +
> + /*
> + * the size of the .text section is used to calculate the address
> + * range for orc lookups. If we just use SIZEOF(.text), we will
> + * miss all the .text.* sections. Calculate the size using _etext
> + * and _stext and save the value for later.
> + */
> + text_size = _etext - _stext;
> . = ALIGN(PAGE_SIZE);
>
> X86_ALIGN_RODATA_BEGIN
> diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h
> index e00f41aa8ec4..edf19f4296e2 100644
> --- a/include/asm-generic/vmlinux.lds.h
> +++ b/include/asm-generic/vmlinux.lds.h
> @@ -798,7 +798,7 @@
> . = ALIGN(4); \
> .orc_lookup : AT(ADDR(.orc_lookup) - LOAD_OFFSET) { \
> orc_lookup = .; \
> - . += (((SIZEOF(.text) + LOOKUP_BLOCK_SIZE - 1) / \
> + . += (((text_size + LOOKUP_BLOCK_SIZE - 1) / \
> LOOKUP_BLOCK_SIZE) + 1) * 4; \
> orc_lookup_end = .; \
> }
> --
> 2.24.1
>
--
Kees Cook