Message ID | 1471331895-29887-5-git-send-email-sjitindarsingh@gmail.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
On Tue, Aug 16, 2016 at 05:18:15PM +1000, Suraj Jitindar Singh wrote: > On Power machines if a guest cedes while a tm transaction is in the > suspended state then the checkpointed state of the vcpu may be lost and we > lose the cpu in the host. > > Add a file for tm tests "powerpc/tm.c" and add a test to check if the fix > has been applied to the host kernel. If this fix hasn't been applied then > the test will never complete and the cpu will be lost. Otherwise the test > should succeed. Since this has the ability to mess things up in the host > mark this test as don't run by default. > > This vulnerability has been assigned the ID CVE-2016-5412. > > Based on initial work done by: Cyril Bur <cyril.bur@au1.ibm.com> > > Signed-off-by: Suraj Jitindar Singh <sjitindarsingh@gmail.com> > --- > > Change Log: > > V2 -> V3: > - Remove get_dec() and sleep() functions as sleep functionality is > now provided by a generic implementation in processor.c. > - Replace TM instructions with raw machine code to support older > compilers > > Signed-off-by: Suraj Jitindar Singh <sjitindarsingh@gmail.com> > --- > lib/powerpc/asm/hcall.h | 1 + > powerpc/Makefile.common | 3 +- > powerpc/tm.c | 121 ++++++++++++++++++++++++++++++++++++++++++++++++ > powerpc/unittests.cfg | 6 +++ > 4 files changed, 130 insertions(+), 1 deletion(-) > create mode 100644 powerpc/tm.c > > diff --git a/lib/powerpc/asm/hcall.h b/lib/powerpc/asm/hcall.h > index 99bce79..80aa3e3 100644 > --- a/lib/powerpc/asm/hcall.h > +++ b/lib/powerpc/asm/hcall.h > @@ -18,6 +18,7 @@ > #define H_SET_SPRG0 0x24 > #define H_SET_DABR 0x28 > #define H_PAGE_INIT 0x2c > +#define H_CEDE 0xE0 > #define H_PUT_TERM_CHAR 0x58 > #define H_RANDOM 0x300 > #define H_SET_MODE 0x31C > diff --git a/powerpc/Makefile.common b/powerpc/Makefile.common > index 677030a..93e4f66 100644 > --- a/powerpc/Makefile.common > +++ b/powerpc/Makefile.common > @@ -8,7 +8,8 @@ tests-common = \ > $(TEST_DIR)/selftest.elf \ > $(TEST_DIR)/spapr_hcall.elf \ > $(TEST_DIR)/rtas.elf \ > - $(TEST_DIR)/emulator.elf > + $(TEST_DIR)/emulator.elf \ > + $(TEST_DIR)/tm.elf > > all: $(TEST_DIR)/boot_rom.bin test_cases > > diff --git a/powerpc/tm.c b/powerpc/tm.c > new file mode 100644 > index 0000000..ba01f95 > --- /dev/null > +++ b/powerpc/tm.c > @@ -0,0 +1,121 @@ > +/* > + * Transactional Memory Unit Tests > + * > + * Copyright 2016 Suraj Jitindar Singh, IBM. > + * > + * This work is licensed under the terms of the GNU LGPL, version 2. > + */ > +#include <libcflat.h> > +#include <asm/hcall.h> > +#include <asm/processor.h> > +#include <asm/handlers.h> > +#include <asm/smp.h> > + > +static int h_cede(void) > +{ > + register uint64_t r3 asm("r3") = H_CEDE; > + > + asm volatile ("sc 1" : "+r"(r3) : > + : "r0", "r4", "r5", "r6", "r7", "r8", "r9", > + "r10", "r11", "r12", "xer", "ctr", "cc"); > + > + return r3; > +} > + > +/* > + * Enable transactional memory > + * Returns: FALSE - Failure > + * TRUE - Success > + */ > +static bool enable_tm(void) > +{ > + uint64_t msr = 0; > + > + asm volatile ("mfmsr %[msr]" : [msr] "=r" (msr)); > + > + msr |= (((uint64_t) 1) << 32); > + > + asm volatile ("mtmsrd %[msr]\n\t" > + "mfmsr %[msr]" : [msr] "+r" (msr)); > + > + return !!(msr & (((uint64_t) 1) << 32)); > +} > + > +/* > + * Test H_CEDE call while transactional memory transaction is suspended > + * > + * WARNING: This tests for a known vulnerability in which the host may go down. > + * Probably best not to run this if your host going down is going to cause > + * problems. > + * > + * If the test passes then your kernel probably has the necessary patch. > + * If the test fails then the H_CEDE call was unsuccessful and the > + * vulnerability wasn't tested. > + * If the test hits the vulnerability then it will never complete or report and > + * the qemu process will block indefinitely. RCU stalls will be detected on the > + * cpu and any process scheduled on the lost cpu will also block indefinitely. > + */ > +static void test_h_cede_tm(int argc, char **argv) > +{ > + bool pass = true; > + int i; > + > + if (argc > 2) > + report_abort("Unsupported argument: '%s'", argv[2]); > + > + handle_exception(0x900, &dec_except_handler, NULL); > + > + if (!start_all_cpus(&halt, 0)) nit: more unnecessary '&' on function pointers > + report_abort("Failed to start secondary cpus"); > + > + if (!enable_tm()) > + report_abort("Failed to enable tm"); > + > + /* > + * Begin a transaction and guarantee we are in the suspend state > + * before continuing > + */ > + asm volatile ("1: .long 0x7c00051d\n\t" /* tbegin. */ > + "beq 2f\n\t" > + ".long 0x7c0005dd\n\t" /* tsuspend. */ > + "2: .long 0x7c00059c\n\t" /* tcheck cr0 */ > + "bf 2,1b" : : : "cr0"); > + > + for (i = 0; i < 500 && pass; i++) { > + uint64_t rval = h_cede(); > + > + if (rval != H_SUCCESS) > + pass = false; nit: you could drop the '&& pass' and just do a break here. > + mdelay(5); > + } > + > + report("H_CEDE TM", pass); nit: with a break above you could just test for i == 500 here. > +} > + > +struct { > + const char *name; > + void (*func)(int argc, char **argv); > +} hctests[] = { > + { "h_cede_tm", test_h_cede_tm }, > + { NULL, NULL } > +}; > + > +int main(int argc, char **argv) > +{ > + bool all; > + int i; > + > + report_prefix_push("tm"); > + > + all = argc == 1 || (argc == 2 && !strcmp(argv[1], "all")); I'd drop the argc == 2 test. You already know argc >= 2 at this point. And, if it's > 2, then you want to call the test function so the "Unsupported argument" abort will fire. > + > + for (i = 0; hctests[i].name != NULL; i++) { > + if (all || strcmp(argv[1], hctests[i].name) == 0) { > + report_prefix_push(hctests[i].name); > + hctests[i].func(argc, argv); > + report_prefix_pop(); > + } > + } > + > + return report_summary(); > +} > diff --git a/powerpc/unittests.cfg b/powerpc/unittests.cfg > index 0098cb6..20dbde6 100644 > --- a/powerpc/unittests.cfg > +++ b/powerpc/unittests.cfg > @@ -53,3 +53,9 @@ groups = rtas > > [emulator] > file = emulator.elf > + > +[h_cede_tm] > +file = tm.elf > +smp = 2,threads=2 > +extra_params = -append "h_cede_tm" > +groups = nodefault,h_cede_tm > -- > 2.5.5 > Not my area of expertise, but I only see nits, so Reviewed-by: Andrew Jones <drjones@redhat.com> -- To unsubscribe from this list: send the line "unsubscribe kvm" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
On Tue, 2016-08-16 at 14:57 +0200, Andrew Jones wrote: > On Tue, Aug 16, 2016 at 05:18:15PM +1000, Suraj Jitindar Singh wrote: > > > > On Power machines if a guest cedes while a tm transaction is in the > > suspended state then the checkpointed state of the vcpu may be lost > > and we > > lose the cpu in the host. > > > > Add a file for tm tests "powerpc/tm.c" and add a test to check if > > the fix > > has been applied to the host kernel. If this fix hasn't been > > applied then > > the test will never complete and the cpu will be lost. Otherwise > > the test > > should succeed. Since this has the ability to mess things up in the > > host > > mark this test as don't run by default. > > > > This vulnerability has been assigned the ID CVE-2016-5412. > > > > Based on initial work done by: Cyril Bur <cyril.bur@au1.ibm.com> > > > > Signed-off-by: Suraj Jitindar Singh <sjitindarsingh@gmail.com> > > --- > > > > Change Log: > > > > V2 -> V3: > > - Remove get_dec() and sleep() functions as sleep functionality > > is > > now provided by a generic implementation in processor.c. > > - Replace TM instructions with raw machine code to support > > older > > compilers > > > > Signed-off-by: Suraj Jitindar Singh <sjitindarsingh@gmail.com> > > --- > > lib/powerpc/asm/hcall.h | 1 + > > powerpc/Makefile.common | 3 +- > > powerpc/tm.c | 121 > > ++++++++++++++++++++++++++++++++++++++++++++++++ > > powerpc/unittests.cfg | 6 +++ > > 4 files changed, 130 insertions(+), 1 deletion(-) > > create mode 100644 powerpc/tm.c > > > > diff --git a/lib/powerpc/asm/hcall.h b/lib/powerpc/asm/hcall.h > > index 99bce79..80aa3e3 100644 > > --- a/lib/powerpc/asm/hcall.h > > +++ b/lib/powerpc/asm/hcall.h > > @@ -18,6 +18,7 @@ > > #define H_SET_SPRG0 0x24 > > #define H_SET_DABR 0x28 > > #define H_PAGE_INIT 0x2c > > +#define H_CEDE 0xE0 > > #define H_PUT_TERM_CHAR 0x58 > > #define H_RANDOM 0x300 > > #define H_SET_MODE 0x31C > > diff --git a/powerpc/Makefile.common b/powerpc/Makefile.common > > index 677030a..93e4f66 100644 > > --- a/powerpc/Makefile.common > > +++ b/powerpc/Makefile.common > > @@ -8,7 +8,8 @@ tests-common = \ > > $(TEST_DIR)/selftest.elf \ > > $(TEST_DIR)/spapr_hcall.elf \ > > $(TEST_DIR)/rtas.elf \ > > - $(TEST_DIR)/emulator.elf > > + $(TEST_DIR)/emulator.elf \ > > + $(TEST_DIR)/tm.elf > > > > all: $(TEST_DIR)/boot_rom.bin test_cases > > > > diff --git a/powerpc/tm.c b/powerpc/tm.c > > new file mode 100644 > > index 0000000..ba01f95 > > --- /dev/null > > +++ b/powerpc/tm.c > > @@ -0,0 +1,121 @@ > > +/* > > + * Transactional Memory Unit Tests > > + * > > + * Copyright 2016 Suraj Jitindar Singh, IBM. > > + * > > + * This work is licensed under the terms of the GNU LGPL, version > > 2. > > + */ > > +#include <libcflat.h> > > +#include <asm/hcall.h> > > +#include <asm/processor.h> > > +#include <asm/handlers.h> > > +#include <asm/smp.h> > > + > > +static int h_cede(void) > > +{ > > + register uint64_t r3 asm("r3") = H_CEDE; > > + > > + asm volatile ("sc 1" : "+r"(r3) : > > + : "r0", "r4", "r5", "r6", "r7", "r8", > > "r9", > > + "r10", "r11", "r12", "xer", "ctr", > > "cc"); > > + > > + return r3; > > +} > > + > > +/* > > + * Enable transactional memory > > + * Returns: FALSE - Failure > > + * TRUE - Success > > + */ > > +static bool enable_tm(void) > > +{ > > + uint64_t msr = 0; > > + > > + asm volatile ("mfmsr %[msr]" : [msr] "=r" (msr)); > > + > > + msr |= (((uint64_t) 1) << 32); > > + > > + asm volatile ("mtmsrd %[msr]\n\t" > > + "mfmsr %[msr]" : [msr] "+r" (msr)); > > + > > + return !!(msr & (((uint64_t) 1) << 32)); > > +} > > + > > +/* > > + * Test H_CEDE call while transactional memory transaction is > > suspended > > + * > > + * WARNING: This tests for a known vulnerability in which the host > > may go down. > > + * Probably best not to run this if your host going down is going > > to cause > > + * problems. > > + * > > + * If the test passes then your kernel probably has the necessary > > patch. > > + * If the test fails then the H_CEDE call was unsuccessful and the > > + * vulnerability wasn't tested. > > + * If the test hits the vulnerability then it will never complete > > or report and > > + * the qemu process will block indefinitely. RCU stalls will be > > detected on the > > + * cpu and any process scheduled on the lost cpu will also block > > indefinitely. > > + */ > > +static void test_h_cede_tm(int argc, char **argv) > > +{ > > + bool pass = true; > > + int i; > > + > > + if (argc > 2) > > + report_abort("Unsupported argument: '%s'", > > argv[2]); > > + > > + handle_exception(0x900, &dec_except_handler, NULL); > > + > > + if (!start_all_cpus(&halt, 0)) > nit: more unnecessary '&' on function pointers Ok, guess I should change both > > > > > + report_abort("Failed to start secondary cpus"); > > + > > + if (!enable_tm()) > > + report_abort("Failed to enable tm"); > > + > > + /* > > + * Begin a transaction and guarantee we are in the suspend > > state > > + * before continuing > > + */ > > + asm volatile ("1: .long 0x7c00051d\n\t" /* tbegin. > > */ > > + "beq 2f\n\t" > > + ".long 0x7c0005dd\n\t" /* tsuspend. > > */ > > + "2: .long 0x7c00059c\n\t" /* tcheck > > cr0 */ > > + "bf 2,1b" : : : "cr0"); > > + > > + for (i = 0; i < 500 && pass; i++) { > > + uint64_t rval = h_cede(); > > + > > + if (rval != H_SUCCESS) > > + pass = false; > nit: you could drop the '&& pass' and just do a break here. Yes > > > > > + mdelay(5); > > + } > > + > > + report("H_CEDE TM", pass); > nit: with a break above you could just test for i == 500 here. ok > > > > > +} > > + > > +struct { > > + const char *name; > > + void (*func)(int argc, char **argv); > > +} hctests[] = { > > + { "h_cede_tm", test_h_cede_tm }, > > + { NULL, NULL } > > +}; > > + > > +int main(int argc, char **argv) > > +{ > > + bool all; > > + int i; > > + > > + report_prefix_push("tm"); > > + > > + all = argc == 1 || (argc == 2 && !strcmp(argv[1], "all")); > I'd drop the argc == 2 test. You already know argc >= 2 at this > point. And, if it's > 2, then you want to call the test function > so the "Unsupported argument" abort will fire. Sounds good > > > > > + > > + for (i = 0; hctests[i].name != NULL; i++) { > > + if (all || strcmp(argv[1], hctests[i].name) == 0) > > { > > + report_prefix_push(hctests[i].name); > > + hctests[i].func(argc, argv); > > + report_prefix_pop(); > > + } > > + } > > + > > + return report_summary(); > > +} > > diff --git a/powerpc/unittests.cfg b/powerpc/unittests.cfg > > index 0098cb6..20dbde6 100644 > > --- a/powerpc/unittests.cfg > > +++ b/powerpc/unittests.cfg > > @@ -53,3 +53,9 @@ groups = rtas > > > > [emulator] > > file = emulator.elf > > + > > +[h_cede_tm] > > +file = tm.elf > > +smp = 2,threads=2 > > +extra_params = -append "h_cede_tm" > > +groups = nodefault,h_cede_tm -- To unsubscribe from this list: send the line "unsubscribe kvm" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
diff --git a/lib/powerpc/asm/hcall.h b/lib/powerpc/asm/hcall.h index 99bce79..80aa3e3 100644 --- a/lib/powerpc/asm/hcall.h +++ b/lib/powerpc/asm/hcall.h @@ -18,6 +18,7 @@ #define H_SET_SPRG0 0x24 #define H_SET_DABR 0x28 #define H_PAGE_INIT 0x2c +#define H_CEDE 0xE0 #define H_PUT_TERM_CHAR 0x58 #define H_RANDOM 0x300 #define H_SET_MODE 0x31C diff --git a/powerpc/Makefile.common b/powerpc/Makefile.common index 677030a..93e4f66 100644 --- a/powerpc/Makefile.common +++ b/powerpc/Makefile.common @@ -8,7 +8,8 @@ tests-common = \ $(TEST_DIR)/selftest.elf \ $(TEST_DIR)/spapr_hcall.elf \ $(TEST_DIR)/rtas.elf \ - $(TEST_DIR)/emulator.elf + $(TEST_DIR)/emulator.elf \ + $(TEST_DIR)/tm.elf all: $(TEST_DIR)/boot_rom.bin test_cases diff --git a/powerpc/tm.c b/powerpc/tm.c new file mode 100644 index 0000000..ba01f95 --- /dev/null +++ b/powerpc/tm.c @@ -0,0 +1,121 @@ +/* + * Transactional Memory Unit Tests + * + * Copyright 2016 Suraj Jitindar Singh, IBM. + * + * This work is licensed under the terms of the GNU LGPL, version 2. + */ +#include <libcflat.h> +#include <asm/hcall.h> +#include <asm/processor.h> +#include <asm/handlers.h> +#include <asm/smp.h> + +static int h_cede(void) +{ + register uint64_t r3 asm("r3") = H_CEDE; + + asm volatile ("sc 1" : "+r"(r3) : + : "r0", "r4", "r5", "r6", "r7", "r8", "r9", + "r10", "r11", "r12", "xer", "ctr", "cc"); + + return r3; +} + +/* + * Enable transactional memory + * Returns: FALSE - Failure + * TRUE - Success + */ +static bool enable_tm(void) +{ + uint64_t msr = 0; + + asm volatile ("mfmsr %[msr]" : [msr] "=r" (msr)); + + msr |= (((uint64_t) 1) << 32); + + asm volatile ("mtmsrd %[msr]\n\t" + "mfmsr %[msr]" : [msr] "+r" (msr)); + + return !!(msr & (((uint64_t) 1) << 32)); +} + +/* + * Test H_CEDE call while transactional memory transaction is suspended + * + * WARNING: This tests for a known vulnerability in which the host may go down. + * Probably best not to run this if your host going down is going to cause + * problems. + * + * If the test passes then your kernel probably has the necessary patch. + * If the test fails then the H_CEDE call was unsuccessful and the + * vulnerability wasn't tested. + * If the test hits the vulnerability then it will never complete or report and + * the qemu process will block indefinitely. RCU stalls will be detected on the + * cpu and any process scheduled on the lost cpu will also block indefinitely. + */ +static void test_h_cede_tm(int argc, char **argv) +{ + bool pass = true; + int i; + + if (argc > 2) + report_abort("Unsupported argument: '%s'", argv[2]); + + handle_exception(0x900, &dec_except_handler, NULL); + + if (!start_all_cpus(&halt, 0)) + report_abort("Failed to start secondary cpus"); + + if (!enable_tm()) + report_abort("Failed to enable tm"); + + /* + * Begin a transaction and guarantee we are in the suspend state + * before continuing + */ + asm volatile ("1: .long 0x7c00051d\n\t" /* tbegin. */ + "beq 2f\n\t" + ".long 0x7c0005dd\n\t" /* tsuspend. */ + "2: .long 0x7c00059c\n\t" /* tcheck cr0 */ + "bf 2,1b" : : : "cr0"); + + for (i = 0; i < 500 && pass; i++) { + uint64_t rval = h_cede(); + + if (rval != H_SUCCESS) + pass = false; + mdelay(5); + } + + report("H_CEDE TM", pass); +} + +struct { + const char *name; + void (*func)(int argc, char **argv); +} hctests[] = { + { "h_cede_tm", test_h_cede_tm }, + { NULL, NULL } +}; + +int main(int argc, char **argv) +{ + bool all; + int i; + + report_prefix_push("tm"); + + all = argc == 1 || (argc == 2 && !strcmp(argv[1], "all")); + + for (i = 0; hctests[i].name != NULL; i++) { + if (all || strcmp(argv[1], hctests[i].name) == 0) { + report_prefix_push(hctests[i].name); + hctests[i].func(argc, argv); + report_prefix_pop(); + } + } + + return report_summary(); +} diff --git a/powerpc/unittests.cfg b/powerpc/unittests.cfg index 0098cb6..20dbde6 100644 --- a/powerpc/unittests.cfg +++ b/powerpc/unittests.cfg @@ -53,3 +53,9 @@ groups = rtas [emulator] file = emulator.elf + +[h_cede_tm] +file = tm.elf +smp = 2,threads=2 +extra_params = -append "h_cede_tm" +groups = nodefault,h_cede_tm