1*49cdfc7eSAndroid Build Coastguard Worker // SPDX-License-Identifier: GPL-2.0-or-later
2*49cdfc7eSAndroid Build Coastguard Worker /*
3*49cdfc7eSAndroid Build Coastguard Worker * Copyright (c) 2019 SUSE LLC <[email protected]>
4*49cdfc7eSAndroid Build Coastguard Worker */
5*49cdfc7eSAndroid Build Coastguard Worker
6*49cdfc7eSAndroid Build Coastguard Worker /*
7*49cdfc7eSAndroid Build Coastguard Worker * CVE-2017-1000405
8*49cdfc7eSAndroid Build Coastguard Worker *
9*49cdfc7eSAndroid Build Coastguard Worker * Check for the Huge Dirty Cow vulnerability which allows a userspace process
10*49cdfc7eSAndroid Build Coastguard Worker * to overwrite the huge zero page. Race fixed in:
11*49cdfc7eSAndroid Build Coastguard Worker *
12*49cdfc7eSAndroid Build Coastguard Worker * commit a8f97366452ed491d13cf1e44241bc0b5740b1f0
13*49cdfc7eSAndroid Build Coastguard Worker * Author: Kirill A. Shutemov <[email protected]>
14*49cdfc7eSAndroid Build Coastguard Worker * Date: Mon Nov 27 06:21:25 2017 +0300
15*49cdfc7eSAndroid Build Coastguard Worker *
16*49cdfc7eSAndroid Build Coastguard Worker * mm, thp: Do not make page table dirty unconditionally in touch_p[mu]d()
17*49cdfc7eSAndroid Build Coastguard Worker *
18*49cdfc7eSAndroid Build Coastguard Worker * More details see the following URL
19*49cdfc7eSAndroid Build Coastguard Worker * https://medium.com/bindecy/huge-dirty-cow-cve-2017-1000405-110eca132de0
20*49cdfc7eSAndroid Build Coastguard Worker *
21*49cdfc7eSAndroid Build Coastguard Worker * On old kernel such as 4.9, it has fixed the Dirty Cow bug but a similar check
22*49cdfc7eSAndroid Build Coastguard Worker * in huge_memory.c was forgotten. As a result, remote memory writes to ro regions
23*49cdfc7eSAndroid Build Coastguard Worker * of memory backed by transparent huge pages cause an infinite loop in the kernel.
24*49cdfc7eSAndroid Build Coastguard Worker * While in this state the process is stil SIGKILLable, but little else works.
25*49cdfc7eSAndroid Build Coastguard Worker * It is also a regression test about kernel
26*49cdfc7eSAndroid Build Coastguard Worker * commit 8310d48b125d("huge_memory.c: respect FOLL_FORCE/FOLL_COW for thp").
27*49cdfc7eSAndroid Build Coastguard Worker */
28*49cdfc7eSAndroid Build Coastguard Worker
29*49cdfc7eSAndroid Build Coastguard Worker #include "tst_test.h"
30*49cdfc7eSAndroid Build Coastguard Worker #include "lapi/mmap.h"
31*49cdfc7eSAndroid Build Coastguard Worker #include "tst_fuzzy_sync.h"
32*49cdfc7eSAndroid Build Coastguard Worker
33*49cdfc7eSAndroid Build Coastguard Worker static char *write_thp, *read_thp;
34*49cdfc7eSAndroid Build Coastguard Worker static int *write_ptr, *read_ptr;
35*49cdfc7eSAndroid Build Coastguard Worker static size_t thp_size;
36*49cdfc7eSAndroid Build Coastguard Worker static int writefd = -1, readfd = -1;
37*49cdfc7eSAndroid Build Coastguard Worker static struct tst_fzsync_pair fzsync_pair;
38*49cdfc7eSAndroid Build Coastguard Worker
alloc_zero_page(void * baseaddr)39*49cdfc7eSAndroid Build Coastguard Worker static void *alloc_zero_page(void *baseaddr)
40*49cdfc7eSAndroid Build Coastguard Worker {
41*49cdfc7eSAndroid Build Coastguard Worker int i;
42*49cdfc7eSAndroid Build Coastguard Worker void *ret;
43*49cdfc7eSAndroid Build Coastguard Worker
44*49cdfc7eSAndroid Build Coastguard Worker /* Find aligned chunk of address space. MAP_HUGETLB doesn't work. */
45*49cdfc7eSAndroid Build Coastguard Worker for (i = 0; i < 16; i++, baseaddr += thp_size) {
46*49cdfc7eSAndroid Build Coastguard Worker ret = mmap(baseaddr, thp_size, PROT_READ,
47*49cdfc7eSAndroid Build Coastguard Worker MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
48*49cdfc7eSAndroid Build Coastguard Worker
49*49cdfc7eSAndroid Build Coastguard Worker if (ret == baseaddr) {
50*49cdfc7eSAndroid Build Coastguard Worker TEST(madvise(ret, thp_size, MADV_HUGEPAGE));
51*49cdfc7eSAndroid Build Coastguard Worker
52*49cdfc7eSAndroid Build Coastguard Worker if (TST_RET == -1 && TST_ERR == EINVAL) {
53*49cdfc7eSAndroid Build Coastguard Worker tst_brk(TCONF | TTERRNO,
54*49cdfc7eSAndroid Build Coastguard Worker "madvise(MADV_HUGEPAGE) not supported");
55*49cdfc7eSAndroid Build Coastguard Worker }
56*49cdfc7eSAndroid Build Coastguard Worker
57*49cdfc7eSAndroid Build Coastguard Worker if (TST_RET) {
58*49cdfc7eSAndroid Build Coastguard Worker tst_brk(TBROK | TTERRNO,
59*49cdfc7eSAndroid Build Coastguard Worker "madvise(MADV_HUGEPAGE) failed");
60*49cdfc7eSAndroid Build Coastguard Worker }
61*49cdfc7eSAndroid Build Coastguard Worker
62*49cdfc7eSAndroid Build Coastguard Worker return ret;
63*49cdfc7eSAndroid Build Coastguard Worker }
64*49cdfc7eSAndroid Build Coastguard Worker
65*49cdfc7eSAndroid Build Coastguard Worker if (ret != MAP_FAILED)
66*49cdfc7eSAndroid Build Coastguard Worker SAFE_MUNMAP(ret, thp_size);
67*49cdfc7eSAndroid Build Coastguard Worker }
68*49cdfc7eSAndroid Build Coastguard Worker
69*49cdfc7eSAndroid Build Coastguard Worker tst_brk(TBROK, "Cannot map huge zero page near the specified address");
70*49cdfc7eSAndroid Build Coastguard Worker return NULL; /* Silence compiler warning */
71*49cdfc7eSAndroid Build Coastguard Worker }
72*49cdfc7eSAndroid Build Coastguard Worker
setup(void)73*49cdfc7eSAndroid Build Coastguard Worker static void setup(void)
74*49cdfc7eSAndroid Build Coastguard Worker {
75*49cdfc7eSAndroid Build Coastguard Worker size_t i;
76*49cdfc7eSAndroid Build Coastguard Worker
77*49cdfc7eSAndroid Build Coastguard Worker thp_size = tst_get_hugepage_size();
78*49cdfc7eSAndroid Build Coastguard Worker
79*49cdfc7eSAndroid Build Coastguard Worker if (!thp_size)
80*49cdfc7eSAndroid Build Coastguard Worker tst_brk(TCONF, "Kernel does not support huge pages");
81*49cdfc7eSAndroid Build Coastguard Worker
82*49cdfc7eSAndroid Build Coastguard Worker write_thp = alloc_zero_page((void *)thp_size);
83*49cdfc7eSAndroid Build Coastguard Worker
84*49cdfc7eSAndroid Build Coastguard Worker for (i = 0; i < thp_size; i++) {
85*49cdfc7eSAndroid Build Coastguard Worker if (write_thp[i])
86*49cdfc7eSAndroid Build Coastguard Worker tst_brk(TCONF, "Huge zero page is pre-polluted");
87*49cdfc7eSAndroid Build Coastguard Worker }
88*49cdfc7eSAndroid Build Coastguard Worker
89*49cdfc7eSAndroid Build Coastguard Worker /* leave a hole between read and write THP to prevent merge */
90*49cdfc7eSAndroid Build Coastguard Worker read_thp = alloc_zero_page(write_thp + 2 * thp_size);
91*49cdfc7eSAndroid Build Coastguard Worker write_ptr = (int *)(write_thp + thp_size - sizeof(int));
92*49cdfc7eSAndroid Build Coastguard Worker read_ptr = (int *)(read_thp + thp_size - sizeof(int));
93*49cdfc7eSAndroid Build Coastguard Worker writefd = SAFE_OPEN("/proc/self/mem", O_RDWR);
94*49cdfc7eSAndroid Build Coastguard Worker readfd = SAFE_OPEN("/proc/self/mem", O_RDWR);
95*49cdfc7eSAndroid Build Coastguard Worker
96*49cdfc7eSAndroid Build Coastguard Worker fzsync_pair.exec_loops = 100000;
97*49cdfc7eSAndroid Build Coastguard Worker tst_fzsync_pair_init(&fzsync_pair);
98*49cdfc7eSAndroid Build Coastguard Worker }
99*49cdfc7eSAndroid Build Coastguard Worker
thread_run(void * arg)100*49cdfc7eSAndroid Build Coastguard Worker static void *thread_run(void *arg)
101*49cdfc7eSAndroid Build Coastguard Worker {
102*49cdfc7eSAndroid Build Coastguard Worker int c;
103*49cdfc7eSAndroid Build Coastguard Worker
104*49cdfc7eSAndroid Build Coastguard Worker while (tst_fzsync_run_b(&fzsync_pair)) {
105*49cdfc7eSAndroid Build Coastguard Worker tst_fzsync_start_race_b(&fzsync_pair);
106*49cdfc7eSAndroid Build Coastguard Worker madvise(write_thp, thp_size, MADV_DONTNEED);
107*49cdfc7eSAndroid Build Coastguard Worker memcpy(&c, write_ptr, sizeof(c));
108*49cdfc7eSAndroid Build Coastguard Worker SAFE_LSEEK(readfd, (off_t)write_ptr, SEEK_SET);
109*49cdfc7eSAndroid Build Coastguard Worker SAFE_READ(1, readfd, &c, sizeof(int));
110*49cdfc7eSAndroid Build Coastguard Worker tst_fzsync_end_race_b(&fzsync_pair);
111*49cdfc7eSAndroid Build Coastguard Worker /* Wait for dirty page handling before next madvise() */
112*49cdfc7eSAndroid Build Coastguard Worker usleep(10);
113*49cdfc7eSAndroid Build Coastguard Worker }
114*49cdfc7eSAndroid Build Coastguard Worker
115*49cdfc7eSAndroid Build Coastguard Worker return arg;
116*49cdfc7eSAndroid Build Coastguard Worker }
117*49cdfc7eSAndroid Build Coastguard Worker
run(void)118*49cdfc7eSAndroid Build Coastguard Worker static void run(void)
119*49cdfc7eSAndroid Build Coastguard Worker {
120*49cdfc7eSAndroid Build Coastguard Worker int c = 0xdeadbeef;
121*49cdfc7eSAndroid Build Coastguard Worker
122*49cdfc7eSAndroid Build Coastguard Worker tst_fzsync_pair_reset(&fzsync_pair, thread_run);
123*49cdfc7eSAndroid Build Coastguard Worker
124*49cdfc7eSAndroid Build Coastguard Worker while (tst_fzsync_run_a(&fzsync_pair)) {
125*49cdfc7eSAndroid Build Coastguard Worker /* Write into the main huge page */
126*49cdfc7eSAndroid Build Coastguard Worker tst_fzsync_start_race_a(&fzsync_pair);
127*49cdfc7eSAndroid Build Coastguard Worker SAFE_LSEEK(writefd, (off_t)write_ptr, SEEK_SET);
128*49cdfc7eSAndroid Build Coastguard Worker madvise(write_thp, thp_size, MADV_DONTNEED);
129*49cdfc7eSAndroid Build Coastguard Worker SAFE_WRITE(SAFE_WRITE_ALL, writefd, &c, sizeof(int));
130*49cdfc7eSAndroid Build Coastguard Worker tst_fzsync_end_race_a(&fzsync_pair);
131*49cdfc7eSAndroid Build Coastguard Worker
132*49cdfc7eSAndroid Build Coastguard Worker /* Check the other huge zero page for pollution */
133*49cdfc7eSAndroid Build Coastguard Worker madvise(read_thp, thp_size, MADV_DONTNEED);
134*49cdfc7eSAndroid Build Coastguard Worker
135*49cdfc7eSAndroid Build Coastguard Worker if (*read_ptr != 0) {
136*49cdfc7eSAndroid Build Coastguard Worker tst_res(TFAIL, "Huge zero page was polluted");
137*49cdfc7eSAndroid Build Coastguard Worker return;
138*49cdfc7eSAndroid Build Coastguard Worker }
139*49cdfc7eSAndroid Build Coastguard Worker }
140*49cdfc7eSAndroid Build Coastguard Worker
141*49cdfc7eSAndroid Build Coastguard Worker tst_res(TPASS, "Huge zero page is still clean");
142*49cdfc7eSAndroid Build Coastguard Worker }
143*49cdfc7eSAndroid Build Coastguard Worker
cleanup(void)144*49cdfc7eSAndroid Build Coastguard Worker static void cleanup(void)
145*49cdfc7eSAndroid Build Coastguard Worker {
146*49cdfc7eSAndroid Build Coastguard Worker tst_fzsync_pair_cleanup(&fzsync_pair);
147*49cdfc7eSAndroid Build Coastguard Worker
148*49cdfc7eSAndroid Build Coastguard Worker if (readfd >= 0)
149*49cdfc7eSAndroid Build Coastguard Worker SAFE_CLOSE(readfd);
150*49cdfc7eSAndroid Build Coastguard Worker
151*49cdfc7eSAndroid Build Coastguard Worker if (writefd >= 0)
152*49cdfc7eSAndroid Build Coastguard Worker SAFE_CLOSE(writefd);
153*49cdfc7eSAndroid Build Coastguard Worker
154*49cdfc7eSAndroid Build Coastguard Worker if (read_thp)
155*49cdfc7eSAndroid Build Coastguard Worker SAFE_MUNMAP(read_thp, thp_size);
156*49cdfc7eSAndroid Build Coastguard Worker if (write_thp)
157*49cdfc7eSAndroid Build Coastguard Worker SAFE_MUNMAP(write_thp, thp_size);
158*49cdfc7eSAndroid Build Coastguard Worker }
159*49cdfc7eSAndroid Build Coastguard Worker
160*49cdfc7eSAndroid Build Coastguard Worker static struct tst_test test = {
161*49cdfc7eSAndroid Build Coastguard Worker .test_all = run,
162*49cdfc7eSAndroid Build Coastguard Worker .setup = setup,
163*49cdfc7eSAndroid Build Coastguard Worker .cleanup = cleanup,
164*49cdfc7eSAndroid Build Coastguard Worker .max_runtime = 150,
165*49cdfc7eSAndroid Build Coastguard Worker .tags = (const struct tst_tag[]) {
166*49cdfc7eSAndroid Build Coastguard Worker {"linux-git", "a8f97366452e"},
167*49cdfc7eSAndroid Build Coastguard Worker {"linux-git", "8310d48b125d"},
168*49cdfc7eSAndroid Build Coastguard Worker {"CVE", "2017-1000405"},
169*49cdfc7eSAndroid Build Coastguard Worker {}
170*49cdfc7eSAndroid Build Coastguard Worker }
171*49cdfc7eSAndroid Build Coastguard Worker };
172