1f08c3bdfSopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
2f08c3bdfSopenharmony_ci/*
3f08c3bdfSopenharmony_ci * Copyright (c) 2018 SUSE LLC <nstange@suse.de>
4f08c3bdfSopenharmony_ci * Copyright (C) 2020 SUSE LLC <mdoucha@suse.cz>
5f08c3bdfSopenharmony_ci *
6f08c3bdfSopenharmony_ci * CVE-2018-8897
7f08c3bdfSopenharmony_ci *
8f08c3bdfSopenharmony_ci * Test that the MOV SS instruction touching a ptrace watchpoint followed by
9f08c3bdfSopenharmony_ci * INT3 breakpoint is handled correctly by the kernel. Kernel crash fixed in:
10f08c3bdfSopenharmony_ci *
11f08c3bdfSopenharmony_ci *  commit d8ba61ba58c88d5207c1ba2f7d9a2280e7d03be9
12f08c3bdfSopenharmony_ci *  Author: Andy Lutomirski <luto@kernel.org>
13f08c3bdfSopenharmony_ci *  Date:   Thu Jul 23 15:37:48 2015 -0700
14f08c3bdfSopenharmony_ci *
15f08c3bdfSopenharmony_ci *  x86/entry/64: Don't use IST entry for #BP stack
16f08c3bdfSopenharmony_ci */
17f08c3bdfSopenharmony_ci
18f08c3bdfSopenharmony_ci#include <stdlib.h>
19f08c3bdfSopenharmony_ci#include <stddef.h>
20f08c3bdfSopenharmony_ci#include <sys/ptrace.h>
21f08c3bdfSopenharmony_ci#include <sys/user.h>
22f08c3bdfSopenharmony_ci#include <signal.h>
23f08c3bdfSopenharmony_ci#include "tst_test.h"
24f08c3bdfSopenharmony_ci
25f08c3bdfSopenharmony_cistatic short watchpoint;
26f08c3bdfSopenharmony_cistatic pid_t child_pid;
27f08c3bdfSopenharmony_ci
28f08c3bdfSopenharmony_cistatic int child_main(void)
29f08c3bdfSopenharmony_ci{
30f08c3bdfSopenharmony_ci	SAFE_PTRACE(PTRACE_TRACEME, 0, NULL, NULL);
31f08c3bdfSopenharmony_ci	raise(SIGSTOP);
32f08c3bdfSopenharmony_ci	/* wait for SIGCONT from parent */
33f08c3bdfSopenharmony_ci
34f08c3bdfSopenharmony_ci	asm volatile(
35f08c3bdfSopenharmony_ci		"mov %%ss, %0\n"
36f08c3bdfSopenharmony_ci		"mov %0, %%ss\n"
37f08c3bdfSopenharmony_ci		"int $3\n"
38f08c3bdfSopenharmony_ci		: "+m" (watchpoint)
39f08c3bdfSopenharmony_ci	);
40f08c3bdfSopenharmony_ci
41f08c3bdfSopenharmony_ci	return 0;
42f08c3bdfSopenharmony_ci}
43f08c3bdfSopenharmony_ci
44f08c3bdfSopenharmony_cistatic void run(void)
45f08c3bdfSopenharmony_ci{
46f08c3bdfSopenharmony_ci	int status;
47f08c3bdfSopenharmony_ci
48f08c3bdfSopenharmony_ci#if defined(__i386__) || defined(__x86_64__)
49f08c3bdfSopenharmony_ci	child_pid = SAFE_FORK();
50f08c3bdfSopenharmony_ci
51f08c3bdfSopenharmony_ci	if (!child_pid) {
52f08c3bdfSopenharmony_ci		exit(child_main());
53f08c3bdfSopenharmony_ci	}
54f08c3bdfSopenharmony_ci
55f08c3bdfSopenharmony_ci	if (SAFE_WAITPID(child_pid, &status, 0) != child_pid)
56f08c3bdfSopenharmony_ci		tst_brk(TBROK, "Received event from unexpected PID");
57f08c3bdfSopenharmony_ci
58f08c3bdfSopenharmony_ci	SAFE_PTRACE(PTRACE_POKEUSER, child_pid,
59f08c3bdfSopenharmony_ci		(void *)offsetof(struct user, u_debugreg[0]), &watchpoint);
60f08c3bdfSopenharmony_ci	SAFE_PTRACE(PTRACE_POKEUSER, child_pid,
61f08c3bdfSopenharmony_ci		(void *)offsetof(struct user, u_debugreg[7]), (void *)0x30001);
62f08c3bdfSopenharmony_ci	SAFE_PTRACE(PTRACE_CONT, child_pid, NULL, NULL);
63f08c3bdfSopenharmony_ci#endif
64f08c3bdfSopenharmony_ci
65f08c3bdfSopenharmony_ci	while (1) {
66f08c3bdfSopenharmony_ci		if (SAFE_WAITPID(child_pid, &status, 0) != child_pid)
67f08c3bdfSopenharmony_ci			tst_brk(TBROK, "Received event from unexpected PID");
68f08c3bdfSopenharmony_ci
69f08c3bdfSopenharmony_ci		if (WIFEXITED(status)) {
70f08c3bdfSopenharmony_ci			child_pid = 0;
71f08c3bdfSopenharmony_ci			break;
72f08c3bdfSopenharmony_ci		}
73f08c3bdfSopenharmony_ci
74f08c3bdfSopenharmony_ci		if (WIFSTOPPED(status)) {
75f08c3bdfSopenharmony_ci			SAFE_PTRACE(PTRACE_CONT, child_pid, NULL, NULL);
76f08c3bdfSopenharmony_ci			continue;
77f08c3bdfSopenharmony_ci		}
78f08c3bdfSopenharmony_ci
79f08c3bdfSopenharmony_ci		tst_brk(TBROK, "Unexpected event from child");
80f08c3bdfSopenharmony_ci	}
81f08c3bdfSopenharmony_ci
82f08c3bdfSopenharmony_ci	tst_res(TPASS, "We're still here. Nothing bad happened, probably.");
83f08c3bdfSopenharmony_ci}
84f08c3bdfSopenharmony_ci
85f08c3bdfSopenharmony_cistatic void cleanup(void)
86f08c3bdfSopenharmony_ci{
87f08c3bdfSopenharmony_ci	/* Main process terminated by tst_brk() with child still paused */
88f08c3bdfSopenharmony_ci	if (child_pid)
89f08c3bdfSopenharmony_ci		SAFE_KILL(child_pid, SIGKILL);
90f08c3bdfSopenharmony_ci}
91f08c3bdfSopenharmony_ci
92f08c3bdfSopenharmony_cistatic struct tst_test test = {
93f08c3bdfSopenharmony_ci	.test_all = run,
94f08c3bdfSopenharmony_ci	.cleanup = cleanup,
95f08c3bdfSopenharmony_ci	.forks_child = 1,
96f08c3bdfSopenharmony_ci	.supported_archs = (const char *const []) {
97f08c3bdfSopenharmony_ci		"x86",
98f08c3bdfSopenharmony_ci		"x86_64",
99f08c3bdfSopenharmony_ci		NULL
100f08c3bdfSopenharmony_ci	},
101f08c3bdfSopenharmony_ci	.tags = (const struct tst_tag[]) {
102f08c3bdfSopenharmony_ci		{"linux-git", "d8ba61ba58c8"},
103f08c3bdfSopenharmony_ci		{"CVE", "2018-8897"},
104f08c3bdfSopenharmony_ci		{}
105f08c3bdfSopenharmony_ci	}
106f08c3bdfSopenharmony_ci};
107