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