[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