1f08c3bdfSopenharmony_ci// SPDX-License-Identifier: GPL-2.0 2f08c3bdfSopenharmony_ci/* 3f08c3bdfSopenharmony_ci * Copyright (c) 2018 Andrew Lutomirski 4f08c3bdfSopenharmony_ci * Copyright (C) 2020 SUSE LLC <mdoucha@suse.cz> 5f08c3bdfSopenharmony_ci * 6f08c3bdfSopenharmony_ci * CVE-2018-1000199 7f08c3bdfSopenharmony_ci * 8f08c3bdfSopenharmony_ci * Test error handling when ptrace(POKEUSER) modified x86 debug registers even 9f08c3bdfSopenharmony_ci * when the call returned error. 10f08c3bdfSopenharmony_ci * 11f08c3bdfSopenharmony_ci * When the bug was present we could create breakpoint in the kernel code, 12f08c3bdfSopenharmony_ci * which shoudn't be possible at all. The original CVE caused a kernel crash by 13f08c3bdfSopenharmony_ci * setting a breakpoint on do_debug kernel function which, when triggered, 14f08c3bdfSopenharmony_ci * caused an infinite loop. However we do not have to crash the kernel in order 15f08c3bdfSopenharmony_ci * to assert if kernel has been fixed or not. 16f08c3bdfSopenharmony_ci * 17f08c3bdfSopenharmony_ci * On newer kernels all we have to do is to try to set a breakpoint, on any 18f08c3bdfSopenharmony_ci * kernel address, then read it back and check if the value has been set or 19f08c3bdfSopenharmony_ci * not. 20f08c3bdfSopenharmony_ci * 21f08c3bdfSopenharmony_ci * The original fix to the CVE however disabled a breakpoint on address change 22f08c3bdfSopenharmony_ci * and the check was deffered to write dr7 that enabled the breakpoint again. 23f08c3bdfSopenharmony_ci * So on older kernels we have to write to dr7 which should fail instead. 24f08c3bdfSopenharmony_ci * 25f08c3bdfSopenharmony_ci * Kernel crash partially fixed in: 26f08c3bdfSopenharmony_ci * 27f08c3bdfSopenharmony_ci * commit f67b15037a7a50c57f72e69a6d59941ad90a0f0f 28f08c3bdfSopenharmony_ci * Author: Linus Torvalds <torvalds@linux-foundation.org> 29f08c3bdfSopenharmony_ci * Date: Mon Mar 26 15:39:07 2018 -1000 30f08c3bdfSopenharmony_ci * 31f08c3bdfSopenharmony_ci * perf/hwbp: Simplify the perf-hwbp code, fix documentation 32f08c3bdfSopenharmony_ci * 33f08c3bdfSopenharmony_ci * On Centos7, this is also a regression test for 34f08c3bdfSopenharmony_ci * commit 27747f8bc355 ("perf/x86/hw_breakpoints: Fix check for kernel-space breakpoints"). 35f08c3bdfSopenharmony_ci */ 36f08c3bdfSopenharmony_ci 37f08c3bdfSopenharmony_ci#include <stdlib.h> 38f08c3bdfSopenharmony_ci#include <stdio.h> 39f08c3bdfSopenharmony_ci#include <stddef.h> 40f08c3bdfSopenharmony_ci#include <sys/ptrace.h> 41f08c3bdfSopenharmony_ci#include <sys/user.h> 42f08c3bdfSopenharmony_ci#include <signal.h> 43f08c3bdfSopenharmony_ci#include "tst_test.h" 44f08c3bdfSopenharmony_ci#include "tst_safe_stdio.h" 45f08c3bdfSopenharmony_ci 46f08c3bdfSopenharmony_cistatic pid_t child_pid; 47f08c3bdfSopenharmony_ci 48f08c3bdfSopenharmony_ci#if defined(__i386__) 49f08c3bdfSopenharmony_ci# define KERN_ADDR_MIN 0xc0000000 50f08c3bdfSopenharmony_ci# define KERN_ADDR_MAX 0xffffffff 51f08c3bdfSopenharmony_ci# define KERN_ADDR_BITS 32 52f08c3bdfSopenharmony_ci#else 53f08c3bdfSopenharmony_ci# define KERN_ADDR_MIN 0xffff800000000000 54f08c3bdfSopenharmony_ci# define KERN_ADDR_MAX 0xffffffffffffffff 55f08c3bdfSopenharmony_ci# define KERN_ADDR_BITS 64 56f08c3bdfSopenharmony_ci#endif 57f08c3bdfSopenharmony_ci 58f08c3bdfSopenharmony_ci 59f08c3bdfSopenharmony_cistatic void child_main(void) 60f08c3bdfSopenharmony_ci{ 61f08c3bdfSopenharmony_ci raise(SIGSTOP); 62f08c3bdfSopenharmony_ci exit(0); 63f08c3bdfSopenharmony_ci} 64f08c3bdfSopenharmony_ci 65f08c3bdfSopenharmony_cistatic void ptrace_try_kern_addr(unsigned long kern_addr) 66f08c3bdfSopenharmony_ci{ 67f08c3bdfSopenharmony_ci int status; 68f08c3bdfSopenharmony_ci unsigned long addr; 69f08c3bdfSopenharmony_ci 70f08c3bdfSopenharmony_ci tst_res(TINFO, "Trying address 0x%lx", kern_addr); 71f08c3bdfSopenharmony_ci 72f08c3bdfSopenharmony_ci child_pid = SAFE_FORK(); 73f08c3bdfSopenharmony_ci 74f08c3bdfSopenharmony_ci if (!child_pid) 75f08c3bdfSopenharmony_ci child_main(); 76f08c3bdfSopenharmony_ci 77f08c3bdfSopenharmony_ci if (SAFE_WAITPID(child_pid, &status, WUNTRACED) != child_pid) 78f08c3bdfSopenharmony_ci tst_brk(TBROK, "Received event from unexpected PID"); 79f08c3bdfSopenharmony_ci 80f08c3bdfSopenharmony_ci#if defined(__i386__) || defined(__x86_64__) 81f08c3bdfSopenharmony_ci SAFE_PTRACE(PTRACE_ATTACH, child_pid, NULL, NULL); 82f08c3bdfSopenharmony_ci SAFE_PTRACE(PTRACE_POKEUSER, child_pid, 83f08c3bdfSopenharmony_ci (void *)offsetof(struct user, u_debugreg[0]), (void *)1); 84f08c3bdfSopenharmony_ci SAFE_PTRACE(PTRACE_POKEUSER, child_pid, 85f08c3bdfSopenharmony_ci (void *)offsetof(struct user, u_debugreg[7]), (void *)1); 86f08c3bdfSopenharmony_ci 87f08c3bdfSopenharmony_ci TEST(ptrace(PTRACE_POKEUSER, child_pid, 88f08c3bdfSopenharmony_ci (void *)offsetof(struct user, u_debugreg[0]), 89f08c3bdfSopenharmony_ci (void *)kern_addr)); 90f08c3bdfSopenharmony_ci 91f08c3bdfSopenharmony_ci if (TST_RET == -1) { 92f08c3bdfSopenharmony_ci addr = ptrace(PTRACE_PEEKUSER, child_pid, 93f08c3bdfSopenharmony_ci (void *)offsetof(struct user, u_debugreg[0]), NULL); 94f08c3bdfSopenharmony_ci if (addr == kern_addr) { 95f08c3bdfSopenharmony_ci TEST(ptrace(PTRACE_POKEUSER, child_pid, 96f08c3bdfSopenharmony_ci (void *)offsetof(struct user, u_debugreg[7]), (void *)1)); 97f08c3bdfSopenharmony_ci } 98f08c3bdfSopenharmony_ci } 99f08c3bdfSopenharmony_ci 100f08c3bdfSopenharmony_ci if (TST_RET != -1) { 101f08c3bdfSopenharmony_ci tst_res(TFAIL, "ptrace() breakpoint with kernel addr succeeded"); 102f08c3bdfSopenharmony_ci } else { 103f08c3bdfSopenharmony_ci if (TST_ERR == EINVAL) { 104f08c3bdfSopenharmony_ci tst_res(TPASS | TTERRNO, 105f08c3bdfSopenharmony_ci "ptrace() breakpoint with kernel addr failed"); 106f08c3bdfSopenharmony_ci } else { 107f08c3bdfSopenharmony_ci tst_res(TFAIL | TTERRNO, 108f08c3bdfSopenharmony_ci "ptrace() breakpoint on kernel addr should return EINVAL, got"); 109f08c3bdfSopenharmony_ci } 110f08c3bdfSopenharmony_ci } 111f08c3bdfSopenharmony_ci 112f08c3bdfSopenharmony_ci#endif 113f08c3bdfSopenharmony_ci 114f08c3bdfSopenharmony_ci SAFE_PTRACE(PTRACE_DETACH, child_pid, NULL, NULL); 115f08c3bdfSopenharmony_ci SAFE_KILL(child_pid, SIGCONT); 116f08c3bdfSopenharmony_ci child_pid = 0; 117f08c3bdfSopenharmony_ci tst_reap_children(); 118f08c3bdfSopenharmony_ci} 119f08c3bdfSopenharmony_ci 120f08c3bdfSopenharmony_cistatic void run(void) 121f08c3bdfSopenharmony_ci{ 122f08c3bdfSopenharmony_ci ptrace_try_kern_addr(KERN_ADDR_MIN); 123f08c3bdfSopenharmony_ci ptrace_try_kern_addr(KERN_ADDR_MAX); 124f08c3bdfSopenharmony_ci ptrace_try_kern_addr(KERN_ADDR_MIN + (KERN_ADDR_MAX - KERN_ADDR_MIN)/2); 125f08c3bdfSopenharmony_ci} 126f08c3bdfSopenharmony_ci 127f08c3bdfSopenharmony_cistatic void cleanup(void) 128f08c3bdfSopenharmony_ci{ 129f08c3bdfSopenharmony_ci /* Main process terminated by tst_brk() with child still paused */ 130f08c3bdfSopenharmony_ci if (child_pid) 131f08c3bdfSopenharmony_ci SAFE_KILL(child_pid, SIGKILL); 132f08c3bdfSopenharmony_ci} 133f08c3bdfSopenharmony_ci 134f08c3bdfSopenharmony_cistatic struct tst_test test = { 135f08c3bdfSopenharmony_ci .test_all = run, 136f08c3bdfSopenharmony_ci .cleanup = cleanup, 137f08c3bdfSopenharmony_ci .forks_child = 1, 138f08c3bdfSopenharmony_ci /* 139f08c3bdfSopenharmony_ci * When running in compat mode we can't pass 64 address to ptrace so we 140f08c3bdfSopenharmony_ci * have to skip the test. 141f08c3bdfSopenharmony_ci */ 142f08c3bdfSopenharmony_ci .skip_in_compat = 1, 143f08c3bdfSopenharmony_ci .supported_archs = (const char *const []) { 144f08c3bdfSopenharmony_ci "x86", 145f08c3bdfSopenharmony_ci "x86_64", 146f08c3bdfSopenharmony_ci NULL 147f08c3bdfSopenharmony_ci }, 148f08c3bdfSopenharmony_ci .tags = (const struct tst_tag[]) { 149f08c3bdfSopenharmony_ci {"linux-git", "f67b15037a7a"}, 150f08c3bdfSopenharmony_ci {"CVE", "2018-1000199"}, 151f08c3bdfSopenharmony_ci {"linux-git", "27747f8bc355"}, 152f08c3bdfSopenharmony_ci {} 153f08c3bdfSopenharmony_ci } 154f08c3bdfSopenharmony_ci}; 155