All of lore.kernel.org
 help / color / mirror / Atom feed
From: Ard Biesheuvel <ard.biesheuvel@linaro.org>
To: kernel-hardening@lists.openwall.com
Cc: keescook@chromium.org, christoffer.dall@arm.com,
	will.deacon@arm.com, catalin.marinas@arm.com,
	mark.rutland@arm.com, labbott@fedoraproject.org,
	linux-arm-kernel@lists.infradead.org,
	Ard Biesheuvel <ard.biesheuvel@linaro.org>
Subject: [RFC/PoC PATCH 3/3] arm64: enable ROP protection by clearing SP bit #55 across function returns
Date: Thu,  2 Aug 2018 15:21:32 +0200	[thread overview]
Message-ID: <20180802132133.23999-4-ard.biesheuvel@linaro.org> (raw)
In-Reply-To: <20180802132133.23999-1-ard.biesheuvel@linaro.org>

ROP attacks rely on a large supply of so-called 'gadgets', which are
(in this context) short sequences of instructions ending in a stack
pop and a return instruction. By exploiting a stack overflow to create
a specially crafted stack frame, each gadget jumps to the next by
popping off the next gadget's address as a fake return address,
allowing non-trivial 'programs' to be executed by piecing together
a large number of such gadgets.

This attack vector relies heavily on the ability to jump to arbitrary
places in the code. If we could limit where a function could return to,
it is much more difficult to obtain critical mass in terms of a gadget
collection that allows arbitrary attacks to be mounted.

So let's try and do so by clearing bit #55 in the stack pointer register
before returning from a function, and setting it again right after a
'bl' or 'blr' instruction. That way, jumping to arbitrary places in the
code and popping the next gadget's address becomes a lot more complicated,
since the stack pointer will not be valid after a function return until
the 'reset' sequence is executed (or after an exception is taken).

Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
---
 arch/arm64/Kconfig                 | 10 ++++++++++
 arch/arm64/include/asm/assembler.h |  9 +++++++++
 arch/arm64/kernel/entry.S          | 18 ++++++++++++++++++
 3 files changed, 37 insertions(+)

diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
index 42c090cf0292..4562af0250b9 100644
--- a/arch/arm64/Kconfig
+++ b/arch/arm64/Kconfig
@@ -1011,6 +1011,16 @@ config ARM64_SW_TTBR0_PAN
 	  zeroed area and reserved ASID. The user access routines
 	  restore the valid TTBR0_EL1 temporarily.
 
+config ARM64_ROP_SHIELD
+	bool "Enable basic ROP protection through the stack pointer sign bit"
+	depends on GCC_PLUGINS && VMAP_STACK
+	select GCC_PLUGIN_ARM64_ROP_SHIELD
+	help
+	  Enable protection against ROP attacks by clearing bit #55 in the
+	  stack pointer register across a function return.
+
+	  If paranoid, say Y here. If unsure, say N.
+
 menu "ARMv8.1 architectural features"
 
 config ARM64_HW_AFDBM
diff --git a/arch/arm64/include/asm/assembler.h b/arch/arm64/include/asm/assembler.h
index 346ada4de48a..95d3ec98eb58 100644
--- a/arch/arm64/include/asm/assembler.h
+++ b/arch/arm64/include/asm/assembler.h
@@ -701,12 +701,21 @@ USER(\label, ic	ivau, \tmp2)			// invalidate I line PoU
 .Lyield_out_\@ :
 	.endm
 
+	.macro		unclobber_sp, tmp
+#ifdef CONFIG_ARM64_ROP_SHIELD
+	mov		\tmp, sp
+	orr		sp, \tmp, #(1 << 55)
+#endif
+	.endm
+
 	.macro		bl_c, target
 	bl		\target
+	unclobber_sp	x30
 	.endm
 
 	.macro		blr_c, reg
 	blr		\reg
+	unclobber_sp	x30
 	.endm
 
 #endif	/* __ASM_ASSEMBLER_H */
diff --git a/arch/arm64/kernel/entry.S b/arch/arm64/kernel/entry.S
index eba5b6b528ea..2adebca74f11 100644
--- a/arch/arm64/kernel/entry.S
+++ b/arch/arm64/kernel/entry.S
@@ -95,6 +95,9 @@ alternative_else_nop_endif
 	 */
 	add	sp, sp, x0			// sp' = sp + x0
 	sub	x0, sp, x0			// x0' = sp' - x0 = (sp + x0) - x0 = sp
+#ifdef CONFIG_ARM64_ROP_SHIELD
+	tbz	x0, #55, 1f
+#endif
 	tbnz	x0, #THREAD_SHIFT, 0f
 	sub	x0, sp, x0			// x0'' = sp' - x0' = (sp + x0) - sp = x0
 	sub	sp, sp, x0			// sp'' = sp' - x0 = (sp + x0) - x0 = sp
