[LTP] [PATCH] cve-2015-3290: Fix broken 32bit assembly
Martin Doucha
mdoucha@suse.cz
Tue Sep 17 15:48:32 CEST 2024
The 32bit corruption assembly has two bugs:
- EBP is missing from asm clobber list which may result in input/output
parameters getting overwritten by the "puzzle"
- orig_ss is allocated on stack which will result in segfault when
restoring %ss to the original value
Add EBP to the clobber list and make sure that mov %[orig_ss], %%ss
will not use address relative to %esp.
Signed-off-by: Martin Doucha <mdoucha@suse.cz>
---
Reproducer verified on affected kernel v3.16 and fixed kernel v5.14.
I had to rewrite the mov %[ss], %%ss to two instructions because
with EBP in the clobber list, 32bit Debian GCC could not find enough
registers for the asm parameters. Now the [ss] parameter is passed
as an integer constant.
testcases/cve/cve-2015-3290.c | 37 +++++++++++++++++++++++------------
1 file changed, 24 insertions(+), 13 deletions(-)
diff --git a/testcases/cve/cve-2015-3290.c b/testcases/cve/cve-2015-3290.c
index 58585bac0..143c98230 100644
--- a/testcases/cve/cve-2015-3290.c
+++ b/testcases/cve/cve-2015-3290.c
@@ -218,7 +218,7 @@ static void set_ldt(void)
}
}
-static void try_corrupt_stack(unsigned short orig_ss)
+static void try_corrupt_stack(unsigned short *orig_ss)
{
#ifdef __x86_64__
asm volatile (
@@ -231,7 +231,8 @@ static void try_corrupt_stack(unsigned short orig_ss)
/*
* Let 'er rip.
*/
- "mov %[ss], %%ss \n\t" /* begin corruption */
+ "mov %[ss], %%edx \n\t"
+ "mov %%edx, %%ss \n\t" /* begin corruption */
"movl $1000, %%edx \n\t"
"1: decl %%edx \n\t"
"jnz 1b \n\t"
@@ -250,7 +251,7 @@ static void try_corrupt_stack(unsigned short orig_ss)
* Stop further corruption. We need to check CPL
* first because we need RPL == CPL.
*/
- "mov %[orig_ss], %%ss \n\t" /* end corruption */
+ "mov (%[orig_ss]), %%ss \n\t" /* end corruption */
"subq $128, %%rsp \n\t"
"pushfq \n\t"
@@ -262,7 +263,7 @@ static void try_corrupt_stack(unsigned short orig_ss)
"3: int3 \n\t"
"4: \n\t"
: [expected_rsp] "=m" (expected_rsp)
- : [ss] "r" (0x7), [orig_ss] "m" (orig_ss)
+ : [ss] "n" (0x7), [orig_ss] "r" (orig_ss)
: "rax", "rcx", "rdx", "rbp", "r11", "flags"
);
#else
@@ -277,7 +278,8 @@ static void try_corrupt_stack(unsigned short orig_ss)
/*
* Let 'er rip.
*/
- "mov %[ss], %%ss \n\t" /* begin corruption */
+ "mov %[ss], %%edx \n\t"
+ "mov %%edx, %%ss \n\t" /* begin corruption */
"movl $1000, %%edx \n\t"
"1: .byte 0xff, 0xca \n\t" /* decl %edx */
"jnz 1b \n\t"
@@ -298,7 +300,7 @@ static void try_corrupt_stack(unsigned short orig_ss)
* Stop further corruption. We need to check CPL
* first because we need RPL == CPL.
*/
- "mov %[orig_ss], %%ss \n\t" /* end corruption */
+ "mov (%[orig_ss]), %%ss \n\t" /* end corruption */
"pushf \n\t"
"testl $(1<<9),(%%esp) \n\t"
@@ -309,8 +311,8 @@ static void try_corrupt_stack(unsigned short orig_ss)
"3: int3 \n\t"
"4: mov %%esi, %%ebp \n\t"
: [expected_rsp] "=m" (expected_rsp)
- : [ss] "r" (0x7), [orig_ss] "m" (orig_ss)
- : "eax", "ecx", "edx", "esi", "flags"
+ : [ss] "n" (0x7), [orig_ss] "r" (orig_ss)
+ : "eax", "ecx", "edx", "esi", "ebp", "flags"
);
#endif
}
@@ -328,10 +330,14 @@ static int perf_event_open(struct perf_event_attr *hw_event, pid_t pid,
static int event_mlock_kb;
static int max_sample_rate;
-static void *child_thread(void *arg LTP_ATTRIBUTE_UNUSED)
+static void *child_thread(void *arg)
{
+ /*
+ * orig_ss must not be accessed via address relative to %esp,
+ * otherwise mov %[orig_ss], %%ss above will always segfault
+ */
+ unsigned short *orig_ss = arg;
long niter = 0;
- unsigned short orig_ss;
struct perf_event_attr pe = {
.size = sizeof(struct perf_event_attr),
@@ -388,7 +394,7 @@ static void *child_thread(void *arg LTP_ATTRIBUTE_UNUSED)
SAFE_CLOSE(fd);
}
- asm volatile ("mov %%ss, %0" : "=rm" (orig_ss));
+ asm volatile ("mov %%ss, (%0)" :: "r" (orig_ss));
for (niter = 0; running && niter < 1000*1000*1000L; niter++) {
@@ -414,6 +420,7 @@ static void do_child(void)
int i, ncpus;
pthread_t *threads;
long iter, total_iter = 0;
+ unsigned short *orig_ss;
tst_res(TINFO, "attempting to corrupt nested NMI stack state");
@@ -421,9 +428,12 @@ static void do_child(void)
ncpus = tst_ncpus();
threads = SAFE_MALLOC(sizeof(*threads) * ncpus);
+ orig_ss = SAFE_MALLOC(sizeof(unsigned short) * ncpus);
- for (i = 0; i < ncpus; i++)
- SAFE_PTHREAD_CREATE(&threads[i], NULL, child_thread, NULL);
+ for (i = 0; i < ncpus; i++) {
+ SAFE_PTHREAD_CREATE(&threads[i], NULL, child_thread,
+ &orig_ss[i]);
+ }
sleep(tst_remaining_runtime());
running = 0;
@@ -432,6 +442,7 @@ static void do_child(void)
SAFE_PTHREAD_JOIN(threads[i], (void **)&iter);
total_iter += iter;
}
+ free(orig_ss);
free(threads);
tst_res(TPASS, "can't corrupt nested NMI state after %ld iterations",
--
2.46.0
More information about the ltp
mailing list