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