• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (c) 2018 Andrew Lutomirski
4  * Copyright (C) 2020 SUSE LLC <mdoucha@suse.cz>
5  *
6  * CVE-2018-1000199
7  *
8  * Test error handling when ptrace(POKEUSER) modifies debug registers.
9  * Even if the call returns error, it may create breakpoint in kernel code.
10  * Kernel crash partially fixed in:
11  *
12  *  commit f67b15037a7a50c57f72e69a6d59941ad90a0f0f
13  *  Author: Linus Torvalds <torvalds@linux-foundation.org>
14  *  Date:   Mon Mar 26 15:39:07 2018 -1000
15  *
16  *  perf/hwbp: Simplify the perf-hwbp code, fix documentation
17  */
18 
19 #include <stdlib.h>
20 #include <stdio.h>
21 #include <stddef.h>
22 #include <sys/ptrace.h>
23 #include <sys/user.h>
24 #include <signal.h>
25 #include "tst_test.h"
26 #include "tst_safe_stdio.h"
27 
28 #if defined(__i386__) || defined(__x86_64__)
29 #define SYMNAME_SIZE 256
30 #define KERNEL_SYM "do_debug"
31 
32 static unsigned long break_addr;
33 static pid_t child_pid;
34 
setup(void)35 static void setup(void)
36 {
37 	int fcount;
38 	char endl, symname[256];
39 	FILE *fr = SAFE_FOPEN("/proc/kallsyms", "r");
40 
41 	/* Find address of do_debug() in /proc/kallsyms */
42 	do {
43 		fcount = fscanf(fr, "%lx %*c %255s%c", &break_addr, symname,
44 			&endl);
45 
46 		if (fcount <= 0 && feof(fr))
47 			break;
48 
49 		if (fcount < 2) {
50 			fclose(fr);
51 			tst_brk(TBROK, "Unexpected data in /proc/kallsyms %d", fcount);
52 		}
53 
54 		if (fcount >= 3 && endl != '\n')
55 			while (!feof(fr) && fgetc(fr) != '\n');
56 	} while (!feof(fr) && strcmp(symname, KERNEL_SYM));
57 
58 	SAFE_FCLOSE(fr);
59 
60 	if (strcmp(symname, KERNEL_SYM))
61 		tst_brk(TBROK, "Cannot find address of kernel symbol \"%s\"",
62 			KERNEL_SYM);
63 
64 	if (!break_addr)
65 		tst_brk(TCONF, "Addresses in /proc/kallsyms are hidden");
66 
67 	tst_res(TINFO, "Kernel symbol \"%s\" found at 0x%lx", KERNEL_SYM,
68 		break_addr);
69 }
70 
debug_trap(void)71 static void debug_trap(void)
72 {
73 	/* x86 instruction INT1 */
74 	asm volatile (".byte 0xf1");
75 }
76 
child_main(void)77 static void child_main(void)
78 {
79 	raise(SIGSTOP);
80 	/* wait for SIGCONT from parent */
81 	debug_trap();
82 	exit(0);
83 }
84 
run(void)85 static void run(void)
86 {
87 	int status;
88 	pid_t child;
89 
90 	child = child_pid = SAFE_FORK();
91 
92 	if (!child_pid) {
93 		child_main();
94 	}
95 
96 	if (SAFE_WAITPID(child_pid, &status, WUNTRACED) != child_pid)
97 		tst_brk(TBROK, "Received event from unexpected PID");
98 
99 	SAFE_PTRACE(PTRACE_ATTACH, child_pid, NULL, NULL);
100 	SAFE_PTRACE(PTRACE_POKEUSER, child_pid,
101 		(void *)offsetof(struct user, u_debugreg[0]), (void *)1);
102 	SAFE_PTRACE(PTRACE_POKEUSER, child_pid,
103 		(void *)offsetof(struct user, u_debugreg[7]), (void *)1);
104 
105 	/* Return value intentionally ignored here */
106 	ptrace(PTRACE_POKEUSER, child_pid,
107 		(void *)offsetof(struct user, u_debugreg[0]),
108 		(void *)break_addr);
109 
110 	SAFE_PTRACE(PTRACE_DETACH, child_pid, NULL, NULL);
111 	SAFE_KILL(child_pid, SIGCONT);
112 	child_pid = 0;
113 
114 	if (SAFE_WAITPID(child, &status, 0) != child)
115 		tst_brk(TBROK, "Received event from unexpected PID");
116 
117 	if (!WIFSIGNALED(status))
118 		tst_brk(TBROK, "Received unexpected event from child");
119 
120 	tst_res(TPASS, "Child killed by %s", tst_strsig(WTERMSIG(status)));
121 	tst_res(TPASS, "We're still here. Nothing bad happened, probably.");
122 }
123 
cleanup(void)124 static void cleanup(void)
125 {
126 	/* Main process terminated by tst_brk() with child still paused */
127 	if (child_pid)
128 		SAFE_KILL(child_pid, SIGKILL);
129 }
130 
131 static struct tst_test test = {
132 	.test_all = run,
133 	.setup = setup,
134 	.cleanup = cleanup,
135 	.forks_child = 1,
136 	.tags = (const struct tst_tag[]) {
137 		{"linux-git", "f67b15037a7a"},
138 		{"CVE", "2018-1000199"},
139 		{}
140 	}
141 };
142 #else
143 TST_TEST_TCONF("This test is only supported on x86 systems");
144 #endif
145