From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mail.openvz.org (unknown [69.168.225.77]) by lore.virtuozzo.com (Postfix) with ESMTPS id 8A5748017D for ; Fri, 16 Oct 2026 21:37:05 +0000 (UTC) Received: from mail.openvz.org (localhost [127.0.0.1]) by mail.openvz.org (8.14.4/8.14.4) with ESMTP id 69GLZqXt021533; Sat, 17 Oct 2026 00:35:53 +0300 DKIM-Filter: OpenDKIM Filter v2.11.0 mail.openvz.org 69GLZqXt021533 Authentication-Results: mail.openvz.org; dkim=fail reason="signature verification failed" (2048-bit key) header.d=virtuozzo.com header.i=@virtuozzo.com header.b="mQnrt5nj" Received: from mail-wr1-f72.google.com (mail-wr1-f72.google.com [209.85.221.72]) by mail.openvz.org (8.14.4/8.14.4) with ESMTP id 69GLZjwX021515 (version=TLSv1/SSLv3 cipher=AES128-GCM-SHA256 bits=128 verify=FAIL) for ; Sat, 17 Oct 2026 00:35:45 +0300 DKIM-Filter: OpenDKIM Filter v2.11.0 mail.openvz.org 69GLZjwX021515 Received: by mail-wr1-f72.google.com with SMTP id ffacd0b85a97d-48c6a1d4034so227443f8f.0 for ; Fri, 16 Oct 2026 14:35:45 -0700 (PDT) X-Gm-Message-State: AFq9FYK1Q8MlPdR4MtY0hzpCq+q/d5gPimv1OxJRYF/8llSy22Aq1xZE 2mUPEt8RgXqpzfBy+aF4JD5zpbZEdSTQ35FGAf92dCRMxrsMJdGSSV6p7enHheBT3K28XoMl4AC 566ffCXW7nZNrS/mEXJjrUPIFbKTb69A2F/kqd8LGgpZFg3Rnz6apPw== X-Gm-Gg: AYBFou3ZoySpyUflDs9MY8adAeLdPMf05BJM/aQSeBTMEsOiMdE+WFJ+NKLP7SMWIsA 2cSeeZXBNK0odLX4HreMxCxrI8q0YNkCFviud5fHWzkrcP64vrteeWVHVjThLhUh2JGY3d4FBRU bKErZbTloIjDq/XwXr2abM4+xMMmaII6el2vevDVKvO+Wjq9Y/Uh7H9f1if+it9kBoyPHW29kIV R3UlT0k7V98VqZW6bJDIEJ0fyioT3YPi2wR956YjuuzI/bWqDq3s1qpcYOrdJet5+xFFM1/1YVN Ad5JEKYarDanw3H8q+GPkZ2vNGm4oQK9CEfDy7Nl2d58a6lraiQ84q4Zr1Lk4op8rb2XN/lmK5K Oj7CMur3wwEh0b4gz X-Received: by 2002:a05:6000:4553:b0:487:7fd:730 with SMTP id ffacd0b85a97d-48e07bb3315mr1240645f8f.13.1792186545530; Fri, 16 Oct 2026 14:35:45 -0700 (PDT) X-Received: by 2002:a05:6000:4553:b0:487:7fd:730 with SMTP id ffacd0b85a97d-48e07bb3315mr1240622f8f.13.1792186544954; Fri, 16 Oct 2026 14:35:44 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1792186544; cv=none; d=google.com; s=arc-20260327; b=qKhTKU2A2a3x0Szhh+xRKuXwiAMxzEp8CgsX3QYIScElFYFiDmEbbaVqjlUMtFzbAY 9GSpj60LtdOz7Xjet2dBGHmxyFVptutOcVtfAEGjteh9LSFCeyfFUW59bFPCcGslbKD2 pwaFimm7ZAn5r5VBau11Pvl17FFJyBLPJou5eGZF16I4XaUsoiLbbrNFM+pbGMRi9Moa pZW57y37T6xe8wnRuf6vDBfbWLvKGi6fIBHpQkF6RMXheFGjPUjfb2SjJzMs8ytoRHcs lko+zPm+thtwBFEMWdrIHh/AECLfUy9yoCClw4NLohI65viB5QibA2L+LOftmLfNCgXw 61bQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20260327; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:dkim-signature; bh=ATsBvECzV0j/psSEKA8NeUu1ZDK0ok+culMIeXfn0bY=; fh=ORqO450YHYjnAvQVnHrQRoSXc/Ljts4WdUx12SqEe+w=; b=cFqQ6Hz1ubTRHVBfkXhxuZE001EXW6h6tcGEqRvcGkISRnlfxsm87NjMsR+tFnGW3C 1gZ/Zc/A1a/TsKvouks4Y33Pa4buEKZMBWIexj8BHFdyaj+Y15tZj6uEaZmMFiIPj7Ch 0r3giA2ffb4YkJfDF290DtDoHM01pNE4IDTDnK50s7mVV2I1RQnfyEBIT4feDBN5XNLb Q++uH78vuysE1vE9TO+tuzyBSDgq9noKqMOSzu4svoU39TNRiUC4glbZMerVNZdFTF/I aOs2SZb3YDg7/koGxZ+TXRwLY2MML4YCDng1rwwUNymprXG26pX1d0FWB4dyN3BuTNoz XKiw==; dara=google.com ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@virtuozzo.com header.s=relay header.b=mQnrt5nj; spf=pass (google.com: domain of khorenko@virtuozzo.com designates 130.117.225.111 as permitted sender) smtp.mailfrom=khorenko@virtuozzo.com; dmarc=pass (p=QUARANTINE sp=QUARANTINE dis=NONE) header.from=virtuozzo.com Received: from relay.virtuozzo.com (relay.virtuozzo.com. [130.117.225.111]) by mx.google.com with ESMTPS id ffacd0b85a97d-48dfd46878dsi9817373f8f.18.2026.10.16.14.35.44 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 16 Oct 2026 14:35:44 -0700 (PDT) Received-SPF: pass (google.com: domain of khorenko@virtuozzo.com designates 130.117.225.111 as permitted sender) client-ip=130.117.225.111; Authentication-Results: mx.google.com; dkim=pass header.i=@virtuozzo.com header.s=relay header.b=mQnrt5nj; spf=pass (google.com: domain of khorenko@virtuozzo.com designates 130.117.225.111 as permitted sender) smtp.mailfrom=khorenko@virtuozzo.com; dmarc=pass (p=QUARANTINE sp=QUARANTINE dis=NONE) header.from=virtuozzo.com DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=virtuozzo.com; s=relay; h=Message-Id:Content-Type:MIME-Version:Subject:Date :From; bh=ATsBvECzV0j/psSEKA8NeUu1ZDK0ok+culMIeXfn0bY=; b=mQnrt5njA/bCENCtNJv rKq22/VTLUqd6Dti0Hx0PiP29wamfs2bqZv+pfFVXtiN8oIfD5OICX8nFVZixCxbvYNVlbFLiMbAt 7Ud6cDpCiKU4eXWMeHB0WNpdZpBt/6TkxvHop1xjc3JcsBTLmwf5v7tfPpiidXEEu+OuarK7kMH+P ZqIej7kYPii7IWVTRji8VgZY8InE7oAObLpKxVwFz6oVTlco2VZCoJs6KNkbqQfCz5PXKfjj6or8q /416/yK8KR84PqBlO4c0vJseP2m+uq0DLxmjIX+V8ldQhT6MbjewuhXwjP/5es2prLJIf5Gb/rqGV iqRiSrByifWDJVg==; Received: from ch-demo-asa.virtuozzo.com ([130.117.225.8] helo=f0.sw.ru) by relay.virtuozzo.com with esmtp (Exim 4.96) (envelope-from ) id 1xHpX6-00GeHO-0a; Fri, 16 Oct 2026 23:35:40 +0200 From: Konstantin Khorenko Date: Fri, 16 Oct 2026 23:35:40 +0200 MIME-Version: 1.0 Message-Id: <20261016-b4-prep-khorenko-vstor-103860-4-doexec-vfio-bars-v2-2-2237f8fbb38e@virtuozzo.com> References: <20261016-b4-prep-khorenko-vstor-103860-4-doexec-vfio-bars-v2-0-2237f8fbb38e@virtuozzo.com> In-Reply-To: <20261016-b4-prep-khorenko-vstor-103860-4-doexec-vfio-bars-v2-0-2237f8fbb38e@virtuozzo.com> To: Andrey Drobyshev , Pavel Tikhomirov , Vasileios Almpanis X-Mailer: b4 0.16.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=8824; i=khorenko@virtuozzo.com; h=from:subject:message-id; bh=J7nqxvY2rbpUnLyshO9LRdT0t/WFmgO8pt9LpTkS1PQ=; b=owEBbQKS/ZANAwAKAVGWCkf5YVwqAcsmYgBq0pivRrADM7boMr84RFjSJK2KsPlj6+LdTxrGv kfO/ZRqQeCJAjMEAAEKAB0WIQRWD23IPcXT0GO9CCxRlgpH+WFcKgUCatKYrwAKCRBRlgpH+WFc Kg3kEAC9nru8oq6vTlW/U2iIoCTwcZtEuXCxrs0b/ChwZiQpYNlRWAIpVFXt9OHX2bGzs9PkV94 VeUWeuxAXHlbZaNxksglJ680ec99IvKoIiZMMXPSw1f6MC4yogyG7UrQyCi+88650OobgEyd3Ik tJdXAuFNTDZONvdswxDZq6JuC5J+mxd1LuezRtDWw5u0vv292r2hQD+LdFnnf4u/m4Pz+51ublj vSoX2r1AaAk78qzzn8Z/+SHHBHIfHoI+zuomqWM5pyVJU/PookuM9BtYjd6ab45HMz0Z3ENLq5A Ab+miSg+iM3FX+m5yHxffkWhLsiC6UFVqqsu9omDI6Ca0QbKs8O/5TEtw2O/pau6iCDmHbkMDcJ q4DLidowKdgqiCgqLR0J8eqZJy3nvobGVdihuGsE8C1Sf08e6zqXisDZDMDrOSDM6pNb8zXN4aW Vpqkfn26LP8NYOcGfLmw6j1Y9w72eEcGXWPxLKdq539TfOTW+LLD+Rl3WUO4qU4MVICUQTWkPtA dekCyNfDfDAtUTtO3HHm5Adwd8RNqclVCohymQt1P26ZWKsj9cLu7ghkmYiO+tUMGAuql1qyr7U 86TdyzOU2wKFg4bWjPmvdspECNntTSmV8O4boNeGdk6gy/wgZsVqIPliIjG36Z+eotKp/3B4Kho KF44tRzAY0OOaGg== X-Developer-Key: i=khorenko@virtuozzo.com; a=openpgp; fpr=560F6DC83DC5D3D063BD082C51960A47F9615C2A X-OZ-Fwd: true Cc: devel@openvz.org Subject: [Devel] [PATCH hci-8.1 v2 2/2] selftests/mm: check MADV_DOEXEC on VFIO BARs and other PFN mappings X-BeenThere: devel@openvz.org X-Mailman-Version: 2.1.12 Precedence: list List-Id: OpenVZ development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Sender: devel-bounces@openvz.org Errors-To: devel-bounces@openvz.org Add three cases to doexec: - A shared mapping of /dev/mem is a PFN mapping of a driver other than VFIO, and MADV_DOEXEC must refuse it. - BAR 0 of the PCI device named by DOEXEC_VFIO_DEVICE, which has to be bound to vfio-pci, is mapped through its VFIO device fd at an address aligned to the mapped size, filled with a pattern and marked, and the process execs. The new image must find the mapping at the same address with the pfn and shared flags in smaps and the pattern in place. It then execs again without marking, and that image must not find the mapping. - The same with the BAR mapped one page off that alignment. The aligned mapping gets huge PFN entries, which the copy leaves to be faulted in again in the new image; this one gets PTEs, which copy_page_range() duplicates. A group that has only a noiommu node is set up with the no-IOMMU container. Without the variable both VFIO cases are skipped. The test overwrites BAR 0, so the variable has to name a spare device whose BAR 0 is memory, such as an unused VGA frame buffer. https://virtuozzo.atlassian.net/browse/VSTOR-150233 Feature: oracle/mm: MADV_DOEXEC madvise() flag Assisted-by: LLM Signed-off-by: Konstantin Khorenko --- tools/testing/selftests/mm/doexec.c | 157 +++++++++++++++++++++++++++++++++++- 1 file changed, 155 insertions(+), 2 deletions(-) diff --git a/tools/testing/selftests/mm/doexec.c b/tools/testing/selftests/mm/doexec.c index 97e045ba678e8..87a9c9493fe35 100644 --- a/tools/testing/selftests/mm/doexec.c +++ b/tools/testing/selftests/mm/doexec.c @@ -4,6 +4,8 @@ #include #include #include +#include +#include #include #include #include @@ -585,6 +587,144 @@ static int doexec_noexec(void) return ret; } +static bool smaps_has_flag(char *addr, const char *flag) +{ + FILE *file = fopen("/proc/self/smaps", "r"); + char line[256], want[32]; + bool in_vma = false, found = false; + + if (!file) + return false; + snprintf(want, sizeof(want), "%lx-", (unsigned long)addr); + while (fgets(line, sizeof(line), file)) { + char *space = strchr(line, ' '); + + if (!space) + continue; + if (memchr(line, '-', space - line) && !memchr(line, ':', space - line)) + in_vma = !strncmp(line, want, strlen(want)); + else if (in_vma && !strncmp(line, "VmFlags:", 8)) + found = strstr(line, flag) != NULL; + } + fclose(file); + return found; +} + +static int after_pfnmap_exec(char *bar, size_t size) +{ + size_t page = sysconf(_SC_PAGESIZE), i; + char addr_arg[32]; + + if (!smaps_has_flag(bar, " pf ") || !smaps_has_flag(bar, " sh ")) + return KSFT_FAIL; + for (i = 0; i < size / page; i++) + if (__atomic_load_n((uint32_t *)(bar + i * page), __ATOMIC_RELAXED) != + (0xd0e00000 | i)) + return KSFT_FAIL; + /* The copy is not marked, so the next exec has to drop it. */ + snprintf(addr_arg, sizeof(addr_arg), "%llu", (unsigned long long)(uintptr_t)bar); + execl("/proc/self/exe", "doexec", "--pfnmap-gone", addr_arg, NULL); + return KSFT_FAIL; +} + +static int after_pfnmap_gone(char *bar) +{ + return smaps_has_flag(bar, " pf ") ? KSFT_FAIL : KSFT_PASS; +} + +/* Only VFIO device mappings may be preserved; /dev/mem is a plain PFN map. */ +static int devmem_refused(void) +{ + size_t size = sysconf(_SC_PAGESIZE); + char *ram; + int fd; + + if (!preserved_ram(size)) + return errno == EINVAL ? KSFT_SKIP : KSFT_FAIL; + /* Without FMODE_WRITE, do_mmap() drops VM_SHARED from the mapping. */ + fd = open("/dev/mem", O_RDWR); + if (fd < 0) + return KSFT_SKIP; + ram = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0); + close(fd); + if (ram == MAP_FAILED || !smaps_has_flag(ram, " pf ")) + return KSFT_SKIP; + return madvise(ram, size, MADV_DOEXEC) && errno == EINVAL ? KSFT_PASS : KSFT_FAIL; +} + +static int vfio_device_fd(const char *bdf) +{ + char path[PATH_MAX], link[PATH_MAX], *group_id; + int container, group, noiommu; + ssize_t len; + + snprintf(path, sizeof(path), "/sys/bus/pci/devices/%s/iommu_group", bdf); + len = readlink(path, link, sizeof(link) - 1); + if (len < 0) + return -1; + link[len] = 0; + group_id = strrchr(link, '/') + 1; + snprintf(path, sizeof(path), "/dev/vfio/%s", group_id); + noiommu = access(path, F_OK); + if (noiommu) + snprintf(path, sizeof(path), "/dev/vfio/noiommu-%s", group_id); + container = open("/dev/vfio/vfio", O_RDWR); + group = open(path, O_RDWR); + if (container < 0 || group < 0 || + ioctl(group, VFIO_GROUP_SET_CONTAINER, &container) || + ioctl(container, VFIO_SET_IOMMU, noiommu ? VFIO_NOIOMMU_IOMMU : VFIO_TYPE1v2_IOMMU)) + return -1; + return ioctl(group, VFIO_GROUP_GET_DEVICE_FD, bdf); +} + +/* Overwrites BAR 0 of DOEXEC_VFIO_DEVICE, so name a spare memory BAR. */ +static int vfio_bar_exec(bool ptes) +{ + struct vfio_region_info info = { + .argsz = sizeof(info), + .index = VFIO_PCI_BAR0_REGION_INDEX, + }; + size_t page = sysconf(_SC_PAGESIZE), size, i; + const char *bdf = getenv("DOEXEC_VFIO_DEVICE"); + char addr_arg[32], size_arg[32], *area, *bar; + uintptr_t aligned; + int dev; + + if (!bdf || !preserved_ram(page)) + return KSFT_SKIP; + dev = vfio_device_fd(bdf); + if (dev < 0 || ioctl(dev, VFIO_DEVICE_GET_REGION_INFO, &info)) { + ksft_print_msg("VFIO device %s: %s\n", bdf, strerror(errno)); + return KSFT_FAIL; + } + if (!(info.flags & VFIO_REGION_INFO_FLAG_MMAP) || info.size < page) { + ksft_print_msg("VFIO device %s: BAR 0 cannot be mapped\n", bdf); + return KSFT_FAIL; + } + size = info.size < (4ul << 20) ? info.size : 4ul << 20; + /* Size-aligned: huge PFN entries, faulted after exec; a page off: PTEs, copied. */ + area = mmap(NULL, 2 * size + page, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + if (area == MAP_FAILED) + return KSFT_FAIL; + aligned = (((uintptr_t)area + size - 1) & ~(size - 1)) + (ptes ? page : 0); + bar = mmap((void *)aligned, size, PROT_READ | PROT_WRITE, + MAP_SHARED | MAP_FIXED, dev, info.offset); + if (bar == MAP_FAILED) + return KSFT_FAIL; + for (i = 0; i < size / page; i++) + __atomic_store_n((uint32_t *)(bar + i * page), 0xd0e00000 | i, + __ATOMIC_RELAXED); + if (madvise(bar, size, MADV_DOEXEC)) { + ksft_print_msg("MADV_DOEXEC on a VFIO BAR: %s\n", strerror(errno)); + return KSFT_FAIL; + } + snprintf(addr_arg, sizeof(addr_arg), "%llu", + (unsigned long long)(uintptr_t)bar); + snprintf(size_arg, sizeof(size_arg), "%zu", size); + execl("/proc/self/exe", "doexec", "--pfnmap", addr_arg, size_arg, NULL); + return KSFT_FAIL; +} + static void *exec_thread(void *arg) { check_exec(arg, sysconf(_SC_PAGESIZE), 1, false); @@ -926,6 +1066,11 @@ int main(int argc, char **argv) return KSFT_FAIL; if (argc == 2 && !strcmp(argv[1], "--euid")) return geteuid() ? KSFT_SKIP : KSFT_PASS; + if (argc == 3 && !strcmp(argv[1], "--pfnmap-gone")) + return after_pfnmap_gone((char *)(uintptr_t)strtoull(argv[2], NULL, 10)); + if (argc == 4 && !strcmp(argv[1], "--pfnmap")) + return after_pfnmap_exec((char *)(uintptr_t)strtoull(argv[2], NULL, 10), + strtoull(argv[3], NULL, 10)); if (argc == 5 && !strcmp(argv[1], "--proc")) return proc_exec((char *)(uintptr_t)strtoull(argv[2], NULL, 10), strtoull(argv[3], NULL, 10), atoi(argv[4])); @@ -945,7 +1090,7 @@ int main(int argc, char **argv) return KSFT_PASS; } ksft_print_header(); - ksft_set_plan(NR_TESTS + 11); + ksft_set_plan(NR_TESTS + 14); for (kind = 0; kind < NR_TESTS; kind++) { fflush(stdout); pid = fork(); @@ -968,7 +1113,7 @@ int main(int argc, char **argv) ksft_test_result_skip("pre-existing COW: DOEXEC unavailable\n"); else ksft_test_result(ret == KSFT_PASS, "pre-existing COW preserved\n"); - for (kind = 0; kind < 10; kind++) { + for (kind = 0; kind < 13; kind++) { static const char * const extra_names[] = { "CLONE_VM: recoverable EBUSY and retry", "own threads: preserving exec allowed", @@ -980,6 +1125,9 @@ int main(int argc, char **argv) "setuid exec: preserving exec refused", "private /dev/zero mapping: preserved across exec", "marked range: not executable until unmarked", + "shared /dev/mem mapping: MADV_DOEXEC refused", + "VFIO BAR: preserved across exec", + "VFIO BAR mapped with PTEs: preserved across exec", }; fflush(stdout); @@ -1005,6 +1153,11 @@ int main(int argc, char **argv) _exit(devzero_exec()); case 9: _exit(doexec_noexec()); + case 10: + _exit(devmem_refused()); + case 11: + case 12: + _exit(vfio_bar_exec(kind == 12)); } } ret = pid < 0 ? KSFT_FAIL : wait_child(pid); -- 2.43.0 _______________________________________________ Devel mailing list Devel@openvz.org https://lists.openvz.org/mailman/listinfo/devel