@@ -129,6 +132,21 @@ alternative_else_nop_endif
 	/* We were already on the overflow stack. Restore sp/x0 and carry on. */
 	sub	sp, sp, x0
 	mrs	x0, tpidrro_el0
+	b	el\()\el\()_\label
+
+#ifdef CONFIG_ARM64_ROP_SHIELD
+1:	/*
+	 * We have to do a little dance here to set bit 55 in the stack
+	 * pointer register without clobbering anything else.
+	 */
+	orr	x0, x0, #(1 << 55)
+	str	x1, [x0]
+	mov	x1, sp
+	mov	sp, x0
+	and	x0, x0, #~(1 << 55)
+	sub	x0, x1, x0
+	ldr	x1, [sp]
+#endif
 #endif
 	b	el\()\el\()_\label
 	.endm
-- 
2.18.0

WARNING: multiple messages have this Message-ID (diff)
From: ard.biesheuvel@linaro.org (Ard Biesheuvel)
To: linux-arm-kernel@lists.infradead.org
Subject: [RFC/PoC PATCH 3/3] arm64: enable ROP protection by clearing SP bit #55 across function returns
Date: Thu,  2 Aug 2018 15:21:32 +0200	[thread overview]
Message-ID: <20180802132133.23999-4-ard.biesheuvel@linaro.org> (raw)
In-Reply-To: <20180802132133.23999-1-ard.biesheuvel@linaro.org>

ROP attacks rely on a large supply of so-called 'gadgets', which are
(in this context) short sequences of instructions ending in a stack
pop and a return instruction. By exploiting a stack overflow to create
a specially crafted stack frame, each gadget jumps to the next by
popping off the next gadget's address as a fake return address,
allowing non-trivial 'programs' to be executed by piecing together
a large number of such gadgets.

This attack vector relies heavily on the ability to jump to arbitrary
places in the code. If we could limit where a function could return to,
it is much more difficult to obtain critical mass in terms of a gadget
collection that allows arbitrary attacks to be mounted.

So let's try and do so by clearing bit #55 in the stack pointer register
before returning from a function, and setting it again right after a
'bl' or 'blr' instruction. That way, jumping to arbitrary places in the
code and popping the next gadget's address becomes a lot more complicated,
since the stack pointer will not be valid after a function return until
the 'reset' sequence is executed (or after an exception is taken).

Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
---
 arch/arm64/Kconfig                 | 10 ++++++++++
 arch/arm64/include/asm/assembler.h |  9 +++++++++
 arch/arm64/kernel/entry.S          | 18 ++++++++++++++++++
 3 files changed, 37 insertions(+)

diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
index 42c090cf0292..4562af0250b9 100644
--- a/arch/arm64/Kconfig
+++ b/arch/arm64/Kconfig
@@ -1011,6 +1011,16 @@ config ARM64_SW_TTBR0_PAN
 	  zeroed area and reserved ASID. The user access routines
 	  restore the valid TTBR0_EL1 temporarily.
 
+config ARM64_ROP_SHIELD
+	bool "Enable basic ROP protection through the stack pointer sign bit"
+	depends on GCC_PLUGINS && VMAP_STACK
+	select GCC_PLUGIN_ARM64_ROP_SHIELD
+	help
+	  Enable protection against ROP attacks by clearing bit #55 in the
+	  stack pointer register across a function return.
+
+	  If paranoid, say Y here. If unsure, say N.
+
 menu "ARMv8.1 architectural features"
 
 config ARM64_HW_AFDBM
diff --git a/arch/arm64/include/asm/assembler.h b/arch/arm64/include/asm/assembler.h
index 346ada4de48a..95d3ec98eb58 100644
--- a/arch/arm64/include/asm/assembler.h
+++ b/arch/arm64/include/asm/assembler.h
@@ -701,12 +701,21 @@ USER(\label, ic	ivau, \tmp2)			// invalidate I line PoU
 .Lyield_out_\@ :
 	.endm
 
+	.macro		unclobber_sp, tmp
+#ifdef CONFIG_ARM64_ROP_SHIELD
+	mov		\tmp, sp
+	orr		sp, \tmp, #(1 << 55)
+#endif
+	.endm
+
 	.macro		bl_c, target
 	bl		\target
+	unclobber_sp	x30
 	.endm
 
 	.macro		blr_c, reg
 	blr		\reg
+	unclobber_sp	x30
 	.endm
 
 #endif	/* __ASM_ASSEMBLER_H */
diff --git a/arch/arm64/kernel/entry.S b/arch/arm64/kernel/entry.S
index eba5b6b528ea..2adebca74f11 100644
--- a/arch/arm64/kernel/entry.S
+++ b/arch/arm64/kernel/entry.S
@@ -95,6 +95,9 @@ alternative_else_nop_endif
 	 */
 	add	sp, sp, x0			// sp' = sp + x0
 	sub	x0, sp, x0			// x0' = sp' - x0 = (sp + x0) - x0 = sp
