diff mbox series

arm64: link with -z norelro regardless of CONFIG_RELOCATABLE

Message ID 20201016175339.2429280-1-ndesaulniers@google.com
State New
Headers show
Series arm64: link with -z norelro regardless of CONFIG_RELOCATABLE | expand

Commit Message

Nick Desaulniers Oct. 16, 2020, 5:53 p.m. UTC
With CONFIG_EXPERT=y, CONFIG_KASAN=y, CONFIG_RANDOMIZE_BASE=n,
CONFIG_RELOCATABLE=n, we observe the following failure when trying to
link the kernel image with LD=ld.lld:

error: section: .exit.data is not contiguous with other relro sections

ld.lld defaults to -z relro while ld.bfd defaults to -z norelro. This
was previously fixed, but only for CONFIG_RELOCATABLE=y.

Cc: stable@vger.kernel.org
Fixes: commit 3bbd3db86470 ("arm64: relocatable: fix inconsistencies in linker script and options")
Signed-off-by: Nick Desaulniers <ndesaulniers@google.com>

---
While upgrading our toolchains for Android, we started seeing the above
failure for a particular config that enabled KASAN but disabled KASLR.
This was on a 5.4 stable branch.  It looks like
commit dd4bc6076587 ("arm64: warn on incorrect placement of the kernel by the bootloader")
made RELOCATABLE=y the default and depend on EXPERT=y. With those two
enabled, we can then reproduce the same failure on mainline.


 arch/arm64/Makefile | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

-- 
2.29.0.rc1.297.gfa9743e501-goog

Comments

Will Deacon Oct. 20, 2020, 5:57 p.m. UTC | #1
On Fri, 16 Oct 2020 10:53:39 -0700, Nick Desaulniers wrote:
> With CONFIG_EXPERT=y, CONFIG_KASAN=y, CONFIG_RANDOMIZE_BASE=n,

> CONFIG_RELOCATABLE=n, we observe the following failure when trying to

> link the kernel image with LD=ld.lld:

> 

> error: section: .exit.data is not contiguous with other relro sections

> 

> ld.lld defaults to -z relro while ld.bfd defaults to -z norelro. This

> was previously fixed, but only for CONFIG_RELOCATABLE=y.


Applied to arm64 (for-next/core), thanks!

[1/1] arm64: link with -z norelro regardless of CONFIG_RELOCATABLE
      https://git.kernel.org/arm64/c/3b92fa7485eb

Cheers,
-- 
Will

https://fixes.arm64.dev
https://next.arm64.dev
https://will.arm64.dev
Nick Desaulniers Oct. 20, 2020, 8:16 p.m. UTC | #2
On Tue, Oct 20, 2020 at 10:57 AM Will Deacon <will@kernel.org> wrote:
>

> On Fri, 16 Oct 2020 10:53:39 -0700, Nick Desaulniers wrote:

> > With CONFIG_EXPERT=y, CONFIG_KASAN=y, CONFIG_RANDOMIZE_BASE=n,

> > CONFIG_RELOCATABLE=n, we observe the following failure when trying to

> > link the kernel image with LD=ld.lld:

> >

> > error: section: .exit.data is not contiguous with other relro sections

> >

> > ld.lld defaults to -z relro while ld.bfd defaults to -z norelro. This

> > was previously fixed, but only for CONFIG_RELOCATABLE=y.

>

> Applied to arm64 (for-next/core), thanks!

>

> [1/1] arm64: link with -z norelro regardless of CONFIG_RELOCATABLE

>       https://git.kernel.org/arm64/c/3b92fa7485eb


IF we wanted to go further and remove `-z norelro`, or even enable `-z
relro` for aarch64, then we would have to detangle some KASAN/GCOV
generated section discard spaghetti.  Fangrui did some more digging
and found that .fini_array.* sections were relro (read only after
relocations, IIUC), so adding them to EXIT_DATA
(include/asm-generic/vmlinux.lds.h) was causing them to get included
in .exit.data (arch/arm64/kernel/vmlinux.lds.S) making that relro.
There's some history here with commits:

- e41f501d39126 ("vmlinux.lds: account for destructor sections")
- 8dcf86caa1e3da ("vmlinux.lds.h: Fix incomplete .text.exit discards")
- d812db78288d7 ("vmlinux.lds.h: Avoid KASAN and KCSAN's unwanted sections")

