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 60D888015D for ; Mon, 14 Sep 2026 09:18:00 +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 68E9GfaL028236; Mon, 14 Sep 2026 12:16:41 +0300 DKIM-Filter: OpenDKIM Filter v2.11.0 mail.openvz.org 68E9GfaL028236 Authentication-Results: mail.openvz.org; dkim=fail reason="signature verification failed" (2048-bit key) header.d=virtuozzo.com header.i=@virtuozzo.com header.b="AFQkjdZD" 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 68E9GYsX028221 (version=TLSv1/SSLv3 cipher=AES128-GCM-SHA256 bits=128 verify=FAIL) for ; Mon, 14 Sep 2026 12:16:35 +0300 DKIM-Filter: OpenDKIM Filter v2.11.0 mail.openvz.org 68E9GYsX028221 Received: by mail-wr1-f72.google.com with SMTP id ffacd0b85a97d-487024dc974so520239f8f.0 for ; Mon, 14 Sep 2026 02:16:34 -0700 (PDT) X-Gm-Message-State: AFuF++k8/XtD6grTwjAq7bENdCbgPejtQlfqIYKwy/cysyLi0adJZV5O P4y0GOfnrQ79sEuQRrzBJInRQsk/vQtl2yPLuIQ7cXRkRN73X6rlqxFgatnAfxp7BnQ8Bbkh7Zm 3ts9t7lsisqGFVtea3vj2edL4lOfJ8/fZrNEzAReOrVsE79bTYB/s1A== X-Gm-Gg: AYBFou3sVbYbPb3199jufo+FwREWhcqmEUtUV2auQ0gZbGPWFwNEPzRaCCc9fq8fLgl LTsH+URQgIcO82A0LomcjvRjGnFCQtzAZUP31TwnufQ3UTmM1bFQ0/Vi6ln1s6dKl9T87dg/UIZ sOHJ3ZF/PWKFrVHSMsYlI9SQs8u7+Qd1bvmD67rr8ZsZr1OIMyXbe8CdQbN+XXO1y7Uth/2EaIa KcTn4szFU1FEWz9+cBc7Cdyf3p+v0uEsk38AmArqhM1dLQuNpbeTl4nczexM7izVlV/D/55DLDt C2EltAnIpgIZkOCNLZiS3qwcBBYtrrHnjv3yN+nMAiWfG1sIGq/kHmKigDVV+0iDRtiFAYj/NUw cdGyV2Ax83TNTFZ0rnwmPOQpn X-Received: by 2002:adf:e012:0:20b0:487:360:f17e with SMTP id ffacd0b85a97d-4870360f1a2mr880289f8f.17.1789377394508; Mon, 14 Sep 2026 02:16:34 -0700 (PDT) X-Received: by 2002:adf:e012:0:20b0:487:360:f17e with SMTP id ffacd0b85a97d-4870360f1a2mr880256f8f.17.1789377393964; Mon, 14 Sep 2026 02:16:33 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1789377393; cv=none; d=google.com; s=arc-20260327; b=JRhYLXyWtph88kermwBlO+igO2m8Lh5EYPyyid3uDQAkWUrS5A/StkBv5c7hoU7dHh johBW8YFzzn/oaMBJnrqLqvteMJnRidW9oA5xv+fsqhKRQo6gBCvqxENkVK/HROoxSCy 2dgA+acGESAYWWWc+eB0Ke0HlsBBn1Ftv33lBLTq/FgQLME+9fe+WhZ5TjobCNRf6Q2o 1WcPocpzX94qE1JxRfvMrTj3KZiqpe7JeaU93abjxS2XLi15LkZyPbdGqdAB5OykmW97 NgwLGsQSi1xolHyqcXju/HuB8sgpiNlCNdfokTq8WEu60uRYihU9pHV6ND9KWxcExOJU Iahw== 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=E28ugjSBE06coYK+PE7SCWHywW6T0R5OycR9h/eWbk8=; fh=WpT6aB7RBAGHZeWZzqAr71yzRFbPTYkPWKsRagLn48Q=; b=qfx5KWnyJ7N4G+CzeRs47jcJ7PQfwgkIBTAhsF0XZWswLLe22DlbtOUWW4AA1yWABL 5TWl4VmAVLTM4s/KAqE5w6yi6auHS5Tum99adykBLS8KHW0lk+qAd9HBJI3/V7hLhLn6 nHKKDMgjLbyJcM2Q7aLY+e71W8PTVqSVXg4ZY/uJR9teucP1ZzrrK9smgDDWAinVjMOC jXe89TYRCaUhR8JfnukHU/9eFDkCUTSqecArdfelYJVifFaL1MfaB0Mt1ISId2IszgSt 1AObZL++Oqac8QX7aZ+jc+Q5y5guvxTO6Wbz8YAgxMTjH3lTSZONQQKmBqqbTiFUVK/s JMsg==; dara=google.com ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@virtuozzo.com header.s=relay header.b=AFQkjdZD; spf=pass (google.com: domain of eva.kurchatova@virtuozzo.com designates 130.117.225.111 as permitted sender) smtp.mailfrom=eva.kurchatova@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-486ec05677asi18236542f8f.470.2026.09.14.02.16.33 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 14 Sep 2026 02:16:33 -0700 (PDT) Received-SPF: pass (google.com: domain of eva.kurchatova@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=AFQkjdZD; spf=pass (google.com: domain of eva.kurchatova@virtuozzo.com designates 130.117.225.111 as permitted sender) smtp.mailfrom=eva.kurchatova@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=E28ugjSBE06coYK+PE7SCWHywW6T0R5OycR9h/eWbk8=; b=AFQkjdZDTNo3 sKhMtZBD6OWG4T83vygCvLN9/hIfwx6lYqz1EhiVIlMqULBcSuI5vZoexOWW3lOnh3KkMIjpyqYVr NvNkLYtRF0HFUZzjBJHWbO/1WGO8hMhiT16TkyYkdhEimgPZ+7vq1f3PyIgkC5gR04qLdblljKV99 S04A7CCRTcWCeWnkv9Xhtb4YctduGs9z9pyvj55fLK6aPAAa9rN0S+uHsTpJnxscOaUw1kBeRtBaZ jcp+psQ2W3YYY6q9K+OMN9mUSEaOEXil0s8dIr4Mt/EzW/wIn2mE2FsXeAScvptLnekzNRRKjeSSR SqjCQrPCNOewAMtpfL992g==; Received: from ch-vpn.virtuozzo.com ([130.117.225.6] helo=LekKit-T14) by relay.virtuozzo.com with esmtp (Exim 4.96) (envelope-from ) id 1x62l0-00ErAH-13; Mon, 14 Sep 2026 11:16:25 +0200 From: Eva Kurchatova To: khorenko@virtuozzo.com Date: Mon, 14 Sep 2026 12:16:26 +0300 Message-ID: <20260914091631.631032-2-eva.kurchatova@virtuozzo.com> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260914091631.631032-1-eva.kurchatova@virtuozzo.com> References: <20260914091631.631032-1-eva.kurchatova@virtuozzo.com> MIME-Version: 1.0 X-OZ-Fwd: true Cc: devel@openvz.org Subject: [Devel] [PATCH vz10 2/2] selftests: ve: introduce vtty pair teardown stress test 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 vtty pair is created by the first master open and torn down by the last close. The close racing the peer's close used to let both sides decide they were the final one and call release_tty() twice, and a master opening again while a teardown was in flight used to hand out a reference to a tty_struct about to be freed. Hammer both ends. The master comes from the vzctl ioctl, which does not look the VE up. The slave is only reachable from inside the VE, as TTY_MAJOR minor 1..MAX_NR_VTTY_CONSOLES, which vtty_driver() maps to vttys index minor - 1, so the test builds a VE with cgroup2, sets ve.veid to the cgroup it made, and runs the slave side there. One of the symtoms of a buggy kernel is dmesg warning, not just an eventual crash, so the test checks that. A crashed node should also report test failure in CI, but that's less verbose, hence the check. https://virtuozzo.atlassian.net/browse/VSTOR-144771 Feature: ve selftest Signed-off-by: Eva Kurchatova --- tools/testing/selftests/ve/Makefile | 1 + tools/testing/selftests/ve/settings | 1 + tools/testing/selftests/ve/ve_vtty_stress.c | 244 ++++++++++++++++++++ 3 files changed, 246 insertions(+) create mode 100644 tools/testing/selftests/ve/settings create mode 100644 tools/testing/selftests/ve/ve_vtty_stress.c diff --git a/tools/testing/selftests/ve/Makefile b/tools/testing/selftests/ve/Makefile index ec40cbc7b3a1..6cd5823c4d93 100644 --- a/tools/testing/selftests/ve/Makefile +++ b/tools/testing/selftests/ve/Makefile @@ -4,5 +4,6 @@ CFLAGS += -g -Wall -O2 TEST_GEN_PROGS += ve_ns_owner_test TEST_GEN_PROGS += ve_perms_test +TEST_GEN_PROGS += ve_vtty_stress include ../lib.mk diff --git a/tools/testing/selftests/ve/settings b/tools/testing/selftests/ve/settings new file mode 100644 index 000000000000..e2206265f67c --- /dev/null +++ b/tools/testing/selftests/ve/settings @@ -0,0 +1 @@ +timeout=900 diff --git a/tools/testing/selftests/ve/ve_vtty_stress.c b/tools/testing/selftests/ve/ve_vtty_stress.c new file mode 100644 index 000000000000..9037f731c58d --- /dev/null +++ b/tools/testing/selftests/ve/ve_vtty_stress.c @@ -0,0 +1,244 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * ve_vtty_stress - close a vtty pair from both ends at once. + * + * A vtty pair is created by the first master open and torn down by the + * last close. A close racing the peer's close used to let both sides + * decide they were the final one and call release_tty() twice, and a + * master opened again while a teardown was still in flight used to hand + * out a reference to a tty_struct that was about to be freed. Either + * way the next close walked freed memory. + * + * The master comes from the vzctl ioctl, which does not look the VE up. + * The slave is only reachable from inside the VE, as TTY_MAJOR minor + * 1..MAX_NR_VTTY_CONSOLES, which vtty_driver() maps to vttys index + * minor - 1. So the test makes a VE with cgroup2, hammers the master + * from the host and the slave from a task inside it, and fails if the + * kernel logs a complaint about the teardown. + * + * Surviving is not a pass on its own: the first symptom is a warning, + * which stops nothing by itself. + * + * Usage: ve_vtty_stress [seconds] (default 600, or $VE_VTTY_SECS) + */ +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include + +#include "ve_selftest.h" +#include "../kselftest.h" + +/* The ioctl argument; the trailing data[] the uapi struct has is unused. */ +struct vzctl_ve_configure { + unsigned int veid, key, val, size; +}; + +#define VZCTL_VE_CONFIGURE _IOW('.', 15, struct vzctl_ve_configure) +#define VE_CONFIGURE_OPEN_TTY 5 + +#define VTTY_IDX 0 /* the pair both ends work on */ +#define SLAVE_MINOR (VTTY_IDX + 1) /* vtty_driver(): index = minor - 1 */ +#define DEFAULT_SECS 600 + +static int open_master(int ctl_fd, unsigned int veid) +{ + struct vzctl_ve_configure c = { + .veid = veid, .key = VE_CONFIGURE_OPEN_TTY, .val = VTTY_IDX, + }; + + return ioctl(ctl_fd, VZCTL_VE_CONFIGURE, &c); +} + +/* + * The slave end, inside the VE. Every error here is one the race is + * expected to produce: the pair is gone once the master has closed it. + */ +static int hammer_slave(time_t deadline, unsigned long *opened) +{ + char node[] = "/tmp/ve_vtty_slave"; + + unlink(node); + if (mknod(node, S_IFCHR | 0600, makedev(TTY_MAJOR, SLAVE_MINOR)) < 0) + return -1; + + while (time(NULL) < deadline) { + int fd = open(node, O_RDWR | O_NOCTTY | O_NONBLOCK); + + if (fd >= 0) { + (*opened)++; + close(fd); + } + } + unlink(node); + return 0; +} + +/* The kernel log, since a warning fails nothing by itself. */ +static int kmsg_open(void) +{ + int fd = open("/dev/kmsg", O_RDONLY | O_NONBLOCK); + + if (fd >= 0) + lseek(fd, 0, SEEK_END); + return fd; +} + +static int kmsg_check(int fd) +{ + static const char * const ours[] = { + "tty_release", "release_tty", "check_tty_count", + "vtty_open_master", "vtty_shutdown", + }; + char buf[8192]; + int found = 0; + ssize_t r; + size_t i; + + while (fd >= 0 && (r = read(fd, buf, sizeof(buf) - 1)) > 0) { + buf[r] = '\0'; + if (!strstr(buf, "WARNING") && !strstr(buf, "BUG") && + !strstr(buf, "Oops") && !strstr(buf, "general protection")) + continue; + for (i = 0; i < ARRAY_SIZE(ours); i++) + if (strstr(buf, ours[i])) { + ksft_print_msg("kernel log: %s", buf); + found++; + break; + } + } + return found; +} + +int main(int argc, char **argv) +{ + unsigned long master_opened = 0, slave_opened = 0; + int cg, ctid, ctl, kmsg, ready[2], count[2], status; + const char *env = getenv("VE_VTTY_SECS"); + char path[PATH_MAX], val[16], c; + time_t deadline; + long secs; + pid_t pid; + + ksft_print_header(); + secs = argc > 1 ? atol(argv[1]) : (env ? atol(env) : DEFAULT_SECS); + if (secs <= 0) + secs = DEFAULT_SECS; + + ctl = open("/dev/vzctl", O_RDWR); + if (ctl < 0) + ksft_exit_skip("cannot open /dev/vzctl: %s, the vz modules " + "(vzdev, vzmon) are needed\n", strerror(errno)); + + cg = mount_cg2_fd(); + if (cg < 0) + ksft_exit_skip("cannot mount cgroup2: %s\n", strerror(errno)); + write_file_at(cg, "cgroup.subtree_control", VE_CONTROLLERS); + + ctid = make_ve(cg, CTID_MIN); + if (ctid < 0) + ksft_exit_skip("cannot make a VE cgroup\n"); + + /* + * The pair is filed under the veid of the VE, so the two ends only + * meet if the cgroup carries the one the master is opened for. + */ + snprintf(path, sizeof(path), "%d/ve.veid", ctid); + snprintf(val, sizeof(val), "%d", ctid); + if (write_file_at(cg, path, val) < 0) { + destroy_ve(cg, ctid); + ksft_exit_skip("cannot set ve.veid: %s\n", strerror(errno)); + } + + /* Do not report a failure where the vtty pair cannot be had at all. */ + status = open_master(ctl, ctid); + if (status < 0 && errno != EBUSY) { + destroy_ve(cg, ctid); + ksft_exit_skip("no vtty master: %s\n", strerror(errno)); + } + if (status >= 0) + close(status); + + ksft_set_plan(1); + kmsg = kmsg_open(); + if (kmsg < 0) + ksft_print_msg("cannot read /dev/kmsg: %s, only a crash will " + "be noticed\n", strerror(errno)); + + if (pipe(ready) || pipe(count)) + ksft_exit_fail_msg("pipe: %s\n", strerror(errno)); + + deadline = time(NULL) + secs; + ksft_print_msg("VE %d, pair %d, %ld seconds\n", ctid, VTTY_IDX, secs); + + pid = fork(); + if (pid == 0) { + close(ready[0]); + close(count[0]); + if (enter_cgroup(cg, ctid) < 0) + _exit(1); + /* + * CLONE_NEWVE puts the task in the VE the cgroup names, so + * the slave lookup finds the pair. The pid namespace is + * what a VE wants of its first task. + */ + if (unshare(CLONE_NEWVE | CLONE_NEWPID | CLONE_NEWNS) < 0) + _exit(1); + if (fork() == 0) { + unsigned long n = 0; + + c = 'r'; + if (write(ready[1], &c, 1) != 1) + _exit(1); + if (hammer_slave(deadline, &n) < 0) + _exit(1); + if (write(count[1], &n, sizeof(n)) != sizeof(n)) + _exit(1); + _exit(0); + } + wait(&status); + _exit(WIFEXITED(status) ? WEXITSTATUS(status) : 1); + } + close(ready[1]); + close(count[1]); + + if (read(ready[0], &c, 1) != 1) { + waitpid(pid, &status, 0); + destroy_ve(cg, ctid); + ksft_exit_fail_msg("the VE side never came up\n"); + } + + while (time(NULL) < deadline) { + int fd = open_master(ctl, ctid); + + if (fd >= 0) { + master_opened++; + close(fd); + } + } + + if (read(count[0], &slave_opened, sizeof(slave_opened)) != + sizeof(slave_opened)) + slave_opened = 0; + waitpid(pid, &status, 0); + + ksft_print_msg("%lu master opens, %lu slave opens\n", + master_opened, slave_opened); + + status = kmsg_check(kmsg); + destroy_ve(cg, ctid); + + if (!master_opened) + ksft_exit_fail_msg("no master was ever opened\n"); + if (!slave_opened) + ksft_exit_fail_msg("no slave was ever opened\n"); + if (status) + ksft_exit_fail_msg("kernel complained %d time(s)\n", status); + + ksft_test_result_pass("vtty pair closed from both ends\n"); + ksft_finished(); +} -- 2.55.0 _______________________________________________ Devel mailing list Devel@openvz.org https://lists.openvz.org/mailman/listinfo/devel