* [PATCH v2] lkdtm: Check for SMEP clearing protections
@ 2019-06-22 20:18 Kees Cook
0 siblings, 0 replies; 2+ messages in thread
From: Kees Cook @ 2019-06-22 20:18 UTC (permalink / raw)
To: Greg Kroah-Hartman; +Cc: linux-kernel
This adds an x86-specific test for pinned cr4 bits. A successful test
will validate pinning and check the ROP-style call-middle-of-function
defense, if needed. For example, in the case of native_write_cr4()
looking like this:
ffffffff8171bce0 <native_write_cr4>:
ffffffff8171bce0: 48 8b 35 79 46 f2 00 mov 0xf24679(%rip),%rsi
ffffffff8171bce7: 48 09 f7 or %rsi,%rdi
ffffffff8171bcea: 0f 22 e7 mov %rdi,%cr4
...
ffffffff8171bd5a: c3 retq
The UNSET_SMEP test will jump to ffffffff8171bcea (the mov to cr4)
instead of ffffffff8171bce0 (native_write_cr4() entry) to simulate a
direct-call bypass attempt.
Expected successful results:
# echo UNSET_SMEP > /sys/kernel/debug/provoke-crash/DIRECT
# dmesg
[ 79.594433] lkdtm: Performing direct entry UNSET_SMEP
[ 79.596459] lkdtm: trying to clear SMEP normally
[ 79.598406] lkdtm: ok: SMEP did not get cleared
[ 79.599981] lkdtm: trying to clear SMEP with call gadget
[ 79.601810] ------------[ cut here ]------------
[ 79.603421] Attempt to unpin cr4 bits: 100000; bypass attack?!
...
[ 79.650170] ---[ end trace 2452ca0f6126242e ]---
[ 79.650937] lkdtm: ok: SMEP removal was reverted
Signed-off-by: Kees Cook <keescook@chromium.org>
---
v2:
- rebase to linux-next
---
drivers/misc/lkdtm/bugs.c | 66 ++++++++++++++++++++++++++++++++++++++
drivers/misc/lkdtm/core.c | 1 +
drivers/misc/lkdtm/lkdtm.h | 1 +
3 files changed, 68 insertions(+)
diff --git a/drivers/misc/lkdtm/bugs.c b/drivers/misc/lkdtm/bugs.c
index d9fcfd3b5af0..99a2dce1625b 100644
--- a/drivers/misc/lkdtm/bugs.c
+++ b/drivers/misc/lkdtm/bugs.c
@@ -266,3 +266,69 @@ void lkdtm_STACK_GUARD_PAGE_TRAILING(void)
pr_err("FAIL: accessed page after stack!\n");
}
+
+void lkdtm_UNSET_SMEP(void)
+{
+#ifdef CONFIG_X86_64
+#define MOV_CR4_DEPTH 64
+ void (*direct_write_cr4)(unsigned long val);
+ unsigned char *insn;
+ unsigned long cr4;
+ int i;
+
+ cr4 = native_read_cr4();
+
+ if ((cr4 & X86_CR4_SMEP) != X86_CR4_SMEP) {
+ pr_err("FAIL: SMEP not in use\n");
+ return;
+ }
+ cr4 &= ~(X86_CR4_SMEP);
+
+ pr_info("trying to clear SMEP normally\n");
+ native_write_cr4(cr4);
+ if (cr4 == native_read_cr4()) {
+ pr_err("FAIL: pinning SMEP failed!\n");
+ cr4 |= X86_CR4_SMEP;
+ pr_info("restoring SMEP\n");
+ native_write_cr4(cr4);
+ return;
+ }
+ pr_info("ok: SMEP did not get cleared\n");
+
+ /*
+ * To test the post-write pinning verification we need to call
+ * directly into the middle of native_write_cr4() where the
+ * cr4 write happens, skipping any pinning. This searches for
+ * the cr4 writing instruction.
+ */
+ insn = (unsigned char *)native_write_cr4;
+ for (i = 0; i < MOV_CR4_DEPTH; i++) {
+ /* mov %rdi, %cr4 */
+ if (insn[i] == 0x0f && insn[i+1] == 0x22 && insn[i+2] == 0xe7)
+ break;
+ /* mov %rdi,%rax; mov %rax, %cr4 */
+ if (insn[i] == 0x48 && insn[i+1] == 0x89 &&
+ insn[i+2] == 0xf8 && insn[i+3] == 0x0f &&
+ insn[i+4] == 0x22 && insn[i+5] == 0xe0)
+ break;
+ }
+ if (i >= MOV_CR4_DEPTH) {
+ pr_info("ok: cannot locate cr4 writing call gadget\n");
+ return;
+ }
+ direct_write_cr4 = (void *)(insn + i);
+
+ pr_info("trying to clear SMEP with call gadget\n");
+ direct_write_cr4(cr4);
+ if (native_read_cr4() & X86_CR4_SMEP) {
+ pr_info("ok: SMEP removal was reverted\n");
+ } else {
+ pr_err("FAIL: cleared SMEP not detected!\n");
+ cr4 |= X86_CR4_SMEP;
+ pr_info("restoring SMEP\n");
+ native_write_cr4(cr4);
+ }
+#else
+ pr_err("FAIL: this test is x86_64-only\n");
+#endif
+}
diff --git a/drivers/misc/lkdtm/core.c b/drivers/misc/lkdtm/core.c
index 945f2059d423..52e99ab9cedc 100644
--- a/drivers/misc/lkdtm/core.c
+++ b/drivers/misc/lkdtm/core.c
@@ -114,6 +114,7 @@ static const struct crashtype crashtypes[] = {
CRASHTYPE(CORRUPT_USER_DS),
CRASHTYPE(STACK_GUARD_PAGE_LEADING),
CRASHTYPE(STACK_GUARD_PAGE_TRAILING),
+ CRASHTYPE(UNSET_SMEP),
CRASHTYPE(UNALIGNED_LOAD_STORE_WRITE),
CRASHTYPE(OVERWRITE_ALLOCATION),
CRASHTYPE(WRITE_AFTER_FREE),
diff --git a/drivers/misc/lkdtm/lkdtm.h b/drivers/misc/lkdtm/lkdtm.h
index c5ae0b37587d..6a284a87a037 100644
--- a/drivers/misc/lkdtm/lkdtm.h
+++ b/drivers/misc/lkdtm/lkdtm.h
@@ -26,6 +26,7 @@ void lkdtm_CORRUPT_LIST_DEL(void);
void lkdtm_CORRUPT_USER_DS(void);
void lkdtm_STACK_GUARD_PAGE_LEADING(void);
void lkdtm_STACK_GUARD_PAGE_TRAILING(void);
+void lkdtm_UNSET_SMEP(void);
/* lkdtm_heap.c */
void __init lkdtm_heap_init(void);
--
2.17.1
--
Kees Cook
^ permalink raw reply [flat|nested] 2+ messages in thread* [PATCH 0/3] x86/asm: More pinning
@ 2019-02-26 23:36 Kees Cook
2019-02-26 23:45 ` [PATCH v2] lkdtm: Check for SMEP clearing protections Kees Cook
0 siblings, 1 reply; 2+ messages in thread
From: Kees Cook @ 2019-02-26 23:36 UTC (permalink / raw)
To: Thomas Gleixner
Cc: Kees Cook, Peter Zijlstra, Jann Horn, Sean Christopherson,
Dominik Brodowski, Kernel Hardening, linux-kernel
This adds CR0 pinning (for WP), and cleans up the CR4 pin to avoid
taking an exception from WARN before fixing up the desired pin.
Additionally adds lkdtm test (which depends on the CR4 patch, otherwise
I'd send it via Greg's tree).
Thanks!
-Kees
Kees Cook (3):
x86/asm: Pin sensitive CR0 bits
x86/asm: Avoid taking an exception before cr4 restore
lkdtm: Check for SMEP clearing protections
arch/x86/include/asm/special_insns.h | 33 +++++++++++++--
drivers/misc/lkdtm/bugs.c | 60 ++++++++++++++++++++++++++++
drivers/misc/lkdtm/core.c | 1 +
drivers/misc/lkdtm/lkdtm.h | 1 +
4 files changed, 91 insertions(+), 4 deletions(-)
--
2.17.1
^ permalink raw reply [flat|nested] 2+ messages in thread
* [PATCH v2] lkdtm: Check for SMEP clearing protections
2019-02-26 23:36 [PATCH 0/3] x86/asm: More pinning Kees Cook
@ 2019-02-26 23:45 ` Kees Cook
0 siblings, 0 replies; 2+ messages in thread
From: Kees Cook @ 2019-02-26 23:45 UTC (permalink / raw)
To: Thomas Gleixner
Cc: Peter Zijlstra, Jann Horn, Sean Christopherson,
Dominik Brodowski, Kernel Hardening, LKML
This adds an x86-specific test for pinned cr4 bits. A successful test
will validate pinning and check the ROP-style call-middle-of-function
defense, if needed. For example, in the case of native_write_cr4()
looking like this:
ffffffff8171bce0 <native_write_cr4>:
ffffffff8171bce0: 48 8b 35 79 46 f2 00 mov 0xf24679(%rip),%rsi
ffffffff8171bce7: 48 09 f7 or %rsi,%rdi
ffffffff8171bcea: 0f 22 e7 mov %rdi,%cr4
...
ffffffff8171bd5a: c3 retq
The UNSET_SMEP test will jump to ffffffff8171bcea (the mov to cr4)
instead of ffffffff8171bce0 (native_write_cr4() entry) to simulate a
direct-call bypass attempt.
Expected successful results:
# echo UNSET_SMEP > /sys/kernel/debug/provoke-crash/DIRECT
# dmesg
[ 79.594433] lkdtm: Performing direct entry UNSET_SMEP
[ 79.596459] lkdtm: trying to clear SMEP normally
[ 79.598406] lkdtm: ok: SMEP did not get cleared
[ 79.599981] lkdtm: trying to clear SMEP with call gadget
[ 79.601810] ------------[ cut here ]------------
[ 79.603421] Attempt to unpin cr4 bits: 100000; bypass attack?!
...
[ 79.650170] ---[ end trace 2452ca0f6126242e ]---
[ 79.650937] lkdtm: ok: SMEP removal was reverted
Signed-off-by: Kees Cook <keescook@chromium.org>
---
v2: fix 64 vs 256 loop-end comparison, use a #define instead
---
drivers/misc/lkdtm/bugs.c | 61 ++++++++++++++++++++++++++++++++++++++
drivers/misc/lkdtm/core.c | 1 +
drivers/misc/lkdtm/lkdtm.h | 1 +
3 files changed, 63 insertions(+)
diff --git a/drivers/misc/lkdtm/bugs.c b/drivers/misc/lkdtm/bugs.c
index 7eebbdfbcacd..6176384b4f85 100644
--- a/drivers/misc/lkdtm/bugs.c
+++ b/drivers/misc/lkdtm/bugs.c
@@ -255,3 +255,64 @@ void lkdtm_STACK_GUARD_PAGE_TRAILING(void)
pr_err("FAIL: accessed page after stack!\n");
}
+
+void lkdtm_UNSET_SMEP(void)
+{
+#ifdef CONFIG_X86_64
+#define MOV_CR4_DEPTH 64
+ void (*direct_write_cr4)(unsigned long val);
+ unsigned char *insn;
+ unsigned long cr4;
+ int i;
+
+ cr4 = native_read_cr4();
+
+ if ((cr4 & X86_CR4_SMEP) != X86_CR4_SMEP) {
+ pr_err("FAIL: SMEP not in use\n");
+ return;
+ }
+ cr4 &= ~(X86_CR4_SMEP);
+
+ pr_info("trying to clear SMEP normally\n");
+ native_write_cr4(cr4);
+ if (cr4 == native_read_cr4()) {
+ pr_err("FAIL: pinning SMEP failed!\n");
+ cr4 |= X86_CR4_SMEP;
+ pr_info("restoring SMEP\n");
+ native_write_cr4(cr4);
+ return;
+ }
+ pr_info("ok: SMEP did not get cleared\n");
+
+ /*
+ * To test the post-write pinning verification we need to call
+ * directly into the the middle of native_write_cr4() where the
+ * cr4 write happens, skipping the pinning. This searches for
+ * the cr4 writing instruction.
+ */
+ insn = (unsigned char *)native_write_cr4;
+ for (i = 0; i < MOV_CR4_DEPTH; i++) {
+ /* mov %rdi, %cr4 */
+ if (insn[i] == 0x0f && insn[i+1] == 0x22 && insn[i+2] == 0xe7)
+ break;
+ }
+ if (i >= MOV_CR4_DEPTH) {
+ pr_info("ok: cannot locate cr4 writing call gadget\n");
+ return;
+ }
+ direct_write_cr4 = (void *)(insn + i);
+
+ pr_info("trying to clear SMEP with call gadget\n");
+ direct_write_cr4(cr4);
+ if (native_read_cr4() & X86_CR4_SMEP) {
+ pr_info("ok: SMEP removal was reverted\n");
+ } else {
+ pr_err("FAIL: cleared SMEP not detected!\n");
+ cr4 |= X86_CR4_SMEP;
+ pr_info("restoring SMEP\n");
+ native_write_cr4(cr4);
+ }
+#else
+ pr_err("FAIL: this test is x86_64-only\n");
+#endif
+}
diff --git a/drivers/misc/lkdtm/core.c b/drivers/misc/lkdtm/core.c
index 2837dc77478e..fd668776414b 100644
--- a/drivers/misc/lkdtm/core.c
+++ b/drivers/misc/lkdtm/core.c
@@ -132,6 +132,7 @@ static const struct crashtype crashtypes[] = {
CRASHTYPE(CORRUPT_LIST_ADD),
CRASHTYPE(CORRUPT_LIST_DEL),
CRASHTYPE(CORRUPT_USER_DS),
+ CRASHTYPE(UNSET_SMEP),
CRASHTYPE(CORRUPT_STACK),
CRASHTYPE(CORRUPT_STACK_STRONG),
CRASHTYPE(STACK_GUARD_PAGE_LEADING),
diff --git a/drivers/misc/lkdtm/lkdtm.h b/drivers/misc/lkdtm/lkdtm.h
index 3c6fd327e166..9c78d7e21c13 100644
--- a/drivers/misc/lkdtm/lkdtm.h
+++ b/drivers/misc/lkdtm/lkdtm.h
@@ -26,6 +26,7 @@ void lkdtm_CORRUPT_LIST_DEL(void);
void lkdtm_CORRUPT_USER_DS(void);
void lkdtm_STACK_GUARD_PAGE_LEADING(void);
void lkdtm_STACK_GUARD_PAGE_TRAILING(void);
+void lkdtm_UNSET_SMEP(void);
/* lkdtm_heap.c */
void lkdtm_OVERWRITE_ALLOCATION(void);
--
2.17.1
--
Kees Cook
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2019-06-22 20:18 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-06-22 20:18 [PATCH v2] lkdtm: Check for SMEP clearing protections Kees Cook
-- strict thread matches above, loose matches on Subject: below --
2019-02-26 23:36 [PATCH 0/3] x86/asm: More pinning Kees Cook
2019-02-26 23:45 ` [PATCH v2] lkdtm: Check for SMEP clearing protections Kees Cook
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®