[PATCH 1/2] selftests/cgroup: read the entire file in read_text()
From: Shaojie Sun
Date: Mon Sep 21 2026 - 09:35:08 EST
read_text() issues a single read(2) and returns whatever it got, so a
caller that asks for a whole file silently gets a prefix of it instead.
For a seq_file larger than one page it never gets more than that prefix:
seq_read_iter() copies out at most one internal PAGE_SIZE buffer per call
and leaves the rest for the next read(2), no matter how much room the
caller's buffer has.
/proc/self/mounts is one of those files, so cg_find_root() only ever sees
the first 4K of the mount table. On a machine with ~80 mounts the cgroup2
entry already sits past that limit: the file is 7177 bytes, the single
read(2) into the 40K buffer in cg_find_root() returns 4035 bytes, and the
cgroup2 line starts at offset 4953. cg_find_root() then fails, and every
test that calls cg_find_unified_root() -- test_core, test_cpu, test_cpuset,
test_freezer, test_hugetlb_memcg, test_kill, test_kmem, test_memcontrol,
test_pids and test_zswap -- exits with SKIP "cgroup v2 isn't mounted"
without running a single test.
proc_mount_contains() searches only that same prefix, so a mount option
listed later in /proc/mounts is reported as absent, which is what the
probes in test_memcontrol and test_hugetlb_memcg are based on.
Read until the buffer is full or EOF instead. A partial read is still
possible when max_len is too small, so callers that deliberately read a
prefix, such as cg_read_strcmp(), keep working.
Signed-off-by: Shaojie Sun <sunshaojie@xxxxxxxxxx>
---
.../selftests/cgroup/lib/cgroup_util.c | 35 +++++++++++++++----
1 file changed, 29 insertions(+), 6 deletions(-)
diff --git a/tools/testing/selftests/cgroup/lib/cgroup_util.c b/tools/testing/selftests/cgroup/lib/cgroup_util.c
index 2596c12cd864..65cd85c467bb 100644
--- a/tools/testing/selftests/cgroup/lib/cgroup_util.c
+++ b/tools/testing/selftests/cgroup/lib/cgroup_util.c
@@ -24,20 +24,43 @@ bool cg_test_v1_named;
/* Returns read len on success, or -errno on failure. */
ssize_t read_text(const char *path, char *buf, size_t max_len)
{
- ssize_t len;
+ size_t total = 0;
+ ssize_t len, ret;
int fd;
fd = open(path, O_RDONLY);
if (fd < 0)
return -errno;
- len = read(fd, buf, max_len - 1);
-
- if (len >= 0)
- buf[len] = 0;
+ /*
+ * A single read() is not enough. procfs and sysfs are backed by
+ * seq_file, and seq_read_iter() copies out at most one internal
+ * buffer (PAGE_SIZE) per call, leaving the rest for the next read().
+ * Reading only once therefore silently drops everything past the
+ * first page, no matter how big the caller's buffer is.
+ *
+ * Loop until the buffer is full or EOF. A full buffer still means
+ * the file may be longer than max_len, but that is now limited by
+ * the caller's buffer rather than by a page of seq_file output.
+ */
+ while (total < max_len - 1) {
+ len = read(fd, buf + total, max_len - 1 - total);
+ if (len < 0) {
+ if (errno == EINTR)
+ continue;
+ ret = -errno;
+ goto out;
+ }
+ if (!len)
+ break;
+ total += len;
+ }
+ buf[total] = 0;
+ ret = total;
+out:
close(fd);
- return len < 0 ? -errno : len;
+ return ret;
}
/* Returns written len on success, or -errno on failure. */
--
2.50.1