+#ifdef CONFIG_ARM64_ROP_SHIELD
+	tbz	x0, #55, 1f
+#endif
 	tbnz	x0, #THREAD_SHIFT, 0f
 	sub	x0, sp, x0			// x0'' = sp' - x0' = (sp + x0) - sp = x0
 	sub	sp, sp, x0			// sp'' = sp' - x0 = (sp + x0) - x0 = sp
@@ -129,6 +132,21 @@ alternative_else_nop_endif
 	/* We were already on the overflow stack. Restore sp/x0 and carry on. */
 	sub	sp, sp, x0
 	mrs	x0, tpidrro_el0
+	b	el\()\el\()_\label
+
+#ifdef CONFIG_ARM64_ROP_SHIELD
+1:	/*
+	 * We have to do a little dance here to set bit 55 in the stack
+	 * pointer register without clobbering anything else.
+	 */
+	orr	x0, x0, #(1 << 55)
+	str	x1, [x0]
+	mov	x1, sp
+	mov	sp, x0
+	and	x0, x0, #~(1 << 55)
+	sub	x0, x1, x0
+	ldr	x1, [sp]
+#endif
 #endif
 	b	el\()\el\()_\label
 	.endm
-- 
2.18.0

  parent reply	other threads:[~2018-08-02 13:21 UTC|newest]

Thread overview: 46+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2018-08-02 13:21 [RFC/PoC PATCH 0/3] arm64: basic ROP mitigation Ard Biesheuvel
2018-08-02 13:21 ` Ard Biesheuvel
2018-08-02 13:21 ` [RFC/PoC PATCH 1/3] arm64: use wrapper macro for bl/blx instructions from asm code Ard Biesheuvel
2018-08-02 13:21   ` Ard Biesheuvel
2018-08-02 13:21 ` [RFC/PoC PATCH 2/3] gcc: plugins: add ROP shield plugin for arm64 Ard Biesheuvel
2018-08-02 13:21   ` Ard Biesheuvel
2018-08-02 13:21 ` Ard Biesheuvel [this message]
2018-08-02 13:21   ` [RFC/PoC PATCH 3/3] arm64: enable ROP protection by clearing SP bit #55 across function returns Ard Biesheuvel
2018-08-06 10:07 ` [RFC/PoC PATCH 0/3] arm64: basic ROP mitigation Florian Weimer
2018-08-06 10:07   ` Florian Weimer
2018-08-06 10:31   ` Ard Biesheuvel
2018-08-06 10:31     ` Ard Biesheuvel
2018-08-06 13:55 ` Robin Murphy
2018-08-06 13:55   ` Robin Murphy
2018-08-06 14:04   ` Ard Biesheuvel
2018-08-06 14:04     ` Ard Biesheuvel
2018-08-06 15:20     ` Ard Biesheuvel
2018-08-06 15:20       ` Ard Biesheuvel
2018-08-06 15:38     ` Robin Murphy
2018-08-06 15:38       ` Robin Murphy
2018-08-06 15:50       ` Ard Biesheuvel
2018-08-06 15:50         ` Ard Biesheuvel
2018-08-06 16:04         ` Ard Biesheuvel
2018-08-06 16:04           ` Ard Biesheuvel
2018-08-06 17:45           ` Robin Murphy
2018-08-06 17:45             ` Robin Murphy
2018-08-06 18:49             ` Kees Cook
2018-08-06 18:49               ` Kees Cook
2018-08-06 19:35               ` Ard Biesheuvel
2018-08-06 19:35                 ` Ard Biesheuvel
2018-08-06 19:50                 ` Kees Cook
2018-08-06 19:50                   ` Kees Cook
2018-08-06 19:54                   ` Ard Biesheuvel
2018-08-06 19:54                     ` Ard Biesheuvel
2018-08-07  3:05                     ` Mark Brand
2018-08-07  9:21                       ` Ard Biesheuvel
2018-08-07  9:21                         ` Ard Biesheuvel
2018-08-08 16:09                         ` Mark Brand
2018-08-08 16:09                           ` Mark Brand
2018-08-08 22:02                           ` Kees Cook
2018-08-08 22:02                             ` Kees Cook
2018-08-13  7:39                           ` Ard Biesheuvel
2018-08-18  1:27 ` Laura Abbott
2018-08-18  1:27   ` Laura Abbott
2018-08-20  6:30   ` Ard Biesheuvel
2018-08-20  6:30     ` Ard Biesheuvel

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20180802132133.23999-4-ard.biesheuvel@linaro.org \
    --to=ard.biesheuvel@linaro.org \
    --cc=catalin.marinas@arm.com \
    --cc=christoffer.dall@arm.com \
    --cc=keescook@chromium.org \
    --cc=kernel-hardening@lists.openwall.com \
    --cc=labbott@fedoraproject.org \
    --cc=linux-arm-kernel@lists.infradead.org \
    --cc=mark.rutland@arm.com \
    --cc=will.deacon@arm.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.