Path: blob/master/tools/testing/selftests/iommu/iommufd_fail_nth.c
26302 views
// SPDX-License-Identifier: GPL-2.0-only1/* Copyright (c) 2021-2022, NVIDIA CORPORATION & AFFILIATES2*3* These tests are "kernel integrity" tests. They are looking for kernel4* WARN/OOPS/kasn/etc splats triggered by kernel sanitizers & debugging5* features. It does not attempt to verify that the system calls are doing what6* they are supposed to do.7*8* The basic philosophy is to run a sequence of calls that will succeed and then9* sweep every failure injection point on that call chain to look for10* interesting things in error handling.11*12* This test is best run with:13* echo 1 > /proc/sys/kernel/panic_on_warn14* If something is actually going wrong.15*/16#include <fcntl.h>17#include <dirent.h>1819#define __EXPORTED_HEADERS__20#include <linux/vfio.h>2122#include "iommufd_utils.h"2324static bool have_fault_injection;2526static int writeat(int dfd, const char *fn, const char *val)27{28size_t val_len = strlen(val);29ssize_t res;30int fd;3132fd = openat(dfd, fn, O_WRONLY);33if (fd == -1)34return -1;35res = write(fd, val, val_len);36assert(res == val_len);37close(fd);38return 0;39}4041static __attribute__((constructor)) void setup_buffer(void)42{43PAGE_SIZE = sysconf(_SC_PAGE_SIZE);4445BUFFER_SIZE = 2*1024*1024;4647buffer = mmap(0, BUFFER_SIZE, PROT_READ | PROT_WRITE,48MAP_SHARED | MAP_ANONYMOUS, -1, 0);4950mfd_buffer = memfd_mmap(BUFFER_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED,51&mfd);52}5354/*55* This sets up fail_injection in a way that is useful for this test.56* It does not attempt to restore things back to how they were.57*/58static __attribute__((constructor)) void setup_fault_injection(void)59{60DIR *debugfs = opendir("/sys/kernel/debug/");61struct dirent *dent;6263if (!debugfs)64return;6566/* Allow any allocation call to be fault injected */67if (writeat(dirfd(debugfs), "failslab/ignore-gfp-wait", "N"))68return;69writeat(dirfd(debugfs), "fail_page_alloc/ignore-gfp-wait", "N");70writeat(dirfd(debugfs), "fail_page_alloc/ignore-gfp-highmem", "N");7172while ((dent = readdir(debugfs))) {73char fn[300];7475if (strncmp(dent->d_name, "fail", 4) != 0)76continue;7778/* We are looking for kernel splats, quiet down the log */79snprintf(fn, sizeof(fn), "%s/verbose", dent->d_name);80writeat(dirfd(debugfs), fn, "0");81}82closedir(debugfs);83have_fault_injection = true;84}8586struct fail_nth_state {87int proc_fd;88unsigned int iteration;89};9091static void fail_nth_first(struct __test_metadata *_metadata,92struct fail_nth_state *nth_state)93{94char buf[300];9596snprintf(buf, sizeof(buf), "/proc/self/task/%u/fail-nth", getpid());97nth_state->proc_fd = open(buf, O_RDWR);98ASSERT_NE(-1, nth_state->proc_fd);99}100101static bool fail_nth_next(struct __test_metadata *_metadata,102struct fail_nth_state *nth_state,103int test_result)104{105static const char disable_nth[] = "0";106char buf[300];107108/*109* This is just an arbitrary limit based on the current kernel110* situation. Changes in the kernel can dramatically change the number of111* required fault injection sites, so if this hits it doesn't112* necessarily mean a test failure, just that the limit has to be made113* bigger.114*/115ASSERT_GT(400, nth_state->iteration);116if (nth_state->iteration != 0) {117ssize_t res;118ssize_t res2;119120buf[0] = 0;121/*122* Annoyingly disabling the nth can also fail. This means123* the test passed without triggering failure124*/125res = pread(nth_state->proc_fd, buf, sizeof(buf), 0);126if (res == -1 && errno == EFAULT) {127buf[0] = '1';128buf[1] = '\n';129res = 2;130}131132res2 = pwrite(nth_state->proc_fd, disable_nth,133ARRAY_SIZE(disable_nth) - 1, 0);134if (res2 == -1 && errno == EFAULT) {135res2 = pwrite(nth_state->proc_fd, disable_nth,136ARRAY_SIZE(disable_nth) - 1, 0);137buf[0] = '1';138buf[1] = '\n';139}140ASSERT_EQ(ARRAY_SIZE(disable_nth) - 1, res2);141142/* printf(" nth %u result=%d nth=%u\n", nth_state->iteration,143test_result, atoi(buf)); */144fflush(stdout);145ASSERT_LT(1, res);146if (res != 2 || buf[0] != '0' || buf[1] != '\n')147return false;148} else {149/* printf(" nth %u result=%d\n", nth_state->iteration,150test_result); */151}152nth_state->iteration++;153return true;154}155156/*157* This is called during the test to start failure injection. It allows the test158* to do some setup that has already been swept and thus reduce the required159* iterations.160*/161void __fail_nth_enable(struct __test_metadata *_metadata,162struct fail_nth_state *nth_state)163{164char buf[300];165size_t len;166167if (!nth_state->iteration)168return;169170len = snprintf(buf, sizeof(buf), "%u", nth_state->iteration);171ASSERT_EQ(len, pwrite(nth_state->proc_fd, buf, len, 0));172}173#define fail_nth_enable() __fail_nth_enable(_metadata, _nth_state)174175#define TEST_FAIL_NTH(fixture_name, name) \176static int test_nth_##name(struct __test_metadata *_metadata, \177FIXTURE_DATA(fixture_name) *self, \178const FIXTURE_VARIANT(fixture_name) \179*variant, \180struct fail_nth_state *_nth_state); \181TEST_F(fixture_name, name) \182{ \183struct fail_nth_state nth_state = {}; \184int test_result = 0; \185\186if (!have_fault_injection) \187SKIP(return, \188"fault injection is not enabled in the kernel"); \189fail_nth_first(_metadata, &nth_state); \190ASSERT_EQ(0, test_nth_##name(_metadata, self, variant, \191&nth_state)); \192while (fail_nth_next(_metadata, &nth_state, test_result)) { \193fixture_name##_teardown(_metadata, self, variant); \194fixture_name##_setup(_metadata, self, variant); \195test_result = test_nth_##name(_metadata, self, \196variant, &nth_state); \197}; \198ASSERT_EQ(0, test_result); \199} \200static int test_nth_##name( \201struct __test_metadata __attribute__((unused)) *_metadata, \202FIXTURE_DATA(fixture_name) __attribute__((unused)) *self, \203const FIXTURE_VARIANT(fixture_name) __attribute__((unused)) \204*variant, \205struct fail_nth_state *_nth_state)206207FIXTURE(basic_fail_nth)208{209int fd;210uint32_t access_id;211uint32_t stdev_id;212uint32_t pasid;213};214215FIXTURE_SETUP(basic_fail_nth)216{217self->fd = -1;218self->access_id = 0;219self->stdev_id = 0;220self->pasid = 0; //test should use a non-zero value221}222223FIXTURE_TEARDOWN(basic_fail_nth)224{225int rc;226227if (self->access_id) {228/* The access FD holds the iommufd open until it closes */229rc = _test_cmd_destroy_access(self->access_id);230assert(rc == 0);231}232if (self->pasid && self->stdev_id)233_test_cmd_pasid_detach(self->fd, self->stdev_id, self->pasid);234teardown_iommufd(self->fd, _metadata);235}236237/* Cover ioas.c */238TEST_FAIL_NTH(basic_fail_nth, basic)239{240struct iommu_iova_range ranges[10];241uint32_t ioas_id;242__u64 iova;243244fail_nth_enable();245246self->fd = open("/dev/iommu", O_RDWR);247if (self->fd == -1)248return -1;249250if (_test_ioctl_ioas_alloc(self->fd, &ioas_id))251return -1;252253{254struct iommu_ioas_iova_ranges ranges_cmd = {255.size = sizeof(ranges_cmd),256.num_iovas = ARRAY_SIZE(ranges),257.ioas_id = ioas_id,258.allowed_iovas = (uintptr_t)ranges,259};260if (ioctl(self->fd, IOMMU_IOAS_IOVA_RANGES, &ranges_cmd))261return -1;262}263264{265struct iommu_ioas_allow_iovas allow_cmd = {266.size = sizeof(allow_cmd),267.ioas_id = ioas_id,268.num_iovas = 1,269.allowed_iovas = (uintptr_t)ranges,270};271272ranges[0].start = 16*1024;273ranges[0].last = BUFFER_SIZE + 16 * 1024 * 600 - 1;274if (ioctl(self->fd, IOMMU_IOAS_ALLOW_IOVAS, &allow_cmd))275return -1;276}277278if (_test_ioctl_ioas_map(self->fd, ioas_id, buffer, BUFFER_SIZE, &iova,279IOMMU_IOAS_MAP_WRITEABLE |280IOMMU_IOAS_MAP_READABLE))281return -1;282283{284struct iommu_ioas_copy copy_cmd = {285.size = sizeof(copy_cmd),286.flags = IOMMU_IOAS_MAP_WRITEABLE |287IOMMU_IOAS_MAP_READABLE,288.dst_ioas_id = ioas_id,289.src_ioas_id = ioas_id,290.src_iova = iova,291.length = sizeof(ranges),292};293294if (ioctl(self->fd, IOMMU_IOAS_COPY, ©_cmd))295return -1;296}297298if (_test_ioctl_ioas_unmap(self->fd, ioas_id, iova, BUFFER_SIZE,299NULL))300return -1;301/* Failure path of no IOVA to unmap */302_test_ioctl_ioas_unmap(self->fd, ioas_id, iova, BUFFER_SIZE, NULL);303return 0;304}305306/* iopt_area_fill_domains() and iopt_area_fill_domain() */307TEST_FAIL_NTH(basic_fail_nth, map_domain)308{309uint32_t ioas_id;310__u32 stdev_id;311__u32 hwpt_id;312__u64 iova;313314self->fd = open("/dev/iommu", O_RDWR);315if (self->fd == -1)316return -1;317318if (_test_ioctl_ioas_alloc(self->fd, &ioas_id))319return -1;320321if (_test_ioctl_set_temp_memory_limit(self->fd, 32))322return -1;323324fail_nth_enable();325326if (_test_cmd_mock_domain(self->fd, ioas_id, &stdev_id, &hwpt_id, NULL))327return -1;328329if (_test_ioctl_ioas_map(self->fd, ioas_id, buffer, 262144, &iova,330IOMMU_IOAS_MAP_WRITEABLE |331IOMMU_IOAS_MAP_READABLE))332return -1;333334if (_test_ioctl_destroy(self->fd, stdev_id))335return -1;336337if (_test_cmd_mock_domain(self->fd, ioas_id, &stdev_id, &hwpt_id, NULL))338return -1;339return 0;340}341342/* iopt_area_fill_domains() and iopt_area_fill_domain() */343TEST_FAIL_NTH(basic_fail_nth, map_file_domain)344{345uint32_t ioas_id;346__u32 stdev_id;347__u32 hwpt_id;348__u64 iova;349350self->fd = open("/dev/iommu", O_RDWR);351if (self->fd == -1)352return -1;353354if (_test_ioctl_ioas_alloc(self->fd, &ioas_id))355return -1;356357if (_test_ioctl_set_temp_memory_limit(self->fd, 32))358return -1;359360fail_nth_enable();361362if (_test_cmd_mock_domain(self->fd, ioas_id, &stdev_id, &hwpt_id, NULL))363return -1;364365if (_test_ioctl_ioas_map_file(self->fd, ioas_id, mfd, 0, 262144, &iova,366IOMMU_IOAS_MAP_WRITEABLE |367IOMMU_IOAS_MAP_READABLE))368return -1;369370if (_test_ioctl_destroy(self->fd, stdev_id))371return -1;372373if (_test_cmd_mock_domain(self->fd, ioas_id, &stdev_id, &hwpt_id, NULL))374return -1;375return 0;376}377378TEST_FAIL_NTH(basic_fail_nth, map_two_domains)379{380uint32_t ioas_id;381__u32 stdev_id2;382__u32 stdev_id;383__u32 hwpt_id2;384__u32 hwpt_id;385__u64 iova;386387self->fd = open("/dev/iommu", O_RDWR);388if (self->fd == -1)389return -1;390391if (_test_ioctl_ioas_alloc(self->fd, &ioas_id))392return -1;393394if (_test_ioctl_set_temp_memory_limit(self->fd, 32))395return -1;396397if (_test_cmd_mock_domain(self->fd, ioas_id, &stdev_id, &hwpt_id, NULL))398return -1;399400fail_nth_enable();401402if (_test_cmd_mock_domain(self->fd, ioas_id, &stdev_id2, &hwpt_id2,403NULL))404return -1;405406if (_test_ioctl_ioas_map(self->fd, ioas_id, buffer, 262144, &iova,407IOMMU_IOAS_MAP_WRITEABLE |408IOMMU_IOAS_MAP_READABLE))409return -1;410411if (_test_ioctl_destroy(self->fd, stdev_id))412return -1;413414if (_test_ioctl_destroy(self->fd, stdev_id2))415return -1;416417if (_test_cmd_mock_domain(self->fd, ioas_id, &stdev_id, &hwpt_id, NULL))418return -1;419if (_test_cmd_mock_domain(self->fd, ioas_id, &stdev_id2, &hwpt_id2,420NULL))421return -1;422return 0;423}424425TEST_FAIL_NTH(basic_fail_nth, access_rw)426{427uint64_t tmp_big[4096];428uint32_t ioas_id;429uint16_t tmp[32];430__u64 iova;431432self->fd = open("/dev/iommu", O_RDWR);433if (self->fd == -1)434return -1;435436if (_test_ioctl_ioas_alloc(self->fd, &ioas_id))437return -1;438439if (_test_ioctl_set_temp_memory_limit(self->fd, 32))440return -1;441442if (_test_ioctl_ioas_map(self->fd, ioas_id, buffer, 262144, &iova,443IOMMU_IOAS_MAP_WRITEABLE |444IOMMU_IOAS_MAP_READABLE))445return -1;446447fail_nth_enable();448449if (_test_cmd_create_access(self->fd, ioas_id, &self->access_id, 0))450return -1;451452{453struct iommu_test_cmd access_cmd = {454.size = sizeof(access_cmd),455.op = IOMMU_TEST_OP_ACCESS_RW,456.id = self->access_id,457.access_rw = { .iova = iova,458.length = sizeof(tmp),459.uptr = (uintptr_t)tmp },460};461462// READ463if (ioctl(self->fd, _IOMMU_TEST_CMD(IOMMU_TEST_OP_ACCESS_RW),464&access_cmd))465return -1;466467access_cmd.access_rw.flags = MOCK_ACCESS_RW_WRITE;468if (ioctl(self->fd, _IOMMU_TEST_CMD(IOMMU_TEST_OP_ACCESS_RW),469&access_cmd))470return -1;471472access_cmd.access_rw.flags = MOCK_ACCESS_RW_SLOW_PATH;473if (ioctl(self->fd, _IOMMU_TEST_CMD(IOMMU_TEST_OP_ACCESS_RW),474&access_cmd))475return -1;476access_cmd.access_rw.flags = MOCK_ACCESS_RW_SLOW_PATH |477MOCK_ACCESS_RW_WRITE;478if (ioctl(self->fd, _IOMMU_TEST_CMD(IOMMU_TEST_OP_ACCESS_RW),479&access_cmd))480return -1;481}482483{484struct iommu_test_cmd access_cmd = {485.size = sizeof(access_cmd),486.op = IOMMU_TEST_OP_ACCESS_RW,487.id = self->access_id,488.access_rw = { .iova = iova,489.flags = MOCK_ACCESS_RW_SLOW_PATH,490.length = sizeof(tmp_big),491.uptr = (uintptr_t)tmp_big },492};493494if (ioctl(self->fd, _IOMMU_TEST_CMD(IOMMU_TEST_OP_ACCESS_RW),495&access_cmd))496return -1;497}498if (_test_cmd_destroy_access(self->access_id))499return -1;500self->access_id = 0;501return 0;502}503504/* pages.c access functions */505TEST_FAIL_NTH(basic_fail_nth, access_pin)506{507uint32_t access_pages_id;508uint32_t ioas_id;509__u64 iova;510511self->fd = open("/dev/iommu", O_RDWR);512if (self->fd == -1)513return -1;514515if (_test_ioctl_ioas_alloc(self->fd, &ioas_id))516return -1;517518if (_test_ioctl_set_temp_memory_limit(self->fd, 32))519return -1;520521if (_test_ioctl_ioas_map(self->fd, ioas_id, buffer, BUFFER_SIZE, &iova,522IOMMU_IOAS_MAP_WRITEABLE |523IOMMU_IOAS_MAP_READABLE))524return -1;525526if (_test_cmd_create_access(self->fd, ioas_id, &self->access_id,527MOCK_FLAGS_ACCESS_CREATE_NEEDS_PIN_PAGES))528return -1;529530fail_nth_enable();531532{533struct iommu_test_cmd access_cmd = {534.size = sizeof(access_cmd),535.op = IOMMU_TEST_OP_ACCESS_PAGES,536.id = self->access_id,537.access_pages = { .iova = iova,538.length = BUFFER_SIZE,539.uptr = (uintptr_t)buffer },540};541542if (ioctl(self->fd, _IOMMU_TEST_CMD(IOMMU_TEST_OP_ACCESS_RW),543&access_cmd))544return -1;545access_pages_id = access_cmd.access_pages.out_access_pages_id;546}547548if (_test_cmd_destroy_access_pages(self->fd, self->access_id,549access_pages_id))550return -1;551552if (_test_cmd_destroy_access(self->access_id))553return -1;554self->access_id = 0;555return 0;556}557558/* iopt_pages_fill_xarray() */559TEST_FAIL_NTH(basic_fail_nth, access_pin_domain)560{561uint32_t access_pages_id;562uint32_t ioas_id;563__u32 stdev_id;564__u32 hwpt_id;565__u64 iova;566567self->fd = open("/dev/iommu", O_RDWR);568if (self->fd == -1)569return -1;570571if (_test_ioctl_ioas_alloc(self->fd, &ioas_id))572return -1;573574if (_test_ioctl_set_temp_memory_limit(self->fd, 32))575return -1;576577if (_test_cmd_mock_domain(self->fd, ioas_id, &stdev_id, &hwpt_id, NULL))578return -1;579580if (_test_ioctl_ioas_map(self->fd, ioas_id, buffer, BUFFER_SIZE, &iova,581IOMMU_IOAS_MAP_WRITEABLE |582IOMMU_IOAS_MAP_READABLE))583return -1;584585if (_test_cmd_create_access(self->fd, ioas_id, &self->access_id,586MOCK_FLAGS_ACCESS_CREATE_NEEDS_PIN_PAGES))587return -1;588589fail_nth_enable();590591{592struct iommu_test_cmd access_cmd = {593.size = sizeof(access_cmd),594.op = IOMMU_TEST_OP_ACCESS_PAGES,595.id = self->access_id,596.access_pages = { .iova = iova,597.length = BUFFER_SIZE,598.uptr = (uintptr_t)buffer },599};600601if (ioctl(self->fd, _IOMMU_TEST_CMD(IOMMU_TEST_OP_ACCESS_RW),602&access_cmd))603return -1;604access_pages_id = access_cmd.access_pages.out_access_pages_id;605}606607if (_test_cmd_destroy_access_pages(self->fd, self->access_id,608access_pages_id))609return -1;610611if (_test_cmd_destroy_access(self->access_id))612return -1;613self->access_id = 0;614615if (_test_ioctl_destroy(self->fd, stdev_id))616return -1;617return 0;618}619620/* device.c */621TEST_FAIL_NTH(basic_fail_nth, device)622{623struct iommu_hwpt_selftest data = {624.iotlb = IOMMU_TEST_IOTLB_DEFAULT,625};626struct iommu_test_hw_info info;627uint32_t fault_id, fault_fd;628uint32_t veventq_id, veventq_fd;629uint32_t fault_hwpt_id;630uint32_t test_hwpt_id;631uint32_t ioas_id;632uint32_t ioas_id2;633uint32_t idev_id;634uint32_t hwpt_id;635uint32_t viommu_id;636uint32_t hw_queue_id;637uint32_t vdev_id;638__u64 iova;639640self->fd = open("/dev/iommu", O_RDWR);641if (self->fd == -1)642return -1;643644if (_test_ioctl_ioas_alloc(self->fd, &ioas_id))645return -1;646647if (_test_ioctl_ioas_alloc(self->fd, &ioas_id2))648return -1;649650iova = MOCK_APERTURE_START;651if (_test_ioctl_ioas_map(self->fd, ioas_id, buffer, PAGE_SIZE, &iova,652IOMMU_IOAS_MAP_FIXED_IOVA |653IOMMU_IOAS_MAP_WRITEABLE |654IOMMU_IOAS_MAP_READABLE))655return -1;656if (_test_ioctl_ioas_map(self->fd, ioas_id2, buffer, PAGE_SIZE, &iova,657IOMMU_IOAS_MAP_FIXED_IOVA |658IOMMU_IOAS_MAP_WRITEABLE |659IOMMU_IOAS_MAP_READABLE))660return -1;661662fail_nth_enable();663664if (_test_cmd_mock_domain_flags(self->fd, ioas_id,665MOCK_FLAGS_DEVICE_PASID,666&self->stdev_id, NULL, &idev_id))667return -1;668669if (_test_cmd_get_hw_info(self->fd, idev_id, IOMMU_HW_INFO_TYPE_DEFAULT,670&info, sizeof(info), NULL, NULL))671return -1;672673if (_test_cmd_hwpt_alloc(self->fd, idev_id, ioas_id, 0,674IOMMU_HWPT_ALLOC_PASID, &hwpt_id,675IOMMU_HWPT_DATA_NONE, 0, 0))676return -1;677678if (_test_cmd_mock_domain_replace(self->fd, self->stdev_id, ioas_id2, NULL))679return -1;680681if (_test_cmd_mock_domain_replace(self->fd, self->stdev_id, hwpt_id, NULL))682return -1;683684if (_test_cmd_hwpt_alloc(self->fd, idev_id, ioas_id, 0,685IOMMU_HWPT_ALLOC_NEST_PARENT |686IOMMU_HWPT_ALLOC_PASID,687&hwpt_id,688IOMMU_HWPT_DATA_NONE, 0, 0))689return -1;690691if (_test_cmd_viommu_alloc(self->fd, idev_id, hwpt_id, 0,692IOMMU_VIOMMU_TYPE_SELFTEST, NULL, 0,693&viommu_id))694return -1;695696if (_test_cmd_vdevice_alloc(self->fd, viommu_id, idev_id, 0, &vdev_id))697return -1;698699if (_test_cmd_hw_queue_alloc(self->fd, viommu_id,700IOMMU_HW_QUEUE_TYPE_SELFTEST, 0, iova,701PAGE_SIZE, &hw_queue_id))702return -1;703704if (_test_ioctl_fault_alloc(self->fd, &fault_id, &fault_fd))705return -1;706close(fault_fd);707708if (_test_cmd_hwpt_alloc(self->fd, idev_id, hwpt_id, fault_id,709IOMMU_HWPT_FAULT_ID_VALID, &fault_hwpt_id,710IOMMU_HWPT_DATA_SELFTEST, &data, sizeof(data)))711return -1;712713if (_test_cmd_veventq_alloc(self->fd, viommu_id,714IOMMU_VEVENTQ_TYPE_SELFTEST, &veventq_id,715&veventq_fd))716return -1;717close(veventq_fd);718719if (_test_cmd_hwpt_alloc(self->fd, idev_id, ioas_id, 0,720IOMMU_HWPT_ALLOC_PASID,721&test_hwpt_id,722IOMMU_HWPT_DATA_NONE, 0, 0))723return -1;724725/* Tests for pasid attach/replace/detach */726727self->pasid = 200;728729if (_test_cmd_pasid_attach(self->fd, self->stdev_id,730self->pasid, hwpt_id)) {731self->pasid = 0;732return -1;733}734735if (_test_cmd_pasid_replace(self->fd, self->stdev_id,736self->pasid, test_hwpt_id))737return -1;738739if (_test_cmd_pasid_detach(self->fd, self->stdev_id, self->pasid))740return -1;741742self->pasid = 0;743744return 0;745}746747TEST_HARNESS_MAIN748749750