* [PATCH 0/2] TSS incremental cleanups
@ 2017-02-22 15:36 Andy Lutomirski
2017-02-22 15:36 ` [PATCH 1/2] x86/asm: Tidy up TSS limit code Andy Lutomirski
2017-02-22 15:36 ` [PATCH 2/2] selftests/x86: Add a basic selftest for ioperm Andy Lutomirski
0 siblings, 2 replies; 3+ messages in thread
From: Andy Lutomirski @ 2017-02-22 15:36 UTC (permalink / raw)
To: Paolo Bonzini, X86 ML
Cc: kvm list, linux-kernel@vger.kernel.org, Borislav Petkov,
Thomas Garnier, Jim Mattson, Andy Lutomirski
Hi all-
These are incremental improvements. I think they should go through
kvm.git since they are functionally KVM improvements and since the
code being improved is in kvm.git.
Andy Lutomirski (2):
x86/asm: Tidy up TSS limit code
selftests/x86: Add a basic selftest for ioperm
arch/x86/include/asm/desc.h | 18 ++--
arch/x86/kernel/ioport.c | 8 +-
arch/x86/kernel/process.c | 6 +-
tools/testing/selftests/x86/Makefile | 2 +-
tools/testing/selftests/x86/ioperm.c | 171 +++++++++++++++++++++++++++++++++++
5 files changed, 193 insertions(+), 12 deletions(-)
create mode 100644 tools/testing/selftests/x86/ioperm.c
--
2.9.3
^ permalink raw reply [flat|nested] 3+ messages in thread
* [PATCH 1/2] x86/asm: Tidy up TSS limit code
2017-02-22 15:36 [PATCH 0/2] TSS incremental cleanups Andy Lutomirski
@ 2017-02-22 15:36 ` Andy Lutomirski
2017-02-22 15:36 ` [PATCH 2/2] selftests/x86: Add a basic selftest for ioperm Andy Lutomirski
1 sibling, 0 replies; 3+ messages in thread
From: Andy Lutomirski @ 2017-02-22 15:36 UTC (permalink / raw)
To: Paolo Bonzini, X86 ML
Cc: kvm list, linux-kernel@vger.kernel.org, Borislav Petkov,
Thomas Garnier, Jim Mattson, Andy Lutomirski
In an earlier version of the patch ("x86/kvm/vmx: Defer TR reload
after VM exit") that introduced TSS limit validity tracking, I
confused which helper was which. On reflection, the names I chose
sucked. Rename the helpers to make it more obvious what's going on
and add some comments.
While I'm at it, clear __tss_limit_invalid when force-reloading as
well as when contitionally reloading, since any TR reload fixes the
limit.
Signed-off-by: Andy Lutomirski <luto@kernel.org>
---
arch/x86/include/asm/desc.h | 18 +++++++++++-------
arch/x86/kernel/ioport.c | 8 +++++++-
arch/x86/kernel/process.c | 6 +++---
3 files changed, 21 insertions(+), 11 deletions(-)
diff --git a/arch/x86/include/asm/desc.h b/arch/x86/include/asm/desc.h
index cb8f9149f6c8..1548ca92ad3f 100644
--- a/arch/x86/include/asm/desc.h
+++ b/arch/x86/include/asm/desc.h
@@ -205,6 +205,8 @@ static inline void native_load_tr_desc(void)
asm volatile("ltr %w0"::"q" (GDT_ENTRY_TSS*8));
}
+DECLARE_PER_CPU(bool, __tss_limit_invalid);
+
static inline void force_reload_TR(void)
{
struct desc_struct *d = get_cpu_gdt_table(smp_processor_id());
@@ -220,18 +222,20 @@ static inline void force_reload_TR(void)
write_gdt_entry(d, GDT_ENTRY_TSS, &tss, DESC_TSS);
load_TR_desc();
+ this_cpu_write(__tss_limit_invalid, false);
}
-DECLARE_PER_CPU(bool, need_tr_refresh);
-
-static inline void refresh_TR(void)
+/*
+ * Call this if you need the TSS limit to be correct, which should be the case
+ * if and only if you have TIF_IO_BITMAP set or you're switching to a task
+ * with TIF_IO_BITMAP set.
+ */
+static inline void refresh_tss_limit(void)
{
DEBUG_LOCKS_WARN_ON(preemptible());
- if (unlikely(this_cpu_read(need_tr_refresh))) {
+ if (unlikely(this_cpu_read(__tss_limit_invalid)))
force_reload_TR();
- this_cpu_write(need_tr_refresh, false);
- }
}
/*
@@ -250,7 +254,7 @@ static inline void invalidate_tss_limit(void)
if (unlikely(test_thread_flag(TIF_IO_BITMAP)))
force_reload_TR();
else
- this_cpu_write(need_tr_refresh, true);
+ this_cpu_write(__tss_limit_invalid, true);
}
static inline void native_load_gdt(const struct desc_ptr *dtr)
diff --git a/arch/x86/kernel/ioport.c b/arch/x86/kernel/ioport.c
index b01bc8517450..875d3d25dd6a 100644
--- a/arch/x86/kernel/ioport.c
+++ b/arch/x86/kernel/ioport.c
@@ -47,8 +47,14 @@ asmlinkage long sys_ioperm(unsigned long from, unsigned long num, int turn_on)
t->io_bitmap_ptr = bitmap;
set_thread_flag(TIF_IO_BITMAP);
+ /*
+ * Now that we have an IO bitmap, we need our TSS limit to be
+ * correct. It's fine if we are preempted after doing this:
+ * with TIF_IO_BITMAP set, context switches will keep our TSS
+ * limit correct.
+ */
preempt_disable();
- refresh_TR();
+ refresh_tss_limit();
preempt_enable();
}
diff --git a/arch/x86/kernel/process.c b/arch/x86/kernel/process.c
index 7780efa635b9..0b302591b51f 100644
--- a/arch/x86/kernel/process.c
+++ b/arch/x86/kernel/process.c
@@ -65,8 +65,8 @@ __visible DEFINE_PER_CPU_SHARED_ALIGNED(struct tss_struct, cpu_tss) = {
};
EXPORT_PER_CPU_SYMBOL(cpu_tss);
-DEFINE_PER_CPU(bool, need_tr_refresh);
-EXPORT_PER_CPU_SYMBOL_GPL(need_tr_refresh);
+DEFINE_PER_CPU(bool, __tss_limit_invalid);
+EXPORT_PER_CPU_SYMBOL_GPL(__tss_limit_invalid);
/*
* this gets called so that we can store lazy state into memory and copy the
@@ -218,7 +218,7 @@ void __switch_to_xtra(struct task_struct *prev_p, struct task_struct *next_p,
* Make sure that the TSS limit is correct for the CPU
* to notice the IO bitmap.
*/
- refresh_TR();
+ refresh_tss_limit();
} else if (test_tsk_thread_flag(prev_p, TIF_IO_BITMAP)) {
/*
* Clear any possible leftover bits:
--
2.9.3
^ permalink raw reply related [flat|nested] 3+ messages in thread
* [PATCH 2/2] selftests/x86: Add a basic selftest for ioperm
2017-02-22 15:36 [PATCH 0/2] TSS incremental cleanups Andy Lutomirski
2017-02-22 15:36 ` [PATCH 1/2] x86/asm: Tidy up TSS limit code Andy Lutomirski
@ 2017-02-22 15:36 ` Andy Lutomirski
1 sibling, 0 replies; 3+ messages in thread
From: Andy Lutomirski @ 2017-02-22 15:36 UTC (permalink / raw)
To: Paolo Bonzini, X86 ML
Cc: kvm list, linux-kernel@vger.kernel.org, Borislav Petkov,
Thomas Garnier, Jim Mattson, Andy Lutomirski
This doesn't fully exercise the interaction between KVM and ioperm(),
but it does test basic functionality.
Signed-off-by: Andy Lutomirski <luto@kernel.org>
---
tools/testing/selftests/x86/Makefile | 2 +-
tools/testing/selftests/x86/ioperm.c | 171 +++++++++++++++++++++++++++++++++++
2 files changed, 172 insertions(+), 1 deletion(-)
create mode 100644 tools/testing/selftests/x86/ioperm.c
diff --git a/tools/testing/selftests/x86/Makefile b/tools/testing/selftests/x86/Makefile
index 8c1cb423cfe6..5743e2966adb 100644
--- a/tools/testing/selftests/x86/Makefile
+++ b/tools/testing/selftests/x86/Makefile
@@ -5,7 +5,7 @@ include ../lib.mk
.PHONY: all all_32 all_64 warn_32bit_failure clean
TARGETS_C_BOTHBITS := single_step_syscall sysret_ss_attrs syscall_nt ptrace_syscall test_mremap_vdso \
- check_initial_reg_state sigreturn ldt_gdt iopl \
+ check_initial_reg_state sigreturn ldt_gdt iopl ioperm \
protection_keys test_vdso
TARGETS_C_32BIT_ONLY := entry_from_vm86 syscall_arg_fault test_syscall_vdso unwind_vdso \
test_FCMOV test_FCOMI test_FISTTP \
diff --git a/tools/testing/selftests/x86/ioperm.c b/tools/testing/selftests/x86/ioperm.c
new file mode 100644
index 000000000000..ddb97adcc321
--- /dev/null
+++ b/tools/testing/selftests/x86/ioperm.c
@@ -0,0 +1,171 @@
+/*
+ * ioperm.c - Test case for ioperm(2)
+ * Copyright (c) 2015 Andrew Lutomirski
+ */
+
+#define _GNU_SOURCE
+#include <err.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <signal.h>
+#include <setjmp.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <stdbool.h>
+#include <sched.h>
+#include <sys/io.h>
+
+static int nerrs = 0;
+
+static void sethandler(int sig, void (*handler)(int, siginfo_t *, void *),
+ int flags)
+{
+ struct sigaction sa;
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_sigaction = handler;
+ sa.sa_flags = SA_SIGINFO | flags;
+ sigemptyset(&sa.sa_mask);
+ if (sigaction(sig, &sa, 0))
+ err(1, "sigaction");
+
+}
+
+static void clearhandler(int sig)
+{
+ struct sigaction sa;
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_handler = SIG_DFL;
+ sigemptyset(&sa.sa_mask);
+ if (sigaction(sig, &sa, 0))
+ err(1, "sigaction");
+}
+
+static jmp_buf jmpbuf;
+
+static void sigsegv(int sig, siginfo_t *si, void *ctx_void)
+{
+ siglongjmp(jmpbuf, 1);
+}
+
+static bool try_outb(unsigned short port)
+{
+ sethandler(SIGSEGV, sigsegv, SA_RESETHAND);
+ if (sigsetjmp(jmpbuf, 1) != 0) {
+ return false;
+ } else {
+ asm volatile ("outb %%al, %w[port]"
+ : : [port] "Nd" (port), "a" (0));
+ return true;
+ }
+ clearhandler(SIGSEGV);
+}
+
+static void expect_ok(unsigned short port)
+{
+ if (!try_outb(port)) {
+ printf("[FAIL]\toutb to 0x%02hx failed\n", port);
+ exit(1);
+ }
+
+ printf("[OK]\toutb to 0x%02hx worked\n", port);
+}
+
+static void expect_gp(unsigned short port)
+{
+ if (try_outb(port)) {
+ printf("[FAIL]\toutb to 0x%02hx worked\n", port);
+ exit(1);
+ }
+
+ printf("[OK]\toutb to 0x%02hx failed\n", port);
+}
+
+int main(void)
+{
+ cpu_set_t cpuset;
+ CPU_ZERO(&cpuset);
+ CPU_SET(0, &cpuset);
+ if (sched_setaffinity(0, sizeof(cpuset), &cpuset) != 0)
+ err(1, "sched_setaffinity to CPU 0");
+
+ expect_gp(0x80);
+ expect_gp(0xed);
+
+ /*
+ * Probe for ioperm support. Note that clearing ioperm bits
+ * works even as nonroot.
+ */
+ printf("[RUN]\tenable 0x80\n");
+ if (ioperm(0x80, 1, 1) != 0) {
+ printf("[OK]\tioperm(0x80, 1, 1) failed (%d) -- try running as root\n",
+ errno);
+ return 0;
+ }
+ expect_ok(0x80);
+ expect_gp(0xed);
+
+ printf("[RUN]\tdisable 0x80\n");
+ if (ioperm(0x80, 1, 0) != 0) {
+ printf("[FAIL]\tioperm(0x80, 1, 0) failed (%d)", errno);
+ return 1;
+ }
+ expect_gp(0x80);
+ expect_gp(0xed);
+
+ /* Make sure that fork() preserves ioperm. */
+ if (ioperm(0x80, 1, 1) != 0) {
+ printf("[FAIL]\tioperm(0x80, 1, 0) failed (%d)", errno);
+ return 1;
+ }
+
+ pid_t child = fork();
+ if (child == -1)
+ err(1, "fork");
+
+ if (child == 0) {
+ printf("[RUN]\tchild: check that we inherited permissions\n");
+ expect_ok(0x80);
+ expect_gp(0xed);
+ return 0;
+ } else {
+ int status;
+ if (waitpid(child, &status, 0) != child ||
+ !WIFEXITED(status)) {
+ printf("[FAIL]\tChild died\n");
+ nerrs++;
+ } else if (WEXITSTATUS(status) != 0) {
+ printf("[FAIL]\tChild failed\n");
+ nerrs++;
+ } else {
+ printf("[OK]\tChild succeeded\n");
+ }
+ }
+
+ /* Test the capability checks. */
+
+ printf("\tDrop privileges\n");
+ if (setresuid(1, 1, 1) != 0) {
+ printf("[WARN]\tDropping privileges failed\n");
+ return 0;
+ }
+
+ printf("[RUN]\tdisable 0x80\n");
+ if (ioperm(0x80, 1, 0) != 0) {
+ printf("[FAIL]\tioperm(0x80, 1, 0) failed (%d)", errno);
+ return 1;
+ }
+ printf("[OK]\tit worked\n");
+
+ printf("[RUN]\tenable 0x80 again\n");
+ if (ioperm(0x80, 1, 1) == 0) {
+ printf("[FAIL]\tit succeeded but should have failed.\n");
+ return 1;
+ }
+ printf("[OK]\tit failed\n");
+ return 0;
+}
+
--
2.9.3
^ permalink raw reply related [flat|nested] 3+ messages in thread
end of thread, other threads:[~2017-02-22 15:36 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2017-02-22 15:36 [PATCH 0/2] TSS incremental cleanups Andy Lutomirski
2017-02-22 15:36 ` [PATCH 1/2] x86/asm: Tidy up TSS limit code Andy Lutomirski
2017-02-22 15:36 ` [PATCH 2/2] selftests/x86: Add a basic selftest for ioperm Andy Lutomirski
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.