/* Backup the original content. */
memcpy(old, mem, size);
if (pipe(fds) < 0) return -errno;
/* Trigger a read-only pin. */
transferred = vmsplice(fds[1], &iov, 1, 0); if (transferred < 0) return -errno; if (transferred == 0) return -EINVAL;
/* Unmap it from our page tables. */ if (munmap(mem, size) < 0) return -errno;
/* Wait until the parent modified it. */
write(comm_pipes->child_ready[1], "0", 1); while (read(comm_pipes->parent_ready[0], &buf, 1) != 1)
;
/* See if we still read the old values via the pipe. */ for (total = 0; total < transferred; total += cur) {
cur = read(fds[0], new + total, transferred - total); if (cur < 0) return -errno;
}
/* Wait until the child is done writing. */
wait(&ret); if (!WIFEXITED(ret)) {
ksft_perror("wait() failed");
log_test_result(KSFT_FAIL); goto close_pipe;
}
/* See if we still read the old values. */ for (total = 0; total < transferred; total += cur) {
cur = read(fds[0], new + total, transferred - total); if (cur < 0) {
ksft_perror("read() failed");
log_test_result(KSFT_FAIL); goto close_pipe;
}
}
if (!memcmp(old, new, transferred)) {
log_test_result(KSFT_PASS);
} elseif (xfail) { /* *Withhugetlb,somevmsplice()testsarecurrentlyexpectedto *failbecause(a)hardertofixand(b)nobodyreallycares. *Flagthemasexpectedfailurefornow.
*/
ksft_print_msg("Leak from child into parent\n");
log_test_result(KSFT_XFAIL);
} else {
ksft_print_msg("Leak from child into parent\n");
log_test_result(KSFT_FAIL);
}
close_pipe:
close(fds[0]);
close(fds[1]);
close_comm_pipes:
close_comm_pipes(&comm_pipes);
free:
free(old);
free(new);
}
/* Skip on errors, as we might just lack kernel support. */
ret = io_uring_queue_init(1, &ring, 0); if (ret < 0) {
ksft_print_msg("io_uring_queue_init() failed\n");
log_test_result(KSFT_SKIP); goto free_tmp;
}
/* Read back the file content to the temporary buffer. */
total = 0; while (total < size) {
cur = pread(fd, tmp + total, size - total, total); if (cur < 0) {
ksft_perror("pread() failed\n");
log_test_result(KSFT_FAIL); goto quit_child;
}
total += cur;
}
/* Finally, check if we read what we expected. */ if (!memcmp(mem, tmp, size)) {
log_test_result(KSFT_PASS);
} else {
ksft_print_msg("Longtom R/W pin is not reliable\n");
log_test_result(KSFT_FAIL);
}
ret = setup_comm_pipes(&comm_pipes); if (ret) {
log_test_result(KSFT_FAIL); goto free_tmp;
}
switch (test) { case RO_PIN_TEST: break; case RO_PIN_TEST_SHARED: case RO_PIN_TEST_PREVIOUSLY_SHARED: /* *Sharethepageswithourchild.Asthepagesarenotpinned, *thisshouldjustwork.
*/
ret = fork(); if (ret < 0) {
ksft_perror("fork() failed");
log_test_result(KSFT_FAIL); goto close_comm_pipes;
} elseif (!ret) {
write(comm_pipes.child_ready[1], "0", 1); while (read(comm_pipes.parent_ready[0], &buf, 1) != 1)
; exit(0);
}
/* Wait until our child is ready. */ while (read(comm_pipes.child_ready[0], &buf, 1) != 1)
;
if (test == RO_PIN_TEST_PREVIOUSLY_SHARED) { /* *Tellthechildtoquitnowandwaituntilitquit. *ThepagesshouldnowbemappedR/Ointoourpage *tables,buttheyarenolongershared.
*/
write(comm_pipes.parent_ready[1], "0", 1);
wait(&ret); if (!WIFEXITED(ret))
ksft_print_msg("[INFO] wait() failed\n");
} break; case RO_PIN_TEST_RO_EXCLUSIVE: /* *MapthepageR/Ointothepagetable.Enablesoftdirty *trackingtostopthepagefromgettingmappedR/Wimmediately *againbymprotect()optimizations.Notethatwedon'thavean *easywaytotestifthatworked(thepagemapdoesnotexport *ifthepageismappedR/Ovs.R/W).
*/
ret = mprotect(mem, size, PROT_READ);
clear_softdirty();
ret |= mprotect(mem, size, PROT_READ | PROT_WRITE); if (ret) {
ksft_perror("mprotect() failed");
log_test_result(KSFT_FAIL); goto close_comm_pipes;
} break; default:
assert(false);
}
/* Take a R/O pin. This should trigger unsharing. */
args.addr = (__u64)(uintptr_t)mem;
args.size = size;
args.flags = fast ? PIN_LONGTERM_TEST_FLAG_USE_FAST : 0;
ret = ioctl(gup_fd, PIN_LONGTERM_TEST_START, &args); if (ret) { if (errno == EINVAL)
ret = KSFT_SKIP; else
ret = KSFT_FAIL;
ksft_perror("PIN_LONGTERM_TEST_START failed");
log_test_result(ret); goto wait;
}
/* Modify the page. */
memset(mem, 0xff, size);
/* *Readbackthecontentviathepintothetemporarybufferand *testifweobservedthemodification.
*/
tmp_val = (__u64)(uintptr_t)tmp;
ret = ioctl(gup_fd, PIN_LONGTERM_TEST_READ, &tmp_val); if (ret) {
ksft_perror("PIN_LONGTERM_TEST_READ failed");
log_test_result(KSFT_FAIL);
} else { if (!memcmp(mem, tmp, size)) {
log_test_result(KSFT_PASS);
} else {
ksft_print_msg("Longterm R/O pin is not reliable\n");
log_test_result(KSFT_FAIL);
}
}
ret = ioctl(gup_fd, PIN_LONGTERM_TEST_STOP); if (ret)
ksft_perror("PIN_LONGTERM_TEST_STOP failed");
wait: switch (test) { case RO_PIN_TEST_SHARED:
write(comm_pipes.parent_ready[1], "0", 1);
wait(&ret); if (!WIFEXITED(ret))
ksft_perror("wait() failed"); break; default: break;
}
close_comm_pipes:
close_comm_pipes(&comm_pipes);
free_tmp:
free(tmp);
}
ret = madvise(mem, pagesize, MADV_NOHUGEPAGE); /* Ignore if not around on a kernel. */ if (ret && errno != EINVAL) {
ksft_perror("MADV_NOHUGEPAGE failed");
log_test_result(KSFT_FAIL); goto munmap;
}
/* Populate a base page. */
memset(mem, 1, pagesize);
if (swapout) {
madvise(mem, pagesize, MADV_PAGEOUT); if (!pagemap_is_swapped(pagemap_fd, mem)) {
ksft_print_msg("MADV_PAGEOUT did not work, is swap enabled?\n");
log_test_result(KSFT_SKIP); goto munmap;
}
}
/* For alignment purposes, we need twice the thp size. */
mmap_size = 2 * thpsize;
mmap_mem = mmap(NULL, mmap_size, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); if (mmap_mem == MAP_FAILED) {
ksft_perror("mmap() failed");
log_test_result(KSFT_FAIL); return;
}
/* We need a THP-aligned memory area. */
mem = (char *)(((uintptr_t)mmap_mem + thpsize) & ~(thpsize - 1));
ret = madvise(mem, thpsize, MADV_HUGEPAGE); if (ret) {
ksft_perror("MADV_HUGEPAGE failed");
log_test_result(KSFT_FAIL); goto munmap;
}
/* *TrytopopulateaTHP.Touchthefirstsub-pageandtestif *wegetthelastsub-pagepopulatedautomatically.
*/
mem[0] = 1; if (!pagemap_is_populated(pagemap_fd, mem + thpsize - pagesize)) {
ksft_print_msg("Did not get a THP populated\n");
log_test_result(KSFT_SKIP); goto munmap;
}
memset(mem, 1, thpsize);
size = thpsize; switch (thp_run) { case THP_RUN_PMD: case THP_RUN_PMD_SWAPOUT:
assert(thpsize == pmdsize); break; case THP_RUN_PTE: case THP_RUN_PTE_SWAPOUT: /* *TriggerPTE-mappingtheTHPbytemporarilymappingasingle *subpageR/O.ThisisanoopiftheTHPisnotpmdsize(and *thereforealreadyPTE-mapped).
*/
ret = mprotect(mem + pagesize, pagesize, PROT_READ); if (ret) {
ksft_perror("mprotect() failed");
log_test_result(KSFT_FAIL); goto munmap;
}
ret = mprotect(mem + pagesize, pagesize, PROT_READ | PROT_WRITE); if (ret) {
ksft_perror("mprotect() failed");
log_test_result(KSFT_FAIL); goto munmap;
} break; case THP_RUN_SINGLE_PTE: case THP_RUN_SINGLE_PTE_SWAPOUT: /* *DiscardallbutasinglesubpageofthatPTE-mappedTHP.What *remainsisasinglePTEmappingasinglesubpage.
*/
ret = madvise(mem + pagesize, thpsize - pagesize, MADV_DONTNEED); if (ret) {
ksft_perror("MADV_DONTNEED failed");
log_test_result(KSFT_FAIL); goto munmap;
}
size = pagesize; break; case THP_RUN_PARTIAL_MREMAP: /* *RemaphalfoftheTHP.Weneedsomenewmemorylocation *forthat.
*/
mremap_size = thpsize / 2;
mremap_mem = mmap(NULL, mremap_size, PROT_NONE,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); if (mremap_mem == MAP_FAILED) {
ksft_perror("mmap() failed");
log_test_result(KSFT_FAIL); goto munmap;
}
tmp = mremap(mem + mremap_size, mremap_size, mremap_size,
MREMAP_MAYMOVE | MREMAP_FIXED, mremap_mem); if (tmp != mremap_mem) {
ksft_perror("mremap() failed");
log_test_result(KSFT_FAIL); goto munmap;
}
size = mremap_size; break; case THP_RUN_PARTIAL_SHARED: /* *SharethefirstpageoftheTHPwithachildandquitthe *child.ThiswillresultinsomepartsoftheTHPnever *havebeenshared.
*/
ret = madvise(mem + pagesize, thpsize - pagesize, MADV_DONTFORK); if (ret) {
ksft_perror("MADV_DONTFORK failed");
log_test_result(KSFT_FAIL); goto munmap;
}
ret = fork(); if (ret < 0) {
ksft_perror("fork() failed");
log_test_result(KSFT_FAIL); goto munmap;
} elseif (!ret) { exit(0);
}
wait(&ret); /* Allow for sharing all pages again. */
ret = madvise(mem + pagesize, thpsize - pagesize, MADV_DOFORK); if (ret) {
ksft_perror("MADV_DOFORK failed");
log_test_result(KSFT_FAIL); goto munmap;
} break; default:
assert(false);
}
switch (thp_run) { case THP_RUN_PMD_SWAPOUT: case THP_RUN_PTE_SWAPOUT: case THP_RUN_SINGLE_PTE_SWAPOUT:
madvise(mem, size, MADV_PAGEOUT); if (!range_is_swapped(mem, size)) {
ksft_print_msg("MADV_PAGEOUT did not work, is swap enabled?\n");
log_test_result(KSFT_SKIP); goto munmap;
} break; default: break;
}
ret = setup_comm_pipes(&comm_pipes); if (ret) {
log_test_result(KSFT_FAIL); return;
}
/* *TriggerPTE-mappingtheTHPbytemporarilymappingasinglesubpage *R/O,suchthatwecantrycollapsingitlater.
*/
ret = mprotect(mem + pagesize, pagesize, PROT_READ); if (ret) {
ksft_perror("mprotect() failed");
log_test_result(KSFT_FAIL); goto close_comm_pipes;
}
ret = mprotect(mem + pagesize, pagesize, PROT_READ | PROT_WRITE); if (ret) {
ksft_perror("mprotect() failed");
log_test_result(KSFT_FAIL); goto close_comm_pipes;
}
switch (test) { case ANON_THP_COLLAPSE_UNSHARED: /* Collapse before actually COW-sharing the page. */
ret = madvise(mem, size, MADV_COLLAPSE); if (ret) {
ksft_perror("MADV_COLLAPSE failed");
log_test_result(KSFT_SKIP); goto close_comm_pipes;
} break; case ANON_THP_COLLAPSE_FULLY_SHARED: /* COW-share the full PTE-mapped THP. */ break; case ANON_THP_COLLAPSE_LOWER_SHARED: /* Don't COW-share the upper part of the THP. */
ret = madvise(mem + size / 2, size / 2, MADV_DONTFORK); if (ret) {
ksft_perror("MADV_DONTFORK failed");
log_test_result(KSFT_FAIL); goto close_comm_pipes;
} break; case ANON_THP_COLLAPSE_UPPER_SHARED: /* Don't COW-share the lower part of the THP. */
ret = madvise(mem, size / 2, MADV_DONTFORK); if (ret) {
ksft_perror("MADV_DONTFORK failed");
log_test_result(KSFT_FAIL); goto close_comm_pipes;
} break; default:
assert(false);
}
ret = fork(); if (ret < 0) {
ksft_perror("fork() failed");
log_test_result(KSFT_FAIL); goto close_comm_pipes;
} elseif (!ret) { switch (test) { case ANON_THP_COLLAPSE_UNSHARED: case ANON_THP_COLLAPSE_FULLY_SHARED: exit(child_memcmp_fn(mem, size, &comm_pipes)); break; case ANON_THP_COLLAPSE_LOWER_SHARED: exit(child_memcmp_fn(mem, size / 2, &comm_pipes)); break; case ANON_THP_COLLAPSE_UPPER_SHARED: exit(child_memcmp_fn(mem + size / 2, size / 2,
&comm_pipes)); break; default:
assert(false);
}
}
while (read(comm_pipes.child_ready[0], &buf, 1) != 1)
;
switch (test) { case ANON_THP_COLLAPSE_UNSHARED: break; case ANON_THP_COLLAPSE_UPPER_SHARED: case ANON_THP_COLLAPSE_LOWER_SHARED: /* *RevertMADV_DONTFORKsuchthatwemergetheVMAsandare *abletoactuallycollapse.
*/
ret = madvise(mem, size, MADV_DOFORK); if (ret) {
ksft_perror("MADV_DOFORK failed");
log_test_result(KSFT_FAIL);
write(comm_pipes.parent_ready[1], "0", 1);
wait(&ret); goto close_comm_pipes;
} /* FALLTHROUGH */ case ANON_THP_COLLAPSE_FULLY_SHARED: /* Collapse before anyone modified the COW-shared page. */
ret = madvise(mem, size, MADV_COLLAPSE); if (ret) {
ksft_perror("MADV_COLLAPSE failed");
log_test_result(KSFT_SKIP);
write(comm_pipes.parent_ready[1], "0", 1);
wait(&ret); goto close_comm_pipes;
} break; default:
assert(false);
}
/* Backup the original content. */
memcpy(old, smem, size);
/* Modify the page. */
memset(mem, 0xff, size);
/* See if we still read the old values via the other mapping. */ if (!memcmp(smem, old, size)) {
log_test_result(KSFT_PASS);
} else {
ksft_print_msg("Other mapping modified\n");
log_test_result(KSFT_FAIL);
}
free(old);
}
/* File consists of a single page filled with zeroes. */ if (fallocate(fd, 0, 0, pagesize)) {
ksft_perror("fallocate() failed");
log_test_result(KSFT_FAIL); goto close;
}
/* Create a private mapping of the memfd. */
mem = mmap(NULL, pagesize, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); if (mem == MAP_FAILED) {
ksft_perror("mmap() failed");
log_test_result(KSFT_FAIL); goto close;
}
smem = mmap(NULL, pagesize, PROT_READ, MAP_SHARED, fd, 0); if (smem == MAP_FAILED) {
ksft_perror("mmap() failed");
log_test_result(KSFT_FAIL); goto munmap;
}
/* Fault the page in. */
FORCE_READ(mem);
FORCE_READ(smem);
/* File consists of a single page filled with zeroes. */ if (fallocate(fd, 0, 0, pagesize)) {
ksft_perror("fallocate() failed");
log_test_result(KSFT_FAIL); goto close;
}
/* Create a private mapping of the memfd. */
mem = mmap(NULL, pagesize, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); if (mem == MAP_FAILED) {
ksft_perror("mmap() failed");
log_test_result(KSFT_FAIL); goto close;
}
smem = mmap(NULL, pagesize, PROT_READ, MAP_SHARED, fd, 0); if (smem == MAP_FAILED) {
ksft_perror("mmap() failed");
log_test_result(KSFT_FAIL); goto munmap;
}
/* Fault the page in. */
FORCE_READ(mem);
FORCE_READ(smem);
/* File consists of a single page filled with zeroes. */ if (fallocate(fd, 0, 0, hugetlbsize)) {
ksft_perror("need more free huge pages");
log_test_result(KSFT_SKIP); goto close;
}
/* Create a private mapping of the memfd. */
mem = mmap(NULL, hugetlbsize, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); if (mem == MAP_FAILED) {
ksft_perror("need more free huge pages");
log_test_result(KSFT_SKIP); goto close;
}
smem = mmap(NULL, hugetlbsize, PROT_READ, MAP_SHARED, fd, 0); if (smem == MAP_FAILED) {
ksft_perror("mmap() failed");
log_test_result(KSFT_FAIL); goto munmap;
}
/* Fault the page in. */
FORCE_READ(mem);
FORCE_READ(smem);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.