On 12/19/25 19:28, Aleksei Oladko wrote:
Add selftests for the printk virtualization feature. The new tests cover
ve_printk, ve_printk_ratelimited, net_ve_ratelimited, and log buffer
overflow handling.

Entering the VE cgroup v1 is required until it is switched to cgroup v2.

Test functions (executed inside containers):
   - ve_printk_test_logct():     Verifies segfault messages are logged to 
container
   - ve_printk_test_logve0():    Verifies trap messages are logged to VE0
   - ve_printk_test_logboth():   Verifies net_veboth_ratelimited messages
   - ve_printk_test_ratelimit(): Verifies ve_printk_ratelimited(VE_LOG) messages
   - ve_printk_test_overflow():  Verifies log buffer overflow handling

https://virtuozzo.atlassian.net/browse/VSTOR-114252

v2:
   - added checks for the return values of system() and write()
   - added comments to clarify the code
   - removed the hunk that set up the gid mapping,
     as it is no required for creating the test container
v3:
   - added entering the VE namespace
   - fixed typos

v4: fixed typos

v5:
   - switched self-attachment to the ve controller from cgroup v1 to cgroup v2
   - made child_func return a posiive exit code on failure.
   - revorked entering the ve namespace to use clone 3 instead of unshare

Signed-off-by: Aleksei Oladko <[email protected]>
---
  tools/testing/selftests/Makefile              |   1 +
  tools/testing/selftests/ve_printk/.gitignore  |   3 +
  tools/testing/selftests/ve_printk/Makefile    |   8 +
  tools/testing/selftests/ve_printk/test_segf.c |  12 +
  tools/testing/selftests/ve_printk/test_trap.c |   5 +
  .../selftests/ve_printk/ve_printk_test.c      | 693 ++++++++++++++++++
  6 files changed, 722 insertions(+)
  create mode 100644 tools/testing/selftests/ve_printk/.gitignore
  create mode 100644 tools/testing/selftests/ve_printk/Makefile
  create mode 100644 tools/testing/selftests/ve_printk/test_segf.c
  create mode 100644 tools/testing/selftests/ve_printk/test_trap.c
  create mode 100644 tools/testing/selftests/ve_printk/ve_printk_test.c

diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile
index 363d031a16f7..7334fb207676 100644
--- a/tools/testing/selftests/Makefile
+++ b/tools/testing/selftests/Makefile
@@ -113,6 +113,7 @@ TARGETS += tty
  TARGETS += uevent
  TARGETS += user_events
  TARGETS += vDSO
+TARGETS += ve_printk
  TARGETS += mm
  TARGETS += x86
  TARGETS += zram
diff --git a/tools/testing/selftests/ve_printk/.gitignore 
b/tools/testing/selftests/ve_printk/.gitignore
new file mode 100644
index 000000000000..a4ad6620b803
--- /dev/null
+++ b/tools/testing/selftests/ve_printk/.gitignore
@@ -0,0 +1,3 @@
+ve_printk_test
+test_segf
+test_trap
diff --git a/tools/testing/selftests/ve_printk/Makefile 
b/tools/testing/selftests/ve_printk/Makefile
new file mode 100644
index 000000000000..e3edcbacda1e
--- /dev/null
+++ b/tools/testing/selftests/ve_printk/Makefile
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: GPL-2.0
+# Makefile for ve_printk selftests.
+CFLAGS = -g -I../../../../usr/include/ -Wall -O2
+
+TEST_GEN_PROGS += ve_printk_test
+TEST_GEN_FILES += test_segf test_trap
+
+include ../lib.mk
diff --git a/tools/testing/selftests/ve_printk/test_segf.c 
b/tools/testing/selftests/ve_printk/test_segf.c
new file mode 100644
index 000000000000..cdc89068ca06
--- /dev/null
+++ b/tools/testing/selftests/ve_printk/test_segf.c
@@ -0,0 +1,12 @@
+#include <stdio.h>
+#include <unistd.h>
+
+int main(void)
+{
+       int *p = (int *)0xffffffffffffff00;
+
+       printf("%d\n", getpid());
+       fflush(stdout);
+       *p = 1;
+       return 0;
+}
diff --git a/tools/testing/selftests/ve_printk/test_trap.c 
b/tools/testing/selftests/ve_printk/test_trap.c
new file mode 100644
index 000000000000..b774e2b9484c
--- /dev/null
+++ b/tools/testing/selftests/ve_printk/test_trap.c
@@ -0,0 +1,5 @@
+int main(void)
+{
+       __asm__("int3");
+       return 0;
+}
diff --git a/tools/testing/selftests/ve_printk/ve_printk_test.c 
b/tools/testing/selftests/ve_printk/ve_printk_test.c
new file mode 100644
index 000000000000..e150366ef580
--- /dev/null
+++ b/tools/testing/selftests/ve_printk/ve_printk_test.c
@@ -0,0 +1,693 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ve_printk selftests
+ *
+ * This file contains tests for the VE printk virtualization feature. It 
verifies
+ * that kernel messages are correctly routed to containers (VE_LOG), host 
(VE0_LOG),
+ * or both (VE_LOG_BOTH), and that rate limiting works correctly.
+ *
+ * Test functions (executed inside containers):
+ *   - ve_printk_test_logct():     Verifies segfault messages are logged to 
container
+ *   - ve_printk_test_logve0():    Verifies trap messages are logged to VE0
+ *   - ve_printk_test_logboth():   Verifies net_veboth_ratelimited messages
+ *   - ve_printk_test_ratelimit(): Verifies ve_printk_ratelimited(VE_LOG) 
messages
+ *   - ve_printk_test_overflow():  Verifies log buffer overflow handling
+ *
+ * Test cases:
+ *   - TEST_F(ve_printk, ve_log):              Verifies VE_LOG routing 
(segfault)
+ *   - TEST_F(ve_printk, ve0_log):             Verifies VE0_LOG routing (trap)
+ *   - TEST_F(ve_printk, ve_log_both):         Verifies VE_LOG_BOTH + ratelimit
+ *   - TEST_F(ve_printk, ve_printk_ratelimited): Verifies VE_LOG ratelimit
+ *   - TEST_F(ve_printk, ve_printk_overflow):   Verifies buffer overflow 
handling
+ */
+#define _GNU_SOURCE
+#include <linux/sched.h>
+#include <time.h>
+#include <sched.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <asm/unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/mount.h>
+#include <linux/limits.h>
+#include <errno.h>
+
+#include "../kselftest_harness.h"
+
+#define __STACK_SIZE (8 * 1024 * 1024)