It seems the following works for quite a few different
configs/toolchains I played with, but the big IF is whether enabling
`-z relro` is worthwhile?  If the kernel does respect that mapping,
then I assume that's a yes, but I haven't checked yet whether relro is
respected within the kernel (`grep -rn RELRO` turns up nothing
interesting).  I also haven't checked yet whether all supported
versions of GNU ld.bfd support -z relro (guessing not, since a quick
test warns: `aarch64-linux-gnu-ld: warning: -z relro ignored` for
v2.34.90.20200706, may be holding it wrong).

(Fangrui also filed https://gcc.gnu.org/bugzilla/show_bug.cgi?id=97507
in regards to GCOV+GCC)

diff --git a/include/asm-generic/vmlinux.lds.h
b/include/asm-generic/vmlinux.lds.h
index cd14444bf600..64578c998e53 100644
--- a/include/asm-generic/vmlinux.lds.h
+++ b/include/asm-generic/vmlinux.lds.h
@@ -744,7 +744,6 @@

 #define EXIT_DATA                                                      \
        *(.exit.data .exit.data.*)                                      \
-       *(.fini_array .fini_array.*)                                    \
        *(.dtors .dtors.*)                                              \
        MEM_DISCARD(exit.data*)                                         \
        MEM_DISCARD(exit.rodata*)
@@ -995,6 +994,7 @@
 #if defined(CONFIG_KASAN_GENERIC) || defined(CONFIG_KCSAN)
 # ifdef CONFIG_CONSTRUCTORS
 #  define SANITIZER_DISCARDS                                           \
+       *(.fini_array .fini_array.*)                                    \
        *(.eh_frame)
 # else
 #  define SANITIZER_DISCARDS                                           \
@@ -1005,8 +1005,16 @@
 # define SANITIZER_DISCARDS
 #endif

+#if defined(CONFIG_GCOV_KERNEL) && defined(CONFIG_CC_IS_GCC)
+# define GCOV_DISCARDS                                                 \
+       *(.fini_array .fini_array.*)
+#else
+# define GCOV_DISCARDS
+#endif
+
 #define COMMON_DISCARDS
         \
        SANITIZER_DISCARDS                                              \
+       GCOV_DISCARDS                                                   \
        *(.discard)                                                     \
        *(.discard.*)                                                   \
        *(.modinfo)                                                     \
-- 
Thanks,
~Nick Desaulniers
Ard Biesheuvel Oct. 21, 2020, 6:58 a.m. UTC | #3
On Tue, 20 Oct 2020 at 22:16, Nick Desaulniers <ndesaulniers@google.com> wrote:
>

> On Tue, Oct 20, 2020 at 10:57 AM Will Deacon <will@kernel.org> wrote:

> >

> > On Fri, 16 Oct 2020 10:53:39 -0700, Nick Desaulniers wrote:

> > > With CONFIG_EXPERT=y, CONFIG_KASAN=y, CONFIG_RANDOMIZE_BASE=n,

> > > CONFIG_RELOCATABLE=n, we observe the following failure when trying to

> > > link the kernel image with LD=ld.lld:

> > >

> > > error: section: .exit.data is not contiguous with other relro sections

> > >

> > > ld.lld defaults to -z relro while ld.bfd defaults to -z norelro. This

> > > was previously fixed, but only for CONFIG_RELOCATABLE=y.

> >

> > Applied to arm64 (for-next/core), thanks!

> >

> > [1/1] arm64: link with -z norelro regardless of CONFIG_RELOCATABLE

> >       https://git.kernel.org/arm64/c/3b92fa7485eb

>

> IF we wanted to go further and remove `-z norelro`, or even enable `-z

> relro` for aarch64, then we would have to detangle some KASAN/GCOV

> generated section discard spaghetti.


Why on earth would we want that?

> Fangrui did some more digging

> and found that .fini_array.* sections were relro (read only after

> relocations, IIUC), so adding them to EXIT_DATA

> (include/asm-generic/vmlinux.lds.h) was causing them to get included

> in .exit.data (arch/arm64/kernel/vmlinux.lds.S) making that relro.

> There's some history here with commits:

>

> - e41f501d39126 ("vmlinux.lds: account for destructor sections")

> - 8dcf86caa1e3da ("vmlinux.lds.h: Fix incomplete .text.exit discards")

> - d812db78288d7 ("vmlinux.lds.h: Avoid KASAN and KCSAN's unwanted sections")

>

> It seems the following works for quite a few different

> configs/toolchains I played with, but the big IF is whether enabling

> `-z relro` is worthwhile?  If the kernel does respect that mapping,

> then I assume that's a yes, but I haven't checked yet whether relro is

> respected within the kernel (`grep -rn RELRO` turns up nothing

> interesting).  I also haven't checked yet whether all supported

> versions of GNU ld.bfd support -z relro (guessing not, since a quick

> test warns: `aarch64-linux-gnu-ld: warning: -z relro ignored` for

> v2.34.90.20200706, may be holding it wrong).

>


RELRO just moves statically initialized const pointers into a separate
section so we can place it in a way that allows us to easily map it
r/w during load, and switch it over to r/o once the relocations have
been applied.

On AArch64, we don't even use -fpic to build the kernel, and load time
relocations may appear everywhere in .text, .rodata etc etc, which is
absolutely fine given that we apply the relocations way before we
finalize the kernel mappings. This means that, in our case, statically
initialized const pointers will be mapped r/o already, and we don't
need RELRO.

In general, we should ensure that the 'relocatable bare metal' case
doesn't get snowed under, as toolchain development is [understandably]
very focused on hosted binaries that use shared libraries, where
things like CoW footprint, ELF symbol preemption, text relocations and
RELRO sections actually matter. For bare metal, it is quite the
opposite: text relocations are fine, there is no CoW so minimizing the
footprint of the .so pages that are modified due to relocations is
unnecessary, and symbols cannot be preempted either. So many of the
shared library tricks actually make things worse for us, because we
have to work around them while they have no benefit for us.

I have suggested this before, but perhaps we should have a
-mcmodel=kernel (like x86 does) that takes these things into account?
As a start, it could imply -cmodel=small (which we rely on today), but
with guarantees that the generated code is position independent, but
without GOT indirections, and that the resulting object code can be
linked with -pie (so that we have access to the load time relocations
in the bare metal binary itself). This is something we rely on today,
and happens to work in practice, but this could easily break in the
future.




> (Fangrui also filed https://gcc.gnu.org/bugzilla/show_bug.cgi?id=97507

> in regards to GCOV+GCC)

>

> diff --git a/include/asm-generic/vmlinux.lds.h

> b/include/asm-generic/vmlinux.lds.h

> index cd14444bf600..64578c998e53 100644

> --- a/include/asm-generic/vmlinux.lds.h

> +++ b/include/asm-generic/vmlinux.lds.h

> @@ -744,7 +744,6 @@

>

>  #define EXIT_DATA                                                      \

>         *(.exit.data .exit.data.*)                                      \

> -       *(.fini_array .fini_array.*)                                    \

>         *(.dtors .dtors.*)                                              \

>         MEM_DISCARD(exit.data*)                                         \

>         MEM_DISCARD(exit.rodata*)

> @@ -995,6 +994,7 @@

>  #if defined(CONFIG_KASAN_GENERIC) || defined(CONFIG_KCSAN)

>  # ifdef CONFIG_CONSTRUCTORS

>  #  define SANITIZER_DISCARDS                                           \

> +       *(.fini_array .fini_array.*)                                    \

>         *(.eh_frame)

>  # else

>  #  define SANITIZER_DISCARDS                                           \

> @@ -1005,8 +1005,16 @@

>  # define SANITIZER_DISCARDS

>  #endif

>

> +#if defined(CONFIG_GCOV_KERNEL) && defined(CONFIG_CC_IS_GCC)

> +# define GCOV_DISCARDS                                                 \

> +       *(.fini_array .fini_array.*)

> +#else

> +# define GCOV_DISCARDS

> +#endif

> +

>  #define COMMON_DISCARDS

>          \

>         SANITIZER_DISCARDS                                              \

> +       GCOV_DISCARDS                                                   \

>         *(.discard)                                                     \

>         *(.discard.*)                                                   \

>         *(.modinfo)                                                     \

> --

> Thanks,

> ~Nick Desaulniers
Nick Desaulniers Dec. 14, 2020, 9:44 p.m. UTC | #4
On Tue, Oct 20, 2020 at 10:57 AM Will Deacon <will@kernel.org> wrote:
>

> On Fri, 16 Oct 2020 10:53:39 -0700, Nick Desaulniers wrote:

> > With CONFIG_EXPERT=y, CONFIG_KASAN=y, CONFIG_RANDOMIZE_BASE=n,

> > CONFIG_RELOCATABLE=n, we observe the following failure when trying to

> > link the kernel image with LD=ld.lld:

