On 10/20/24 10:20, Lorenzo Stoakes wrote:
Utilise the kselftest harmness to implement tests for the guard page implementation.
We start by implement basic tests asserting that guard pages can be established (poisoned), cleared (remedied) and that touching poisoned pages result in SIGSEGV. We also assert that, in remedying a range, non-poison pages remain intact.
We then examine different operations on regions containing poison markers behave to ensure correct behaviour:
- Operations over multiple VMAs operate as expected.
- Invoking MADV_GUARD_POISION / MADV_GUARD_REMEDY via process_madvise() in batches works correctly.
- Ensuring that munmap() correctly tears down poison markers.
- Using mprotect() to adjust protection bits does not in any way override or cause issues with poison markers.
- Ensuring that splitting and merging VMAs around poison markers causes no issue - i.e. that a marker which 'belongs' to one VMA can function just as well 'belonging' to another.
- Ensuring that madvise(..., MADV_DONTNEED) does not remove poison markers.
- Ensuring that mlock()'ing a range containing poison markers does not cause issues.
- Ensuring that mremap() can move a poisoned range and retain poison markers.
- Ensuring that mremap() can expand a poisoned range and retain poison markers (perhaps moving the range).
- Ensuring that mremap() can shrink a poisoned range and retain poison markers.
- Ensuring that forking a process correctly retains poison markers.
- Ensuring that forking a VMA with VM_WIPEONFORK set behaves sanely.
- Ensuring that lazyfree simply clears poison markers.
- Ensuring that userfaultfd can co-exist with guard pages.
- Ensuring that madvise(..., MADV_POPULATE_READ) and madvise(..., MADV_POPULATE_WRITE) error out when encountering poison markers.
- Ensuring that madvise(..., MADV_COLD) and madvise(..., MADV_PAGEOUT) do not remove poison markers.
If any test is unable to be run due to lack of permissions, that test is skipped.
Signed-off-by: Lorenzo Stoakes lorenzo.stoakes@oracle.com
tools/testing/selftests/mm/.gitignore | 1 + tools/testing/selftests/mm/Makefile | 1 + tools/testing/selftests/mm/guard-pages.c | 1228 ++++++++++++++++++++++ 3 files changed, 1230 insertions(+) create mode 100644 tools/testing/selftests/mm/guard-pages.c
diff --git a/tools/testing/selftests/mm/.gitignore b/tools/testing/selftests/mm/.gitignore index 689bbd520296..8f01f4da1c0d 100644 --- a/tools/testing/selftests/mm/.gitignore +++ b/tools/testing/selftests/mm/.gitignore @@ -54,3 +54,4 @@ droppable hugetlb_dio pkey_sighandler_tests_32 pkey_sighandler_tests_64 +guard-pages diff --git a/tools/testing/selftests/mm/Makefile b/tools/testing/selftests/mm/Makefile index 02e1204971b0..15c734d6cfec 100644 --- a/tools/testing/selftests/mm/Makefile +++ b/tools/testing/selftests/mm/Makefile @@ -79,6 +79,7 @@ TEST_GEN_FILES += hugetlb_fault_after_madv TEST_GEN_FILES += hugetlb_madv_vs_map TEST_GEN_FILES += hugetlb_dio TEST_GEN_FILES += droppable +TEST_GEN_FILES += guard-pages ifneq ($(ARCH),arm64) TEST_GEN_FILES += soft-dirty diff --git a/tools/testing/selftests/mm/guard-pages.c b/tools/testing/selftests/mm/guard-pages.c new file mode 100644 index 000000000000..f67d2700d44a --- /dev/null +++ b/tools/testing/selftests/mm/guard-pages.c @@ -0,0 +1,1228 @@ +// SPDX-License-Identifier: GPL-2.0-or-later
+#define _GNU_SOURCE +#include "../kselftest_harness.h" +#include <asm-generic/mman.h> /* Force the import of the tools version. */ +#include <assert.h> +#include <errno.h> +#include <fcntl.h> +#include <linux/userfaultfd.h> +#include <setjmp.h> +#include <signal.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/ioctl.h> +#include <sys/mman.h> +#include <sys/syscall.h> +#include <sys/uio.h> +#include <unistd.h>
+/*
- Ignore the checkpatch warning, as per the C99 standard, section 7.14.1.1:
- "If the signal occurs other than as the result of calling the abort or raise
- function, the behavior is undefined if the signal handler refers to any
- object with static storage duration other than by assigning a value to an
- object declared as volatile sig_atomic_t"
- */
+static volatile sig_atomic_t signal_jump_set; +static sigjmp_buf signal_jmp_buf;
+/*
- Ignore the checkpatch warning, we must read from x but don't want to do
- anything with it in order to trigger a read page fault. We therefore must use
- volatile to stop the compiler from optimising this away.
- */
+#define FORCE_READ(x) (*(volatile typeof(x) *)x)
Thank you.
Reviewed-by: Shuah Khan skhan@linuxfoundation.org
thanks, -- Shuah