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