summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndy Lutomirski <luto@kernel.org>2015-03-18 17:19:19 -0700
committerAndy Lutomirski <luto@kernel.org>2015-03-18 17:19:19 -0700
commit8b0d8912aaea07e207d93829294bb1b3cd8e0c6a (patch)
treed098dd98947138337fb7280c8650c31a93bd6b2a
parent2c10d8ca605c8ebd24fbb2e920a91bd5ae04ec36 (diff)
downloadmisc-tests-8b0d8912aaea07e207d93829294bb1b3cd8e0c6a.tar.gz
segregs: Test gs switch failures
Signed-off-by: Andy Lutomirski <luto@kernel.org>
-rw-r--r--segregs.c80
1 files changed, 78 insertions, 2 deletions
diff --git a/segregs.c b/segregs.c
index a0c34b8..db11cd1 100644
--- a/segregs.c
+++ b/segregs.c
@@ -5,6 +5,7 @@
#include <stdio.h>
#include <unistd.h>
+#include <string.h>
#include <time.h>
#include <err.h>
#include <asm/ldt.h>
@@ -15,6 +16,11 @@ static unsigned short GDT3(int idx)
return (idx << 3) | 3;
}
+static unsigned short LDT3(int idx)
+{
+ return (idx << 3) | 7;
+}
+
static int create_tls(int idx, unsigned int base)
{
struct user_desc desc = {
@@ -35,8 +41,73 @@ static int create_tls(int idx, unsigned int base)
return desc.entry_number;
}
+static void do_gs_test(int idx, int np)
+{
+ unsigned short orig_gs, new_gs;
+ int ax;
+
+ /*
+ * Install a valid LDT entry (set_thread_area's hardening measures
+ * defeat the #NP part of this test).
+ */
+ struct user_desc desc;
+ memset(&desc, 0, sizeof(desc));
+ desc.entry_number = 0;
+ desc.limit = 0xffff;
+ desc.seg_32bit = 1;
+ desc.contents = 0; /* Data, grow-up */
+ desc.read_exec_only = 0;
+ desc.limit_in_pages = 1;
+ desc.seg_not_present = 0;
+ if (syscall(SYS_modify_ldt, 1, &desc, sizeof(desc)) != 0)
+ err(1, "modify_ldt");
+
+ if (np) {
+ /* valid but not present */
+ desc.seg_not_present = 1;
+ } else {
+ /* "empty" user_desc -> destroy the segment. */
+ desc.limit = 0;
+ desc.seg_32bit = 0;
+ desc.limit_in_pages = 0;
+ desc.read_exec_only = 1;
+ desc.seg_not_present = 1;
+ }
+
+ struct timespec req = {
+ .tv_sec = 0,
+ .tv_nsec = 100000,
+ };
+
+ printf("[RUN]\tGS %s\n", (np ? "not present" : "deleted"));
+ asm volatile ("mov %%gs,%0" : "=rm" (orig_gs));
+ asm volatile ("mov %0,%%gs" : : "rm" (LDT3(0)));
+
+ asm volatile ("int $0x80"
+ : "=a" (ax)
+ : "a" (SYS_modify_ldt), "b" (1), "c" (&desc),
+ "d" (sizeof(desc)));
+ if (ax != 0)
+ err(1, "set_thread_area");
+
+ /*
+ * Force rescheduling. On 32-bit kernels, fast syscalls
+ * destroy DS and ES, so force int 80.
+ */
+ asm volatile ("int $0x80"
+ : "=a" (ax)
+ : "a" (SYS_nanosleep), "b" (&req),
+ "c" (0));
+
+ asm volatile ("mov %%gs,%0" : "=rm" (new_gs));
+ asm volatile ("mov %0,%%gs" : : "rm" (orig_gs));
+ printf("[OK]\tGS changed from %x to %x\n", (unsigned)GDT3(idx),
+ (unsigned)new_gs);
+}
+
int main()
{
+ int ret;
int idx = create_tls(-1, 0);
printf("Allocated GDT index %d\n", idx);
@@ -76,9 +147,14 @@ int main()
if (errors) {
printf("[FAIL]\tES was corrupted %d/%d times\n", errors, total);
- return 1;
+ ret = 1;
} else {
printf("[OK]\tES was preserved\n");
- return 0;
+ ret = 0;
}
+
+ do_gs_test(idx, 1);
+ create_tls(idx, 0);
+ do_gs_test(idx, 0);
+ return ret;
}