* [LTP] [PATCH v4 0/4] tst_kconfig: automatic module load/unload for CONFIG_*=m
@ 2026-10-14 6:11 Li Wang
2026-10-14 6:11 ` [LTP] [PATCH v4 1/4] lib: add module is loaded check Li Wang
` (3 more replies)
0 siblings, 4 replies; 5+ messages in thread
From: Li Wang @ 2026-10-14 6:11 UTC (permalink / raw)
To: ltp
From: "Li Wang (Hieco)" <li.wang@linux.dev>
Tests that depend on a config set to =m currently load the module
themselves, and may fail with misleading errors when it is missing,
e.g. bind() returning EADDRNOTAVAIL for AF_VSOCK without vsock_loopback.
This series makes tst_kconfig record the module for every
config_module_maps[] entry seen as =m, load them once the kernel
config check passes, and unload them at cleanup time.
Changes in v4:
- tst_kconfig: fold module_name_for_config() into
kconfig_module_record()
- tst_kconfig: move the choice == 'm' gate into
kconfig_module_record() and pass struct tst_kconfig_var * instead
of the raw config name.
Changes in v3:
- Queue only configs that stay 'm' after kconfig_module_check(),
so a module downgraded to 'n' does not abort an otherwise
runnable test.
- Unload with "modprobe -r" instead of "rmmod" to also remove
dependencies pulled in by modprobe.
- Drop the redundant geteuid() check in the unload path.
- Raise MAX_LOADED_MODULES from 16 to 32.
- cve-2025-21756: drop privileges to nobody after module loading
and regain root in cleanup(), so the reproducer runs from a
non-root context while the framework can still unload modules.
Changes in v2:
- Add tst_module_is_loaded() helper.
- Only load/unload modules that are not already loaded.
- Skip automatic loading when KCONFIG_SKIP_CHECK=1, keeping the
semantics consistent with skipping the config check.
- Document the KCONFIG_SKIP_CHECK=1 behaviour for keyctl29/keyctl30
and the vsock/vsock_loopback mapping.
Li Wang (Hieco) (4):
lib: add module is loaded check
tst_kconfig: load and unload kernel modules for CONFIG_*=m
tst_kconfig: add module mappings for vsock/vsock_loopback
testcases: drop manual tst_modprobe() now handled by tst_kconfig
include/tst_kconfig.h | 28 ++++++++
include/tst_module.h | 1 +
lib/tst_kconfig.c | 77 +++++++++++++++++++++
lib/tst_module.c | 39 +++++++++++
lib/tst_test.c | 11 ++-
testcases/cve/cve-2025-21756.c | 40 +++++++++++
testcases/cve/cve-2026-46331.c | 7 --
testcases/cve/sctphantom.c | 3 -
testcases/kernel/syscalls/keyctl/keyctl29.c | 3 -
testcases/kernel/syscalls/keyctl/keyctl30.c | 3 -
10 files changed, 194 insertions(+), 18 deletions(-)
--
2.56.0
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply [flat|nested] 5+ messages in thread
* [LTP] [PATCH v4 1/4] lib: add module is loaded check
2026-10-14 6:11 [LTP] [PATCH v4 0/4] tst_kconfig: automatic module load/unload for CONFIG_*=m Li Wang
@ 2026-10-14 6:11 ` Li Wang
2026-10-14 6:12 ` [LTP] [PATCH v4 2/4] tst_kconfig: load and unload kernel modules for CONFIG_*=m Li Wang
` (2 subsequent siblings)
3 siblings, 0 replies; 5+ messages in thread
From: Li Wang @ 2026-10-14 6:11 UTC (permalink / raw)
To: ltp
From: "Li Wang (Hieco)" <li.wang@linux.dev>
Add a helper that checks whether a kernel module is loaded by
looking it up in /proc/modules. Module names are normalized so that
hyphens match the underscores used by the kernel.
Signed-off-by: Li Wang (Hieco) <li.wang@linux.dev>
---
include/tst_module.h | 1 +
lib/tst_module.c | 39 +++++++++++++++++++++++++++++++++++++++
2 files changed, 40 insertions(+)
diff --git a/include/tst_module.h b/include/tst_module.h
index e55321d19..9b789e289 100644
--- a/include/tst_module.h
+++ b/include/tst_module.h
@@ -49,5 +49,6 @@ static inline void tst_requires_module_signature_disabled(void)
void tst_modprobe(const char *mod_name, char *const argv[]);
void tst_module_reload(const char *mod_name, char *const argv[]);
+bool tst_module_is_loaded(const char *mod_name);
#endif /* TST_MODULE_H */
diff --git a/lib/tst_module.c b/lib/tst_module.c
index cbd2415d5..db341ec57 100644
--- a/lib/tst_module.c
+++ b/lib/tst_module.c
@@ -21,6 +21,7 @@
*/
#define _GNU_SOURCE
+#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
@@ -174,3 +175,41 @@ void tst_module_reload(const char *mod_name, char *const argv[])
tst_module_unload_(NULL, mod_name);
tst_modprobe(mod_name, argv);
}
+
+bool tst_module_is_loaded(const char *mod_name)
+{
+ char line[4096];
+ size_t mod_len = strlen(mod_name);
+ FILE *file;
+
+ file = fopen("/proc/modules", "r");
+ if (!file)
+ return false;
+
+ while (fgets(line, sizeof(line), file)) {
+ char *p = line;
+ size_t i;
+
+ while (*p && !isspace(*p))
+ p++;
+
+ if ((size_t)(p - line) != mod_len)
+ continue;
+
+ for (i = 0; i < mod_len; i++) {
+ char a = (line[i] == '-') ? '_' : line[i];
+ char b = (mod_name[i] == '-') ? '_' : mod_name[i];
+
+ if (a != b)
+ break;
+ }
+
+ if (i == mod_len) {
+ fclose(file);
+ return true;
+ }
+ }
+
+ fclose(file);
+ return false;
+}
--
2.56.0
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [LTP] [PATCH v4 2/4] tst_kconfig: load and unload kernel modules for CONFIG_*=m
2026-10-14 6:11 [LTP] [PATCH v4 0/4] tst_kconfig: automatic module load/unload for CONFIG_*=m Li Wang
2026-10-14 6:11 ` [LTP] [PATCH v4 1/4] lib: add module is loaded check Li Wang
@ 2026-10-14 6:12 ` Li Wang
2026-10-14 6:12 ` [LTP] [PATCH v4 3/4] tst_kconfig: add module mappings for vsock/vsock_loopback Li Wang
2026-10-14 6:12 ` [LTP] [PATCH v4 4/4] testcases: drop manual tst_modprobe() now handled by tst_kconfig Li Wang
3 siblings, 0 replies; 5+ messages in thread
From: Li Wang @ 2026-10-14 6:12 UTC (permalink / raw)
To: ltp
From: "Li Wang (Hieco)" <li.wang@linux.dev>
Tests that depend on a config set to =m must currently load the
module themselves, and may fail with misleading errors when it is
not loaded, e.g. bind() returning EADDRNOTAVAIL for AF_VSOCK when
vsock_loopback is missing.
Record the module for every config_module_maps[] entry seen as =m
during kconfig_parse_line(), load them in
tst_kconfig_module_load() via tst_modprobe(), and unload them in
reverse order from tst_kconfig_module_unload_all(), called by
do_test_cleanup(). Modules still in use are skipped without
aborting the test.
Loading and unloading are performed only as root; otherwise the
recorded list is left untouched.
Signed-off-by: Li Wang (Hieco) <li.wang@linux.dev>
---
include/tst_kconfig.h | 28 ++++++++++++++++
lib/tst_kconfig.c | 75 +++++++++++++++++++++++++++++++++++++++++++
lib/tst_test.c | 11 +++++--
3 files changed, 112 insertions(+), 2 deletions(-)
diff --git a/include/tst_kconfig.h b/include/tst_kconfig.h
index c454f3467..a525429d4 100644
--- a/include/tst_kconfig.h
+++ b/include/tst_kconfig.h
@@ -105,4 +105,32 @@ void tst_kcmdline_parse(struct tst_kcmdline_var params[], size_t params_len);
*/
int tst_has_slow_kconfig(void);
+/**
+ * tst_kconfig_module_load() - Load kernel modules for CONFIG_*=m entries.
+ *
+ * Loads every module recorded during the last kernel config check, in
+ * the order they were discovered. The list is populated by
+ * kconfig_module_record() from kconfig_parse_line() for each CONFIG_*
+ * entry that is set to =m and has a matching entry in
+ * config_module_maps[].
+ *
+ * Loading is skipped when not running as root, and does nothing when
+ * the recorded list is empty.
+ */
+void tst_kconfig_module_load(void);
+
+/**
+ * tst_kconfig_module_unload_all() - Unload modules loaded by
+ * tst_kconfig_module_load().
+ *
+ * Unloads every module recorded by tst_kconfig_module_load(), in
+ * reverse load order so that inter-module dependencies are respected.
+ * Modules still in use are skipped silently, leaving the system state
+ * as close as possible to what it was before the test started.
+ *
+ * Called from do_test_cleanup() so that the modules are released when
+ * the test finishes, regardless of the test result.
+ */
+void tst_kconfig_module_unload_all(void);
+
#endif /* TST_KCONFIG_H__ */
diff --git a/lib/tst_kconfig.c b/lib/tst_kconfig.c
index 49957e28d..421852ba5 100644
--- a/lib/tst_kconfig.c
+++ b/lib/tst_kconfig.c
@@ -16,8 +16,15 @@
#include "tst_bool_expr.h"
#include "tst_safe_stdio.h"
+#include "tst_module.h"
#include "tst_kconfig_checks.h"
+#define MAX_LOADED_MODULES 32
+
+static const char *loaded_modules[MAX_LOADED_MODULES];
+static unsigned int loaded_module_count;
+static bool modules_loaded;
+
static int kconfig_skip_check(void)
{
char *skipped = getenv("KCONFIG_SKIP_CHECK");
@@ -189,6 +196,36 @@ static void kconfig_module_check(struct tst_kconfig_var *var)
}
}
+static void kconfig_module_record(struct tst_kconfig_var *var)
+{
+ unsigned int i;
+ const char *mod = NULL;
+
+ if (var->choice != 'm')
+ return;
+
+ for (i = 0; config_module_maps[i].config; i++) {
+ if (!strcmp(config_module_maps[i].config, var->id)) {
+ mod = config_module_maps[i].module_name;
+ break;
+ }
+ }
+
+ if (!mod)
+ return;
+
+ if (tst_module_is_loaded(mod))
+ return;
+
+ for (i = 0; i < loaded_module_count; i++) {
+ if (!strcmp(loaded_modules[i], mod))
+ return;
+ }
+
+ if (loaded_module_count < MAX_LOADED_MODULES)
+ loaded_modules[loaded_module_count++] = mod;
+}
+
static inline int kconfig_parse_line(const char *line,
struct tst_kconfig_var *vars,
unsigned int vars_len)
@@ -268,6 +305,7 @@ out:
vars[i].choice = 'm';
kconfig_runtime_check(&vars[i]);
kconfig_module_check(&vars[i]);
+ kconfig_module_record(&vars[i]);
return 1;
}
}
@@ -300,6 +338,43 @@ exit:
close_kconfig(fp);
}
+void tst_kconfig_module_load(void)
+{
+ unsigned int i;
+
+ if (geteuid() != 0)
+ return;
+
+ for (i = 0; i < loaded_module_count; i++) {
+ tst_res(TDEBUG, "Loading module '%s'", loaded_modules[i]);
+ tst_modprobe(loaded_modules[i], NULL);
+ }
+
+ modules_loaded = true;
+}
+
+void tst_kconfig_module_unload_all(void)
+{
+ unsigned int i;
+
+ if (!loaded_module_count)
+ return;
+
+ if (!modules_loaded)
+ return;
+
+ for (i = loaded_module_count; i > 0; i--) {
+ const char *const argv[] = { "modprobe", "-r",
+ loaded_modules[i - 1], NULL };
+
+ tst_res(TDEBUG, "Unloading module '%s'", loaded_modules[i - 1]);
+ tst_cmd(argv, "/dev/null", "/dev/null", TST_CMD_PASS_RETVAL);
+ }
+
+ loaded_module_count = 0;
+ modules_loaded = false;
+}
+
static size_t array_len(const char *const kconfigs[])
{
size_t i = 0;
diff --git a/lib/tst_test.c b/lib/tst_test.c
index 10ff73397..f55830983 100644
--- a/lib/tst_test.c
+++ b/lib/tst_test.c
@@ -1471,8 +1471,12 @@ static void do_setup(int argc, char *argv[])
check_supported_kver();
- if (tst_test->needs_kconfigs && tst_kconfig_check(tst_test->needs_kconfigs))
- tst_brk(TCONF, "Aborting due to unsuitable kernel config, see above!");
+ if (tst_test->needs_kconfigs) {
+ if (tst_kconfig_check(tst_test->needs_kconfigs))
+ tst_brk(TCONF, "Aborting due to unsuitable kernel config, see above!");
+
+ tst_kconfig_module_load();
+ }
if (tst_test->needs_root && geteuid() != 0)
tst_brk(TCONF, "Test needs to be run as root");
@@ -1690,6 +1694,9 @@ static void do_cleanup(void)
if (tst_test->restore_wallclock)
tst_wallclock_restore();
+ if (tst_test->needs_kconfigs)
+ tst_kconfig_module_unload_all();
+
cleanup_ipc();
}
--
2.56.0
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [LTP] [PATCH v4 3/4] tst_kconfig: add module mappings for vsock/vsock_loopback
2026-10-14 6:11 [LTP] [PATCH v4 0/4] tst_kconfig: automatic module load/unload for CONFIG_*=m Li Wang
2026-10-14 6:11 ` [LTP] [PATCH v4 1/4] lib: add module is loaded check Li Wang
2026-10-14 6:12 ` [LTP] [PATCH v4 2/4] tst_kconfig: load and unload kernel modules for CONFIG_*=m Li Wang
@ 2026-10-14 6:12 ` Li Wang
2026-10-14 6:12 ` [LTP] [PATCH v4 4/4] testcases: drop manual tst_modprobe() now handled by tst_kconfig Li Wang
3 siblings, 0 replies; 5+ messages in thread
From: Li Wang @ 2026-10-14 6:12 UTC (permalink / raw)
To: ltp
From: "Li Wang (Hieco)" <li.wang@linux.dev>
Map CONFIG_VSOCKETS to "vsock" and CONFIG_VSOCKETS_LOOPBACK to
"vsock_loopback" so tst_kconfig can load them on demand.
cve-2025-21756 binds to VMADDR_CID_LOCAL and therefore needs
vsock_loopback. Without the module loaded, bind() fails with
EADDRNOTAVAIL and the test aborts with TBROK. Declare both configs in
needs_kconfigs so the modules are loaded first.
The test needs root only to load the modules; the reproducer runs as
an ordinary non-root process in the initial namespace, where it can
use loopback VSOCK on non-privileged ports. Drop privileges to nobody
after the modules are loaded, and regain root in cleanup() so the
modules can be unloaded again.
Signed-off-by: Li Wang (Hieco) <li.wang@linux.dev>
---
lib/tst_kconfig.c | 2 ++
testcases/cve/cve-2025-21756.c | 34 ++++++++++++++++++++++++++++++++++
2 files changed, 36 insertions(+)
diff --git a/lib/tst_kconfig.c b/lib/tst_kconfig.c
index 421852ba5..dc025c9f0 100644
--- a/lib/tst_kconfig.c
+++ b/lib/tst_kconfig.c
@@ -174,6 +174,8 @@ static struct config_module_map {
{"CONFIG_NET_ACT_PEDIT", "act_pedit"},
{"CONFIG_PKCS8_PRIVATE_KEY_PARSER", "pkcs8_key_parser"},
{"CONFIG_X509_CERTIFICATE_PARSER", "x509_key_parser"},
+ {"CONFIG_VSOCKETS", "vsock"},
+ {"CONFIG_VSOCKETS_LOOPBACK", "vsock_loopback"},
{}
};
diff --git a/testcases/cve/cve-2025-21756.c b/testcases/cve/cve-2025-21756.c
index 4245a4c95..70dddabb7 100644
--- a/testcases/cve/cve-2025-21756.c
+++ b/testcases/cve/cve-2025-21756.c
@@ -13,12 +13,38 @@
* Beware, this test will crash the system.
*/
+#include <pwd.h>
+
#include "tst_test.h"
#include "lapi/vm_sockets.h"
#define MAX_PORT_RETRIES 24
#define VMADDR_CID_NONEXISTING 42
+static void drop_privileges(void)
+{
+ struct passwd *pw = SAFE_GETPWNAM("nobody");
+
+ SAFE_SETEGID(pw->pw_gid);
+ SAFE_SETEUID(pw->pw_uid);
+}
+
+static void restore_privileges(void)
+{
+ SAFE_SETEGID(0);
+ SAFE_SETEUID(0);
+}
+
+static void setup(void)
+{
+ drop_privileges();
+}
+
+static void cleanup(void)
+{
+ restore_privileges();
+}
+
static int vsock_bind(unsigned int cid, unsigned int port, int type)
{
int sock;
@@ -85,8 +111,16 @@ static void run(void)
}
static struct tst_test test = {
+ .needs_root = 1,
.test_all = run,
+ .setup = setup,
+ .cleanup = cleanup,
.taint_check = TST_TAINT_W | TST_TAINT_D,
+ .needs_kconfigs = (const char *const[]) {
+ "CONFIG_VSOCKETS",
+ "CONFIG_VSOCKETS_LOOPBACK",
+ NULL
+ },
.tags = (const struct tst_tag[]) {
{"linux-git", "fcdd2242c0231032fc84e1404315c245ae56322a"},
{"CVE", "2025-21756"},
--
2.56.0
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [LTP] [PATCH v4 4/4] testcases: drop manual tst_modprobe() now handled by tst_kconfig
2026-10-14 6:11 [LTP] [PATCH v4 0/4] tst_kconfig: automatic module load/unload for CONFIG_*=m Li Wang
` (2 preceding siblings ...)
2026-10-14 6:12 ` [LTP] [PATCH v4 3/4] tst_kconfig: add module mappings for vsock/vsock_loopback Li Wang
@ 2026-10-14 6:12 ` Li Wang
3 siblings, 0 replies; 5+ messages in thread
From: Li Wang @ 2026-10-14 6:12 UTC (permalink / raw)
To: ltp
From: "Li Wang (Hieco)" <li.wang@linux.dev>
Now that tst_kconfig loads the modules for every CONFIG_*=m entry
listed in config_module_maps[] and unloads them again at cleanup
time, tests no longer have to call tst_modprobe() from setup().
Drop the explicit tst_modprobe() calls, and the now unused
tst_module.h includes, from:
- cve-2026-46331.c: sch_ingress, cls_matchall, act_pedit
- sctphantom.c: sctp
- keyctl29.c: x509_key_parser
- keyctl30.c: pkcs8_key_parser
All four tests already declare the matching configs in
.needs_kconfigs, so tst_kconfig takes care of loading the modules
before setup() runs and unloading them once the test finishes.
BTW, with KCONFIG_SKIP_CHECK=1 the config check returns early,
so the automatic module loading is skipped as well. Users who set
that variable are expected to ensure the required modules are
loaded themselves.
Signed-off-by: Li Wang (Hieco) <li.wang@linux.dev>
---
testcases/cve/cve-2026-46331.c | 7 -------
testcases/cve/sctphantom.c | 3 ---
testcases/kernel/syscalls/keyctl/keyctl29.c | 3 ---
testcases/kernel/syscalls/keyctl/keyctl30.c | 3 ---
4 files changed, 16 deletions(-)
diff --git a/testcases/cve/cve-2026-46331.c b/testcases/cve/cve-2026-46331.c
index 3d32a1cbb..3c8db5042 100644
--- a/testcases/cve/cve-2026-46331.c
+++ b/testcases/cve/cve-2026-46331.c
@@ -44,7 +44,6 @@
#include <linux/rtnetlink.h>
#include "tst_test.h"
-#include "tst_module.h"
#include "tst_net.h"
#include "tst_netdevice.h"
#include "lapi/if_ether.h"
@@ -136,12 +135,6 @@ static void setup(void)
{
struct sockaddr_in addr;
struct passwd *pw;
- static const char *const mods[] = {
- "sch_ingress", "cls_matchall", "act_pedit"
- };
-
- for (unsigned int i = 0; i < ARRAY_SIZE(mods); i++)
- tst_modprobe(mods[i], NULL);
/* Drop privileges so the reproducer runs as an unprivileged user */
pw = SAFE_GETPWNAM("nobody");
diff --git a/testcases/cve/sctphantom.c b/testcases/cve/sctphantom.c
index 83834b848..bdbe40286 100644
--- a/testcases/cve/sctphantom.c
+++ b/testcases/cve/sctphantom.c
@@ -50,7 +50,6 @@
*/
#include "tst_test.h"
-#include "tst_module.h"
#include "tst_checksum.h"
#include "tst_safe_net.h"
@@ -205,8 +204,6 @@ static void setup(void)
};
const struct tst_path_val *sysctl;
- tst_modprobe("sctp", NULL);
-
for (sysctl = sysctls; sysctl->path; sysctl++)
tst_sys_conf_save(sysctl);
diff --git a/testcases/kernel/syscalls/keyctl/keyctl29.c b/testcases/kernel/syscalls/keyctl/keyctl29.c
index afc0a093c..e66b39bcb 100644
--- a/testcases/kernel/syscalls/keyctl/keyctl29.c
+++ b/testcases/kernel/syscalls/keyctl/keyctl29.c
@@ -23,7 +23,6 @@
#include "keyctl_common.h"
#include "keyctl_pkey_data.h"
-#include "tst_module.h"
static key_serial_t cert_key;
static struct keyctl_pkey_query *query_buf;
@@ -32,8 +31,6 @@ static void setup(void)
{
SAFE_KEYCTL(KEYCTL_JOIN_SESSION_KEYRING, 0, 0, 0, 0);
- tst_modprobe("x509_key_parser", NULL);
-
cert_key = add_asymmetric_key_or_tconf("cert", rsa2048_cert,
sizeof(rsa2048_cert),
"CONFIG_X509_CERTIFICATE_PARSER");
diff --git a/testcases/kernel/syscalls/keyctl/keyctl30.c b/testcases/kernel/syscalls/keyctl/keyctl30.c
index ef51bba33..4f43007e0 100644
--- a/testcases/kernel/syscalls/keyctl/keyctl30.c
+++ b/testcases/kernel/syscalls/keyctl/keyctl30.c
@@ -20,7 +20,6 @@
#include "keyctl_common.h"
#include "keyctl_pkey_data.h"
-#include "tst_module.h"
static key_serial_t priv_key;
static struct keyctl_pkey_query *query_buf;
@@ -29,8 +28,6 @@ static void setup(void)
{
SAFE_KEYCTL(KEYCTL_JOIN_SESSION_KEYRING, 0, 0, 0, 0);
- tst_modprobe("pkcs8_key_parser", NULL);
-
priv_key = add_asymmetric_key_or_tconf("priv", rsa2048_pkcs8,
sizeof(rsa2048_pkcs8),
"CONFIG_PKCS8_PRIVATE_KEY_PARSER");
--
2.56.0
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply related [flat|nested] 5+ messages in thread
end of thread, other threads:[~2026-10-14 6:13 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-14 6:11 [LTP] [PATCH v4 0/4] tst_kconfig: automatic module load/unload for CONFIG_*=m Li Wang
2026-10-14 6:11 ` [LTP] [PATCH v4 1/4] lib: add module is loaded check Li Wang
2026-10-14 6:12 ` [LTP] [PATCH v4 2/4] tst_kconfig: load and unload kernel modules for CONFIG_*=m Li Wang
2026-10-14 6:12 ` [LTP] [PATCH v4 3/4] tst_kconfig: add module mappings for vsock/vsock_loopback Li Wang
2026-10-14 6:12 ` [LTP] [PATCH v4 4/4] testcases: drop manual tst_modprobe() now handled by tst_kconfig Li Wang
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox