diff mbox

[4/4] arm64: Enable TEXT_OFFSET fuzzing

Message ID 1400233839-15140-5-git-send-email-mark.rutland@arm.com (mailing list archive)
State New, archived
Headers show

Commit Message

Mark Rutland May 16, 2014, 9:50 a.m. UTC
The arm64 Image header contains a text_offset field which bootloaders
are supposed to read to determine the offset (from a 2MB aligned "start
of memory" per booting.txt) at which to load the kernel. The offset is
not well respected by bootloaders at present, and due to the lack of
variation there is little incentive to support it. This is unfortunate
for the sake of future kernels where we may wish to vary the text offset
(even zeroing it).

This patch adds options to arm64 to enable fuzz-testing of text_offset.
CONFIG_ARM64_RANDOMIZE_TEXT_OFFSET forces the text offset to a random
value upon a build of the kernel, and CONFIG_ARM64_TEXT_OFFSET can be
used to set the text offset to a specific value (between 16B and 2MB).
It is recommended that distribution kernels enable randomization to test
bootloaders such that any compliance issues can be fixed early.

Signed-off-by: Mark Rutland <mark.rutland@arm.com>
---
 arch/arm64/Kconfig.debug        | 31 +++++++++++++++++++++++++++++++
 arch/arm64/Makefile             |  6 +++++-
 arch/arm64/kernel/head.S        |  8 ++++++--
 arch/arm64/kernel/vmlinux.lds.S |  5 +++++
 4 files changed, 47 insertions(+), 3 deletions(-)

Comments

Catalin Marinas May 16, 2014, 2:06 p.m. UTC | #1
On Fri, May 16, 2014 at 10:50:39AM +0100, Mark Rutland wrote:
> --- a/arch/arm64/Kconfig.debug
> +++ b/arch/arm64/Kconfig.debug
> @@ -37,4 +37,35 @@ config PID_IN_CONTEXTIDR
>  	  instructions during context switch. Say Y here only if you are
>  	  planning to use hardware trace tools with this kernel.
>  
> +config ARM64_RANDOMIZE_TEXT_OFFSET
> +	bool "Randomize TEXT_OFFSET at build time (EXPERIMENTAL)"
> +	default N

(nitpick: no need for default n)

I think that's good for testing. It would have been nice to be able to
set some limits for the random offset but I can't figure out an easy way
to do this via Kconfig (maybe with additional options).

> +config ARM64_TEXT_OFFSET
> +	hex "Required image load offset"
> +	depends on !ARM64_RANDOMIZE_TEXT_OFFSET
> +	default "0x0000000000080000"

I don't think we should include this. It encourages people to set
specific offsets for their SoCs.
Mark Rutland May 16, 2014, 4:55 p.m. UTC | #2
On Fri, May 16, 2014 at 03:06:07PM +0100, Catalin Marinas wrote:
> On Fri, May 16, 2014 at 10:50:39AM +0100, Mark Rutland wrote:
> > --- a/arch/arm64/Kconfig.debug
> > +++ b/arch/arm64/Kconfig.debug
> > @@ -37,4 +37,35 @@ config PID_IN_CONTEXTIDR
> >  	  instructions during context switch. Say Y here only if you are
> >  	  planning to use hardware trace tools with this kernel.
> >  
> > +config ARM64_RANDOMIZE_TEXT_OFFSET
> > +	bool "Randomize TEXT_OFFSET at build time (EXPERIMENTAL)"
> > +	default N
> 
> (nitpick: no need for default n)

Thanks for pointing that out, I'll remove it :)

> I think that's good for testing. It would have been nice to be able to
> set some limits for the random offset but I can't figure out an easy way
> to do this via Kconfig (maybe with additional options).

There are hard-coded limits implicit in the randomization -- between 0B
and 2MB in 16B increments:

TEXT_OFFSET := $(shell awk 'BEGIN {srand(); printf "0x%05x\n", and(int(0xfffff * rand()), 0xffff0)}')

The 16B increment is required due to some code in head.S (__turn_mmu_on)
requiring a minimum 16B alignment for the object.

The 2MB maximum comes from the fact we rely on the start of memory being
2MB aligned. I'm not sure there's a compelling reason to limit the
randomization if enabled at all -- either you can handle it or you
can't. Are we ever likely to want an offset larger than the memory
alignment?

> > +config ARM64_TEXT_OFFSET
> > +	hex "Required image load offset"
> > +	depends on !ARM64_RANDOMIZE_TEXT_OFFSET
> > +	default "0x0000000000080000"
> 
> I don't think we should include this. It encourages people to set
> specific offsets for their SoCs.

Sure, I was worried about potential abuse also (hence the warning in the
help text). I'll drop this portion.

Cheers,
Mark.
Tom Rini May 20, 2014, 2:11 p.m. UTC | #3
On Fri, May 16, 2014 at 05:55:48PM +0100, Mark Rutland wrote:
> On Fri, May 16, 2014 at 03:06:07PM +0100, Catalin Marinas wrote:
> > On Fri, May 16, 2014 at 10:50:39AM +0100, Mark Rutland wrote:
> > > --- a/arch/arm64/Kconfig.debug
> > > +++ b/arch/arm64/Kconfig.debug
> > > @@ -37,4 +37,35 @@ config PID_IN_CONTEXTIDR
> > >  	  instructions during context switch. Say Y here only if you are
> > >  	  planning to use hardware trace tools with this kernel.
> > >  
> > > +config ARM64_RANDOMIZE_TEXT_OFFSET
> > > +	bool "Randomize TEXT_OFFSET at build time (EXPERIMENTAL)"
> > > +	default N
> > 
> > (nitpick: no need for default n)
> 
> Thanks for pointing that out, I'll remove it :)
> 
> > I think that's good for testing. It would have been nice to be able to
> > set some limits for the random offset but I can't figure out an easy way
> > to do this via Kconfig (maybe with additional options).
> 
> There are hard-coded limits implicit in the randomization -- between 0B
> and 2MB in 16B increments:
> 
> TEXT_OFFSET := $(shell awk 'BEGIN {srand(); printf "0x%05x\n", and(int(0xfffff * rand()), 0xffff0)}')

Note, is mawk expected to be able to be used for kernel building?  It
doesn't have 'and' as a function.

> The 16B increment is required due to some code in head.S (__turn_mmu_on)
> requiring a minimum 16B alignment for the object.
> 
> The 2MB maximum comes from the fact we rely on the start of memory being
> 2MB aligned. I'm not sure there's a compelling reason to limit the
> randomization if enabled at all -- either you can handle it or you
> can't. Are we ever likely to want an offset larger than the memory
> alignment?

A reason to limit the randomization is to make it easier on the
bootloaders to be able to rule of thumb initial loads.  It's not a big
deal with Image if it gets loaded lower than the offset, we can start
shifting data at the end.  But when we start looking at Image.gz (or xz
or ...), in some cases we'll get the whole image read into memory (network
booting for example), uncompress the first block so we can confirm a
good Image header and see about text_offset/image_size.  If we know that
text_offset is somewhere random inside of 2MB (or some other documented
max), we can then go with saying an initial load should be at say +32MB
(to mirror Documentation/arm/Booting).  If it can be anywhere, then
things get hard, or at least annoying (error out and tell the user to
re-load things because of a random value?  I can see testing frameworks
being annoyed about that).

And we should document the range of the offset in
Documentation/arm64/booting.txt as well.
Mark Rutland May 20, 2014, 4:08 p.m. UTC | #4
On Tue, May 20, 2014 at 03:11:26PM +0100, Tom Rini wrote:
> On Fri, May 16, 2014 at 05:55:48PM +0100, Mark Rutland wrote:
> > On Fri, May 16, 2014 at 03:06:07PM +0100, Catalin Marinas wrote:
> > > On Fri, May 16, 2014 at 10:50:39AM +0100, Mark Rutland wrote:
> > > > --- a/arch/arm64/Kconfig.debug
> > > > +++ b/arch/arm64/Kconfig.debug
> > > > @@ -37,4 +37,35 @@ config PID_IN_CONTEXTIDR
> > > >  	  instructions during context switch. Say Y here only if you are
> > > >  	  planning to use hardware trace tools with this kernel.
> > > >  
> > > > +config ARM64_RANDOMIZE_TEXT_OFFSET
> > > > +	bool "Randomize TEXT_OFFSET at build time (EXPERIMENTAL)"
> > > > +	default N
> > > 
> > > (nitpick: no need for default n)
> > 
> > Thanks for pointing that out, I'll remove it :)
> > 
> > > I think that's good for testing. It would have been nice to be able to
> > > set some limits for the random offset but I can't figure out an easy way
> > > to do this via Kconfig (maybe with additional options).
> > 
> > There are hard-coded limits implicit in the randomization -- between 0B
> > and 2MB in 16B increments:
> > 
> > TEXT_OFFSET := $(shell awk 'BEGIN {srand(); printf "0x%05x\n", and(int(0xfffff * rand()), 0xffff0)}')
> 
> Note, is mawk expected to be able to be used for kernel building?  It
> doesn't have 'and' as a function.

