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 8E74F80036 for ; Wed, 26 Aug 2026 11:05:55 +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 67QB4bjv007041; Wed, 26 Aug 2026 14:04:39 +0300 DKIM-Filter: OpenDKIM Filter v2.11.0 mail.openvz.org 67QB4bjv007041 Authentication-Results: mail.openvz.org; dkim=fail reason="signature verification failed" (2048-bit key) header.d=virtuozzo.com header.i=@virtuozzo.com header.b="KNVk6G5Y" Received: from mail-wm1-f71.google.com (mail-wm1-f71.google.com [209.85.128.71]) by mail.openvz.org (8.14.4/8.14.4) with ESMTP id 67QB4Nfw007021 (version=TLSv1/SSLv3 cipher=AES128-GCM-SHA256 bits=128 verify=FAIL) for ; Wed, 26 Aug 2026 14:04:23 +0300 DKIM-Filter: OpenDKIM Filter v2.11.0 mail.openvz.org 67QB4Nfw007021 Received: by mail-wm1-f71.google.com with SMTP id 5b1f17b1804b1-499bd779522so3212295e9.3 for ; Wed, 26 Aug 2026 04:04:23 -0700 (PDT) X-Gm-Message-State: AFuF++mIwONUcuP8MB38IzTl72imsBIERUP+EUYuLEWow/1YcJo60SOu N4tMvmiIwlsUk5x7hWC/56DcoPZS6Ds5ZXaXfHw+KG8Z010W+wWjGe8Ezx4IqnapJNVyIS0F9/S x+ijYxX/VsVISaMfCV0LmBN9Sd0cKXsLHnjWR+A2x66hbkYxDubnqtg== X-Gm-Gg: AR+sD10vvSipmCmg/Z1kYwUGsrdP5DhBfdbwdRCi+PheHDxFI5mMb1Dv56D0dbIl5TG ZF1XrDudo8nNvQBNAubMcnh26hPhui8WdN0MQ0MaLrOGl19AFHBhjmI2yZUgByUwIzWtFNBznVz Su5mbMWLUz/VeEZ8LnahUm9SVzBcn0oTcl60XjPzVQpGImyqI+p0DEBVxNuJE+75kEAecy8vMeD DeukRavOVoPhf27TOaUXqNGglAGyE41IZAx7PjIKza7FOfV8qQa2S9uM01cBWikWggizQviJF6M AnNUa2U8rpDf+CcMccSgVOg4m2EEvPDE6D/QYkeHGTHZXgMk5MJwx331d0ketrrG2xCPRa7IndD 1mZ9OyYqey+ExbBfKOfHEBwpcCNCG X-Received: by 2002:a05:600c:8518:b0:499:a5c8:c6f3 with SMTP id 5b1f17b1804b1-499dc6e998cmr57206425e9.3.1787742262831; Wed, 26 Aug 2026 04:04:22 -0700 (PDT) X-Received: by 2002:a05:600c:8518:b0:499:a5c8:c6f3 with SMTP id 5b1f17b1804b1-499dc6e998cmr57205295e9.3.1787742262207; Wed, 26 Aug 2026 04:04:22 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1787742262; cv=none; d=google.com; s=arc-20260327; b=XuY/iagCJNM3fRFJkk80Q3Swod2fZxjsk2GgLhfmABVysDt7BVE2z+DGqQDI3QUMTG O0bzq9LWeM7NXO4lufjGorll0vYTffa7jNROPRBwQSrC7suEej3rPPcCZXUuq1IBAvEU pbkFj7BKnX7t3hULDPLAhBXy67k2phF+CeUhMqZxbjFLooyoqEL8gRtMX/HvDnOoU0yo Mw/nFXAnBz4j6G3XkuIyz/2dHRFuf8f9cE7astSOmucedWD/iM7cGF1zibFuNT5ey+za KRu/tz6CKxbLZtATT5k1RZ5GibnaWgdR8N495Ey8nLdS4sbLoAopIHDWmhsGZ8DxPvMn bnkg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20260327; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:dkim-signature; bh=L6sV5n5hWa3LBnHoV1X0P00u23YoZXHLFnnwbhEDXoA=; fh=HgZU6gWxVSDFWCEWiGWrMzzoxLctZeZ+0Da/uTKKnNs=; b=APqnG1tyGv71S/yJmFakjBqjPvrmBhq32iWPcNR/L9dFAfMgCEPvMTzjSEOSizrXDN 8Zvj9aiiUPxBLAqMbnmGcDL36jYNwd3gRKBUC6op1CRrLK3ll+0ibz1QyELnUGn8V3Se s3C3VQYCyl4GL8VZiDJstQQiwjYhNVEmrm5xTJsDHmCzQze3i5fqGh6m1D9pFci6zn0V 1i44ILpJpbXofozibscLKjDjsFCDkpJc59X0OgSYJlKN3178j85r/jJICbOxsquYigFd MvCEyYKgU+loqpNULMwu6aZrCdaI7eu31AX7oV+IVfuqI9tXebOeXi8He8n24XM86TsR 3u7Q==; dara=google.com ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@virtuozzo.com header.s=relay header.b=KNVk6G5Y; spf=pass (google.com: domain of mirian.shilakadze@virtuozzo.com designates 130.117.225.111 as permitted sender) smtp.mailfrom=mirian.shilakadze@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 5b1f17b1804b1-499de79b219si9655595e9.19.2026.08.26.04.04.21 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 26 Aug 2026 04:04:22 -0700 (PDT) Received-SPF: pass (google.com: domain of mirian.shilakadze@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=KNVk6G5Y; spf=pass (google.com: domain of mirian.shilakadze@virtuozzo.com designates 130.117.225.111 as permitted sender) smtp.mailfrom=mirian.shilakadze@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=MIME-Version:Message-ID:Date:Subject:From: Content-Type; bh=L6sV5n5hWa3LBnHoV1X0P00u23YoZXHLFnnwbhEDXoA=; b=KNVk6G5Y21ny r1WBPKhOIRmTA1v2s8pAu5Pk4nP51rd12dqLyBgdbIiX1XVSKvfqxraiDR5yHrCROdaJ4K88nX9Ts 1iiwNppc9KPuSZ3gcp3eFTpKwf65aphdZ3blCd32BO2R2lqGX4srLLyjbchr+a4P77v4G7xHcZAEh 35BpdnJZUPdQgT/1KODiEHXaTRIWiUhkk3t1JVFDDGezUf+g4iiZmYnRK1JfbqhNNO8ES03thSxFM cQx+V5J1U9pvHr45+qK/1gGIK6yQSOJxmnkrOnWGsrPOKVl9EVz6WXk7efwlTH/NUJbSYquOf7fnC 9noXkN9nw7SeS//CnXBIRA==; Received: from ch-demo-asa.virtuozzo.com ([130.117.225.8] helo=localhost.localdomain) by relay.virtuozzo.com with esmtp (Exim 4.96) (envelope-from ) id 1wzBON-00H4OO-0I; Wed, 26 Aug 2026 13:04:21 +0200 From: Mirian Shilakadze To: khorenko@virtuozzo.com, ptikhomirov@virtuozzo.com Date: Wed, 26 Aug 2026 15:04:11 +0400 Message-ID: <20260826110415.41119-3-mirian.shilakadze@virtuozzo.com> X-Mailer: git-send-email 2.47.3 In-Reply-To: <20260826110415.41119-1-mirian.shilakadze@virtuozzo.com> References: <20260826110415.41119-1-mirian.shilakadze@virtuozzo.com> MIME-Version: 1.0 X-OZ-Fwd: true Cc: devel@openvz.org Subject: [Devel] [PATCH vz10 v2 2/2] selftests/ve: check that hiding an entry does not unmount it 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 kernfs_dop_revalidate() answered the per VE visibility check with the same "return 0" the staleness checks use, and the VFS reads 0 as a global fact: d_invalidate() hands every mountpoint under that dentry to __detach_mounts(), whose mountpoint hash is not scoped to a mount namespace. A single lookup from inside a Container unmounted the host's bpffs, and libvzctl needs bpffs for the cgroup v2 device controller, so the whole node stopped being manageable. Mount a tmpfs on the entry the variant already keeps host only, look it up from inside a VE, and require both that the VE is told ENOENT and that the mount is still there afterwards. The mount is made in the test's own mount namespace so the machine running the test cannot lose a mount it needs, while the dentry the mount hangs on is still the shared one the bug worked through. The mount check uses openat2() with RESOLVE_NO_XDEV, which fails with EXDEV when the final component is a mount point, rather than reading /proc/self/mountinfo. Fails without the preceding fix, on both the sysfs and the proc variant. Feature: kernfs: per-CT entries visibility and permissions configuration https://virtuozzo.atlassian.net/browse/VSTOR-142552 Signed-off-by: Mirian Shilakadze --- tools/testing/selftests/ve/ve_perms_test.c | 52 ++++++++++++++++++++++ tools/testing/selftests/ve/ve_selftest.h | 40 ++++++++++++++++- 2 files changed, 90 insertions(+), 2 deletions(-) diff --git a/tools/testing/selftests/ve/ve_perms_test.c b/tools/testing/selftests/ve/ve_perms_test.c index 4522950c17f2..25ffbd42c380 100644 --- a/tools/testing/selftests/ve/ve_perms_test.c +++ b/tools/testing/selftests/ve/ve_perms_test.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -412,4 +413,55 @@ TEST_F(ve_perms, enforce_denies) absent, O_RDONLY), EACCES); } +/* + * Looking up an entry that a VE cannot see must not disturb a mount that + * sits on it. + * + * The lookup used to answer "this dentry is stale" where it meant "this name + * is not here for you", and the VFS acts on stale globally: d_invalidate() + * detaches every mount on that dentry in every mount namespace. One lookup + * from inside a Container took the host's bpffs and tracefs with it. + * + * The tmpfs is mounted in the test's own mount namespace, so the machine + * running this cannot lose a mount it needs, while the dentry the mount hangs + * on is still the shared one the bug worked through. + */ +TEST_F(ve_perms, hidden_entry_keeps_its_mount) +{ + char path[PATH_MAX]; + int status; + pid_t pid; + + if (!entry_present(variant->dir_prefix, variant->dir)) + SKIP(return, "%s/%s absent", variant->dir_prefix, variant->dir); + snprintf(path, sizeof(path), "%s/%s", variant->dir_prefix, variant->dir); + + pid = fork(); + ASSERT_GE(pid, 0); + if (pid == 0) { + if (unshare(CLONE_NEWNS) != 0 || + mount(NULL, "/", NULL, MS_REC | MS_PRIVATE, NULL) != 0 || + mount("ve_selftest", path, "tmpfs", 0, NULL) != 0) + _exit(255); + if (is_mounted(path) != 1) + _exit(254); + + /* + * The lookup that used to unmount it. What the VE is told + * depends on the filesystem and on the mount now covering the + * entry, and enforce_denies() already covers that. Here only + * the mount surviving the lookup is the point. + */ + ve_open_rel(self->cgv2_fd, self->ctid_a, variant->dir_prefix, + variant->dir, O_RDONLY | O_DIRECTORY); + + _exit(is_mounted(path) == 1 ? 0 : 2); + } + ASSERT_EQ(waitpid(pid, &status, 0), pid); + ASSERT_TRUE(WIFEXITED(status)); + if (WEXITSTATUS(status) == 2) + TH_LOG("the VE lookup unmounted %s", path); + EXPECT_EQ(WEXITSTATUS(status), 0); +} + TEST_HARNESS_MAIN diff --git a/tools/testing/selftests/ve/ve_selftest.h b/tools/testing/selftests/ve/ve_selftest.h index 69c0a52dd7ef..c53bf7900d20 100644 --- a/tools/testing/selftests/ve/ve_selftest.h +++ b/tools/testing/selftests/ve/ve_selftest.h @@ -1,8 +1,8 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* * Shared helpers for the ve selftests: a private cgroup2 mount, small file and - * cgroup helpers, and VE cgroup create and destroy, used across the tests in - * this directory. + * cgroup helpers, VE cgroup create and destroy, and a mount point query, used + * across the tests in this directory. */ #ifndef __SELFTESTS_VE_VE_SELFTEST_H #define __SELFTESTS_VE_VE_SELFTEST_H @@ -16,6 +16,8 @@ #include #include #include +#include +#include #ifndef CLONE_NEWVE #define CLONE_NEWVE 0x00000040 @@ -180,4 +182,38 @@ static inline void destroy_ve(int cgv2_fd, int id) __func__, id, strerror(errno)); } +/* + * Is @path a mount point? RESOLVE_NO_XDEV makes openat2() fail with EXDEV + * when the final component is a mount point, which answers the question + * without reading the mount table. + */ +static inline int is_mounted(const char *path) +{ + struct open_how how = { + .flags = O_PATH | O_CLOEXEC, + .resolve = RESOLVE_NO_XDEV, + }; + char buf[PATH_MAX], *dir, *base; + int dfd, fd; + + if (snprintf(buf, sizeof(buf), "%s", path) >= (int)sizeof(buf)) + return -1; + base = strrchr(buf, '/'); + if (!base) + return -1; + *base++ = '\0'; + dir = buf[0] ? buf : "/"; + + dfd = open(dir, O_PATH | O_DIRECTORY | O_CLOEXEC); + if (dfd < 0) + return -1; + fd = syscall(__NR_openat2, dfd, base, &how, sizeof(how)); + close(dfd); + if (fd >= 0) { + close(fd); + return 0; + } + return errno == EXDEV ? 1 : -1; +} + #endif /* __SELFTESTS_VE_VE_SELFTEST_H */ -- 2.43.0 _______________________________________________ Devel mailing list Devel@openvz.org https://lists.openvz.org/mailman/listinfo/devel