From nobody Mon Sep 28 04:53:46 2026 Received: from stravinsky.debian.org (stravinsky.debian.org [82.195.75.108]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id E497A3C1F26; Wed, 26 Aug 2026 10:47:33 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=82.195.75.108 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1787741259; cv=none; b=RrRb2J1gh25d2TcxMwX6q/zisA3yo2GUhLYFpUL764Xw94jFDzEUOZ2h/KzsMgrEvTlCxNJwINpHKnRW1HEWVFnYgNeTcscNuS5kSVInwlZvN7M5a3BCF+EiMO8UE9/8ws+gOf8SeJoHfE6Nk7Buao8yAxefjuFp0bkrmnz2qEQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1787741259; c=relaxed/simple; bh=R0Jc5G8dbVSvocONbaEU81JeHaeFHc3batHC8Wrep4k=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=dIqP/ZpdteUpHkjtDIQ4Ks55gH4iv0LdRi+HrmlqtCNtIMrWu+0l12ZHkRg1W6/ShC9WV0i+m848W1wl5+Xx5rctyBVlW6dOJRoQv1fO3qhPs2LzJ7hkcOim8vHaM4yiRMeBni6UXsb8qzJmalgy/vY1lsT9Q1G7jqGFV59R6lo= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=debian.org; spf=pass smtp.mailfrom=debian.org; dkim=pass (2048-bit key) header.d=debian.org header.i=@debian.org header.b=rP5bBYGT; arc=none smtp.client-ip=82.195.75.108 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=debian.org Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=debian.org Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=debian.org header.i=@debian.org header.b="rP5bBYGT" DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=debian.org; s=smtpauto.stravinsky; h=X-Debian-User:Cc:To:In-Reply-To:References: Message-Id:Content-Transfer-Encoding:Content-Type:MIME-Version:Subject:Date: From:Reply-To:Content-ID:Content-Description; bh=40yjb18p74u31dH41pR2d6raPKWYVY5/NV+YejrNyBI=; b=rP5bBYGT53gxV2WXVSpR3zFmxa ELa/gyO1K0krlMqyWtt8O6nSgXL3sLm7kSpM8R29gydhvZkaTP8vLi1bn0taO7zf8FrK5zIALFEy3 P977ZGSVA1h5anPmCSkn8w9PQWwXcwWHwxVxzRn5Xe82YkLWdpxKJuiPVUqBfqIhOu376YvHNdEwt sWTJmPLY7m+jHOLLIORaWGPeZnOONsqecPIuyXGoMoL+O+u0kQpE6odzJv2F09JVxz4acd6+LJ3/y frkxKqWQxX6RVvyjIK6JRQfnGKn63d5ctZCQrcN9Wx2WLvgatAbt5kb8ESG0/xzejtZRl1ZN9L+km eq5OdP4A==; Received: from authenticated-user by stravinsky.debian.org with esmtpsa (TLS1.3:ECDHE_X25519__RSA_PSS_RSAE_SHA256__AES_256_GCM:256) (Exim 4.96) (envelope-from ) id 1wzBAB-00FA0j-2b; Wed, 26 Aug 2026 10:47:24 +0000 From: Breno Leitao Date: Wed, 26 Aug 2026 03:47:02 -0700 Subject: [PATCH 1/4] selftests/configfs: add tests for the userspace interface Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260826-config_selftest-v1-1-e364b07e91ac@debian.org> References: <20260826-config_selftest-v1-0-e364b07e91ac@debian.org> In-Reply-To: <20260826-config_selftest-v1-0-e364b07e91ac@debian.org> To: Shuah Khan , Andreas Hindborg Cc: linux-kernel@vger.kernel.org, linux-kselftest@vger.kernel.org, gustavold@gmail.com, linux-fsdevel@vger.kernel.org, Breno Leitao , kernel-team@meta.com, vasilisalmpanis@gmail.com X-Mailer: b4 0.16-dev-f8e9d X-Developer-Signature: v=1; a=openpgp-sha256; l=11481; i=leitao@debian.org; h=from:subject:message-id; bh=R0Jc5G8dbVSvocONbaEU81JeHaeFHc3batHC8Wrep4k=; b=owEBbQKS/ZANAwAIATWjk5/8eHdtAcsmYgBqjsQzDCy2sakmpk0wqGkMBTibVO9MaXpSq2B+p PpuL6FK1ZmJAjMEAAEIAB0WIQSshTmm6PRnAspKQ5s1o5Of/Hh3bQUCao7EMwAKCRA1o5Of/Hh3 baZ9D/9Kq2wYSDLkK9s/9XbOm6E6g5+hDqhAAGb0lYBRedKUVbCdmmy1Hb0NKHDrRBl6CIu5/n+ GkNlRce1//HYareA8FVuKsYvFQuUspDkVuoflmEuv6P5u31hcVQK3vFhaOrOrzCuG38SF52QK7a UEGo8EXfFqArIS1b0uMxey9XQrlQRZgYcf6IjxTVxiLwa43MQk19wKDrJbn72ocnR4EtWfINHXe oNepkcWwSdABRfhQpUEmd1Sf23L9GAw6z+UdX04JCaIQV5SEsjUbKKZY/OIgDlYh8nIjRupCj9K vBLJ7AGu7ttzvOygdMrbAhGahgCexR0JXvvf+hHxKgrg1i7LvxxZh4pWQbdEQE0BpO2k/koyndK QF6k86K+Ab7EFiltHdlWtQvpZu2clh3DDI6zIXAtDQu3xL16J8/XDRIZmG7M8O7Ap8IEBzo8Oi/ 8mQ1N7YoxX8FwP+7UGofNPi9qAvM7POokrd8vkII6P5A5JGdPAbq97mXShImAX1DRT1gDcY/Ajs 38bDNmFXdSx2hZJQjFGHH+FCyt94fm6hrmleNLT58MdkgOP8nC4TOuMMBz70s4JSC0HG60JsOR5 kCQGz4u5caECYpNd9DRWg4Py7vFkyYykY7f3DeOuqSETm0lSoF+sXaoVfjZvEOveZHmZWwKRYfy tEy82Ii/O8QIefw== X-Developer-Key: i=leitao@debian.org; a=openpgp; fpr=AC8539A6E8F46702CA4A439B35A3939FFC78776D X-Debian-User: leitao configfs has no selftest coverage. Its userspace ABI is entirely mkdir/rmdir/read/write on attribute files, and the error each of those returns is part of the contract. Add tests driving the subsystems samples/configfs registers, each in its own mount namespace on a private configfs mount. Signed-off-by: Breno Leitao --- MAINTAINERS | 1 + tools/testing/selftests/Makefile | 1 + .../selftests/filesystems/configfs/.gitignore | 2 + .../selftests/filesystems/configfs/Makefile | 8 + .../testing/selftests/filesystems/configfs/config | 5 + .../selftests/filesystems/configfs/configfs_test.c | 338 +++++++++++++++++= ++++ 6 files changed, 355 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index fe10d29b7657b..69aea9931b774 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6604,6 +6604,7 @@ S: Supported F: fs/configfs/ F: include/linux/configfs.h F: samples/configfs/ +F: tools/testing/selftests/filesystems/configfs/ =20 CONFIGFS [RUST] M: Andreas Hindborg diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Mak= efile index 2d960626750e3..b328a235f1295 100644 --- a/tools/testing/selftests/Makefile +++ b/tools/testing/selftests/Makefile @@ -34,6 +34,7 @@ TARGETS +=3D exec TARGETS +=3D fchmodat2 TARGETS +=3D filesystems TARGETS +=3D filesystems/binderfs +TARGETS +=3D filesystems/configfs TARGETS +=3D filesystems/epoll TARGETS +=3D filesystems/failfs TARGETS +=3D filesystems/fat diff --git a/tools/testing/selftests/filesystems/configfs/.gitignore b/tool= s/testing/selftests/filesystems/configfs/.gitignore new file mode 100644 index 0000000000000..accfb6bb48268 --- /dev/null +++ b/tools/testing/selftests/filesystems/configfs/.gitignore @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0-only +configfs_test diff --git a/tools/testing/selftests/filesystems/configfs/Makefile b/tools/= testing/selftests/filesystems/configfs/Makefile new file mode 100644 index 0000000000000..359296356c831 --- /dev/null +++ b/tools/testing/selftests/filesystems/configfs/Makefile @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: GPL-2.0 +# Copyright (c) 2026 Meta Platforms, Inc. and affiliates +# Copyright (c) 2026 Breno Leitao + +CFLAGS +=3D -Wall -Werror +TEST_GEN_PROGS :=3D configfs_test + +include ../../lib.mk diff --git a/tools/testing/selftests/filesystems/configfs/config b/tools/te= sting/selftests/filesystems/configfs/config new file mode 100644 index 0000000000000..5ea17df535b3f --- /dev/null +++ b/tools/testing/selftests/filesystems/configfs/config @@ -0,0 +1,5 @@ +CONFIG_CONFIGFS_FS=3Dy +CONFIG_MODULES=3Dy +CONFIG_MODULE_UNLOAD=3Dy +CONFIG_SAMPLES=3Dy +CONFIG_SAMPLE_CONFIGFS=3Dm diff --git a/tools/testing/selftests/filesystems/configfs/configfs_test.c b= /tools/testing/selftests/filesystems/configfs/configfs_test.c new file mode 100644 index 0000000000000..072d0dcf3f96b --- /dev/null +++ b/tools/testing/selftests/filesystems/configfs/configfs_test.c @@ -0,0 +1,338 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Exercise the configfs userspace interface through the three subsystems + * registered by samples/configfs. + * + * Copyright (c) 2026 Meta Platforms, Inc. and affiliates + * Copyright (c) 2026 Breno Leitao + */ +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "kselftest_harness.h" + +/* Private to fs/configfs/mount.c. */ +#define CONFIGFS_MAGIC 0x62656570 + +#define SAMPLE_MODULE "configfs_sample" + +#define CHILDLESS "01-childless" +#define SIMPLE "02-simple-children" +#define GROUPS "03-group-children" + +#define ITEM_A SIMPLE "/kselftest-a" +#define ITEM_B SIMPLE "/kselftest-b" +#define GROUP GROUPS "/kselftest-group" +#define GROUP_ITEM GROUP "/kselftest-a" + +/* Deepest first, so one pass empties the tree. */ +static const char * const test_dirs[] =3D { + GROUP_ITEM, + GROUP, + ITEM_A, + ITEM_B, +}; + +static void drop_test_dirs(void) +{ + size_t i; + + for (i =3D 0; i < ARRAY_SIZE(test_dirs); i++) + rmdir(test_dirs[i]); +} + +static ssize_t read_attr(const char *path, char *buf, size_t len) +{ + ssize_t ret; + int fd; + + fd =3D open(path, O_RDONLY); + if (fd < 0) + return -1; + + ret =3D read(fd, buf, len - 1); + close(fd); + if (ret < 0) + return -1; + + buf[ret] =3D '\0'; + return ret; +} + +static ssize_t write_attr(const char *path, const char *val) +{ + ssize_t ret; + int fd, err; + + fd =3D open(path, O_WRONLY); + if (fd < 0) + return -1; + + ret =3D write(fd, val, strlen(val)); + err =3D errno; + close(fd); + errno =3D err; + + return ret; +} + +FIXTURE(configfs) { + char mnt[sizeof(P_tmpdir "/configfs_XXXXXX")]; + bool mounted; +}; + +FIXTURE_SETUP(configfs) +{ + char tmpl[] =3D P_tmpdir "/configfs_XXXXXX"; + + if (geteuid()) + SKIP(return, "need root to load modules and mount configfs"); + + ASSERT_EQ(system("modprobe -q " SAMPLE_MODULE), 0) + TH_LOG(SAMPLE_MODULE " missing, is CONFIG_SAMPLE_CONFIGFS=3Dm?"); + + ASSERT_EQ(unshare(CLONE_NEWNS), 0); + ASSERT_EQ(mount(NULL, "/", NULL, MS_REC | MS_PRIVATE, NULL), 0); + + ASSERT_NE(mkdtemp(tmpl), NULL); + strcpy(self->mnt, tmpl); + + ASSERT_EQ(mount("configfs", self->mnt, "configfs", 0, NULL), 0); + ASSERT_EQ(chdir(self->mnt), 0); + self->mounted =3D true; + + /* configfs items outlive the mount, so a killed run leaves some. */ + drop_test_dirs(); +} + +FIXTURE_TEARDOWN(configfs) +{ + if (self->mounted) { + drop_test_dirs(); + EXPECT_EQ(chdir("/"), 0); + EXPECT_EQ(umount2(self->mnt, MNT_DETACH), 0); + } + + if (self->mnt[0]) + EXPECT_EQ(rmdir(self->mnt), 0); +} + +TEST_F(configfs, mount_and_subsystems) +{ + const char * const subsys[] =3D { CHILDLESS, SIMPLE, GROUPS }; + struct statfs sfs; + struct stat st; + size_t i; + + ASSERT_EQ(statfs(".", &sfs), 0); + EXPECT_EQ(sfs.f_type, CONFIGFS_MAGIC); + + for (i =3D 0; i < ARRAY_SIZE(subsys); i++) { + ASSERT_EQ(stat(subsys[i], &st), 0) + TH_LOG("%s is missing", subsys[i]); + EXPECT_TRUE(S_ISDIR(st.st_mode)); + } +} + +TEST_F(configfs, mkdir_at_root) +{ + /* The root has no ->mkdir(); only subsystems register there. */ + ASSERT_EQ(mkdir("kselftest-root", 0755), -1); + EXPECT_EQ(errno, EPERM); +} + +TEST_F(configfs, rmdir_subsystem) +{ + ASSERT_EQ(rmdir(CHILDLESS), -1); + EXPECT_EQ(errno, EPERM); +} + +TEST_F(configfs, mkdir_without_group_ops) +{ + /* 01-childless has attributes but no ->make_item()/->make_group(). */ + ASSERT_EQ(mkdir(CHILDLESS "/kselftest-a", 0755), -1); + EXPECT_EQ(errno, EPERM); +} + +TEST_F(configfs, attr_store_and_show) +{ + char buf[64]; + + ASSERT_GT(write_attr(CHILDLESS "/storeme", "42"), 0); + ASSERT_GT(read_attr(CHILDLESS "/storeme", buf, sizeof(buf)), 0); + EXPECT_STREQ(buf, "42\n"); +} + +TEST_F(configfs, attr_store_rejects_garbage) +{ + ASSERT_EQ(write_attr(CHILDLESS "/storeme", "not-a-number"), -1); + EXPECT_EQ(errno, EINVAL); +} + +TEST_F(configfs, attr_show_runs_on_every_open) +{ + char first[64], second[64]; + + /* 01-childless/showme increments the value it just returned. */ + ASSERT_GT(read_attr(CHILDLESS "/showme", first, sizeof(first)), 0); + ASSERT_GT(read_attr(CHILDLESS "/showme", second, sizeof(second)), 0); + EXPECT_EQ(atoi(second), atoi(first) + 1); +} + +TEST_F(configfs, attr_read_only) +{ + ASSERT_EQ(open(CHILDLESS "/description", O_WRONLY), -1); + EXPECT_EQ(errno, EACCES); +} + +TEST_F(configfs, attr_unlink) +{ + /* ->unlink() only accepts the symlinks configfs itself created. */ + ASSERT_EQ(unlink(CHILDLESS "/storeme"), -1); + EXPECT_EQ(errno, EPERM); +} + +TEST_F(configfs, attr_read_length) +{ + char buf[8192]; + struct stat st; + ssize_t n; + int fd; + + fd =3D open(CHILDLESS "/description", O_RDONLY); + ASSERT_GE(fd, 0); + + /* Attributes report a page, whatever ->show() ends up producing. */ + ASSERT_EQ(fstat(fd, &st), 0); + EXPECT_EQ(st.st_size, sysconf(_SC_PAGESIZE)); + + n =3D read(fd, buf, sizeof(buf)); + ASSERT_GT(n, 0); + EXPECT_LT(n, st.st_size); + EXPECT_EQ(read(fd, buf, sizeof(buf)), 0); + + EXPECT_EQ(close(fd), 0); +} + +TEST_F(configfs, attr_write_is_not_incremental) +{ + char buf[64]; + int fd; + + /* + * Every write hands the whole buffer to ->store() and the file + * position is ignored, so the second write replaces the first. + */ + fd =3D open(CHILDLESS "/storeme", O_WRONLY); + ASSERT_GE(fd, 0); + ASSERT_EQ(write(fd, "1", 1), 1); + ASSERT_EQ(write(fd, "2", 1), 1); + EXPECT_EQ(close(fd), 0); + + ASSERT_GT(read_attr(CHILDLESS "/storeme", buf, sizeof(buf)), 0); + EXPECT_STREQ(buf, "2\n"); +} + +TEST_F(configfs, item_create_and_drop) +{ + struct stat st; + + ASSERT_EQ(mkdir(ITEM_A, 0755), 0); + EXPECT_EQ(stat(ITEM_A "/storeme", &st), 0); + + /* The item carries its own attributes, not the subsystem's. */ + ASSERT_EQ(stat(ITEM_A "/description", &st), -1); + EXPECT_EQ(errno, ENOENT); + + ASSERT_EQ(rmdir(ITEM_A), 0); + ASSERT_EQ(stat(ITEM_A, &st), -1); + EXPECT_EQ(errno, ENOENT); +} + +TEST_F(configfs, item_create_twice) +{ + ASSERT_EQ(mkdir(ITEM_A, 0755), 0); + ASSERT_EQ(mkdir(ITEM_A, 0755), -1); + EXPECT_EQ(errno, EEXIST); +} + +TEST_F(configfs, item_has_no_children) +{ + /* ->make_item() produces an item, so it cannot nest. */ + ASSERT_EQ(mkdir(ITEM_A, 0755), 0); + ASSERT_EQ(mkdir(ITEM_A "/kselftest-b", 0755), -1); + EXPECT_EQ(errno, EPERM); +} + +TEST_F(configfs, item_attrs_are_private) +{ + char buf[64]; + + ASSERT_EQ(mkdir(ITEM_A, 0755), 0); + ASSERT_EQ(mkdir(ITEM_B, 0755), 0); + + ASSERT_GT(write_attr(ITEM_A "/storeme", "11"), 0); + ASSERT_GT(write_attr(ITEM_B "/storeme", "22"), 0); + + ASSERT_GT(read_attr(ITEM_A "/storeme", buf, sizeof(buf)), 0); + EXPECT_STREQ(buf, "11\n"); + ASSERT_GT(read_attr(ITEM_B "/storeme", buf, sizeof(buf)), 0); + EXPECT_STREQ(buf, "22\n"); +} + +TEST_F(configfs, group_create_and_drop) +{ + struct stat st; + + /* 03-group-children hands out groups that take items of their own. */ + ASSERT_EQ(mkdir(GROUP, 0755), 0); + EXPECT_EQ(stat(GROUP "/description", &st), 0); + + ASSERT_EQ(mkdir(GROUP_ITEM, 0755), 0); + EXPECT_EQ(stat(GROUP_ITEM "/storeme", &st), 0); + + ASSERT_EQ(rmdir(GROUP), -1); + EXPECT_EQ(errno, ENOTEMPTY); + + ASSERT_EQ(rmdir(GROUP_ITEM), 0); + ASSERT_EQ(rmdir(GROUP), 0); +} + +TEST_F(configfs, rename_item) +{ + ASSERT_EQ(mkdir(ITEM_A, 0755), 0); + ASSERT_EQ(rename(ITEM_A, ITEM_B), -1); + EXPECT_EQ(errno, EPERM); +} + +TEST_F(configfs, symlink_without_allow_link) +{ + ASSERT_EQ(mkdir(ITEM_A, 0755), 0); + ASSERT_EQ(symlink(ITEM_A, SIMPLE "/kselftest-link"), -1); + EXPECT_EQ(errno, EPERM); +} + +TEST_F(configfs, module_pinned_by_item) +{ + ASSERT_EQ(mkdir(ITEM_A, 0755), 0); + + /* mkdir() pins both the subsystem's module and the new item's. */ + ASSERT_EQ(syscall(__NR_delete_module, SAMPLE_MODULE, O_NONBLOCK), -1); + if (errno =3D=3D ENOSYS) + SKIP(return, "kernel built without CONFIG_MODULE_UNLOAD"); + EXPECT_EQ(errno, EWOULDBLOCK); +} + +TEST_HARNESS_MAIN --=20 2.53.0-Meta From nobody Mon Sep 28 04:53:46 2026 Received: from stravinsky.debian.org (stravinsky.debian.org [82.195.75.108]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 8C7AB3E00B4; Wed, 26 Aug 2026 10:47:31 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=82.195.75.108 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1787741259; cv=none; b=tkx7DeZNMQEY4p5HQqswn9+JL1xI1e/BLlEpv+VDO7mWdI/O1JGpeUpo39O6lGBdxU3QWxUeEm/qglom7fEulFEmlXHQLOl9B+rLcAFqHT7wlI7pU/dXeEoWIepPr/NGwgvacNj8OmPFhBQopFbMKagN5ML1q4D08iGAnrMhL+c= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1787741259; c=relaxed/simple; bh=S4DPjK3jIGGJ/C0gVoovxcEwKAcMxJB0rJtZf3g4pg4=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=FIPDkRdr1Jm14IeLpAtOjuwSrZ46WMiToUtIYWA/cH6pv97M9L5iqSJEwL3KSI2FmR9vrQfbgWSxBkW0SFUtKZYDP+oEc7ssPPOVtqWJ1TOCWouwsoWh//DC8g2dpEcZxvI+wBtZiFSguhjcjzMbnV1j1TzqCAZlLxNpKkW/kVQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=debian.org; spf=pass smtp.mailfrom=debian.org; dkim=pass (2048-bit key) header.d=debian.org header.i=@debian.org header.b=I4kERVVZ; arc=none smtp.client-ip=82.195.75.108 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=debian.org Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=debian.org Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=debian.org header.i=@debian.org header.b="I4kERVVZ" DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=debian.org; s=smtpauto.stravinsky; h=X-Debian-User:Cc:To:In-Reply-To:References: Message-Id:Content-Transfer-Encoding:Content-Type:MIME-Version:Subject:Date: From:Reply-To:Content-ID:Content-Description; bh=oqzejDd9vzId1IO7Db04ZVtuc3tXVUqp9W2Dq8YmPK0=; b=I4kERVVZVum4DGj61JyBeq5ZO/ 7/CwRtKmhB1zAUKbhpSIYIjzM0SFAzr9vtjwbbXSsnfs8t6gzsabKzxIspXE2kZWjUDPfG4qzv428 QXJNVHy+NJxwwAYnh4NtGaOFYE6bFZrHrQqzo5z+gc5vo0Jr26IWNfAJ/36HPq4t2fnV48ZaK+3GT MzwYENT2HO6fVMIBijnPw1oK6JCUXmrKXtYwc8lTfrXJg5i+ZgI0Dbq6ZapyEiQc5P1tB5DFNTb2+ ESCg0ZSAPIIRyDUv5uLN1UsrgzwR+dFup6o3Ej7ZAhJFgR1RyK8dCWx7wSTFUkXtSQX76TaZ7XwER JiAca2TQ==; Received: from authenticated-user by stravinsky.debian.org with esmtpsa (TLS1.3:ECDHE_X25519__RSA_PSS_RSAE_SHA256__AES_256_GCM:256) (Exim 4.96) (envelope-from ) id 1wzBAF-00FA0t-1J; Wed, 26 Aug 2026 10:47:27 +0000 From: Breno Leitao Date: Wed, 26 Aug 2026 03:47:03 -0700 Subject: [PATCH 2/4] samples/configfs: add a subsystem that accepts symlinks Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260826-config_selftest-v1-2-e364b07e91ac@debian.org> References: <20260826-config_selftest-v1-0-e364b07e91ac@debian.org> In-Reply-To: <20260826-config_selftest-v1-0-e364b07e91ac@debian.org> To: Shuah Khan , Andreas Hindborg Cc: linux-kernel@vger.kernel.org, linux-kselftest@vger.kernel.org, gustavold@gmail.com, linux-fsdevel@vger.kernel.org, Breno Leitao , kernel-team@meta.com, vasilisalmpanis@gmail.com X-Mailer: b4 0.16-dev-f8e9d X-Developer-Signature: v=1; a=openpgp-sha256; l=4457; i=leitao@debian.org; h=from:subject:message-id; bh=S4DPjK3jIGGJ/C0gVoovxcEwKAcMxJB0rJtZf3g4pg4=; b=owEBbQKS/ZANAwAIATWjk5/8eHdtAcsmYgBqjsQz21/UyPcl87Dez5CZ5Vq/9C0bhiIGU4lTW ae9RjAKRHiJAjMEAAEIAB0WIQSshTmm6PRnAspKQ5s1o5Of/Hh3bQUCao7EMwAKCRA1o5Of/Hh3 baMlD/0Y8zdnerYGCGikoGok4UoH+bLY5YyUC8l45KaWmmrpKcdH0zBBDCvPf0DxFCOVidUJC7F 3/hPtCPzpwF7p78Euptna3XZuhXoXmlbYcETEDtxcqGrzDxA0QkMgskTV1XynM3XG4p67HjIgA+ 9Yy4fColOj7r7f+d1YVSTAPeF3lyZH84zwoydU/jSVEYwCClujtpNLq30+w26ttRGW2lMl2a/cF 9/aqJmwMcjWxUv7c9yo6JWuSCNuptPpODP26B2S6p/CZ9yzIKelgxgACob6iWEwirFYJKgbT8ug xH6zVxZT5Nv+WCne8/QV5fo4/7sllOjTk5qSDad0shjBF21HRxZMsC/Vx38IJnSPiIpqjnFqxk2 vw7fAN5ai/QElZkS8MNKOdQnxtFXiDu4b6Y5x0RO3c/TvmySgyKfZdZoM+vVfHG6BYSPMybh5X0 rxG2fXfBuzP6G3jESQuC5njY9fzlw89ZalmYiw+pE1PqT8etjKjXWKiZeKbJUh4TWQdMxos0KM/ paycX3+j2zjJe/SkvSOKup/8YDiygcNlLhsuKmlwMkg8Zaxuk7JfySIIFf3bfNtWOdYmhh8qilZ Fd/YkPEPApszN3o1zNtuS04OPVYA6wWstdEHvXCcmu/0iyrU9e40kvus/Qe1Vb48iqO7fvKtHmO mUuP+Cys90lm8dg== X-Developer-Key: i=leitao@debian.org; a=openpgp; fpr=AC8539A6E8F46702CA4A439B35A3939FFC78776D X-Debian-User: leitao The sample registers three subsystems and none implements ->allow_link(), so the symlink(2) half of the configfs ABI has no small in-tree user to test against. Add 04-symlink-children, whose children take a link to any config_item and export the number of links they hold. Signed-off-by: Breno Leitao --- samples/configfs/configfs_sample.c | 121 +++++++++++++++++++++++++++++++++= ++++ 1 file changed, 121 insertions(+) diff --git a/samples/configfs/configfs_sample.c b/samples/configfs/configfs= _sample.c index c1b108ec4ea02..08403e787aef6 100644 --- a/samples/configfs/configfs_sample.c +++ b/samples/configfs/configfs_sample.c @@ -313,6 +313,126 @@ static struct configfs_subsystem group_children_subsy= s =3D { =20 /* ----------------------------------------------------------------- */ =20 +/* + * 04-symlink-children + * + * This example has children that are valid sources for symlink(2). A + * child accepts a link to any other config_item and reports how many + * links it currently holds, so ->allow_link() and ->drop_link() are + * observable from userspace. + */ + +struct symlink_child { + struct config_item item; + int nlinks; +}; + +static inline struct symlink_child *to_symlink_child(struct config_item *i= tem) +{ + return container_of(item, struct symlink_child, item); +} + +static ssize_t symlink_child_nlinks_show(struct config_item *item, char *p= age) +{ + return sprintf(page, "%d\n", to_symlink_child(item)->nlinks); +} + +CONFIGFS_ATTR_RO(symlink_child_, nlinks); + +static struct configfs_attribute *symlink_child_attrs[] =3D { + &symlink_child_attr_nlinks, + NULL, +}; + +/* + * The VFS holds the source item's directory locked across symlink(2) and + * unlink(2), so ->nlinks needs no lock of its own. + */ +static int symlink_child_allow_link(struct config_item *src, + struct config_item *target) +{ + to_symlink_child(src)->nlinks++; + + return 0; +} + +static void symlink_child_drop_link(struct config_item *src, + struct config_item *target) +{ + to_symlink_child(src)->nlinks--; +} + +static void symlink_child_release(struct config_item *item) +{ + kfree(to_symlink_child(item)); +} + +static const struct configfs_item_operations symlink_child_item_ops =3D { + .release =3D symlink_child_release, + .allow_link =3D symlink_child_allow_link, + .drop_link =3D symlink_child_drop_link, +}; + +static const struct config_item_type symlink_child_type =3D { + .ct_item_ops =3D &symlink_child_item_ops, + .ct_attrs =3D symlink_child_attrs, + .ct_owner =3D THIS_MODULE, +}; + +static struct config_item *symlink_children_make_item( + struct config_group *group, const char *name) +{ + struct symlink_child *symlink_child; + + symlink_child =3D kzalloc_obj(*symlink_child, GFP_KERNEL); + if (!symlink_child) + return ERR_PTR(-ENOMEM); + + config_item_init_type_name(&symlink_child->item, name, + &symlink_child_type); + + return &symlink_child->item; +} + +static ssize_t symlink_children_description_show(struct config_item *item, + char *page) +{ + return sprintf(page, +"[04-symlink-children]\n" +"\n" +"This subsystem allows the creation of child config_items that\n" +"symlink(2) can point at other config_items from. Each child\n" +"reports the number of links it holds.\n"); +} + +CONFIGFS_ATTR_RO(symlink_children_, description); + +static struct configfs_attribute *symlink_children_attrs[] =3D { + &symlink_children_attr_description, + NULL, +}; + +static const struct configfs_group_operations symlink_children_group_ops = =3D { + .make_item =3D symlink_children_make_item, +}; + +static const struct config_item_type symlink_children_type =3D { + .ct_group_ops =3D &symlink_children_group_ops, + .ct_attrs =3D symlink_children_attrs, + .ct_owner =3D THIS_MODULE, +}; + +static struct configfs_subsystem symlink_children_subsys =3D { + .su_group =3D { + .cg_item =3D { + .ci_namebuf =3D "04-symlink-children", + .ci_type =3D &symlink_children_type, + }, + }, +}; + +/* ----------------------------------------------------------------- */ + /* * We're now done with our subsystem definitions. * For convenience in this module, here's a list of them all. It @@ -324,6 +444,7 @@ static struct configfs_subsystem *example_subsys[] =3D { &childless_subsys.subsys, &simple_children_subsys, &group_children_subsys, + &symlink_children_subsys, NULL, }; =20 --=20 2.53.0-Meta From nobody Mon Sep 28 04:53:46 2026 Received: from stravinsky.debian.org (stravinsky.debian.org [82.195.75.108]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 237513E51FE; Wed, 26 Aug 2026 10:47:35 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=82.195.75.108 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1787741263; cv=none; b=bTeV1Ue+LmuDZE3Ao3iq6R4/70GJ4AGZ8sBf8iV+745b/+qU2ZQ9qnKxscX7BAp3IGDwnxlcVieo2F5JnE7oaisxTmDoOKtUDzShwTqpc/ItugQO/UAoDsEODt/ZxGBwdnV7VdVrRsuFdLXbo8CDIG1HLkiWUxkYWUw1UwCnuBI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1787741263; c=relaxed/simple; bh=EsCfNGDMAD0kqB2bJvJPVAMQnbDdNoA3cfyCUB4ulu0=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=qetruMOZ6KpgwsBOqSFRTsexytMLnZsbbfC+cB2C9NgY6dTIQztwg8MnFSZ2kYU54vqoGHHmV3h/DsF8WXJ/tqHEzhF5XRhfnrdYrknBe7XsIHniw5B1i5VjtTcWafpkauoVyuhfSFeV69k7KIxr947bxdCMCyXNfO31/dAT6yI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=debian.org; spf=pass smtp.mailfrom=debian.org; dkim=pass (2048-bit key) header.d=debian.org header.i=@debian.org header.b=p87huBnn; arc=none smtp.client-ip=82.195.75.108 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=debian.org Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=debian.org Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=debian.org header.i=@debian.org header.b="p87huBnn" DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=debian.org; s=smtpauto.stravinsky; h=X-Debian-User:Cc:To:In-Reply-To:References: Message-Id:Content-Transfer-Encoding:Content-Type:MIME-Version:Subject:Date: From:Reply-To:Content-ID:Content-Description; bh=VQVWFa01CPFDyXs47ACU6VBWm1qbgJ5kbePtNrURpkQ=; b=p87huBnn0HXuiAqTqalC8qEBqZ fsBngg3Foh2e04znsjcMVyPqFknD/5UC0oQmQRaQsafZqvU8dVZ6odW/zggpHE4+W4IdtTF/fWauy KKgeoaZ7rReRnTP31YBtryKXqBpnrrLL5gaKVnQ5vOKTK5C0mwcKTM16vScHmHPmJQQ1SmvEyJiK/ xbhoat5fg1Ws0xAlLJW5vXRU7AFd+GC5VEJrfaMTeylVX2X64WZRhVHg31zzUp9hFS0M4qCpTMW8Z b6AgJfirSQZV9d2ZfQMKjJISy8NKn3N1TvstkF+6Wl/ee6utw2WbbX8s61rlN3/Ax+gDIt+sdUBYE Zg24As4g==; Received: from authenticated-user by stravinsky.debian.org with esmtpsa (TLS1.3:ECDHE_X25519__RSA_PSS_RSAE_SHA256__AES_256_GCM:256) (Exim 4.96) (envelope-from ) id 1wzBAJ-00FA18-02; Wed, 26 Aug 2026 10:47:31 +0000 From: Breno Leitao Date: Wed, 26 Aug 2026 03:47:04 -0700 Subject: [PATCH 3/4] selftests/configfs: cover symlink and unlink Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260826-config_selftest-v1-3-e364b07e91ac@debian.org> References: <20260826-config_selftest-v1-0-e364b07e91ac@debian.org> In-Reply-To: <20260826-config_selftest-v1-0-e364b07e91ac@debian.org> To: Shuah Khan , Andreas Hindborg Cc: linux-kernel@vger.kernel.org, linux-kselftest@vger.kernel.org, gustavold@gmail.com, linux-fsdevel@vger.kernel.org, Breno Leitao , kernel-team@meta.com, vasilisalmpanis@gmail.com X-Mailer: b4 0.16-dev-f8e9d X-Developer-Signature: v=1; a=openpgp-sha256; l=4576; i=leitao@debian.org; h=from:subject:message-id; bh=EsCfNGDMAD0kqB2bJvJPVAMQnbDdNoA3cfyCUB4ulu0=; b=owEBbQKS/ZANAwAIATWjk5/8eHdtAcsmYgBqjsQz/hePnaPXqpVkzcWaZVOY88OTjEBJTpz48 goLeTDxOtOJAjMEAAEIAB0WIQSshTmm6PRnAspKQ5s1o5Of/Hh3bQUCao7EMwAKCRA1o5Of/Hh3 bT+OD/9lFh69T1a8+mXdx2+tpIMeYAFdbcvEJeYYcC0U7UNYswF/oN/P3xYCVnB3190AfJPNwct ZPfnrpVZC6VWAAdGjlx/7wrcDFqJ+jdfp9HvNZl+eGh9nXYwyCV9KGSYt+6TJtQ5iKtHwP/ehp/ 0BXONonHJveSlLZ05zVhti/fWq6VlF/yFZVSCXC/kDzCbqKShIbmKe6BZgjCsRNi6VZ6F0VFYF2 XkXYWCrGll4wbpNJQw9ZWn7qT9STeLE+d5oEpDwPFpZ+MQOIr7O7/qGrjNaFqHJfbAWzJJT5GL0 Ut4z9FFvBFzPRn/2UnftuKLeegSdd28A6mGgfTkpm0LyQvYxoUhFsoyUm9U7NpNP6g/vVrCOHXO uZIqOMWVcnSTiQZXUnvNvXmGazjOk45V2E/3u9inm2SY3yeGW6mFCrV76UdmZG25ZiAZqXtgBbm G6s5NibSdBrGmf6vL+JS/n3bjZipn3LGt8PYSSzJIHly5Mu22NX6K/Kh20rlTnPai1dTUJu+wnM IrH4b3/xdjZURVXcGHl8u1fI5Ph9U3JFSJV06SrwKbSQ4vxPcP669f1BtYFAAN09rwffgvpk8fs rdXIvhRbXsAw4Nnoi0A8DQPEnkLJfH223FTli1bdmGPcQA8k4DMqmqGgCBdVPoR8UtB3uIGEOXq lzPcvX/FogNaYyg== X-Developer-Key: i=leitao@debian.org; a=openpgp; fpr=AC8539A6E8F46702CA4A439B35A3939FFC78776D X-Debian-User: leitao samples/configfs had nothing to link from, so symlink(2) went untested beyond the EPERM a subsystem without ->allow_link() returns. Cover creating and removing a link, the relative body configfs stores, the count ->allow_link() and ->drop_link() keep, the busy errors both ends return, and what get_target() rejects. Signed-off-by: Breno Leitao --- .../selftests/filesystems/configfs/configfs_test.c | 85 ++++++++++++++++++= +++- 1 file changed, 83 insertions(+), 2 deletions(-) diff --git a/tools/testing/selftests/filesystems/configfs/configfs_test.c b= /tools/testing/selftests/filesystems/configfs/configfs_test.c index 072d0dcf3f96b..9b15e1fd69e5b 100644 --- a/tools/testing/selftests/filesystems/configfs/configfs_test.c +++ b/tools/testing/selftests/filesystems/configfs/configfs_test.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Exercise the configfs userspace interface through the three subsystems + * Exercise the configfs userspace interface through the subsystems * registered by samples/configfs. * * Copyright (c) 2026 Meta Platforms, Inc. and affiliates @@ -10,6 +10,7 @@ =20 #include #include +#include #include #include #include @@ -31,16 +32,24 @@ #define CHILDLESS "01-childless" #define SIMPLE "02-simple-children" #define GROUPS "03-group-children" +#define SYMLINKS "04-symlink-children" =20 #define ITEM_A SIMPLE "/kselftest-a" #define ITEM_B SIMPLE "/kselftest-b" #define GROUP GROUPS "/kselftest-group" #define GROUP_ITEM GROUP "/kselftest-a" +#define LINK_SRC SYMLINKS "/kselftest-src" +#define LINK LINK_SRC "/kselftest-link" + +static const char * const test_links[] =3D { + LINK, +}; =20 /* Deepest first, so one pass empties the tree. */ static const char * const test_dirs[] =3D { GROUP_ITEM, GROUP, + LINK_SRC, ITEM_A, ITEM_B, }; @@ -49,6 +58,10 @@ static void drop_test_dirs(void) { size_t i; =20 + /* Links first: they hold both their source and their target. */ + for (i =3D 0; i < ARRAY_SIZE(test_links); i++) + unlink(test_links[i]); + for (i =3D 0; i < ARRAY_SIZE(test_dirs); i++) rmdir(test_dirs[i]); } @@ -131,7 +144,7 @@ FIXTURE_TEARDOWN(configfs) =20 TEST_F(configfs, mount_and_subsystems) { - const char * const subsys[] =3D { CHILDLESS, SIMPLE, GROUPS }; + const char * const subsys[] =3D { CHILDLESS, SIMPLE, GROUPS, SYMLINKS }; struct statfs sfs; struct stat st; size_t i; @@ -324,6 +337,74 @@ TEST_F(configfs, symlink_without_allow_link) EXPECT_EQ(errno, EPERM); } =20 +TEST_F(configfs, symlink_and_unlink) +{ + char buf[PATH_MAX]; + struct stat st; + ssize_t n; + + ASSERT_EQ(mkdir(ITEM_A, 0755), 0); + ASSERT_EQ(mkdir(LINK_SRC, 0755), 0); + + ASSERT_EQ(symlink(ITEM_A, LINK), 0); + + /* configfs stores its own body, a path relative to the link. */ + n =3D readlink(LINK, buf, sizeof(buf) - 1); + ASSERT_GT(n, 0); + buf[n] =3D '\0'; + EXPECT_STREQ(buf, "../../" ITEM_A); + EXPECT_EQ(stat(LINK "/storeme", &st), 0); + + /* ->allow_link() ran on the source, not on the target. */ + ASSERT_GT(read_attr(LINK_SRC "/nlinks", buf, sizeof(buf)), 0); + EXPECT_STREQ(buf, "1\n"); + + ASSERT_EQ(unlink(LINK), 0); + ASSERT_GT(read_attr(LINK_SRC "/nlinks", buf, sizeof(buf)), 0); + EXPECT_STREQ(buf, "0\n"); +} + +TEST_F(configfs, symlink_pins_both_ends) +{ + ASSERT_EQ(mkdir(ITEM_A, 0755), 0); + ASSERT_EQ(mkdir(LINK_SRC, 0755), 0); + ASSERT_EQ(symlink(ITEM_A, LINK), 0); + + /* A linked item cannot go away under the link. */ + ASSERT_EQ(rmdir(ITEM_A), -1); + EXPECT_EQ(errno, EBUSY); + + /* The link counts as a child of its source. */ + ASSERT_EQ(rmdir(LINK_SRC), -1); + EXPECT_EQ(errno, ENOTEMPTY); + + ASSERT_EQ(unlink(LINK), 0); + EXPECT_EQ(rmdir(ITEM_A), 0); +} + +TEST_F(configfs, symlink_target_outside_configfs) +{ + ASSERT_EQ(mkdir(LINK_SRC, 0755), 0); + ASSERT_EQ(symlink("/", LINK), -1); + EXPECT_EQ(errno, EPERM); +} + +TEST_F(configfs, symlink_target_missing) +{ + ASSERT_EQ(mkdir(LINK_SRC, 0755), 0); + ASSERT_EQ(symlink(SIMPLE "/kselftest-gone", LINK), -1); + EXPECT_EQ(errno, ENOENT); +} + +TEST_F(configfs, symlink_target_is_an_attribute) +{ + ASSERT_EQ(mkdir(LINK_SRC, 0755), 0); + + /* The target is resolved with LOOKUP_DIRECTORY. */ + ASSERT_EQ(symlink(CHILDLESS "/storeme", LINK), -1); + EXPECT_EQ(errno, ENOTDIR); +} + TEST_F(configfs, module_pinned_by_item) { ASSERT_EQ(mkdir(ITEM_A, 0755), 0); --=20 2.53.0-Meta From nobody Mon Sep 28 04:53:46 2026 Received: from stravinsky.debian.org (stravinsky.debian.org [82.195.75.108]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 5D5383E3D86; Wed, 26 Aug 2026 10:47:41 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=82.195.75.108 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1787741272; cv=none; b=AiWrcEGM0ge59b2hOiPfZPyOWaW7zBuTOwCEVcpI4OtL7TG2K0vw5pNLNOkslil7r8zho/LyUKSjrJNcqB4kWUGWAr7dy3cw3PLlILnnHPynRt1Nl8g0KmisQ8tRytz66HhXYZtqg6XXzlPrtY6sSg47wWsFQAXFNit2XuF3wMA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1787741272; c=relaxed/simple; bh=GkzTfTFbLQND+U+cNyP76kQ59TA5pINipegGDM2lRpg=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=FJhU/uL8jZpbnDxLeXcXZXYRr84goHnhslyXwAE6HH8D5tA6JwrdJrfzAHnx7bG0qK64E+k3yAM67Om+OYzcyF9NSApwrieBihW4sL0bmCKxP0ogEu2lypv26c2vRBwtc8BwAPDs893oQ3cgb+YgApmJ0UxHRI2M4mZ+Y80YOVw= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=debian.org; spf=pass smtp.mailfrom=debian.org; dkim=pass (2048-bit key) header.d=debian.org header.i=@debian.org header.b=PAFkpgUv; arc=none smtp.client-ip=82.195.75.108 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=debian.org Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=debian.org Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=debian.org header.i=@debian.org header.b="PAFkpgUv" DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=debian.org; s=smtpauto.stravinsky; h=X-Debian-User:Cc:To:In-Reply-To:References: Message-Id:Content-Transfer-Encoding:Content-Type:MIME-Version:Subject:Date: From:Reply-To:Content-ID:Content-Description; bh=tT85b5NbK6bRM9en6qm/KfwNm49d+ZHKHssrA665pMw=; b=PAFkpgUvM2CGE9ZPFA6++L8Bya kk6kKe9YHglCuXt+97yD8XJFgU4t1D6YIKh79ld5NZ2egxvmdMp5BH1hUCrxN0u/S7CSbPaXUjbo7 uRBwAo9rmVVj1uXKiGVLr0uymSlK/eNsOb+CZAYQz722xvG27Fh8J/ztjHpNZ/GBb655gPPE7COwK lm00kf6VQF2kpsKcA5czzrSQ+jDIujgS/2qZKPjyNdO4jv0+tKGiyjvdaOTSfT+trvuuT5uGWJqrI yql7/DRoIZU2zDIe98S51h9CtYc190vO/MwSGNLgA9wDgfd+VUIkVNpEKMl/ug5flbn/VL734YzK8 j6CEom+Q==; Received: from authenticated-user by stravinsky.debian.org with esmtpsa (TLS1.3:ECDHE_X25519__RSA_PSS_RSAE_SHA256__AES_256_GCM:256) (Exim 4.96) (envelope-from ) id 1wzBAM-00FA1G-36; Wed, 26 Aug 2026 10:47:35 +0000 From: Breno Leitao Date: Wed, 26 Aug 2026 03:47:05 -0700 Subject: [PATCH 4/4] selftests/configfs: race symlink against rmdir of the target Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260826-config_selftest-v1-4-e364b07e91ac@debian.org> References: <20260826-config_selftest-v1-0-e364b07e91ac@debian.org> In-Reply-To: <20260826-config_selftest-v1-0-e364b07e91ac@debian.org> To: Shuah Khan , Andreas Hindborg Cc: linux-kernel@vger.kernel.org, linux-kselftest@vger.kernel.org, gustavold@gmail.com, linux-fsdevel@vger.kernel.org, Breno Leitao , kernel-team@meta.com, vasilisalmpanis@gmail.com X-Mailer: b4 0.16-dev-f8e9d X-Developer-Signature: v=1; a=openpgp-sha256; l=3688; i=leitao@debian.org; h=from:subject:message-id; bh=GkzTfTFbLQND+U+cNyP76kQ59TA5pINipegGDM2lRpg=; b=owEBbQKS/ZANAwAIATWjk5/8eHdtAcsmYgBqjsQzVqC+PwufOkmq+yuSfBUzFxuQ3bTp68IE2 ma7cvb7mBqJAjMEAAEIAB0WIQSshTmm6PRnAspKQ5s1o5Of/Hh3bQUCao7EMwAKCRA1o5Of/Hh3 bTuXD/0bIXrSL3l1F9U1heqW28ABmDwM8hA2nAqvTtYDSPxpvjAQVRqva6hA9B433BcqvzQ10jT 9yrpeSbithEsFY1kengjlhrGjg08yMqtYBpw5dEF+BJ5fcuNwFM1Kq2At2urYBaWYv8sIiBhy4U 7VAUnbuLRuW4A62nzw5Dkn7Afa6e/SzuDH6cCiXIS8k2bQoA2oW4BqUyYMBwUssd9HCmLHwK1yr 6RVpW6GX+d5x6GFWYDuZcx0tYgUtFqXUNkfXLucbnBFXU5xl6EzzJy+hYw+GqF+5qWTXkQwqYnM ILi0Qy3lPOMp7S7u9AAc0nLXYYigm9oUu4eCoPgoyymsUEChIe3/F/43mNNhsx+IU8Hk3v2/giH xRthjFt4/hZsYwGwfJKytpISrOu1oPyd9d0pB06NuQEair9mi/ZspDvDZScOdg/D4vzMKUFDx8w 9oof2KEnzfTgAsj7aZF6+jZGWGrzMSg8cthBRORqvWx/ANPviYX5itpyUB+Oo/PKErqqoUxewAx WSlbDNkfluRp8IbNJjZ8vBsFgoPRU5pXj0vRpoghIQ0FrDfMKzPx0jVixoKlnxPOtOql4N07cDm SPpWDHP3fQMBGYP8i+orYHs1/JHrK0GXObbAGrddWEwS6v9+w9u7FYDnZbSEUuS/iiLqGfGIuGN dF7R8x3peBbOApg== X-Developer-Key: i=leitao@debian.org; a=openpgp; fpr=AC8539A6E8F46702CA4A439B35A3939FFC78776D X-Debian-User: leitao configfs_rmdir() drops the last reference to an item while its dentry is still hashed, so a symlink(2) resolving that target takes a reference on freed memory. syzbot reported it [1]. Race mkdir/rmdir of a target against symlink/unlink of a link to it, watching /sys/kernel/warn_count. Fails until the fix [2] lands. Link: https://syzkaller.appspot.com/bug?extid=3D6b16e3d085833cbf3e25 [1] Link: https://lore.kernel.org/all/20260730093435.195441-1-vasilisalmpanis@g= mail.com/ [2] Signed-off-by: Breno Leitao --- .../selftests/filesystems/configfs/Makefile | 2 +- .../selftests/filesystems/configfs/configfs_test.c | 62 ++++++++++++++++++= ++++ 2 files changed, 63 insertions(+), 1 deletion(-) diff --git a/tools/testing/selftests/filesystems/configfs/Makefile b/tools/= testing/selftests/filesystems/configfs/Makefile index 359296356c831..40a91ed788ed2 100644 --- a/tools/testing/selftests/filesystems/configfs/Makefile +++ b/tools/testing/selftests/filesystems/configfs/Makefile @@ -2,7 +2,7 @@ # Copyright (c) 2026 Meta Platforms, Inc. and affiliates # Copyright (c) 2026 Breno Leitao =20 -CFLAGS +=3D -Wall -Werror +CFLAGS +=3D -Wall -Werror -pthread TEST_GEN_PROGS :=3D configfs_test =20 include ../../lib.mk diff --git a/tools/testing/selftests/filesystems/configfs/configfs_test.c b= /tools/testing/selftests/filesystems/configfs/configfs_test.c index 9b15e1fd69e5b..c6a1049e5852e 100644 --- a/tools/testing/selftests/filesystems/configfs/configfs_test.c +++ b/tools/testing/selftests/filesystems/configfs/configfs_test.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -41,6 +42,8 @@ #define LINK_SRC SYMLINKS "/kselftest-src" #define LINK LINK_SRC "/kselftest-link" =20 +#define RACE_ITERATIONS 20000 + static const char * const test_links[] =3D { LINK, }; @@ -405,6 +408,65 @@ TEST_F(configfs, symlink_target_is_an_attribute) EXPECT_EQ(errno, ENOTDIR); } =20 +static volatile int race_stop; + +static void *rmdir_target(void *arg) +{ + while (!race_stop) { + if (mkdir(ITEM_A, 0755) =3D=3D 0 || errno =3D=3D EEXIST) + rmdir(ITEM_A); + } + + return NULL; +} + +/* -1 if the kernel does not export a warning counter. */ +static long warn_count(void) +{ + char buf[32]; + + if (read_attr("/sys/kernel/warn_count", buf, sizeof(buf)) < 0) + return -1; + + return strtol(buf, NULL, 10); +} + +TEST_F(configfs, symlink_races_with_target_rmdir) +{ + pthread_t thread; + long warns; + int i; + + warns =3D warn_count(); + if (warns < 0) + SKIP(return, "no /sys/kernel/warn_count to watch"); + + ASSERT_EQ(mkdir(LINK_SRC, 0755), 0); + ASSERT_EQ(pthread_create(&thread, NULL, rmdir_target, NULL), 0); + + /* + * configfs_rmdir() drops the last reference to the item while its + * dentry is still hashed, and get_target() takes a hashed dentry as + * proof that the item behind it is alive. The symlink then walks + * ->ci_dentry into a released dirent, which configfs_get() warns + * about. KASAN sees the freed item itself. + */ + for (i =3D 0; i < RACE_ITERATIONS; i++) { + if (symlink(ITEM_A, LINK) =3D=3D 0) + unlink(LINK); + + /* Give up on the first splat rather than flood the log. */ + if (!(i % 128) && warn_count() !=3D warns) + break; + } + + race_stop =3D 1; + ASSERT_EQ(pthread_join(thread, NULL), 0); + + EXPECT_EQ(warn_count(), warns) + TH_LOG("kernel warned after %d iterations", i); +} + TEST_F(configfs, module_pinned_by_item) { ASSERT_EQ(mkdir(ITEM_A, 0755), 0); --=20 2.53.0-Meta