> >

> > error: section: .exit.data is not contiguous with other relro sections

> >

> > ld.lld defaults to -z relro while ld.bfd defaults to -z norelro. This

> > was previously fixed, but only for CONFIG_RELOCATABLE=y.

>

> Applied to arm64 (for-next/core), thanks!

>

> [1/1] arm64: link with -z norelro regardless of CONFIG_RELOCATABLE

>       https://git.kernel.org/arm64/c/3b92fa7485eb


It looks like this is now producing warnings when linking with BFD.
$ make ...
...
  LD      .tmp_vmlinux.kallsyms1
aarch64-linux-gnu-ld: warning: -z norelro ignored
  KSYMS   .tmp_vmlinux.kallsyms1.S
  AS      .tmp_vmlinux.kallsyms1.S
  LD      .tmp_vmlinux.kallsyms2
aarch64-linux-gnu-ld: warning: -z norelro ignored
  KSYMS   .tmp_vmlinux.kallsyms2.S
  AS      .tmp_vmlinux.kallsyms2.S
  LD      vmlinux
aarch64-linux-gnu-ld: warning: -z norelro ignored

Alan, looking at binutils-gdb commit 5fd104addfddb ("Emit a warning
when -z relro is unsupported") mentions targets lacking relro support
will produce this warning.  I thought aarch64 supports relro
though...?
Looks like we're invoking:
+ aarch64-linux-gnu-ld -EL -maarch64elf --no-undefined -X -z norelro
-shared -Bsymbolic -z notext --no-apply-dynamic-relocs
--fix-cortex-a53-843419 --build-id=sha1 --orphan-handling=warn
--strip-debug -o .tmp_vmlinux.kallsyms1 -T
./arch/arm64/kernel/vmlinux.lds --whole-archive
arch/arm64/kernel/head.o init/built-in.a usr/built-in.a
arch/arm64/built-in.a kernel/built-in.a certs/built-in.a mm/built-in.a
fs/built-in.a ipc/built-in.a security/built-in.a crypto/built-in.a
block/built-in.a arch/arm64/lib/built-in.a lib/built-in.a
arch/arm64/lib/lib.a lib/lib.a drivers/built-in.a sound/built-in.a
net/built-in.a virt/built-in.a --no-whole-archive --start-group
./drivers/firmware/efi/libstub/lib.a --end-group
aarch64-linux-gnu-ld: warning: -z norelro ignored

So we set the emulation mode via -maarch64elf, and our preprocessed
linker script has `OUTPUT_ARCH(aarch64)`. From that commit, there's a
linked mailing list discussion:
https://sourceware.org/legacy-ml/binutils/2017-01/msg00441.html

Is there something more we need to do to our linker script
(https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/arch/arm64/kernel/vmlinux.lds.S)
for BFD not to warn when passing `-z norelro`?  It looks like common
page size might need to be specified?  I tried:

diff --git a/arch/arm64/kernel/vmlinux.lds.S b/arch/arm64/kernel/vmlinux.lds.S
index 1bda604f4c70..ae8cce140fdf 100644
--- a/arch/arm64/kernel/vmlinux.lds.S
+++ b/arch/arm64/kernel/vmlinux.lds.S
@@ -121,7 +121,7 @@ SECTIONS
                _text = .;
                HEAD_TEXT
        }