non-used define, i'll drop it

+#define CTID_MIN 108
+#define CTID_MAX 200
+#define SEGFAULT_PROG "test_segf"
+#define TRAP_PROG "test_trap"
+#define TEST_RATELIMIT_BURST 10
+#define TEST_RATELIMIT 5
+
+static int has_substr(char *buf, const char *str)
+{
+       char *token;
+       char *str_ptr = buf;
+
+       while ((token = strsep(&str_ptr, ",")) != NULL) {
+               if (!strcmp(token, str))
+                       return 1;
+       }
+       return 0;
+}
+
+static int get_mount_path(const char *fstype, const char *subsys, char *out, 
int size)
+{
+       FILE *fp;
+       int n;
+       char buf[PATH_MAX];
+       char target[4096];
+       char ops[4096];
+       char format[4096];
+       int ret = 1;
+
+       snprintf(format, sizeof(format), "%%*s %%4095s %s %%4095s", fstype);
+
+       fp = fopen("/proc/mounts", "r");
+       if (fp == NULL)
+               return -1;
+
+       while (fgets(buf, sizeof(buf), fp)) {
+               n = sscanf(buf, format, target, ops);
+               if (n != 2)
+                       continue;
+               if (subsys == NULL || has_substr(ops, subsys)) {
+                       strncpy(out, target, size);
+                       out[size-1] = '\0';
+                       ret = 0;
+                       break;
+               }
+       }
+       fclose(fp);
+
+       return ret;
+}
+
+FIXTURE(ve_printk)
+{
+       char cgv2_path[PATH_MAX];
+       int ctid;
+};
+
+FIXTURE_SETUP(ve_printk)
+{
+       char path[PATH_MAX * 2];
+
+       ASSERT_EQ(get_mount_path("cgroup2", NULL, self->cgv2_path, 
sizeof(self->cgv2_path)), 0);
+
+       self->ctid = CTID_MIN;
+       while (self->ctid < CTID_MAX) {
+               snprintf(path, sizeof(path), "%s/%d", self->cgv2_path, 
self->ctid);
+               if (access(path, F_OK) && errno == ENOENT) {
+                       snprintf(path, sizeof(path), "%s/%d", self->cgv2_path, 
self->ctid);
+                       if (access(path, F_OK)) {
+                               break;
+                       }
+               }
+               self->ctid++;
+       }

  Explanation of the Fix

  Original code (lines 108-112):

     1 │if (access(path, F_OK) && errno == ENOENT) {
     2 │    snprintf(path, sizeof(path), "%s/%d", self->cgv2_path, self->ctid);
     3 │    if (access(path, F_OK)) {
     4 │        break;
     5 │    }
     6 │}

  Problems:
1. Incorrect condition: access(path, F_OK) && errno == ENOENT is wrong. access() returns 0 on success (file exists) and -1 on failure (file doesn't exist). In C, access(path, F_OK) in an if is true when non-zero (i.e., -1). So the condition means "file doesn't exist AND errno == ENOENT", but errno is only valid when access() returns -1. The
     check is redundant and confusing.
2. Code duplication: snprintf() and access() are called twice with the same arguments, which is unnecessary.

  Fixed code:

     1 │if (access(path, F_OK) != 0 && errno == ENOENT) {
     2 │    break;
     3 │}

  Why this is correct:
  • access(path, F_OK) != 0 explicitly checks for failure (file doesn't exist).
  • errno == ENOENT confirms the specific error (file/directory not found).
  • Removed duplicate snprintf() and access() calls.
• The logic is now clear: if the path doesn't exist (ENOENT), we found a free ctid and can break out of the loop.

  This correctly finds an available container ID by checking that the cgroup 
path doesn't exist yet.

i will handle this.

+       ASSERT_LT(self->ctid, CTID_MAX);
+
+       snprintf(path, sizeof(path), "%s/%d", self->cgv2_path, self->ctid);

This line is redundant i suppose.

i will remove it.

+       ASSERT_EQ(mkdir(path, 0755), 0);
+
+       snprintf(path, sizeof(path), "echo -ve > 
%s/%d/cgroup.controllers_hidden",
+                       self->cgv2_path, self->ctid);
+       ASSERT_EQ(system(path), 0);
+
+       snprintf(path, sizeof(path), "echo %d > %s/%d/ve.veid",
+                       self->ctid, self->cgv2_path, self->ctid);
+       ASSERT_EQ(system(path), 0);
+};
+
+FIXTURE_TEARDOWN(ve_printk)
+{
+       char path[PATH_MAX * 2];
+
+       snprintf(path, sizeof(path), "%s/%d/vz.slice", self->cgv2_path, 
self->ctid);
+       rmdir(path);
+       snprintf(path, sizeof(path), "%s/%d", self->cgv2_path, self->ctid);
+       rmdir(path);
+}
+
...
_______________________________________________
Devel mailing list
[email protected]
https://lists.openvz.org/mailman/listinfo/devel

Reply via email to