• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * Copyright (C) 2021 SUSE LLC <rpalethorpe@suse.com>
4  */
5 /*\
6  *
7  * [Description]
8  *
9  * This will reproduce an information leak in the set_mempolicy 32-bit
10  * compat syscall. The catch is that the 32-bit compat syscall is not
11  * used in x86_64 upstream. So at the time of writing, 32-bit programs
12  * on large x86_64 numa systems will be broken if they use
13  * set_mempolicy. OTOH they could not have been exploited either.
14  *
15  * On other architectures the compat syscall is connected. Including
16  * PowerPC which has also been included as well. It is possible some
17  * vendors connected the x86_64 compat call in their kernel branch.
18  *
19  * The kernel allocates memory from the user's stack as a temporary
20  * work area. Allowing it to copy the node array of 32-bit fields to
21  * 64-bit fields. It uses user memory so that it can share the
22  * non-compatability syscall functions which use copy_from_user()
23  * internally.
24  *
25  * Originally the compat call would copy a chunk of the
26  * uninitialized kernel stack to the user stack before checking the
27  * validation result. This meant when the user passed in an invalid
28  * node_mask_ptr. They would get kernel stack data somewhere below
29  * their stack pointer.
30  *
31  * So we allocate and set an array on the stack (larger than any
32  * redzone). Then move the stack pointer to the beginning of the
33  * array. Then move it back after the syscall. We can then check to
34  * see if the array has been modified.
35  */
36 
37 #include "config.h"
38 #include "tst_test.h"
39 
40 #include <string.h>
41 
42 static unsigned int i;
43 static int sys_ret;
44 static volatile char *stack_ptr;
45 
run(void)46 static void run(void)
47 {
48 #ifdef __powerpc__
49 	register long sys_num __asm__("r0");
50 	register long mode __asm__("r3");
51 	register long node_mask_ptr __asm__("r4");
52 	register long node_mask_sz __asm__("r5");
53 #else
54 	const int sys_num = 276;
55 	const int mode;
56 	const int node_mask_ptr = UINT_MAX;
57 	const int node_mask_sz = UINT_MAX;
58 #endif
59 	char stack_pattern[0x400];
60 
61 	stack_ptr = stack_pattern;
62 	memset(stack_pattern, 0xA5, sizeof(stack_pattern));
63 	tst_res(TINFO, "stack pattern is in %p-%p", stack_ptr, stack_ptr + 0x400);
64 
65 #ifdef __powerpc__
66 	sys_num = 261;
67 	mode = 0;
68 	node_mask_ptr = ~0UL;
69 	node_mask_sz = ~0UL;
70 	asm volatile (
71 		"addi 1,1,1024\n\t"
72 		"sc\n\t"
73 		"addi 1,1,-1024\n\t" :
74 		"+r"(sys_num), "+r"(mode), "+r"(node_mask_ptr), "+r"(node_mask_sz) :
75 		:
76 		"memory", "cr0", "r6", "r7", "r8", "r9", "r10", "r11", "r12");
77 	sys_ret = mode;
78 #endif
79 #ifdef __i386__
80 	asm volatile (
81 		"add $0x400, %%esp\n\t"
82 		"int $0x80\n\t"
83 		"sub $0x400, %%esp\n\t" :
84 		"=a"(sys_ret) :
85 		"a"(sys_num), "b"(mode), "c"(node_mask_ptr), "d"(node_mask_sz) :
86 		"memory");
87 	sys_ret = -sys_ret;
88 #endif
89 
90 	for (i = 0; i < sizeof(stack_pattern); i++) {
91 		if (stack_ptr[i] != (char)0xA5) {
92 			tst_brk(TFAIL,
93 				"User stack was overwritten with something at %d", i);
94 		}
95 	}
96 
97 	switch (sys_ret) {
98 	case EFAULT:
99 		tst_res(TPASS,
100 			"set_mempolicy returned EFAULT (compat assumed)");
101 		break;
102 	case EINVAL:
103 		tst_res(TCONF,
104 			"set_mempolicy returned EINVAL (non compat assumed)");
105 		break;
106 	default:
107 		tst_res(TFAIL,
108 			"set_mempolicy should fail with EFAULT or EINVAL, instead returned %ld",
109 			(long)sys_ret);
110 	}
111 }
112 
113 static struct tst_test test = {
114 	.test_all = run,
115 	.supported_archs = (const char *const []) {
116 		"x86",
117 		"ppc",
118 		NULL
119 	},
120 	.tags = (const struct tst_tag[]) {
121 		{"linux-git", "cf01fb9985e8"},
122 		{"CVE", "CVE-2017-7616"},
123 		{}
124 	}
125 };
126