-       .text : {                       /* Real text segment            */
+       .text ALIGN (CONSTANT (COMMONPAGESIZE)): {      /* Real text
segment    */

and passing `-z common-page-size=4096` but neither seemed to do the
trick. (https://docs.adacore.com/live/wave/binutils-stable/html/ld/ld.html#index-COMMONPAGESIZE-553

Worst case, we add `-z norelro` just for LLD:

diff --git a/arch/arm64/Makefile b/arch/arm64/Makefile
index 6a87d592bd00..6a6235e1e8a9 100644
--- a/arch/arm64/Makefile
+++ b/arch/arm64/Makefile
@@ -10,7 +10,7 @@
 #
 # Copyright (C) 1995-2001 by Russell King

-LDFLAGS_vmlinux        :=--no-undefined -X -z norelro
+LDFLAGS_vmlinux        :=--no-undefined -X

 ifeq ($(CONFIG_RELOCATABLE), y)
 # Pass --no-apply-dynamic-relocs to restore pre-binutils-2.27 behaviour
@@ -28,6 +28,10 @@ LDFLAGS_vmlinux      += --fix-cortex-a53-843419
   endif
 endif

+ifeq ($(CONFIG_LD_IS_LLD), y)
+LDFLAGS_vmlinux        += -z norelro
+endif
+
 ifeq ($(CONFIG_ARM64_USE_LSE_ATOMICS), y)
   ifneq ($(CONFIG_ARM64_LSE_ATOMICS), y)
 $(warning LSE atomics not supported by binutils)

-- 
Thanks,
~Nick Desaulniers
Alan Modra Dec. 14, 2020, 11:18 p.m. UTC | #5
On Mon, Dec 14, 2020 at 01:44:06PM -0800, Nick Desaulniers wrote:
> aarch64-linux-gnu-ld: warning: -z norelro ignored

> 

> So we set the emulation mode via -maarch64elf, and our preprocessed


The default linker emulation for an aarch64-linux ld.bfd is
-maarch64linux, the default for an aarch64-elf linker is
-maarch64elf.  They are not equivalent.  If you choose -maarch64elf
you get an emulation that doesn't support -z relro.

Now I don't know why the kernel uses -maarch64elf so you shouldn't
interpret my comment as a recommendation to use -maarch64linux
instead.  That may have other unwanted effects.

-- 
Alan Modra
Australia Development Lab, IBM
Nick Desaulniers Dec. 14, 2020, 11:33 p.m. UTC | #6
On Mon, Dec 14, 2020 at 3:18 PM Alan Modra <amodra@gmail.com> wrote:
>

> On Mon, Dec 14, 2020 at 01:44:06PM -0800, Nick Desaulniers wrote:

> > aarch64-linux-gnu-ld: warning: -z norelro ignored

> >

> > So we set the emulation mode via -maarch64elf, and our preprocessed

>

> The default linker emulation for an aarch64-linux ld.bfd is

> -maarch64linux, the default for an aarch64-elf linker is

> -maarch64elf.  They are not equivalent.  If you choose -maarch64elf

> you get an emulation that doesn't support -z relro.

>

> Now I don't know why the kernel uses -maarch64elf so you shouldn't

> interpret my comment as a recommendation to use -maarch64linux

> instead.  That may have other unwanted effects.


Cool, thanks for the context.  The kernel currently has:

arch/arm64/Makefile:
...
ifeq ($(CONFIG_CPU_BIG_ENDIAN), y)
...
# Prefer the baremetal ELF build target, but not all toolchains include
# it so fall back to the standard linux version if needed.
LDFLAGS                += -EB $(call ld-option, -maarch64elfb, -maarch64linuxb)
...
else
...
# Same as above, prefer ELF but fall back to linux target if needed.
LDFLAGS                += -EL $(call ld-option, -maarch64elf, -maarch64linux)
...

Then
$ git log -S maarch64elf arch/arm64/Makefile
provides more context:
1. https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=c931d34ea0853d41349e93f871bd3f17f1c03a6b
2. https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=96f95a17c1cfe65a002e525114d96616e91a8f2d
3. https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=38fc4248677552ce35efc09902fdcb06b61d7ef9

So it seems more that the kernel is relying on whichever emulation
targets are supported in local distros, not necessarily the semantic
differences between the two.  Since the kernel might be linked as
either, I'll send the patch described in my last post to add `-z
norelro` just when linking with LLD, since it sounds like it's only
possible to specify when -maarch64linux/-maarch64linuxb is used, which
is unlikely.
-- 
Thanks,
~Nick Desaulniers
diff mbox series

Patch

diff --git a/arch/arm64/Makefile b/arch/arm64/Makefile
index f4717facf31e..674241df91ab 100644
--- a/arch/arm64/Makefile
+++ b/arch/arm64/Makefile
@@ -10,13 +10,13 @@ 
 #
 # Copyright (C) 1995-2001 by Russell King
 
-LDFLAGS_vmlinux	:=--no-undefined -X
+LDFLAGS_vmlinux	:=--no-undefined -X -z norelro
 
 ifeq ($(CONFIG_RELOCATABLE), y)
 # Pass --no-apply-dynamic-relocs to restore pre-binutils-2.27 behaviour
 # for relative relocs, since this leads to better Image compression
 # with the relocation offsets always being zero.
-LDFLAGS_vmlinux		+= -shared -Bsymbolic -z notext -z norelro \
+LDFLAGS_vmlinux		+= -shared -Bsymbolic -z notext \
 			$(call ld-option, --no-apply-dynamic-relocs)
 endif