Ouch. I hadn't realised this was gawk-only. My new local machine only
has mawk and builds the kernel fine otherwise, so it seems like we can't
just expect gawk. It also seems that mawk doesn't like inline hex
values...

The masking is only there to ensure the value is a multiple of 16 (i.e.
the last digit is 0), so we can just handle that in the print instead.
Then 0xffff is 65535, so:

TEXT_OFFSET := $(shell awk 'BEGIN {srand(); printf "0x%04x0\n", int(65535 * rand())}')

That seems to work in gawk and mawk.

> 
> > The 16B increment is required due to some code in head.S (__turn_mmu_on)
> > requiring a minimum 16B alignment for the object.
> > 
> > The 2MB maximum comes from the fact we rely on the start of memory being
> > 2MB aligned. I'm not sure there's a compelling reason to limit the
> > randomization if enabled at all -- either you can handle it or you
> > can't. Are we ever likely to want an offset larger than the memory
> > alignment?
> 
> A reason to limit the randomization is to make it easier on the
> bootloaders to be able to rule of thumb initial loads.  It's not a big
> deal with Image if it gets loaded lower than the offset, we can start
> shifting data at the end.  But when we start looking at Image.gz (or xz
> or ...), in some cases we'll get the whole image read into memory (network
> booting for example), uncompress the first block so we can confirm a
> good Image header and see about text_offset/image_size.  If we know that
> text_offset is somewhere random inside of 2MB (or some other documented
> max), we can then go with saying an initial load should be at say +32MB
> (to mirror Documentation/arm/Booting).  If it can be anywhere, then
> things get hard, or at least annoying (error out and tell the user to
> re-load things because of a random value?  I can see testing frameworks
> being annoyed about that).

Ouch, that is somewhat painful.

I don't think we expect to see a text_offset larger than 2MB, and I
can't immediately see why we'd care about any particular text offset
assuming the page tables are at the end of the runtime image. That said,
I'm not completely clear on the history of the text_offset.

> And we should document the range of the offset in
> Documentation/arm64/booting.txt as well.

As far as I am aware, we have a 64-bit field specifically to allow for
an arbitrarily large value, so placing limitations on that may be a
little difficult.

As stated above, I don't think there'd be a reason for having a
text_offset larger than our memory alignment restriction (2MB), but
there may be something I've missed. If others are reasonably confident
with an upper limit, I'd be happy to go with it.

Cheers,
Mark.
Mark Rutland May 21, 2014, 10:18 a.m. UTC | #5
On Tue, May 20, 2014 at 05:08:27PM +0100, Mark Rutland wrote:

[...]

> > > The 16B increment is required due to some code in head.S (__turn_mmu_on)
> > > requiring a minimum 16B alignment for the object.
> > > 
> > > The 2MB maximum comes from the fact we rely on the start of memory being
> > > 2MB aligned. I'm not sure there's a compelling reason to limit the
> > > randomization if enabled at all -- either you can handle it or you
> > > can't. Are we ever likely to want an offset larger than the memory
> > > alignment?
> > 
> > A reason to limit the randomization is to make it easier on the
> > bootloaders to be able to rule of thumb initial loads.  It's not a big
> > deal with Image if it gets loaded lower than the offset, we can start
> > shifting data at the end.  But when we start looking at Image.gz (or xz
> > or ...), in some cases we'll get the whole image read into memory (network
> > booting for example), uncompress the first block so we can confirm a
> > good Image header and see about text_offset/image_size.  If we know that
> > text_offset is somewhere random inside of 2MB (or some other documented
> > max), we can then go with saying an initial load should be at say +32MB
> > (to mirror Documentation/arm/Booting).  If it can be anywhere, then
> > things get hard, or at least annoying (error out and tell the user to
> > re-load things because of a random value?  I can see testing frameworks
> > being annoyed about that).
> 
> Ouch, that is somewhat painful.
> 
> I don't think we expect to see a text_offset larger than 2MB, and I
> can't immediately see why we'd care about any particular text offset
> assuming the page tables are at the end of the runtime image. That said,
> I'm not completely clear on the history of the text_offset.
> 
> > And we should document the range of the offset in
> > Documentation/arm64/booting.txt as well.
> 
> As far as I am aware, we have a 64-bit field specifically to allow for
> an arbitrarily large value, so placing limitations on that may be a
> little difficult.
> 
> As stated above, I don't think there'd be a reason for having a
> text_offset larger than our memory alignment restriction (2MB), but
> there may be something I've missed. If others are reasonably confident
> with an upper limit, I'd be happy to go with it.

Having thought about it a little more, the primary reason for having
text_offset is to allow for memory below the kernel to be addressable.
If we decouple the linear mapping from the kernel text mapping this
would not be a problem -- we'd be able to load the kernel at any
2MB-aligned address + text_offset and use memory below it.

