[PATCH v1 1/3] thp04: Simplify to focus on CVE-2017-1000405 race test only
Jan Polensky <[email protected]>
| Newsgroups | gmane.linux.ltp |
|---|---|
| Message-ID | <[email protected]> |
Remove ptrace mode support from thp04.c to keep the test focused on its original purpose: testing the CVE-2017-1000405 race condition. The ptrace-based /proc/pid/mem write functionality is now covered by dedicated tests in testcases/kernel/syscalls/ptrace/: - ptrace12: Tests that CONFIG_PROC_MEM_FORCE_PTRACE blocks self-writes - ptrace13: Tests that ptrace allows parent-to-child memory writes This separation follows the principle of one test per feature and makes the test suite more maintainable. Signed-off-by: Jan Polensky <[email protected]> --- testcases/kernel/mem/thp/thp04.c | 166 +++++++++++++++++++++---------- 1 file changed, 113 insertions(+), 53 deletions(-) diff --git a/testcases/kernel/mem/thp/thp04.c b/testcases/kernel/mem/thp/thp04.c index 16d766c349b7..ffdad60c798f 100644 --- a/testcases/kernel/mem/thp/thp04.c +++ b/testcases/kernel/mem/thp/thp04.c @@ -3,7 +3,7 @@ * Copyright (c) 2019 SUSE LLC <[email protected]> */ -/* +/*\ * CVE-2017-1000405 * * Check for the Huge Dirty Cow vulnerability which allows a userspace process @@ -21,27 +21,38 @@ * On old kernel such as 4.9, it has fixed the Dirty Cow bug but a similar check * in huge_memory.c was forgotten. As a result, remote memory writes to ro regions * of memory backed by transparent huge pages cause an infinite loop in the kernel. - * While in this state the process is stil SIGKILLable, but little else works. + * While in this state the process is still SIGKILLable, but little else works. * It is also a regression test about kernel * commit 8310d48b125d("huge_memory.c: respect FOLL_FORCE/FOLL_COW for thp"). + * + * This test uses direct writes to /proc/self/mem with fuzzy-sync to trigger + * the race condition. If direct writes are not available (e.g., when + * CONFIG_PROC_MEM_FORCE_PTRACE=y is set), the test reports TCONF. + * For ptrace-based /proc/pid/mem testing, see testcases/kernel/syscalls/ptrace/. */ -#include "tst_test.h" #include "lapi/mmap.h" #include "tst_fuzzy_sync.h" +#include "tst_test.h" -static char *write_thp, *read_thp; -static int *write_ptr, *read_ptr; -static size_t thp_size; -static int writefd = -1, readfd = -1; -static struct tst_fzsync_pair fzsync_pair; +struct child_state { + char *write_thp; + char *read_thp; + int *write_ptr; + int *read_ptr; + size_t thp_size; + int writefd; + int readfd; + struct tst_fzsync_pair fzsync_pair; +}; -static void *alloc_zero_page(void *baseaddr) +static struct child_state *child; + +static void *alloc_zero_page(void *baseaddr, size_t thp_size) { int i; void *ret; - /* Find aligned chunk of address space. MAP_HUGETLB doesn't work. */ for (i = 0; i < 16; i++, baseaddr += thp_size) { ret = mmap(baseaddr, thp_size, PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); @@ -67,47 +78,70 @@ static void *alloc_zero_page(void *baseaddr) } tst_brk(TBROK, "Cannot map huge zero page near the specified address"); - return NULL; /* Silence compiler warning */ + return NULL; } -static void setup(void) +static void child_cleanup(void) +{ + if (!child) + return; + + tst_fzsync_pair_cleanup(&child->fzsync_pair); + + if (child->readfd >= 0) + SAFE_CLOSE(child->readfd); + + if (child->writefd >= 0) + SAFE_CLOSE(child->writefd); + + if (child->read_thp) + SAFE_MUNMAP(child->read_thp, child->thp_size); + + if (child->write_thp) + SAFE_MUNMAP(child->write_thp, child->thp_size); +} + +static void child_setup(void) { size_t i; - thp_size = tst_get_hugepage_size(); + child->thp_size = tst_get_hugepage_size(); - if (!thp_size) + if (!child->thp_size) tst_brk(TCONF, "Kernel does not support huge pages"); - write_thp = alloc_zero_page((void *)thp_size); + child->write_thp = alloc_zero_page((void *)child->thp_size, + child->thp_size); - for (i = 0; i < thp_size; i++) { - if (write_thp[i]) + for (i = 0; i < child->thp_size; i++) { + if (child->write_thp[i]) tst_brk(TCONF, "Huge zero page is pre-polluted"); } - /* leave a hole between read and write THP to prevent merge */ - read_thp = alloc_zero_page(write_thp + 2 * thp_size); - write_ptr = (int *)(write_thp + thp_size - sizeof(int)); - read_ptr = (int *)(read_thp + thp_size - sizeof(int)); - writefd = SAFE_OPEN("/proc/self/mem", O_RDWR); - readfd = SAFE_OPEN("/proc/self/mem", O_RDWR); + child->read_thp = alloc_zero_page(child->write_thp + 2 * child->thp_size, + child->thp_size); + /* write_ptr points to last int in write_thp page */ + child->write_ptr = (int *)(child->write_thp + child->thp_size - sizeof(int)); + /* read_ptr points to last int in read_thp page */ + child->read_ptr = (int *)(child->read_thp + child->thp_size - sizeof(int)); - fzsync_pair.exec_loops = 100000; - tst_fzsync_pair_init(&fzsync_pair); + child->writefd = SAFE_OPEN("/proc/self/mem", O_RDWR); + child->readfd = SAFE_OPEN("/proc/self/mem", O_RDWR); + child->fzsync_pair.exec_loops = 100000; + tst_fzsync_pair_init(&child->fzsync_pair); } static void *thread_run(void *arg) { int c; - while (tst_fzsync_run_b(&fzsync_pair)) { - tst_fzsync_start_race_b(&fzsync_pair); - madvise(write_thp, thp_size, MADV_DONTNEED); - memcpy(&c, write_ptr, sizeof(c)); - SAFE_LSEEK(readfd, (off_t)write_ptr, SEEK_SET); - SAFE_READ(1, readfd, &c, sizeof(int)); - tst_fzsync_end_race_b(&fzsync_pair); + while (tst_fzsync_run_b(&child->fzsync_pair)) { + tst_fzsync_start_race_b(&child->fzsync_pair); + madvise(child->write_thp, child->thp_size, MADV_DONTNEED); + memcpy(&c, child->write_ptr, sizeof(c)); + SAFE_LSEEK(child->readfd, (off_t)child->write_ptr, SEEK_SET); + SAFE_READ(1, child->readfd, &c, sizeof(int)); + tst_fzsync_end_race_b(&child->fzsync_pair); /* Wait for dirty page handling before next madvise() */ usleep(10); } @@ -119,20 +153,20 @@ static void run(void) { int c = 0xdeadbeef; - tst_fzsync_pair_reset(&fzsync_pair, thread_run); + tst_fzsync_pair_reset(&child->fzsync_pair, thread_run); - while (tst_fzsync_run_a(&fzsync_pair)) { + while (tst_fzsync_run_a(&child->fzsync_pair)) { /* Write into the main huge page */ - tst_fzsync_start_race_a(&fzsync_pair); - SAFE_LSEEK(writefd, (off_t)write_ptr, SEEK_SET); - madvise(write_thp, thp_size, MADV_DONTNEED); - SAFE_WRITE(SAFE_WRITE_ALL, writefd, &c, sizeof(int)); - tst_fzsync_end_race_a(&fzsync_pair); + tst_fzsync_start_race_a(&child->fzsync_pair); + SAFE_LSEEK(child->writefd, (off_t)child->write_ptr, SEEK_SET); + madvise(child->write_thp, child->thp_size, MADV_DONTNEED); + SAFE_WRITE(SAFE_WRITE_ALL, child->writefd, &c, sizeof(int)); + tst_fzsync_end_race_a(&child->fzsync_pair); /* Check the other huge zero page for pollution */ - madvise(read_thp, thp_size, MADV_DONTNEED); + madvise(child->read_thp, child->thp_size, MADV_DONTNEED); - if (*read_ptr != 0) { + if (*child->read_ptr != 0) { tst_res(TFAIL, "Huge zero page was polluted"); return; } @@ -141,20 +175,45 @@ static void run(void) tst_res(TPASS, "Huge zero page is still clean"); } +static void setup(void) +{ + int test_val = 0; + + child = SAFE_MMAP(NULL, sizeof(*child), PROT_READ | PROT_WRITE, + MAP_SHARED | MAP_ANONYMOUS, -1, 0); + memset(child, 0, sizeof(*child)); + child->writefd = -1; + child->readfd = -1; + + child_setup(); + + /* Verify that direct writes to /proc/self/mem work */ + TEST(lseek(child->writefd, (off_t)child->write_ptr, SEEK_SET)); + if (TST_RET == -1) + tst_brk(TBROK | TTERRNO, "lseek on /proc/self/mem failed"); + + TEST(write(child->writefd, &test_val, sizeof(test_val))); + + if (TST_RET == -1 && TST_ERR == EIO) { + tst_brk(TCONF, + "Direct writes to /proc/self/mem disabled " + "(CONFIG_PROC_MEM_FORCE_PTRACE=y). " + "See testcases/kernel/syscalls/ptrace/ptrace12 and ptrace13 for ptrace coverage."); + } + + if (TST_RET == -1) + tst_brk(TBROK | TTERRNO, "test write to /proc/self/mem failed"); + + if (TST_RET != sizeof(test_val)) + tst_brk(TBROK, "short write to /proc/self/mem: %ld bytes", TST_RET); +} + static void cleanup(void) { - tst_fzsync_pair_cleanup(&fzsync_pair); + child_cleanup(); - if (readfd >= 0) - SAFE_CLOSE(readfd); - - if (writefd >= 0) - SAFE_CLOSE(writefd); - - if (read_thp) - SAFE_MUNMAP(read_thp, thp_size); - if (write_thp) - SAFE_MUNMAP(write_thp, thp_size); + if (child) + SAFE_MUNMAP(child, sizeof(*child)); } static struct tst_test test = { @@ -162,10 +221,11 @@ static struct tst_test test = { .setup = setup, .cleanup = cleanup, .runtime = 150, + .forks_child = 1, .tags = (const struct tst_tag[]) { {"linux-git", "a8f97366452e"}, {"linux-git", "8310d48b125d"}, {"CVE", "2017-1000405"}, {} } -}; +}; \ No newline at end of file -- 2.55.0 -- Mailing list info: https://lists.linux.it/listinfo/ltp