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 8E02980BE1 for ; Wed, 21 Oct 2026 16:58:59 +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 69LGvmld023039; Wed, 21 Oct 2026 19:57:48 +0300 DKIM-Filter: OpenDKIM Filter v2.11.0 mail.openvz.org 69LGvmld023039 Authentication-Results: mail.openvz.org; dkim=fail reason="signature verification failed" (2048-bit key) header.d=virtuozzo.com header.i=@virtuozzo.com header.b="HqZB77WS" 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 69LGvNDr022945 (version=TLSv1/SSLv3 cipher=AES128-GCM-SHA256 bits=128 verify=FAIL) for ; Wed, 21 Oct 2026 19:57:23 +0300 DKIM-Filter: OpenDKIM Filter v2.11.0 mail.openvz.org 69LGvNDr022945 Received: by mail-wr1-f72.google.com with SMTP id ffacd0b85a97d-48dfaaea30cso640284f8f.0 for ; Wed, 21 Oct 2026 09:57:23 -0700 (PDT) X-Gm-Message-State: AFq9FYJ4Kr7aip0HrZYG4NcDSvQIyH1/ffN9IPbRF6XFUSWkqUUhswk6 fCPuqOrJD4GaV5XTZde3kPZvv+vjW3s/LgJmp5TmXsCUmfnLy00kyTy5wbbK/gxhMkkvKcxZlYI 9RUlLHe53y0fdIVx8NSz3EjReSnEaZwRMePhOVdPQSViGpOhfcSZyYg== X-Gm-Gg: AYBFou29Ap6AePqya7LP1cII6b/NayypA73Ay0VbkoXLQr60NP4tLfi7VBFoMDxeq7P Xt/YVLss1syN2LpBMBeHKayKNfdgxNJIU+HkjvR5NNopHpY28MMSINKytkOWcgY5N/Z8aUBrRRe mcyxh5BZSEIvJOBb8GF0XGyF//+3W/Uz2IxdvBZKEPLTpNAfmy99KI2jKTV59Lma6S+4S1kdx/+ mqWnDmZ1geS0O2Up0dwp0B0nAOZdRTKAo3Oh4nUtJLwz5V7FNoHXB8SMtJX8VYFwjvhl17mDIVV io8gLaWT/u9+Jqdy1S9lyLrccuEBSyrlwGtc/ZcO3FrMn24hzGs6f3LkwNjZKuh4igDyDjd336w mzkQLrlzAJRbOcZr0TQ== X-Received: by 2002:a05:600c:3513:b0:4a1:cedc:ab49 with SMTP id 5b1f17b1804b1-4a1fdd1e97emr60300495e9.31.1792601842862; Wed, 21 Oct 2026 09:57:22 -0700 (PDT) X-Received: by 2002:a05:600c:3513:b0:4a1:cedc:ab49 with SMTP id 5b1f17b1804b1-4a1fdd1e97emr60299935e9.31.1792601842382; Wed, 21 Oct 2026 09:57:22 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1792601842; cv=none; d=google.com; s=arc-20260327; b=XnePNajQVpnNUkAgohooiuAuww849OnrcV+0Unk2PyAw1AG2JmRYzaxN3U0IjTDOMK PIqZPH8dxBdUbuJG7j/WYPgxIljoTBvsLKXxaZqBRve3lIKF9A8V8YdovmWo/DTqASuu zgzE1rcLKHAdTfCkTs+UPJkGBtZi4tkQVHewI+F9axwYzJkxJkwjsRswWJ/xsnNF5arn SW1/inEythMwYdpaXEvKRndlaNCFpe0CPN6efNqEQ1nK+RZz0oXs655KNxRCGgOXvFnj O/oinGi9mn9WTPfx17ntFYjw4dt40JDiXS+8W4vClCo+wHFX925eVDhRizrB/xQS7Rgd g/mQ== 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=2GdVFuYgfO83w199gBNlwIPjYFR9CXNgmVeWpEIl8ZA=; fh=ORqO450YHYjnAvQVnHrQRoSXc/Ljts4WdUx12SqEe+w=; b=a8q+t7G0oz8ir9s9qwQw98GK27GyF5kzHgFHa8NV4JaiGGuSTPgvvDF+kdM6UpmP8d LNd9Het/WYDhYvt+xiKNcY81pwULdx98FHr6OXIBhBYjy9ch80l26raVskLrcoeH0gyX bRe/GuvEDWAtw8pMqptONpWvilW52nurpPKExwOhmpUTxzy60urATKkISrGdL73Q07X/ ap+G9DyEAU3uJEe+gEyOvfZ4/j8z56bxv1XSy/EoQjlVxzM0n2+bg/mdWrQzL80jSPwC zWqpryvE6Fb5/0XVfaHCcS8XrwRv1cepeOb5srx9NOqNWHtryhtAS3uOK/MlOgnrvsdU MDAA==; dara=google.com ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@virtuozzo.com header.s=relay header.b=HqZB77WS; 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-48e25891aa6si5869464f8f.447.2026.10.21.09.57.22 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 21 Oct 2026 09:57:22 -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=HqZB77WS; 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=2GdVFuYgfO83w199gBNlwIPjYFR9CXNgmVeWpEIl8ZA=; b=HqZB77WSac9eboIslD0 grnlBLG08b5/SbQNnkevXOc+n9LPIX4cq81IsP5wsVoNtTKC3aM0wpL7lpu79KzNgKuSA0FJ/UCr8 H4NyBVEKK1TYDbgW0M/UD904tdKNysDsPsOPyL7+M+A2urFNigg4h+mAsw4eaJjuZi4dReZuEkbqN XEDxjnB3JQINf3jRQi2KBqiIKm+i6B+V3PZly8TW1Bh1XafNbzuEJPP2Hn3S2hy6OAB/GgTdiPVkg oXsJgWicBd6IXE/2tEdIMZX15KGZP66u52ognUN9p4vb9xB2HFAeog1buP++LxzRfqY+iyFp1hZwO ZcfG2vcumO+NQTg==; 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 1xJZZK-008y7H-13; Wed, 21 Oct 2026 18:57:21 +0200 From: Konstantin Khorenko Date: Wed, 21 Oct 2026 18:57:19 +0200 MIME-Version: 1.0 Message-Id: <20261021-b4-prep-khorenko-vstor-149847-5-doexec-selftests-v2-6-97dbb5c6e0ab@virtuozzo.com> References: <20261021-b4-prep-khorenko-vstor-149847-5-doexec-selftests-v2-0-97dbb5c6e0ab@virtuozzo.com> In-Reply-To: <20261021-b4-prep-khorenko-vstor-149847-5-doexec-selftests-v2-0-97dbb5c6e0ab@virtuozzo.com> To: Andrey Drobyshev , Pavel Tikhomirov , 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=TgyFeNVuDlaHYu5U1gtKB2jE1LR5uTRKYBQkT/853Ok=; b=owEBbQKS/ZANAwAKAVGWCkf5YVwqAcsmYgBq2O7v1+jVgraP75ujjUU1TRdoezsE3Cl7Akrlw bCXOtJ0RkWJAjMEAAEKAB0WIQRWD23IPcXT0GO9CCxRlgpH+WFcKgUCatju7wAKCRBRlgpH+WFc KpO1EAC4XK/hEdWOGRJ1SPdLKTH2YFsPPqbWgHdtdUgmlvTLxIaX+2nMQQE6Y6CIg4cRFVK9rpC o+yOG7pYkjwlTypZtZP53MIHZfGCgrE9FrXNPqOoDiALvsaUQtv8qSH8361PeXgFpxec6FpI1Kd nGh+aUcmqnjXy21FnJMRAk6fnwya+2Ildio050hHHs3FcRr45w/WumYX+QYGUICbxYopWzzoX53 bbowwRXS/KaikVqmJ1FtHy47yOHYujVii3pdYr1nX4nBF51yCzQgA0EXbj/ljp0QSpX02WPLKUl la4+fiRbPcVKkLqHDdZap4W09VcXj2qNguUVO6R2jfqssPh8pOsbHRysQMJqMfLqyfeooiUgwA8 Zx07nUBAuK5YHLlwkmbNKEzGgpAO8QtUvAnz6Peb46cfIhAtUufRXd1Zln7jaUiLPPW83oYo0rH bTtGFCrWrtANn5PE3jnZ6M2AelTX2J6+vieBQUire4tMfSz7hyobYIcOPqDUKpTF7VUcnwMko57 werJKLCWyBbMiQRnh38IbS8jAmA57bQ99V7tdk53gSHuf8ahtIC+rtAPACy2w+BRaDEa7oH4ehF JjdPFTm/uqPvFlJyoflnqMETJFH+wslswY0G5k45mbx0brGzHZQXvDCSEhopO2EVuPVDHa4JAeg kMuu+bvee4E74aQ== 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 6/9] 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 00bbc8d948b63..c9f9f9f1befe5 100644 --- a/tools/testing/selftests/mm/doexec.c +++ b/tools/testing/selftests/mm/doexec.c @@ -1879,6 +1879,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); @@ -1932,6 +2083,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) @@ -1963,6 +2116,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