So I think we could get away with limiting text_offset to 2MB.

Cheers,
Mark.
diff mbox

Patch

diff --git a/arch/arm64/Kconfig.debug b/arch/arm64/Kconfig.debug
index d10ec33..471ef0d 100644
--- a/arch/arm64/Kconfig.debug
+++ b/arch/arm64/Kconfig.debug
@@ -37,4 +37,35 @@  config PID_IN_CONTEXTIDR
 	  instructions during context switch. Say Y here only if you are
 	  planning to use hardware trace tools with this kernel.
 
+config ARM64_RANDOMIZE_TEXT_OFFSET
+	bool "Randomize TEXT_OFFSET at build time (EXPERIMENTAL)"
+	default N
+	help
+	  Say Y here if you want the image load offset (AKA TEXT_OFFSET)
+	  of the kernel to be randomized at build-time. When selected,
+	  this option will cause TEXT_OFFSET to be randomized upon any
+	  build of the kernel, and the offset will be reflected in the
+	  text_offset field of the resulting Image. This can be used to
+	  fuzz-test bootloaders which respect text_offset.
+
+	  This option is intended for bootloader and/or kernel testing
+	  only. Bootloaders must make no assumptions regarding the value
+	  of TEXT_OFFSET and platforms must not require a specific
+	  value.
+
+config ARM64_TEXT_OFFSET
+	hex "Required image load offset"
+	depends on !ARM64_RANDOMIZE_TEXT_OFFSET
+	default "0x0000000000080000"
+	help
+	  The image load offset (AKA TEXT_OFFSET) of the kernel. The
+	  load offset will be reflected in the text_offset field of the
+	  Image. TEXT_OFFSET must be a 16-byte aligned value less than
+	  2MB.
+
+	  This option is intended for bootloader and/or kernel testing
+	  only. Bootloaders must make no assumptions regarding the value
+	  of TEXT_OFFSET and platforms must not require a specific
+	  value.
+
 endmenu
diff --git a/arch/arm64/Makefile b/arch/arm64/Makefile
index 2fceb71..a4962d5 100644
--- a/arch/arm64/Makefile
+++ b/arch/arm64/Makefile
@@ -38,7 +38,11 @@  CHECKFLAGS	+= -D__aarch64__
 head-y		:= arch/arm64/kernel/head.o
 
 # The byte offset of the kernel image in RAM from the start of RAM.
-TEXT_OFFSET := 0x00080000
+ifeq ($(CONFIG_ARM64_RANDOMIZE_TEXT_OFFSET), y)
+TEXT_OFFSET := $(shell awk 'BEGIN {srand(); printf "0x%05x\n", and(int(0xfffff * rand()), 0xffff0)}')
+else
+TEXT_OFFSET := $(CONFIG_ARM64_TEXT_OFFSET)
+endif
 
 export	TEXT_OFFSET GZFLAGS
 
diff --git a/arch/arm64/kernel/head.S b/arch/arm64/kernel/head.S
index 542ca97..1a731bc 100644
--- a/arch/arm64/kernel/head.S
+++ b/arch/arm64/kernel/head.S
@@ -37,8 +37,12 @@ 
 
 #define KERNEL_RAM_VADDR	(PAGE_OFFSET + TEXT_OFFSET)
 
-#if (KERNEL_RAM_VADDR & 0xfffff) != 0x80000
-#error KERNEL_RAM_VADDR must start at 0xXXX80000
+#if (TEXT_OFFSET & 0xf) != 0
+#error TEXT_OFFSET must be at least 16B aligned
+#elif (PAGE_OFFSET & 0xfffff) != 0
+#error PAGE_OFFSET must be at least 2MB aligned
+#elif TEXT_OFFSET > 0xfffff
+#error TEXT_OFFSET must be less than 2MB
 #endif
 
 	.macro	pgtbl, ttb0, ttb1, virt_to_phys
diff --git a/arch/arm64/kernel/vmlinux.lds.S b/arch/arm64/kernel/vmlinux.lds.S
index 21a8ad1..05fc047 100644
--- a/arch/arm64/kernel/vmlinux.lds.S
+++ b/arch/arm64/kernel/vmlinux.lds.S
@@ -149,3 +149,8 @@  SECTIONS
  */
 ASSERT(((__hyp_idmap_text_start + PAGE_SIZE) > __hyp_idmap_text_end),
        "HYP init code too big")
+
+/*
+ * If padding is applied before .head.text, virt<->phys conversions will fail.
+ */
+ASSERT(_text == (PAGE_OFFSET + TEXT_OFFSET), "HEAD is misaligned")