diff options
author | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2022-05-10 06:59:24 +0000 |
---|---|---|
committer | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2022-05-10 06:59:24 +0000 |
commit | 94bde983396bfc2cdcf12a56cc0825b4a2d33d83 (patch) | |
tree | df1ccb26decb5dd8062ef10828afbe499f3fe3b6 | |
parent | 8d0eea2c808bed9f1cb597a33d8076f08c811a4f (diff) | |
parent | d01e24273830a794e73d0d67b9ec9c248f2b211e (diff) | |
download | linux-kselftest-android13-mainline-wifi-release.tar.gz |
Snap for 8564071 from d01e24273830a794e73d0d67b9ec9c248f2b211e to mainline-wifi-releaseaml_wif_331810010aml_wif_331710030aml_wif_331613000aml_wif_331511020aml_wif_331414000aml_wif_331310070aml_wif_331112000aml_wif_331016070aml_wif_330910030aml_wif_330810040android13-mainline-wifi-release
Change-Id: I3162a33ef1819d9d8b258e33ed188f4b3ea0f454
-rw-r--r-- | Android.bp | 30 | ||||
-rw-r--r-- | OWNERS | 1 | ||||
-rw-r--r-- | android/README.md | 3 | ||||
-rw-r--r-- | android/kselftest_test_list.mk | 3 | ||||
-rw-r--r-- | android/patches/0019-userfaultfd.patch | 261 | ||||
-rw-r--r-- | android/patches/0020-selftests-binderfs-fix-Wunused-label-error.patch | 31 | ||||
-rw-r--r-- | android/patches/0021-selftests-binderfs-skip-unprivileged-test-when-USER_.patch | 36 | ||||
-rw-r--r-- | android/patches/0023-ANDROID-kvm-Test-that-pVM-memory-is-wiped-during-tea.patch | 246 | ||||
-rw-r--r-- | android/patches/0025-selftests-userfaultfd-skip-if-unavailable.patch | 33 | ||||
-rw-r--r-- | tools/testing/selftests/filesystems/binderfs/binderfs_test.c | 8 | ||||
-rw-r--r-- | tools/testing/selftests/kvm/aarch64/pvm_wipe_mem.c | 174 | ||||
-rw-r--r-- | tools/testing/selftests/vm/userfaultfd.c | 106 |
12 files changed, 900 insertions, 32 deletions
diff --git a/Android.bp b/Android.bp index e56904c3722e..e9e4d049591a 100644 --- a/Android.bp +++ b/Android.bp @@ -76,7 +76,6 @@ cc_defaults { "-Wno-parentheses-equality", "-Wno-pointer-arith", "-Wno-sign-compare", - "-Wno-unused-parameter", "-Wno-shift-negative-value", "-Wno-switch", "-Wno-absolute-value", @@ -96,6 +95,14 @@ cc_defaults { test_per_src: true, } +// binderfs test +cc_test { + name: "kselftest_binderfs_tests", + relative_install_path: "linux-kselftest/filesystems/binderfs", + srcs: ["tools/testing/selftests/filesystems/binderfs/binderfs_test.c"], + defaults: ["kselftest_defaults"], +} + // breakpoints test cc_test { name: "kselftest_breakpoints_tests", @@ -118,6 +125,11 @@ cc_test { "tools/testing/selftests/breakpoints/breakpoint_test.c", ], }, + x86_64: { + srcs: [ + "tools/testing/selftests/breakpoints/breakpoint_test.c", + ], + }, }, } @@ -332,6 +344,21 @@ cc_test { defaults: ["kselftest_defaults"], } +// KVM test +cc_test { + name: "kselftest_kvm_arm64_tests", + relative_install_path: "linux-kselftest/kvm/aarch64", + local_include_dirs: [ "tools/testing/selftests"], + enabled: false, + arch: { + arm64: { + enabled: true, + srcs: ["tools/testing/selftests/kvm/aarch64/pvm_wipe_mem.c"], + }, + }, + defaults: ["kselftest_defaults"], +} + // Lib test sh_test { name: "kselftest_lib_printf", @@ -575,6 +602,7 @@ cc_test { "tools/testing/selftests/vm/mlock-random-test.c", "tools/testing/selftests/vm/mlock2-tests.c", "tools/testing/selftests/vm/on-fault-limit.c", + "tools/testing/selftests/vm/userfaultfd.c", "tools/testing/selftests/vm/thuge-gen.c", "tools/testing/selftests/vm/transhuge-stress.c", ], @@ -1,2 +1,3 @@ drosen@google.com +smuckle@google.com willmcvicker@google.com diff --git a/android/README.md b/android/README.md index 2c94e05b4cdf..7a12a1d3aa21 100644 --- a/android/README.md +++ b/android/README.md @@ -334,9 +334,6 @@ kernel module is not required by Android. The hugepage-mmap, hugepage-shm, compaction, and map_hugetlb tests rely on hugetlbfs/hugetlb page support which is not required by Android. -The userfaultfd test relies on the userfaultfd syscall which is not required by -Android. - ### watchdog/ The watchdog test depends on CONFIG_WATCHDOG which is not required by Android. diff --git a/android/kselftest_test_list.mk b/android/kselftest_test_list.mk index 5445bd12d26f..46b9b92c58fb 100644 --- a/android/kselftest_test_list.mk +++ b/android/kselftest_test_list.mk @@ -12,6 +12,7 @@ kselftest_modules := \ # From Android.bp kselftest_modules += \ + kselftest_binderfs_tests_binderfs_test \ kselftest_breakpoints_tests_breakpoint_test \ kselftest_breakpoints_tests_breakpoint_test_arm64 \ kselftest_breakpoints_tests_step_after_suspend_test \ @@ -30,6 +31,7 @@ kselftest_modules += \ kselftest_intel_pstate_tests_aperf \ kselftest_intel_pstate_tests_msr \ kselftest_kcmp_tests_kcmp_test \ + kselftest_kvm_arm64_tests_pvm_wipe_mem \ kselftest_net_tests_psock_tpacket \ kselftest_net_tests_socket \ kselftest_net_tests_reuseaddr_conflict \ @@ -52,6 +54,7 @@ kselftest_modules += \ kselftest_vdso_test \ kselftest_vm_tests_mlock2-tests \ kselftest_vm_tests_on-fault-limit \ + kselftest_vm_tests_userfaultfd \ kselftest_vm64_tests_virtual_address_range \ kselftest_x86_ptrace_syscall \ kselftest_x86_check_initial_reg_state \ diff --git a/android/patches/0019-userfaultfd.patch b/android/patches/0019-userfaultfd.patch new file mode 100644 index 000000000000..67fe8edc9848 --- /dev/null +++ b/android/patches/0019-userfaultfd.patch @@ -0,0 +1,261 @@ +--- b/tools/testing/selftests/vm/userfaultfd.c ++++ b/tools/testing/selftests/vm/userfaultfd.c +@@ -82,9 +82,11 @@ + static char *huge_fd_off0; + static unsigned long long *count_verify; + static int uffd, uffd_flags, finished, *pipefd; ++static volatile bool ready_for_fork; + static char *area_src, *area_src_alias, *area_dst, *area_dst_alias; + static char *zeropage; + pthread_attr_t attr; ++pthread_key_t long_jmp_key; + + /* pthread_mutex_t starts at page offset 0 */ + #define area_mutex(___area, ___nr) \ +@@ -139,8 +141,11 @@ + + static void anon_allocate_area(void **alloc_area) + { +- if (posix_memalign(alloc_area, page_size, nr_pages * page_size)) { +- fprintf(stderr, "out of memory\n"); ++ // We can't use posix_memalign due to pointer-tagging used in bionic. ++ *alloc_area = mmap(NULL, nr_pages * page_size, PROT_READ | PROT_WRITE, ++ MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); ++ if (*alloc_area == MAP_FAILED) { ++ fprintf(stderr, "anon memory mmap failed\n"); + *alloc_area = NULL; + } + } +@@ -284,23 +289,11 @@ + static void *locking_thread(void *arg) + { + unsigned long cpu = (unsigned long) arg; +- struct random_data rand; + unsigned long page_nr = *(&(page_nr)); /* uninitialized warning */ +- int32_t rand_nr; + unsigned long long count; +- char randstate[64]; +- unsigned int seed; + time_t start; + +- if (bounces & BOUNCE_RANDOM) { +- seed = (unsigned int) time(NULL) - bounces; +- if (!(bounces & BOUNCE_RACINGFAULTS)) +- seed += cpu; +- bzero(&rand, sizeof(rand)); +- bzero(&randstate, sizeof(randstate)); +- if (initstate_r(seed, randstate, sizeof(randstate), &rand)) +- fprintf(stderr, "srandom_r error\n"), exit(1); +- } else { ++ if (!(bounces & BOUNCE_RANDOM)) { + page_nr = -bounces; + if (!(bounces & BOUNCE_RACINGFAULTS)) + page_nr += cpu * nr_pages_per_cpu; +@@ -308,13 +301,9 @@ + + while (!finished) { + if (bounces & BOUNCE_RANDOM) { +- if (random_r(&rand, &rand_nr)) +- fprintf(stderr, "random_r 1 error\n"), exit(1); +- page_nr = rand_nr; +- if (sizeof(page_nr) > sizeof(rand_nr)) { +- if (random_r(&rand, &rand_nr)) +- fprintf(stderr, "random_r 2 error\n"), exit(1); +- page_nr |= (((unsigned long) rand_nr) << 16) << ++ page_nr = random(); ++ if (sizeof(page_nr) > sizeof(uint32_t)) { ++ page_nr |= (((unsigned long) random()) << 16) << + 16; + } + } else +@@ -501,6 +490,9 @@ + pollfd[1].fd = pipefd[cpu*2]; + pollfd[1].events = POLLIN; + ++ // Notify the main thread that it can now fork. ++ ready_for_fork = true; ++ + for (;;) { + ret = poll(pollfd, 2, -1); + if (!ret) +@@ -548,18 +540,31 @@ + + pthread_mutex_t uffd_read_mutex = PTHREAD_MUTEX_INITIALIZER; + ++static void sigusr1_handler(int signum, siginfo_t *siginfo, void *ptr) ++{ ++ jmp_buf *env; ++ env = pthread_getspecific(long_jmp_key); ++ longjmp(*env, 1); ++} ++ + static void *uffd_read_thread(void *arg) + { + unsigned long *this_cpu_userfaults; + struct uffd_msg msg; ++ jmp_buf env; ++ int setjmp_ret; ++ ++ pthread_setspecific(long_jmp_key, &env); + + this_cpu_userfaults = (unsigned long *) arg; + *this_cpu_userfaults = 0; + + pthread_mutex_unlock(&uffd_read_mutex); +- /* from here cancellation is ok */ + +- for (;;) { ++ // One first return setjmp return 0. On second (fake) return from ++ // longjmp() it returns the provided value, which will be 1 in our case. ++ setjmp_ret = setjmp(env); ++ while (!setjmp_ret) { + if (uffd_read_msg(uffd, &msg)) + continue; + (*this_cpu_userfaults) += uffd_handle_page_fault(&msg); +@@ -608,6 +613,7 @@ + background_thread, (void *)cpu)) + return 1; + } ++ + for (cpu = 0; cpu < nr_cpus; cpu++) + if (pthread_join(background_threads[cpu], NULL)) + return 1; +@@ -640,7 +646,7 @@ + if (pthread_join(uffd_threads[cpu], &_userfaults[cpu])) + return 1; + } else { +- if (pthread_cancel(uffd_threads[cpu])) ++ if (pthread_kill(uffd_threads[cpu], SIGUSR1)) + return 1; + if (pthread_join(uffd_threads[cpu], NULL)) + return 1; +@@ -654,7 +660,7 @@ + { + struct uffdio_api uffdio_api; + +- uffd = syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK); ++ uffd = syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK | UFFD_USER_MODE_ONLY); + if (uffd < 0) { + fprintf(stderr, + "userfaultfd syscall not available in this kernel\n"); +@@ -914,6 +920,10 @@ + pid_t pid; + char c; + ++ // All the syscalls below up to pthread_create will ensure that this ++ // write is completed before, the uffd_thread sets it to true. ++ ready_for_fork = false; ++ + printf("testing events (fork, remap, remove): "); + fflush(stdout); + +@@ -942,6 +952,11 @@ + if (pthread_create(&uffd_mon, &attr, uffd_poll_thread, NULL)) + perror("uffd_poll_thread create"), exit(1); + ++ // Wait for the poll_thread to start executing before forking. This is ++ // required to avoid a deadlock, which can happen if poll_thread doesn't ++ // start getting executed by the time fork is invoked. ++ while (!ready_for_fork); ++ + pid = fork(); + if (pid < 0) + perror("fork"), exit(1); +@@ -974,6 +989,10 @@ + pid_t pid; + char c; + ++ // All the syscalls below up to pthread_create will ensure that this ++ // write is completed before, the uffd_thread sets it to true. ++ ready_for_fork = false; ++ + printf("testing signal delivery: "); + fflush(stdout); + +@@ -1007,6 +1026,11 @@ + if (pthread_create(&uffd_mon, &attr, uffd_poll_thread, NULL)) + perror("uffd_poll_thread create"), exit(1); + ++ // Wait for the poll_thread to start executing before forking. This is ++ // required to avoid a deadlock, which can happen if poll_thread doesn't ++ // start getting executed by the time fork is invoked. ++ while (!ready_for_fork); ++ + pid = fork(); + if (pid < 0) + perror("fork"), exit(1); +@@ -1036,6 +1060,7 @@ + char *tmp_area; + unsigned long nr; + struct uffdio_register uffdio_register; ++ struct sigaction act; + unsigned long cpu; + int err; + unsigned long userfaults[nr_cpus]; +@@ -1094,6 +1119,17 @@ + pthread_attr_init(&attr); + pthread_attr_setstacksize(&attr, 16*1024*1024); + ++ // For handling thread termination of read thread in the absense of ++ // pthread_cancel(). ++ pthread_key_create(&long_jmp_key, NULL); ++ memset(&act, 0, sizeof(act)); ++ act.sa_sigaction = sigusr1_handler; ++ act.sa_flags = SA_SIGINFO; ++ if (sigaction(SIGUSR1, &act, 0)) { ++ perror("sigaction"); ++ return 1; ++ } ++ + err = 0; + while (bounces--) { + unsigned long expected_ioctls; +@@ -1215,6 +1251,8 @@ + printf("\n"); + } + ++ pthread_key_delete(long_jmp_key); ++ + if (err) + return err; + +@@ -1291,6 +1329,9 @@ + + int main(int argc, char **argv) + { ++ char randstate[64]; ++ unsigned int seed; ++ + if (argc < 4) + usage(); + +@@ -1332,6 +1373,12 @@ + } + printf("nr_pages: %lu, nr_pages_per_cpu: %lu\n", + nr_pages, nr_pages_per_cpu); ++ ++ seed = (unsigned int) time(NULL); ++ bzero(&randstate, sizeof(randstate)); ++ if (!initstate(seed, randstate, sizeof(randstate))) ++ fprintf(stderr, "srandom_r error\n"), exit(1); ++ + return userfaultfd_stress(); + } + +--- a/tools/testing/selftests/vm/userfaultfd.c ++++ b/tools/testing/selftests/vm/userfaultfd.c +@@ -662,8 +662,13 @@ static int userfaultfd_open(int features) + + uffd = syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK | UFFD_USER_MODE_ONLY); + if (uffd < 0) { ++ if (errno == ENOSYS) { ++ fprintf(stderr, ++ "userfaultfd syscall not available in this kernel\n"); ++ exit(KSFT_PASS); ++ } + fprintf(stderr, +- "userfaultfd syscall not available in this kernel\n"); ++ "userfaultfd syscall failed with errno: %d\n", errno); + return 1; + } + uffd_flags = fcntl(uffd, F_GETFD, NULL); diff --git a/android/patches/0020-selftests-binderfs-fix-Wunused-label-error.patch b/android/patches/0020-selftests-binderfs-fix-Wunused-label-error.patch new file mode 100644 index 000000000000..8b872f18e51f --- /dev/null +++ b/android/patches/0020-selftests-binderfs-fix-Wunused-label-error.patch @@ -0,0 +1,31 @@ +From c4970a0b5461b65ce3b7d1c40a76672bd18d3ba5 Mon Sep 17 00:00:00 2001 +From: Carlos Llamas <cmllamas@google.com> +Date: Mon, 26 Jul 2021 17:06:02 +0000 +Subject: [PATCH 1/2] selftests/binderfs: fix Wunused-label error + +Fix the following build issue: + tools/testing/selftests/filesystems/binderfs/binderfs_test.c:244:1: + error: unused label 'on_error' [-Werror,-Wunused-label] + on_error: + ^~~~~~~~~ + +Signed-off-by: Carlos Llamas <cmllamas@google.com> +--- + tools/testing/selftests/filesystems/binderfs/binderfs_test.c | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/tools/testing/selftests/filesystems/binderfs/binderfs_test.c b/tools/testing/selftests/filesystems/binderfs/binderfs_test.c +index 8c2ed962e1c7..540ad4840495 100644 +--- a/tools/testing/selftests/filesystems/binderfs/binderfs_test.c ++++ b/tools/testing/selftests/filesystems/binderfs/binderfs_test.c +@@ -241,7 +241,6 @@ static void __do_binderfs_test(void) + /* binder-control device removal failed as expected */ + ksft_inc_xfail_cnt(); + +-on_error: + ret = umount2("/dev/binderfs", MNT_DETACH); + keep ?: rmdir_protect_errno("/dev/binderfs"); + if (ret < 0) +-- +2.32.0.432.gabb21c7263-goog + diff --git a/android/patches/0021-selftests-binderfs-skip-unprivileged-test-when-USER_.patch b/android/patches/0021-selftests-binderfs-skip-unprivileged-test-when-USER_.patch new file mode 100644 index 000000000000..8a5af5794c01 --- /dev/null +++ b/android/patches/0021-selftests-binderfs-skip-unprivileged-test-when-USER_.patch @@ -0,0 +1,36 @@ +From e51806a0b6918cefbd068cbb8100f410e7b01970 Mon Sep 17 00:00:00 2001 +From: Carlos Llamas <cmllamas@google.com> +Date: Mon, 26 Jul 2021 17:12:42 +0000 +Subject: [PATCH 2/2] selftests/binderfs: skip unprivileged test when !USER_NS + +Binderfs unprivileged test requires CONFIG_USER_NS to be set. However, +this option is not required by Android platforms. Lets skip the test +as the underlying routine __do_binderfs_test() has already been checked +by the binderfs_test_privileged() anyway. + +Signed-off-by: Carlos Llamas <cmllamas@google.com> +--- + .../testing/selftests/filesystems/binderfs/binderfs_test.c | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/tools/testing/selftests/filesystems/binderfs/binderfs_test.c b/tools/testing/selftests/filesystems/binderfs/binderfs_test.c +index 540ad4840495..397bf265ba97 100644 +--- a/tools/testing/selftests/filesystems/binderfs/binderfs_test.c ++++ b/tools/testing/selftests/filesystems/binderfs/binderfs_test.c +@@ -262,6 +262,13 @@ static void binderfs_test_privileged() + + static void binderfs_test_unprivileged() + { ++ /* This test depends on USER_NS which is not required by Android */ ++ if (access("/proc/self/ns/user", F_OK) != 0) { ++ ksft_test_result_skip("%s: user namespace not supported\n", ++ __func__); ++ return; ++ } ++ + change_to_userns(); + __do_binderfs_test(); + } +-- +2.32.0.432.gabb21c7263-goog + diff --git a/android/patches/0023-ANDROID-kvm-Test-that-pVM-memory-is-wiped-during-tea.patch b/android/patches/0023-ANDROID-kvm-Test-that-pVM-memory-is-wiped-during-tea.patch new file mode 100644 index 000000000000..590ee72368dc --- /dev/null +++ b/android/patches/0023-ANDROID-kvm-Test-that-pVM-memory-is-wiped-during-tea.patch @@ -0,0 +1,246 @@ +From a9dc703806031c8535c84c9905979f86dfaec1d3 Mon Sep 17 00:00:00 2001 +From: Quentin Perret <qperret@google.com> +Date: Thu, 17 Feb 2022 19:34:27 +0000 +Subject: [PATCH] ANDROID: kvm: Test that pVM memory is wiped during teardown + +In protected KVM mode, we expect the hypervisor to protect guest secrets +when they are torn down. Add a test checking this property by running a +minimal guest, and checking that the content of memory has been wiped by +the hypervisor after teardown. + +Note: although some of the pKVM code has already landed upstream, the +functionality tested here hasn't at the time of writing. Once it does, +this test should be sent upstream for review to replace this ANDROID +patch. + +Bug: 218934075 +Signed-off-by: Quentin Perret <qperret@google.com> +Change-Id: I4a347908d189f2c4835fd576b26fae7c10ec9b22 +--- + Android.bp | 15 ++ + android/kselftest_test_list.mk | 1 + + .../selftests/kvm/aarch64/pvm_wipe_mem.c | 174 ++++++++++++++++++ + 3 files changed, 190 insertions(+) + create mode 100644 tools/testing/selftests/kvm/aarch64/pvm_wipe_mem.c + +diff --git a/Android.bp b/Android.bp +index 0b9166c56315..08805a161450 100644 +--- a/Android.bp ++++ b/Android.bp +@@ -340,6 +340,21 @@ cc_test { + defaults: ["kselftest_defaults"], + } + ++// KVM test ++cc_test { ++ name: "kselftest_kvm_arm64_tests", ++ relative_install_path: "linux-kselftest/kvm/aarch64", ++ local_include_dirs: [ "tools/testing/selftests"], ++ enabled: false, ++ arch: { ++ arm64: { ++ enabled: true, ++ srcs: ["tools/testing/selftests/kvm/aarch64/pvm_wipe_mem.c"], ++ }, ++ }, ++ defaults: ["kselftest_defaults"], ++} ++ + // Lib test + sh_test { + name: "kselftest_lib_printf", +diff --git a/android/kselftest_test_list.mk b/android/kselftest_test_list.mk +index 07df6fc5d758..46b9b92c58fb 100644 +--- a/android/kselftest_test_list.mk ++++ b/android/kselftest_test_list.mk +@@ -31,6 +31,7 @@ kselftest_modules += \ + kselftest_intel_pstate_tests_aperf \ + kselftest_intel_pstate_tests_msr \ + kselftest_kcmp_tests_kcmp_test \ ++ kselftest_kvm_arm64_tests_pvm_wipe_mem \ + kselftest_net_tests_psock_tpacket \ + kselftest_net_tests_socket \ + kselftest_net_tests_reuseaddr_conflict \ +diff --git a/tools/testing/selftests/kvm/aarch64/pvm_wipe_mem.c b/tools/testing/selftests/kvm/aarch64/pvm_wipe_mem.c +new file mode 100644 +index 000000000000..4af8ca3c4bad +--- /dev/null ++++ b/tools/testing/selftests/kvm/aarch64/pvm_wipe_mem.c +@@ -0,0 +1,174 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++/* ++ * Test checking that memory of protected guests is wiped after teardown. ++ * ++ * Copyright (C) 2022, Google LLC. ++ */ ++ ++#define _GNU_SOURCE ++ ++#include <err.h> ++#include <errno.h> ++#include <fcntl.h> ++#include <stdio.h> ++#include <stdint.h> ++#include <stdlib.h> ++#include <string.h> ++#include <unistd.h> ++ ++#include <linux/kvm.h> ++#include <sys/ioctl.h> ++#include <sys/mman.h> ++ ++#include "kselftest.h" ++ ++#define KVM_VM_TYPE_ARM_PROTECTED (1UL << 31) ++ ++#define REG_X(number) (0x6030000000100000ULL + (number) * 2UL) ++#define REG_PC 0x6030000000100040ULL ++ ++static void set_one_reg(int vcpufd, uint64_t reg_id, uint64_t val) ++{ ++ uint64_t reg_data; ++ struct kvm_one_reg reg; ++ int ret; ++ ++ reg.addr = (__u64) ®_data; ++ reg_data = val; ++ reg.id = reg_id; ++ ++ ret = ioctl(vcpufd, KVM_SET_ONE_REG, ®); ++ if (ret < 0) ++ ksft_exit_fail_msg("Failed to set reg: %d\n", ret); ++} ++ ++static int get_kvm(void) ++{ ++ size_t run_size; ++ int kvm, ret; ++ ++ kvm = open("/dev/kvm", O_RDWR | O_CLOEXEC); ++ if (kvm < 0) ++ ksft_exit_skip("KVM not supported\n"); ++ ++ ret = ioctl(kvm, KVM_GET_API_VERSION, NULL); ++ if (ret != 12) ++ ksft_exit_fail_msg("KVM_GET_API_VERSION %d, expected 12", ret); ++ ++ run_size = ioctl(kvm, KVM_GET_VCPU_MMAP_SIZE, NULL); ++ if (run_size < sizeof(struct kvm_run)) ++ ksft_exit_fail_msg("KVM_GET_VCPU_MMAP_SIZE unexpectedly small\n"); ++ ++ return kvm; ++} ++ ++static int create_protected_vm(int kvm) ++{ ++ int vmfd = ioctl(kvm, KVM_CREATE_VM, KVM_VM_TYPE_ARM_PROTECTED); ++ ++ if (vmfd < 0) ++ ksft_exit_skip("Protected guests not supported: %d\n", vmfd); ++ ++ return vmfd; ++} ++ ++static int create_vcpu(int vmfd, struct kvm_run **run) ++{ ++ struct kvm_vcpu_init vcpu_init; ++ int vcpufd, ret; ++ ++ ret = ioctl(vmfd, KVM_ARM_PREFERRED_TARGET, &vcpu_init); ++ if (ret) ++ ksft_exit_fail_msg("Failed to set kvm_vcpu_init %d\n", ret); ++ ++ vcpufd = ioctl(vmfd, KVM_CREATE_VCPU, (unsigned long)0); ++ if (vcpufd < 0) ++ ksft_exit_fail_msg("Failed to create VCPU: %d\n", vcpufd); ++ ++ *run = mmap(NULL, sizeof(**run), PROT_READ | PROT_WRITE, MAP_SHARED, vcpufd, 0); ++ if (!run) ++ ksft_exit_fail_msg("Failed to mmap vcpu_run struct\n"); ++ ++ ret = ioctl(vcpufd, KVM_ARM_VCPU_INIT, &vcpu_init); ++ if (ret) ++ ksft_exit_fail_msg("Failed to initialize VCPU %d\n", ret); ++ ++ return vcpufd; ++} ++ ++static void teardown(int kvm, int vmfd, int vcpufd, struct kvm_run *run) ++{ ++ int ret = munmap(run, sizeof(*run)); ++ ++ if (ret) ++ ksft_exit_fail_msg("Failed to unmap vCPU run: %d\n", ret); ++ ++ ret = close(vcpufd); ++ if (ret) ++ ksft_exit_fail_msg("Failed to destroy VCPU: %d\n", ret); ++ ++ ret = close(vmfd); ++ if (ret) ++ ksft_exit_fail_msg("Failed to destroy VM: %d\n", ret); ++ ++ ret = close(kvm); ++ if (ret) ++ ksft_exit_fail_msg("Failed to close KVM fd: %d\n", ret); ++} ++ ++int main(void) ++{ ++ struct kvm_userspace_memory_region region; ++ long page_size = sysconf(_SC_PAGESIZE); ++ int ret, kvm, vmfd, vcpufd; ++ uint32_t guest_code[2]; ++ struct kvm_run *run; ++ uint8_t *guest_mem; ++ size_t run_size; ++ ++ kvm = get_kvm(); ++ vmfd = create_protected_vm(kvm); ++ vcpufd = create_vcpu(vmfd, &run); ++ ++ /* Create a one-page memslot for the guest */ ++ guest_mem = mmap(NULL, page_size, PROT_READ | PROT_WRITE, ++ MAP_SHARED | MAP_ANONYMOUS, -1, 0); ++ if (guest_mem == MAP_FAILED) ++ ksft_exit_fail_msg("Failed to mmap guest memory\n"); ++ region = (struct kvm_userspace_memory_region) { ++ .slot = 0, ++ .guest_phys_addr = 1UL << 30, ++ .memory_size = page_size, ++ .userspace_addr = (uint64_t)guest_mem, ++ }; ++ ++ /* Copy some code in guest memory. */ ++ guest_code[0] = 0xf9400001; /* 1: ldr x1, [x0] */ ++ guest_code[1] = 0x17ffffff; /* b 1b */ ++ memcpy(guest_mem, guest_code, sizeof(guest_code)); ++ ret = ioctl(vmfd, KVM_SET_USER_MEMORY_REGION, ®ion); ++ if (ret) ++ ksft_exit_fail_msg("Failed to set memory region: %d\n", ret); ++ ++ /* ++ * Get the VCPU to run one instruction, to be sure the page containing ++ * the code has been faulted in. ++ */ ++ set_one_reg(vcpufd, REG_PC, region.guest_phys_addr); ++ set_one_reg(vcpufd, REG_X(0), region.guest_phys_addr + region.memory_size); ++ ret = ioctl(vcpufd, KVM_RUN, NULL); ++ if (ret) ++ ksft_exit_fail_msg("Failed to run vcpu: %d\n", ret); ++ if (run->exit_reason != KVM_EXIT_MMIO) ++ ksft_exit_fail_msg("Unexpected KVM exit reason: %u\n", run->exit_reason); ++ ++ /* ++ * Tear the guest down, and check that the donated memory has been ++ * wiped by the hypervisor. ++ */ ++ teardown(kvm, vmfd, vcpufd, run); ++ if (!memcmp(guest_mem, guest_code, sizeof(guest_code))) ++ ksft_exit_fail_msg("Protected guest memory has not been poisoned\n"); ++ ++ ksft_exit_pass(); ++} +-- +2.35.1.473.g83b2b277ed-goog + diff --git a/android/patches/0025-selftests-userfaultfd-skip-if-unavailable.patch b/android/patches/0025-selftests-userfaultfd-skip-if-unavailable.patch new file mode 100644 index 000000000000..0920fb307ed0 --- /dev/null +++ b/android/patches/0025-selftests-userfaultfd-skip-if-unavailable.patch @@ -0,0 +1,33 @@ +From 30c060a86f0e56c19e18ade48c0ee7592edfc317 Mon Sep 17 00:00:00 2001 +From: Edward Liaw <edliaw@google.com> +Date: Thu, 24 Mar 2022 00:29:56 +0000 +Subject: [PATCH] selftests: userfaultfd skip if unavailable + +Skip userfaultfd selftest if the syscall is not available for this +kernel. + +Bug: 207175332 +Bug: 206503000 +Test: atest vts_linux_kselftest_arm_64:vm_userfaultfd_shmem_arm_64 +Change-Id: I60e4d6cc5b3886048ffa1995e4b090f6fbe26b3d +Signed-off-by: Edward Liaw <edliaw@google.com> +--- + tools/testing/selftests/vm/userfaultfd.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/tools/testing/selftests/vm/userfaultfd.c b/tools/testing/selftests/vm/userfaultfd.c +index ffc78f3..17947fe 100644 +--- a/tools/testing/selftests/vm/userfaultfd.c ++++ b/tools/testing/selftests/vm/userfaultfd.c +@@ -665,7 +665,7 @@ static int userfaultfd_open(int features) + if (errno == ENOSYS) { + fprintf(stderr, + "userfaultfd syscall not available in this kernel\n"); +- exit(KSFT_PASS); ++ exit(KSFT_SKIP); + } + fprintf(stderr, + "userfaultfd syscall failed with errno: %d\n", errno); +-- +2.35.1.1021.g381101b075-goog + diff --git a/tools/testing/selftests/filesystems/binderfs/binderfs_test.c b/tools/testing/selftests/filesystems/binderfs/binderfs_test.c index 8c2ed962e1c7..397bf265ba97 100644 --- a/tools/testing/selftests/filesystems/binderfs/binderfs_test.c +++ b/tools/testing/selftests/filesystems/binderfs/binderfs_test.c @@ -241,7 +241,6 @@ static void __do_binderfs_test(void) /* binder-control device removal failed as expected */ ksft_inc_xfail_cnt(); -on_error: ret = umount2("/dev/binderfs", MNT_DETACH); keep ?: rmdir_protect_errno("/dev/binderfs"); if (ret < 0) @@ -263,6 +262,13 @@ static void binderfs_test_privileged() static void binderfs_test_unprivileged() { + /* This test depends on USER_NS which is not required by Android */ + if (access("/proc/self/ns/user", F_OK) != 0) { + ksft_test_result_skip("%s: user namespace not supported\n", + __func__); + return; + } + change_to_userns(); __do_binderfs_test(); } diff --git a/tools/testing/selftests/kvm/aarch64/pvm_wipe_mem.c b/tools/testing/selftests/kvm/aarch64/pvm_wipe_mem.c new file mode 100644 index 000000000000..4af8ca3c4bad --- /dev/null +++ b/tools/testing/selftests/kvm/aarch64/pvm_wipe_mem.c @@ -0,0 +1,174 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Test checking that memory of protected guests is wiped after teardown. + * + * Copyright (C) 2022, Google LLC. + */ + +#define _GNU_SOURCE + +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <linux/kvm.h> +#include <sys/ioctl.h> +#include <sys/mman.h> + +#include "kselftest.h" + +#define KVM_VM_TYPE_ARM_PROTECTED (1UL << 31) + +#define REG_X(number) (0x6030000000100000ULL + (number) * 2UL) +#define REG_PC 0x6030000000100040ULL + +static void set_one_reg(int vcpufd, uint64_t reg_id, uint64_t val) +{ + uint64_t reg_data; + struct kvm_one_reg reg; + int ret; + + reg.addr = (__u64) ®_data; + reg_data = val; + reg.id = reg_id; + + ret = ioctl(vcpufd, KVM_SET_ONE_REG, ®); + if (ret < 0) + ksft_exit_fail_msg("Failed to set reg: %d\n", ret); +} + +static int get_kvm(void) +{ + size_t run_size; + int kvm, ret; + + kvm = open("/dev/kvm", O_RDWR | O_CLOEXEC); + if (kvm < 0) + ksft_exit_skip("KVM not supported\n"); + + ret = ioctl(kvm, KVM_GET_API_VERSION, NULL); + if (ret != 12) + ksft_exit_fail_msg("KVM_GET_API_VERSION %d, expected 12", ret); + + run_size = ioctl(kvm, KVM_GET_VCPU_MMAP_SIZE, NULL); + if (run_size < sizeof(struct kvm_run)) + ksft_exit_fail_msg("KVM_GET_VCPU_MMAP_SIZE unexpectedly small\n"); + + return kvm; +} + +static int create_protected_vm(int kvm) +{ + int vmfd = ioctl(kvm, KVM_CREATE_VM, KVM_VM_TYPE_ARM_PROTECTED); + + if (vmfd < 0) + ksft_exit_skip("Protected guests not supported: %d\n", vmfd); + + return vmfd; +} + +static int create_vcpu(int vmfd, struct kvm_run **run) +{ + struct kvm_vcpu_init vcpu_init; + int vcpufd, ret; + + ret = ioctl(vmfd, KVM_ARM_PREFERRED_TARGET, &vcpu_init); + if (ret) + ksft_exit_fail_msg("Failed to set kvm_vcpu_init %d\n", ret); + + vcpufd = ioctl(vmfd, KVM_CREATE_VCPU, (unsigned long)0); + if (vcpufd < 0) + ksft_exit_fail_msg("Failed to create VCPU: %d\n", vcpufd); + + *run = mmap(NULL, sizeof(**run), PROT_READ | PROT_WRITE, MAP_SHARED, vcpufd, 0); + if (!run) + ksft_exit_fail_msg("Failed to mmap vcpu_run struct\n"); + + ret = ioctl(vcpufd, KVM_ARM_VCPU_INIT, &vcpu_init); + if (ret) + ksft_exit_fail_msg("Failed to initialize VCPU %d\n", ret); + + return vcpufd; +} + +static void teardown(int kvm, int vmfd, int vcpufd, struct kvm_run *run) +{ + int ret = munmap(run, sizeof(*run)); + + if (ret) + ksft_exit_fail_msg("Failed to unmap vCPU run: %d\n", ret); + + ret = close(vcpufd); + if (ret) + ksft_exit_fail_msg("Failed to destroy VCPU: %d\n", ret); + + ret = close(vmfd); + if (ret) + ksft_exit_fail_msg("Failed to destroy VM: %d\n", ret); + + ret = close(kvm); + if (ret) + ksft_exit_fail_msg("Failed to close KVM fd: %d\n", ret); +} + +int main(void) +{ + struct kvm_userspace_memory_region region; + long page_size = sysconf(_SC_PAGESIZE); + int ret, kvm, vmfd, vcpufd; + uint32_t guest_code[2]; + struct kvm_run *run; + uint8_t *guest_mem; + size_t run_size; + + kvm = get_kvm(); + vmfd = create_protected_vm(kvm); + vcpufd = create_vcpu(vmfd, &run); + + /* Create a one-page memslot for the guest */ + guest_mem = mmap(NULL, page_size, PROT_READ | PROT_WRITE, + MAP_SHARED | MAP_ANONYMOUS, -1, 0); + if (guest_mem == MAP_FAILED) + ksft_exit_fail_msg("Failed to mmap guest memory\n"); + region = (struct kvm_userspace_memory_region) { + .slot = 0, + .guest_phys_addr = 1UL << 30, + .memory_size = page_size, + .userspace_addr = (uint64_t)guest_mem, + }; + + /* Copy some code in guest memory. */ + guest_code[0] = 0xf9400001; /* 1: ldr x1, [x0] */ + guest_code[1] = 0x17ffffff; /* b 1b */ + memcpy(guest_mem, guest_code, sizeof(guest_code)); + ret = ioctl(vmfd, KVM_SET_USER_MEMORY_REGION, ®ion); + if (ret) + ksft_exit_fail_msg("Failed to set memory region: %d\n", ret); + + /* + * Get the VCPU to run one instruction, to be sure the page containing + * the code has been faulted in. + */ + set_one_reg(vcpufd, REG_PC, region.guest_phys_addr); + set_one_reg(vcpufd, REG_X(0), region.guest_phys_addr + region.memory_size); + ret = ioctl(vcpufd, KVM_RUN, NULL); + if (ret) + ksft_exit_fail_msg("Failed to run vcpu: %d\n", ret); + if (run->exit_reason != KVM_EXIT_MMIO) + ksft_exit_fail_msg("Unexpected KVM exit reason: %u\n", run->exit_reason); + + /* + * Tear the guest down, and check that the donated memory has been + * wiped by the hypervisor. + */ + teardown(kvm, vmfd, vcpufd, run); + if (!memcmp(guest_mem, guest_code, sizeof(guest_code))) + ksft_exit_fail_msg("Protected guest memory has not been poisoned\n"); + + ksft_exit_pass(); +} diff --git a/tools/testing/selftests/vm/userfaultfd.c b/tools/testing/selftests/vm/userfaultfd.c index d3362777a425..17947feb6d8e 100644 --- a/tools/testing/selftests/vm/userfaultfd.c +++ b/tools/testing/selftests/vm/userfaultfd.c @@ -82,9 +82,11 @@ static int huge_fd; static char *huge_fd_off0; static unsigned long long *count_verify; static int uffd, uffd_flags, finished, *pipefd; +static volatile bool ready_for_fork; static char *area_src, *area_src_alias, *area_dst, *area_dst_alias; static char *zeropage; pthread_attr_t attr; +pthread_key_t long_jmp_key; /* pthread_mutex_t starts at page offset 0 */ #define area_mutex(___area, ___nr) \ @@ -139,8 +141,11 @@ static int anon_release_pages(char *rel_area) static void anon_allocate_area(void **alloc_area) { - if (posix_memalign(alloc_area, page_size, nr_pages * page_size)) { - fprintf(stderr, "out of memory\n"); + // We can't use posix_memalign due to pointer-tagging used in bionic. + *alloc_area = mmap(NULL, nr_pages * page_size, PROT_READ | PROT_WRITE, + MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); + if (*alloc_area == MAP_FAILED) { + fprintf(stderr, "anon memory mmap failed\n"); *alloc_area = NULL; } } @@ -284,23 +289,11 @@ static int my_bcmp(char *str1, char *str2, size_t n) static void *locking_thread(void *arg) { unsigned long cpu = (unsigned long) arg; - struct random_data rand; unsigned long page_nr = *(&(page_nr)); /* uninitialized warning */ - int32_t rand_nr; unsigned long long count; - char randstate[64]; - unsigned int seed; time_t start; - if (bounces & BOUNCE_RANDOM) { - seed = (unsigned int) time(NULL) - bounces; - if (!(bounces & BOUNCE_RACINGFAULTS)) - seed += cpu; - bzero(&rand, sizeof(rand)); - bzero(&randstate, sizeof(randstate)); - if (initstate_r(seed, randstate, sizeof(randstate), &rand)) - fprintf(stderr, "srandom_r error\n"), exit(1); - } else { + if (!(bounces & BOUNCE_RANDOM)) { page_nr = -bounces; if (!(bounces & BOUNCE_RACINGFAULTS)) page_nr += cpu * nr_pages_per_cpu; @@ -308,13 +301,9 @@ static void *locking_thread(void *arg) while (!finished) { if (bounces & BOUNCE_RANDOM) { - if (random_r(&rand, &rand_nr)) - fprintf(stderr, "random_r 1 error\n"), exit(1); - page_nr = rand_nr; - if (sizeof(page_nr) > sizeof(rand_nr)) { - if (random_r(&rand, &rand_nr)) - fprintf(stderr, "random_r 2 error\n"), exit(1); - page_nr |= (((unsigned long) rand_nr) << 16) << + page_nr = random(); + if (sizeof(page_nr) > sizeof(uint32_t)) { + page_nr |= (((unsigned long) random()) << 16) << 16; } } else @@ -501,6 +490,9 @@ static void *uffd_poll_thread(void *arg) pollfd[1].fd = pipefd[cpu*2]; pollfd[1].events = POLLIN; + // Notify the main thread that it can now fork. + ready_for_fork = true; + for (;;) { ret = poll(pollfd, 2, -1); if (!ret) @@ -548,18 +540,31 @@ static void *uffd_poll_thread(void *arg) pthread_mutex_t uffd_read_mutex = PTHREAD_MUTEX_INITIALIZER; +static void sigusr1_handler(int signum, siginfo_t *siginfo, void *ptr) +{ + jmp_buf *env; + env = pthread_getspecific(long_jmp_key); + longjmp(*env, 1); +} + static void *uffd_read_thread(void *arg) { unsigned long *this_cpu_userfaults; struct uffd_msg msg; + jmp_buf env; + int setjmp_ret; + + pthread_setspecific(long_jmp_key, &env); this_cpu_userfaults = (unsigned long *) arg; *this_cpu_userfaults = 0; pthread_mutex_unlock(&uffd_read_mutex); - /* from here cancellation is ok */ - for (;;) { + // One first return setjmp return 0. On second (fake) return from + // longjmp() it returns the provided value, which will be 1 in our case. + setjmp_ret = setjmp(env); + while (!setjmp_ret) { if (uffd_read_msg(uffd, &msg)) continue; (*this_cpu_userfaults) += uffd_handle_page_fault(&msg); @@ -608,6 +613,7 @@ static int stress(unsigned long *userfaults) background_thread, (void *)cpu)) return 1; } + for (cpu = 0; cpu < nr_cpus; cpu++) if (pthread_join(background_threads[cpu], NULL)) return 1; @@ -640,7 +646,7 @@ static int stress(unsigned long *userfaults) if (pthread_join(uffd_threads[cpu], &_userfaults[cpu])) return 1; } else { - if (pthread_cancel(uffd_threads[cpu])) + if (pthread_kill(uffd_threads[cpu], SIGUSR1)) return 1; if (pthread_join(uffd_threads[cpu], NULL)) return 1; @@ -654,10 +660,15 @@ static int userfaultfd_open(int features) { struct uffdio_api uffdio_api; - uffd = syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK); + uffd = syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK | UFFD_USER_MODE_ONLY); if (uffd < 0) { + if (errno == ENOSYS) { + fprintf(stderr, + "userfaultfd syscall not available in this kernel\n"); + exit(KSFT_SKIP); + } fprintf(stderr, - "userfaultfd syscall not available in this kernel\n"); + "userfaultfd syscall failed with errno: %d\n", errno); return 1; } uffd_flags = fcntl(uffd, F_GETFD, NULL); @@ -914,6 +925,10 @@ static int userfaultfd_events_test(void) pid_t pid; char c; + // All the syscalls below up to pthread_create will ensure that this + // write is completed before, the uffd_thread sets it to true. + ready_for_fork = false; + printf("testing events (fork, remap, remove): "); fflush(stdout); @@ -942,6 +957,11 @@ static int userfaultfd_events_test(void) if (pthread_create(&uffd_mon, &attr, uffd_poll_thread, NULL)) perror("uffd_poll_thread create"), exit(1); + // Wait for the poll_thread to start executing before forking. This is + // required to avoid a deadlock, which can happen if poll_thread doesn't + // start getting executed by the time fork is invoked. + while (!ready_for_fork); + pid = fork(); if (pid < 0) perror("fork"), exit(1); @@ -974,6 +994,10 @@ static int userfaultfd_sig_test(void) pid_t pid; char c; + // All the syscalls below up to pthread_create will ensure that this + // write is completed before, the uffd_thread sets it to true. + ready_for_fork = false; + printf("testing signal delivery: "); fflush(stdout); @@ -1007,6 +1031,11 @@ static int userfaultfd_sig_test(void) if (pthread_create(&uffd_mon, &attr, uffd_poll_thread, NULL)) perror("uffd_poll_thread create"), exit(1); + // Wait for the poll_thread to start executing before forking. This is + // required to avoid a deadlock, which can happen if poll_thread doesn't + // start getting executed by the time fork is invoked. + while (!ready_for_fork); + pid = fork(); if (pid < 0) perror("fork"), exit(1); @@ -1036,6 +1065,7 @@ static int userfaultfd_stress(void) char *tmp_area; unsigned long nr; struct uffdio_register uffdio_register; + struct sigaction act; unsigned long cpu; int err; unsigned long userfaults[nr_cpus]; @@ -1094,6 +1124,17 @@ static int userfaultfd_stress(void) pthread_attr_init(&attr); pthread_attr_setstacksize(&attr, 16*1024*1024); + // For handling thread termination of read thread in the absense of + // pthread_cancel(). + pthread_key_create(&long_jmp_key, NULL); + memset(&act, 0, sizeof(act)); + act.sa_sigaction = sigusr1_handler; + act.sa_flags = SA_SIGINFO; + if (sigaction(SIGUSR1, &act, 0)) { + perror("sigaction"); + return 1; + } + err = 0; while (bounces--) { unsigned long expected_ioctls; @@ -1215,6 +1256,8 @@ static int userfaultfd_stress(void) printf("\n"); } + pthread_key_delete(long_jmp_key); + if (err) return err; @@ -1291,6 +1334,9 @@ static void sigalrm(int sig) int main(int argc, char **argv) { + char randstate[64]; + unsigned int seed; + if (argc < 4) usage(); @@ -1332,6 +1378,12 @@ int main(int argc, char **argv) } printf("nr_pages: %lu, nr_pages_per_cpu: %lu\n", nr_pages, nr_pages_per_cpu); + + seed = (unsigned int) time(NULL); + bzero(&randstate, sizeof(randstate)); + if (!initstate(seed, randstate, sizeof(randstate))) + fprintf(stderr, "srandom_r error\n"), exit(1); + return userfaultfd_stress(); } |