unrecognizable insn generated in plugin?

2019-05-30 Thread Tycho Andersen
Hi all,

I've been trying to implement an idea Andy suggested recently for
preventing some kinds of ROP attacks. The discussion of the idea is
here:
https://lore.kernel.org/linux-mm/dfa69954-3f0f-4b79-a9b5-893d33d87...@amacapital.net/

Right now I'm struggling to get my plugin to compile without crashing. The
basic idea is to insert some code before every "pop rbp" and "pop rsp"; I've
figured out how to find these instructions, and I'm inserting code using:

emit_insn(gen_rtx_XOR(DImode, gen_rtx_REG(DImode, HARD_FRAME_POINTER_REGNUM),
  gen_rtx_MEM(DImode, gen_rtx_REG(DImode, 
HARD_FRAME_POINTER_REGNUM;

The plugin completes successfully, but GCC complains later,

kernel/seccomp.c: In function ‘seccomp_check_filter’:
kernel/seccomp.c:242:1: error: unrecognizable insn:
 }
 ^
(insn 698 645 699 17 (xor:DI (reg:DI 6 bp)
(mem:DI (reg:DI 6 bp) [0  S8 A8])) "kernel/seccomp.c":242 -1
 (nil))
during RTL pass: shorten
kernel/seccomp.c:242:1: internal compiler error: in insn_min_length, at 
config/i386/i386.md:14714

I assume this is because some internal metadata is screwed up, but I have no
clue as to what that is or how to fix it. My gcc version is 8.3.0, and
config/i386/i386.md:14714 of that tag looks mostly unrelated.

I had problems earlier because I was trying to run it after *clean_state which
is the thing that does init_insn_lengths(), but now I'm running it after
*stack_regs, so I thought it should be ok...

Anyway, the full plugin draft is below. You can run it by adding
CONFIG_GCC_PLUGIN_HEAPLEAP=y to your kernel config.

Thanks!

Tycho


>From 83b0631f14784ce11362ebd64e40c8d25c0decee Mon Sep 17 00:00:00 2001
From: Tycho Andersen 
Date: Fri, 19 Apr 2019 19:24:58 -0600
Subject: [PATCH] heapleap

Signed-off-by: Tycho Andersen 
---
 scripts/Makefile.gcc-plugins  |   8 ++
 scripts/gcc-plugins/Kconfig   |   4 +
 scripts/gcc-plugins/heapleap_plugin.c | 189 ++
 3 files changed, 201 insertions(+)

diff --git a/scripts/Makefile.gcc-plugins b/scripts/Makefile.gcc-plugins
index 5f7df50cfe7a..283b81dc5742 100644
--- a/scripts/Makefile.gcc-plugins
+++ b/scripts/Makefile.gcc-plugins
@@ -44,6 +44,14 @@ ifdef CONFIG_GCC_PLUGIN_ARM_SSP_PER_TASK
 endif
 export DISABLE_ARM_SSP_PER_TASK_PLUGIN
 
+gcc-plugin-$(CONFIG_GCC_PLUGIN_HEAPLEAP)   += heapleap_plugin.so
+gcc-plugin-cflags-$(CONFIG_GCC_PLUGIN_HEAPLEAP)\
+   += -DHEAPLEAP_PLUGIN
+ifdef CONFIG_GCC_PLUGIN_HEAPLEAP
+DISABLE_HEAPLEAP_PLUGIN += -fplugin-arg-heapleap_plugin-disable
+endif
+export DISABLE_HEAPLEAP_PLUGIN
+
 # All the plugin CFLAGS are collected here in case a build target needs to
 # filter them out of the KBUILD_CFLAGS.
 GCC_PLUGINS_CFLAGS := $(strip $(addprefix 
-fplugin=$(objtree)/scripts/gcc-plugins/, $(gcc-plugin-y)) 
$(gcc-plugin-cflags-y))
diff --git a/scripts/gcc-plugins/Kconfig b/scripts/gcc-plugins/Kconfig
index 74271dba4f94..491b9cd5df1a 100644
--- a/scripts/gcc-plugins/Kconfig
+++ b/scripts/gcc-plugins/Kconfig
@@ -226,4 +226,8 @@ config GCC_PLUGIN_ARM_SSP_PER_TASK
bool
depends on GCC_PLUGINS && ARM
 
+config GCC_PLUGIN_HEAPLEAP
+   bool "Prevent 'pop esp' type instructions from loading an address in 
the heap"
+   depends on GCC_PLUGINS
+
 endif
diff --git a/scripts/gcc-plugins/heapleap_plugin.c 
b/scripts/gcc-plugins/heapleap_plugin.c
new file mode 100644
index ..5051b96d79f4
--- /dev/null
+++ b/scripts/gcc-plugins/heapleap_plugin.c
@@ -0,0 +1,189 @@
+/*
+ * This is based on an idea from Andy Lutomirski described here:
+ * 
https://lore.kernel.org/linux-mm/dfa69954-3f0f-4b79-a9b5-893d33d87...@amacapital.net/
+ *
+ * unsigned long offset = *rsp - rsp;
+ * offset >>= THREAD_SHIFT;
+ * if (unlikely(offset))
+ * BUG();
+ * POP RSP;
+ */
+
+#include "gcc-common.h"
+
+__visible int plugin_is_GPL_compatible;
+static bool disable = false;
+
+static struct plugin_info heapleap_plugin_info = {
+   .version = "1",
+   .help = "disable\t\tdo not activate the plugin\n"
+};
+
+static bool heapleap_gate(void)
+{
+   tree section;
+
+   /*
+* Similar to stackleak, we only do this for user code for now.
+*/
+   section = lookup_attribute("section",
+  DECL_ATTRIBUTES(current_function_decl));
+   if (section && TREE_VALUE(section)) {
+   section = TREE_VALUE(TREE_VALUE(section));
+
+   if (!strncmp(TREE_STRING_POINTER(section), ".init.text", 10))
+   return false;
+   if (!strncmp(TREE_STRING_POINTER(section), ".devinit.text", 13))
+   return false;
+   if (!strncmp(TREE_STRING_POINTER(section), ".cpuinit.text", 13))
+   return false;
+   if (!strncm

Re: unrecognizable insn generated in plugin?

2019-05-30 Thread Tycho Andersen
Hi Andrew,

On Thu, May 30, 2019 at 10:09:44AM -0700, Andrew Pinski wrote:
> On Thu, May 30, 2019 at 10:01 AM Tycho Andersen  wrote:
> >
> > Hi all,
> >
> > I've been trying to implement an idea Andy suggested recently for
> > preventing some kinds of ROP attacks. The discussion of the idea is
> > here:
> > https://lore.kernel.org/linux-mm/dfa69954-3f0f-4b79-a9b5-893d33d87...@amacapital.net/
> >
> > Right now I'm struggling to get my plugin to compile without crashing. The
> > basic idea is to insert some code before every "pop rbp" and "pop rsp"; I've
> > figured out how to find these instructions, and I'm inserting code using:
> >
> > emit_insn(gen_rtx_XOR(DImode, gen_rtx_REG(DImode, 
> > HARD_FRAME_POINTER_REGNUM),
> >   gen_rtx_MEM(DImode, gen_rtx_REG(DImode, 
> > HARD_FRAME_POINTER_REGNUM;
> 
> Simplely this xor does not set anything.
> I think you want something like:
> emit_insn(gen_rtx_SET(gen_rtx_REG(DImode, HARD_FRAME_POINTER_REGNUM),
>   gen_rtx_XOR(DImode, gen_rtx_REG(DImode, HARD_FRAME_POINTER_REGNUM),
>   gen_rtx_MEM(DImode, gen_rtx_REG(DImode, HARD_FRAME_POINTER_REGNUM);
> 
> But that might not work either, you might need some thing more.

Yes, thanks. You're also right that I still see the same problem:

kernel/seccomp.c: In function ‘seccomp_check_filter’:
kernel/seccomp.c:242:1: error: unrecognizable insn:
 }
 ^
(insn 698 645 699 17 (set (reg:DI 6 bp)
(xor:DI (reg:DI 6 bp)
(mem:DI (reg:DI 6 bp) [0  S8 A8]))) "kernel/seccomp.c":242 -1
 (nil))
during RTL pass: shorten
kernel/seccomp.c:242:1: internal compiler error: in insn_min_length, at 
config/i386/i386.md:14714

Thanks,

Tycho


Re: unrecognizable insn generated in plugin?

2019-05-31 Thread Tycho Andersen
On Fri, May 31, 2019 at 05:43:44PM +0200, Mark Brand wrote:
> On Thu, May 30, 2019 at 9:26 PM Tycho Andersen  wrote:
> >
> > Hi Andrew,
> >
> > On Thu, May 30, 2019 at 10:09:44AM -0700, Andrew Pinski wrote:
> > > On Thu, May 30, 2019 at 10:01 AM Tycho Andersen  wrote:
> > > >
> > > > Hi all,
> > > >
> > > > I've been trying to implement an idea Andy suggested recently for
> > > > preventing some kinds of ROP attacks. The discussion of the idea is
> > > > here:
> > > > https://lore.kernel.org/linux-mm/dfa69954-3f0f-4b79-a9b5-893d33d87...@amacapital.net/
> > > >
> 
> 
> Hi Tycho,
> 
> I realise this is maybe not relevant to the topic of fixing the
> plugin; but I'm struggling to understand what this is intending to
> protect against.
> 
> The idea seems to be to make sure that restored rbp, rsp values are
> "close" to the current rbp, rsp values? The only scenario I can see
> this providing any benefit is if an attacker can only corrupt a saved
> stack/frame pointer, which seems like such an unlikely situation that
> it's not really worth adding any complexity to defend against.

> An attacker who has control of rip can surely get a controlled value
> into rsp in various ways;

Yes, if you already have control of rip this doesn't help you.

> a quick scan of the current Ubuntu 18.04
> kernel image offers the following sequence (which shows up
> everywhere):
> 
> lea rsp, qword ptr [r10 - 8]
> ret
> 
> I'd assume that it's not tremendously difficult for an attacker to
> chain to this without needing to previously pivot out the stack
> pointer, assuming that at the point at which they gain control of rip
> they have control over some state somewhere. If you could explain the
> exact attack scenario that you have in mind then perhaps I could
> provide a better explanation of how one might bypass it.

The core bit that's important here is the writes to rsp/rbp, not the
fact that they're pop instructions. The insight is that we know how
the thread's stack should be aligned, and so any value that's written
to these registers outside of that alignment (during "normal"
execution) is a bug.

The idea is that a ROP attack requires a payload to be injected
somewhere so that it can return to this payload and execute this
attack. This is most probably done via some allocation elsewhere
(add_key() or unreceived data or whatever) since as you note, the
stack should be mostly well protected.

So then, if we don't allow anyone to write anything that's not on the
stack to rsp/rbp, in principle we should be safe from ROP attacks
where the payload is elsewhere. As you note, preventing bad "pop
rpb" instructions is not enough, nor is preventing bad "pop rsp", as
Andy initially proposed. We need to prevent all bad writes to these
registers, including the sequence you mentioned, and presumably
others. So there would need to be a lot more matching and checks
inserted, and maybe it would ultimately be slow. Right now I just
wanted to play with it for a bit to see if I can get it to work at all
even in one case :)

Am I thinking about this wrong? Any discussion is welcome, thanks!

Tycho