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 6D86D802A5 for ; Wed, 14 Oct 2026 17:16:46 +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 69EHFTDd001532; Wed, 14 Oct 2026 20:15:30 +0300 DKIM-Filter: OpenDKIM Filter v2.11.0 mail.openvz.org 69EHFTDd001532 Authentication-Results: mail.openvz.org; dkim=fail reason="signature verification failed" (2048-bit key) header.d=virtuozzo.com header.i=@virtuozzo.com header.b="aOwuAlAy" 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 69EHF6nL001468 (version=TLSv1/SSLv3 cipher=AES128-GCM-SHA256 bits=128 verify=FAIL) for ; Wed, 14 Oct 2026 20:15:07 +0300 DKIM-Filter: OpenDKIM Filter v2.11.0 mail.openvz.org 69EHF6nL001468 Received: by mail-wr1-f72.google.com with SMTP id ffacd0b85a97d-4887e5afd67so3383334f8f.3 for ; Wed, 14 Oct 2026 10:15:07 -0700 (PDT) X-Gm-Message-State: AFq9FYIso1iPNzEwPI5n136pdLUp/v7OGJfImWjMEIjFlZ9HfEPblp/O B+59I7LJBXFipmFz7duYeUkUrgrg9IxvzC6m3zLy45akAzWgmZwIbUdAjVnZKU+Ibi9UreB4jeJ 9HcUBo9mt+KyYtYRKrVIwZRSMPVUkGHkNXI25WM/vqG2on0e+51iUNA== X-Gm-Gg: AYBFou0vhaKUrWhv40PH9tQYN+NABZhnh4bvxw17ser0ZaoUQJKzIlfkPILS8i8Jp++ WUaOy+mwMGMQAkNh1xcjRrhV2w6ZpPijlx57xPoCvZbEYc0aO6cJuVIEK1j4b+vBbkqK+LYR9DK SQjtjWo7saBB6fd9L6cgTxXDqjOMEEhd1R/4spkik0nmba2/pqBko+Uv15ZAbMiac0xdI+hN41Z 6pJW3qvv4LAejxFHjfnh0IPUlD49crTLCobzvBC/KpVNaWgn17rvc6oTyjI608g9mnf9MZg2EQO PZ7lH4WkWTUY2FXoZcT3FLwOi7F5YYRyaDAKFi8evGysBnw3Gt/jqbVTLURNb+pZf7/KLCMUz6H rv2RiRcAJ4754lmdPoQ== X-Received: by 2002:a05:6000:24c8:b0:48d:bb54:779d with SMTP id ffacd0b85a97d-48dbb547a7amr33214539f8f.9.1791998106517; Wed, 14 Oct 2026 10:15:06 -0700 (PDT) X-Received: by 2002:a05:6000:24c8:b0:48d:bb54:779d with SMTP id ffacd0b85a97d-48dbb547a7amr33214473f8f.9.1791998105988; Wed, 14 Oct 2026 10:15:05 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1791998105; cv=none; d=google.com; s=arc-20260327; b=brScNPVUpl65W8uCFmYaYTPOBQcOjL4rxu9hXigh6Y9WbgX2l3LDspa7KopXQ9c2UQ 6MQA9tyUaQACn+ioG3kehKbJyFPKhJP+HpMtTLsfx+Ef7bgjamHqLokR7ZEtbBaDLvqM yDth6U8kOYL5OL+OxNiO0n5/PLrqWc6xQ2zdaDgb+JxCRStAXtS6vVfNpzhEAILjwHKT qBSbYPeb8lBfIsYYrBcDmgFjkVndWlNmCwNiHwlxUSs/uYWUkvME7ClVaWNvz8JgRJSj B3y6SRYic7yexUUCEgA8LezRyEwHQVAgGW5338S8W/3NDeP73+CfwTSysMYIzXbD0dJF A/gQ== 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=3lloPpeREUTnADBli5HMt0UOcl9+Nw70ZCx14HnWvXA=; fh=xUcS46XhSaUOWFT7CPWNuABJ4EJ7/+S5u4N+e+rlWN4=; b=b/6rjIKc5eqIwLhymIP4wDgGs6+bpOeNBVryzWlaKGisMAmfwK58w+VZbJSlE8JKfB Ger7FqCjufqgU+zZvic1I3pdSi/p+aop0EhBpamQ/aPI91g77598oVv6//fBISz5XpOv nh8XEZid7w5IGVZGW5IxZk1YXC/RGlwDSTK3eTYqMr7JFRL34myRoAY45QtLQ+hiH5YU 7bQSalTilkCMwiBLl02+6ONr6u6E3gMYyJeQc0SvvPFgL8kFdEpQx+875PAr9n7Z8cf/ zijVHBylzJni4JlGYPiCtWcXRs/bdcuTTL9A71QyGUuKDqXjbr/zurwCwlQtaLC6jfrG 2AtA==; dara=google.com ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@virtuozzo.com header.s=relay header.b=aOwuAlAy; 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-48de0bea804si12826048f8f.262.2026.10.14.10.15.05 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 14 Oct 2026 10:15:05 -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=aOwuAlAy; 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=3lloPpeREUTnADBli5HMt0UOcl9+Nw70ZCx14HnWvXA=; b=aOwuAlAy2/3vvMl1A04 UYd44lJ7ceLaohrzn6zziiB7z+obWlinH50aYltvOdk4PoIM8/2M+nWA7Et8+3QHtgwHm5NfFLMTF lTi1TrPy73Ak9QsoNuaCLLpjT8Abeicub6w9K8x9TT7vAbc29GxzeFkIho5Njg3HS74rozqG1AOX0 UuAD+wXVvtsXaEfYuAcueN8LR+eg018yKtCMz4fLEkJ32dclqDiCH7VEeHvAhHoR3vk7rcR79iEq7 NFqgfqhLRi5R0us+9gvufPN1eKBxwWbd0ytJaInu4Q+Au//NbBAuMfQqjB1fv+ZcTWK48hfMNgQJj w0PirohBwMXdQzA==; 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 1xH2Vo-008657-1f; Wed, 14 Oct 2026 19:15:04 +0200 From: Konstantin Khorenko Date: Wed, 14 Oct 2026 19:15:04 +0200 MIME-Version: 1.0 Message-Id: <20261014-b4-prep-khorenko-vstor-149847-5-doexec-selftests-v1-6-b23b454691db@virtuozzo.com> References: <20261014-b4-prep-khorenko-vstor-149847-5-doexec-selftests-v1-0-b23b454691db@virtuozzo.com> In-Reply-To: <20261014-b4-prep-khorenko-vstor-149847-5-doexec-selftests-v1-0-b23b454691db@virtuozzo.com> To: Pavel Tikhomirov , Andrey Drobyshev , Vasileios Almpanis X-Mailer: b4 0.16.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=6957; i=khorenko@virtuozzo.com; h=from:subject:message-id; bh=aPmvcrpgHfQdbdp9D8CPzfcp/dUULHg48jMXTxv2oes=; b=owEBbQKS/ZANAwAKAVGWCkf5YVwqAcsmYgBqz7iXveZhwUgQfaecPyWz8FeUvOswhZ0Yddj9R 0xJ0HLumV2JAjMEAAEKAB0WIQRWD23IPcXT0GO9CCxRlgpH+WFcKgUCas+4lwAKCRBRlgpH+WFc KsoiEACSYg/jViaDivGu6ZLuvJUCM0N5grQEs183c7WURXI5tU7MAuJgXcWjQUheNf0e4C9l4z2 AuiWO2LEZWUOH53Aep2n6wD+elzyzXbs7+cLwnxe7si9HT7lBGW11l4kOfTV/67reZkQNQw/sJk Ku4EVtvLbgUC+rjvM1TYn6eKXKnXOAIMP1tsMqPpWxP6z2ESlrE9KeIYEc0QFr0xngSzKq4Tat4 8kQ2WCoYnXHQw0NmcEWAr3pBSgE1yuNhKL5fmTJwnUqpd7J6oRcJYObT7zz3o0Fm0KGM5w5HrfY KPKSowq3p1joARblDKN9NvkoOER+7mGdQV91iD2OQNhwZDlrAZ5FLtMlbYUEXWICmzaBstIQDxX smzLqqYw36inokjJSBCid+696lkN+LwDaatR8huCFUmu4vb+38RMqT+wRJem8EhcMsK8YJVQl6Q 5qsHksSTV375Q0qSffP+fNWjo+4V0J18MLD2AllpWc9LoMrNIBDDXNtCt2dz2Tz7TINImZdPH0S Vy39SlI7u//qvYKpiKJoYrec2izxOy0vc69Z9sjabLRaGwdAbm5x6qb+mEPkAeP6GbDMBG7UP9t IxtPdZg7pyvusXxPbU1mymHZQBTTHV1xdATfBkiQveNJksTQ7yyavZCDnkPl/4x7lZZ1AF8eNOV fW/Cj9tASoq06Xw== 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 6/7] selftests/mm: check who can still reach the old mm of a preserving exec 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 - A /proc/pid/mem write that took the mm before the exec and stalls on a userfaultfd source until after it must land in the old mm, not in the pages handed to the new image. Needs userfaultfd for kernel faults, so CAP_SYS_PTRACE or vm.unprivileged_userfaultfd. - A vhost worker in kthread mode runs on the owner's mm through kthread_use_mm() and survives the exec: the exec fails with EBUSY until the owner is reset. https://virtuozzo.atlassian.net/browse/VSTOR-150234 Feature: oracle/mm: MADV_DOEXEC madvise() flag Assisted-by: LLM Signed-off-by: Konstantin Khorenko --- tools/testing/selftests/mm/doexec.c | 156 ++++++++++++++++++++++++++++++++++++ 1 file changed, 156 insertions(+) diff --git a/tools/testing/selftests/mm/doexec.c b/tools/testing/selftests/mm/doexec.c index 89862e0b6a4dc..1b33bc489eb71 100644 --- a/tools/testing/selftests/mm/doexec.c +++ b/tools/testing/selftests/mm/doexec.c @@ -1867,6 +1867,157 @@ static const struct after_mode after_modes[] = { { "--mayexec", after_mayexec }, }; +/* The new image tells the parent it runs, then checks @ram once told to. */ +static int after_old_mm(char *ram, size_t size, int done, int check) +{ + char c; + + if (write(done, "x", 1) != 1 || read(check, &c, 1) != 1) + return KSFT_FAIL; + if (check_bytes(ram, size, 0x31)) + return fail("a write through the old mm reached the new image"); + return KSFT_PASS; +} + +struct mem_write { + int fd; + char *src; + char *dst; + size_t size; + ssize_t ret; +}; + +static void *mem_writer(void *arg) +{ + struct mem_write *w = arg; + + w->ret = pwrite(w->fd, w->src, w->size, (off_t)(uintptr_t)w->dst); + return NULL; +} + +/* A /proc/pid/mem write stalled across the exec must land in the old mm. */ +static int old_mm_write_exec(void) +{ + size_t size = sysconf(_SC_PAGESIZE); + struct uffdio_api api = { .api = UFFD_API }; + struct uffdio_register reg = { .mode = UFFDIO_REGISTER_MODE_MISSING }; + struct uffdio_copy copy = { .len = size }; + struct mem_write w = { .fd = -1, .size = size }; + int to_child[2], to_parent[2], uffd, ret = KSFT_FAIL; + bool stalled = false, waited = false, copied; + char path[64], *fill, c; + struct uffd_msg msg; + struct pollfd pfd; + pthread_t thread; + pid_t pid; + + uffd = syscall(__NR_userfaultfd, O_CLOEXEC); + if (uffd < 0) + return KSFT_SKIP; + w.src = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + fill = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + reg.range.start = (uintptr_t)w.src; + reg.range.len = size; + if (w.src == MAP_FAILED || fill == MAP_FAILED || ioctl(uffd, UFFDIO_API, &api) || + ioctl(uffd, UFFDIO_REGISTER, ®) || pipe(to_child) || pipe(to_parent)) + return KSFT_FAIL; + memset(fill, 0x77, size); + pid = fork(); + if (pid < 0) + return KSFT_FAIL; + if (!pid) { + char *ram = preserved_ram(size), args[4][32]; + + close(uffd); + close(to_child[1]); + close(to_parent[0]); + if (!ram) + _exit(unsupported()); + if (write(to_parent[1], &ram, sizeof(ram)) != sizeof(ram) || + read(to_child[0], &c, 1) != 1) + _exit(KSFT_FAIL); + snprintf(args[0], sizeof(args[0]), "%llu", (unsigned long long)(uintptr_t)ram); + snprintf(args[1], sizeof(args[1]), "%zu", size); + snprintf(args[2], sizeof(args[2]), "%d", to_parent[1]); + snprintf(args[3], sizeof(args[3]), "%d", to_child[0]); + execl("/proc/self/exe", "doexec", "--old-mm", args[0], args[1], args[2], + args[3], NULL); + _exit(KSFT_FAIL); + } + close(to_child[0]); + close(to_parent[1]); + if (read(to_parent[0], &w.dst, sizeof(w.dst)) != sizeof(w.dst)) { + ret = wait_child(pid); + waited = true; + goto out; + } + snprintf(path, sizeof(path), "/proc/%d/mem", pid); + w.fd = open(path, O_RDWR); + if (w.fd < 0) { + ret = KSFT_SKIP; + goto out; + } + if (pthread_create(&thread, NULL, mem_writer, &w)) + goto out; + /* The writer holds the child's mm while it faults on its source. */ + pfd.fd = uffd; + pfd.events = POLLIN; + if (poll(&pfd, 1, 10000) == 1 && read(uffd, &msg, sizeof(msg)) == sizeof(msg) && + msg.event == UFFD_EVENT_PAGEFAULT && write(to_child[1], "x", 1) == 1) + stalled = read(to_parent[0], &c, 1) == 1; + copy.dst = (uintptr_t)w.src; + copy.src = (uintptr_t)fill; + copied = !ioctl(uffd, UFFDIO_COPY, ©); + if (!copied) + close(uffd); /* releases the writer as well */ + pthread_join(thread, NULL); + if (!copied) + ret = fail("UFFDIO_COPY failed"); + else if (!stalled) + ret = fail("could not exec while a /proc/pid/mem write was stalled"); + else if (w.ret != (ssize_t)size) + ret = fail("write through the old mm failed"); + else if (write(to_child[1], "x", 1) == 1) { + ret = wait_child(pid); + waited = true; + } +out: + if (!waited) { + kill(pid, SIGKILL); + waitpid(pid, NULL, 0); + } + return ret; +} + +/* A vhost worker in kthread mode runs on the owner's mm and outlives the exec. */ +static int vhost_kthread_exec(void) +{ +#ifdef VHOST_SET_FORK_FROM_OWNER + size_t size = sysconf(_SC_PAGESIZE); + __u8 mode = VHOST_FORK_OWNER_KTHREAD; + int fd = open("/dev/vhost-net", O_RDWR), err; + char *ram; + + if (fd < 0) + return KSFT_SKIP; + ram = preserved_ram(size); + if (!ram) + return unsupported(); + if (ioctl(fd, VHOST_SET_FORK_FROM_OWNER, &mode) || ioctl(fd, VHOST_SET_OWNER)) + return KSFT_SKIP; + err = check_exec(ram, size, 1, true); + if (err != EBUSY || check_bytes(ram, size, 0x31)) + return fail("preserving exec with a vhost kthread not refused"); + if (ioctl(fd, VHOST_RESET_OWNER)) + return KSFT_FAIL; + close(fd); + check_exec(ram, size, 1, false); + return KSFT_FAIL; +#else + return KSFT_SKIP; +#endif +} + struct more_case { const char *name; int (*fn)(void); @@ -1920,6 +2071,8 @@ static const struct more_case more_cases[] = { { "half of a THP: preserved", thp_half_exec }, { "vfork() child: preserving exec refused", vfork_exec }, { "exec permission back in a fork child and the new image", mayexec_exec }, + { "/proc/pid/mem write stalled across exec: lands in the old mm", old_mm_write_exec }, + { "vhost owner in kthread mode: preserving exec refused", vhost_kthread_exec }, }; int main(int argc, char **argv) @@ -1949,6 +2102,9 @@ int main(int argc, char **argv) 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 == 6 && !strcmp(argv[1], "--old-mm")) + return after_old_mm((char *)(uintptr_t)strtoull(argv[2], NULL, 10), + strtoull(argv[3], NULL, 10), atoi(argv[4]), atoi(argv[5])); 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])); -- 2.43.0 _______________________________________________ Devel mailing list Devel@openvz.org https://lists.openvz.org/mailman/listinfo/devel