* [LTP] [PATCH 01/16] syscalls: add v7.3 syscall numbers
2026-08-19 21:24 [LTP] [PATCH 00/16] fchroot: add fchroot() testing suite Andrea Cervesato
@ 2026-08-19 21:24 ` Andrea Cervesato
2026-08-19 21:24 ` [LTP] [PATCH 02/16] syscalls: update outdated syscall entries Andrea Cervesato
` (14 subsequent siblings)
15 siblings, 0 replies; 21+ messages in thread
From: Andrea Cervesato @ 2026-08-19 21:24 UTC (permalink / raw)
To: Linux Test Project
From: Andrea Cervesato <andrea.cervesato@suse.com>
Add listns (470), rseq_slice_yield (471) and fchroot (472), which are
reserved for all architectures during the Linux v7.3 development cycle.
Signed-off-by: Andrea Cervesato <andrea.cervesato@suse.com>
---
include/lapi/syscalls/arc.in | 3 +++
include/lapi/syscalls/arm.in | 3 +++
include/lapi/syscalls/arm64.in | 3 +++
include/lapi/syscalls/i386.in | 3 +++
include/lapi/syscalls/loongarch64.in | 3 +++
include/lapi/syscalls/mips64.in | 3 +++
include/lapi/syscalls/mips64n32.in | 3 +++
include/lapi/syscalls/mipso32.in | 3 +++
include/lapi/syscalls/parisc.in | 3 +++
include/lapi/syscalls/powerpc.in | 3 +++
include/lapi/syscalls/powerpc64.in | 3 +++
include/lapi/syscalls/s390.in | 3 +++
include/lapi/syscalls/s390x.in | 3 +++
include/lapi/syscalls/sh.in | 3 +++
include/lapi/syscalls/sparc.in | 3 +++
include/lapi/syscalls/sparc64.in | 3 +++
include/lapi/syscalls/x86_64.in | 3 +++
17 files changed, 51 insertions(+)
diff --git a/include/lapi/syscalls/arc.in b/include/lapi/syscalls/arc.in
index 0f0fbef6b..96b14c1b9 100644
--- a/include/lapi/syscalls/arc.in
+++ b/include/lapi/syscalls/arc.in
@@ -347,3 +347,6 @@ removexattrat 466
open_tree_attr 467
file_getattr 468
file_setattr 469
+listns 470
+rseq_slice_yield 471
+fchroot 472
diff --git a/include/lapi/syscalls/arm.in b/include/lapi/syscalls/arm.in
index d0238dec5..c4058e8de 100644
--- a/include/lapi/syscalls/arm.in
+++ b/include/lapi/syscalls/arm.in
@@ -420,3 +420,6 @@ removexattrat 466
open_tree_attr 467
file_getattr 468
file_setattr 469
+listns 470
+rseq_slice_yield 471
+fchroot 472
diff --git a/include/lapi/syscalls/arm64.in b/include/lapi/syscalls/arm64.in
index c76930e20..26a61e830 100644
--- a/include/lapi/syscalls/arm64.in
+++ b/include/lapi/syscalls/arm64.in
@@ -323,3 +323,6 @@ removexattrat 466
open_tree_attr 467
file_getattr 468
file_setattr 469
+listns 470
+rseq_slice_yield 471
+fchroot 472
diff --git a/include/lapi/syscalls/i386.in b/include/lapi/syscalls/i386.in
index 38ea71fb2..21d5da053 100644
--- a/include/lapi/syscalls/i386.in
+++ b/include/lapi/syscalls/i386.in
@@ -454,3 +454,6 @@ removexattrat 466
open_tree_attr 467
file_getattr 468
file_setattr 469
+listns 470
+rseq_slice_yield 471
+fchroot 472
diff --git a/include/lapi/syscalls/loongarch64.in b/include/lapi/syscalls/loongarch64.in
index a1dfce79e..88e77ef8a 100644
--- a/include/lapi/syscalls/loongarch64.in
+++ b/include/lapi/syscalls/loongarch64.in
@@ -320,3 +320,6 @@ removexattrat 466
open_tree_attr 467
file_getattr 468
file_setattr 469
+listns 470
+rseq_slice_yield 471
+fchroot 472
diff --git a/include/lapi/syscalls/mips64.in b/include/lapi/syscalls/mips64.in
index 436d5c681..3c19066b8 100644
--- a/include/lapi/syscalls/mips64.in
+++ b/include/lapi/syscalls/mips64.in
@@ -369,3 +369,6 @@ removexattrat 5466
open_tree_attr 5467
file_getattr 5468
file_setattr 5469
+listns 5470
+rseq_slice_yield 5471
+fchroot 5472
diff --git a/include/lapi/syscalls/mips64n32.in b/include/lapi/syscalls/mips64n32.in
index 860a19c8b..0c539bd62 100644
--- a/include/lapi/syscalls/mips64n32.in
+++ b/include/lapi/syscalls/mips64n32.in
@@ -393,3 +393,6 @@ removexattrat 6466
open_tree_attr 6467
file_getattr 6468
file_setattr 6469
+listns 6470
+rseq_slice_yield 6471
+fchroot 6472
diff --git a/include/lapi/syscalls/mipso32.in b/include/lapi/syscalls/mipso32.in
index 5e53e46ce..ecbe4d4b9 100644
--- a/include/lapi/syscalls/mipso32.in
+++ b/include/lapi/syscalls/mipso32.in
@@ -433,3 +433,6 @@ removexattrat 4466
open_tree_attr 4467
file_getattr 4468
file_setattr 4469
+listns 4470
+rseq_slice_yield 4471
+fchroot 4472
diff --git a/include/lapi/syscalls/parisc.in b/include/lapi/syscalls/parisc.in
index 238756694..e84c3b344 100644
--- a/include/lapi/syscalls/parisc.in
+++ b/include/lapi/syscalls/parisc.in
@@ -402,3 +402,6 @@ removexattrat 466
open_tree_attr 467
file_getattr 468
file_setattr 469
+listns 470
+rseq_slice_yield 471
+fchroot 472
diff --git a/include/lapi/syscalls/powerpc.in b/include/lapi/syscalls/powerpc.in
index 6911f9af9..9354dbe2b 100644
--- a/include/lapi/syscalls/powerpc.in
+++ b/include/lapi/syscalls/powerpc.in
@@ -444,3 +444,6 @@ removexattrat 466
open_tree_attr 467
file_getattr 468
file_setattr 469
+listns 470
+rseq_slice_yield 471
+fchroot 472
diff --git a/include/lapi/syscalls/powerpc64.in b/include/lapi/syscalls/powerpc64.in
index 98190ba26..e4bef6838 100644
--- a/include/lapi/syscalls/powerpc64.in
+++ b/include/lapi/syscalls/powerpc64.in
@@ -416,3 +416,6 @@ removexattrat 466
open_tree_attr 467
file_getattr 468
file_setattr 469
+listns 470
+rseq_slice_yield 471
+fchroot 472
diff --git a/include/lapi/syscalls/s390.in b/include/lapi/syscalls/s390.in
index a6cb85da8..4a11871b6 100644
--- a/include/lapi/syscalls/s390.in
+++ b/include/lapi/syscalls/s390.in
@@ -437,3 +437,6 @@ removexattrat 466
open_tree_attr 467
file_getattr 468
file_setattr 469
+listns 470
+rseq_slice_yield 471
+fchroot 472
diff --git a/include/lapi/syscalls/s390x.in b/include/lapi/syscalls/s390x.in
index 31f3ec553..055a039fb 100644
--- a/include/lapi/syscalls/s390x.in
+++ b/include/lapi/syscalls/s390x.in
@@ -385,3 +385,6 @@ removexattrat 466
open_tree_attr 467
file_getattr 468
file_setattr 469
+listns 470
+rseq_slice_yield 471
+fchroot 472
diff --git a/include/lapi/syscalls/sh.in b/include/lapi/syscalls/sh.in
index ac281acf8..29cd8f8df 100644
--- a/include/lapi/syscalls/sh.in
+++ b/include/lapi/syscalls/sh.in
@@ -431,3 +431,6 @@ removexattrat 466
open_tree_attr 467
file_getattr 468
file_setattr 469
+listns 470
+rseq_slice_yield 471
+fchroot 472
diff --git a/include/lapi/syscalls/sparc.in b/include/lapi/syscalls/sparc.in
index ffc0d9f7a..ea2bfc9c1 100644
--- a/include/lapi/syscalls/sparc.in
+++ b/include/lapi/syscalls/sparc.in
@@ -435,3 +435,6 @@ removexattrat 466
open_tree_attr 467
file_getattr 468
file_setattr 469
+listns 470
+rseq_slice_yield 471
+fchroot 472
diff --git a/include/lapi/syscalls/sparc64.in b/include/lapi/syscalls/sparc64.in
index 992bd307d..692652c3b 100644
--- a/include/lapi/syscalls/sparc64.in
+++ b/include/lapi/syscalls/sparc64.in
@@ -398,3 +398,6 @@ removexattrat 466
open_tree_attr 467
file_getattr 468
file_setattr 469
+listns 470
+rseq_slice_yield 471
+fchroot 472
diff --git a/include/lapi/syscalls/x86_64.in b/include/lapi/syscalls/x86_64.in
index e9c0d0599..7d3d8163c 100644
--- a/include/lapi/syscalls/x86_64.in
+++ b/include/lapi/syscalls/x86_64.in
@@ -380,3 +380,6 @@ removexattrat 466
open_tree_attr 467
file_getattr 468
file_setattr 469
+listns 470
+rseq_slice_yield 471
+fchroot 472
--
2.51.0
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply related [flat|nested] 21+ messages in thread* [LTP] [PATCH 02/16] syscalls: update outdated syscall entries
2026-08-19 21:24 [LTP] [PATCH 00/16] fchroot: add fchroot() testing suite Andrea Cervesato
2026-08-19 21:24 ` [LTP] [PATCH 01/16] syscalls: add v7.3 syscall numbers Andrea Cervesato
@ 2026-08-19 21:24 ` Andrea Cervesato
2026-08-19 21:24 ` [LTP] [PATCH 03/16] lapi: fallback fchroot() parameters Andrea Cervesato
` (13 subsequent siblings)
15 siblings, 0 replies; 21+ messages in thread
From: Andrea Cervesato @ 2026-08-19 21:24 UTC (permalink / raw)
To: Linux Test Project
From: Andrea Cervesato <andrea.cervesato@suse.com>
Add uprobe (336) on x86_64 and clone3 (435) on sparc, which were
missing from the architecture syscall tables.
Drop the _sysctl and timerfd entries because both are wired to
sys_ni_syscall in the kernel syscall tables and cannot be called.
Signed-off-by: Andrea Cervesato <andrea.cervesato@suse.com>
---
include/lapi/syscalls/arm.in | 1 -
include/lapi/syscalls/i386.in | 1 -
include/lapi/syscalls/mips64.in | 2 --
include/lapi/syscalls/mips64n32.in | 2 --
include/lapi/syscalls/mipso32.in | 2 --
include/lapi/syscalls/parisc.in | 1 -
include/lapi/syscalls/powerpc.in | 1 -
include/lapi/syscalls/powerpc64.in | 1 -
include/lapi/syscalls/s390.in | 2 --
include/lapi/syscalls/s390x.in | 2 --
include/lapi/syscalls/sh.in | 1 -
include/lapi/syscalls/sparc.in | 2 +-
include/lapi/syscalls/sparc64.in | 2 +-
include/lapi/syscalls/x86_64.in | 2 +-
14 files changed, 3 insertions(+), 19 deletions(-)
diff --git a/include/lapi/syscalls/arm.in b/include/lapi/syscalls/arm.in
index c4058e8de..158350976 100644
--- a/include/lapi/syscalls/arm.in
+++ b/include/lapi/syscalls/arm.in
@@ -109,7 +109,6 @@ readv 145
writev 146
getsid 147
fdatasync 148
-_sysctl 149
mlock 150
munlock 151
mlockall 152
diff --git a/include/lapi/syscalls/i386.in b/include/lapi/syscalls/i386.in
index 21d5da053..ee229b567 100644
--- a/include/lapi/syscalls/i386.in
+++ b/include/lapi/syscalls/i386.in
@@ -145,7 +145,6 @@ readv 145
writev 146
getsid 147
fdatasync 148
-_sysctl 149
mlock 150
munlock 151
mlockall 152
diff --git a/include/lapi/syscalls/mips64.in b/include/lapi/syscalls/mips64.in
index 3c19066b8..d1087e049 100644
--- a/include/lapi/syscalls/mips64.in
+++ b/include/lapi/syscalls/mips64.in
@@ -150,7 +150,6 @@ mlockall 5148
munlockall 5149
vhangup 5150
pivot_root 5151
-_sysctl 5152
prctl 5153
adjtimex 5154
setrlimit 5155
@@ -272,7 +271,6 @@ ioprio_set 5273
ioprio_get 5274
utimensat 5275
signalfd 5276
-timerfd 5277
eventfd 5278
fallocate 5279
timerfd_create 5280
diff --git a/include/lapi/syscalls/mips64n32.in b/include/lapi/syscalls/mips64n32.in
index 0c539bd62..2a3883198 100644
--- a/include/lapi/syscalls/mips64n32.in
+++ b/include/lapi/syscalls/mips64n32.in
@@ -150,7 +150,6 @@ mlockall 6148
munlockall 6149
vhangup 6150
pivot_root 6151
-_sysctl 6152
prctl 6153
adjtimex 6154
setrlimit 6155
@@ -276,7 +275,6 @@ ioprio_set 6277
ioprio_get 6278
utimensat 6279
signalfd 6280
-timerfd 6281
eventfd 6282
fallocate 6283
timerfd_create 6284
diff --git a/include/lapi/syscalls/mipso32.in b/include/lapi/syscalls/mipso32.in
index ecbe4d4b9..0d682d072 100644
--- a/include/lapi/syscalls/mipso32.in
+++ b/include/lapi/syscalls/mipso32.in
@@ -144,7 +144,6 @@ cachectl 4148
sysmips 4149
getsid 4151
fdatasync 4152
-_sysctl 4153
mlock 4154
munlock 4155
mlockall 4156
@@ -307,7 +306,6 @@ ioprio_set 4314
ioprio_get 4315
utimensat 4316
signalfd 4317
-timerfd 4318
eventfd 4319
fallocate 4320
timerfd_create 4321
diff --git a/include/lapi/syscalls/parisc.in b/include/lapi/syscalls/parisc.in
index e84c3b344..71d40f7b2 100644
--- a/include/lapi/syscalls/parisc.in
+++ b/include/lapi/syscalls/parisc.in
@@ -142,7 +142,6 @@ readv 145
writev 146
getsid 147
fdatasync 148
-_sysctl 149
mlock 150
munlock 151
mlockall 152
diff --git a/include/lapi/syscalls/powerpc.in b/include/lapi/syscalls/powerpc.in
index 9354dbe2b..85d7aec9a 100644
--- a/include/lapi/syscalls/powerpc.in
+++ b/include/lapi/syscalls/powerpc.in
@@ -145,7 +145,6 @@ readv 145
writev 146
getsid 147
fdatasync 148
-_sysctl 149
mlock 150
munlock 151
mlockall 152
diff --git a/include/lapi/syscalls/powerpc64.in b/include/lapi/syscalls/powerpc64.in
index e4bef6838..def01478d 100644
--- a/include/lapi/syscalls/powerpc64.in
+++ b/include/lapi/syscalls/powerpc64.in
@@ -145,7 +145,6 @@ readv 145
writev 146
getsid 147
fdatasync 148
-_sysctl 149
mlock 150
munlock 151
mlockall 152
diff --git a/include/lapi/syscalls/s390.in b/include/lapi/syscalls/s390.in
index 4a11871b6..5024313a5 100644
--- a/include/lapi/syscalls/s390.in
+++ b/include/lapi/syscalls/s390.in
@@ -126,7 +126,6 @@ readv 145
writev 146
getsid 147
fdatasync 148
-_sysctl 149
mlock 150
munlock 151
mlockall 152
@@ -291,7 +290,6 @@ utimes 313
fallocate 314
utimensat 315
signalfd 316
-timerfd 317
eventfd 318
timerfd_create 319
timerfd_settime 320
diff --git a/include/lapi/syscalls/s390x.in b/include/lapi/syscalls/s390x.in
index 055a039fb..357b04ceb 100644
--- a/include/lapi/syscalls/s390x.in
+++ b/include/lapi/syscalls/s390x.in
@@ -107,7 +107,6 @@ readv 145
writev 146
getsid 147
fdatasync 148
-_sysctl 149
mlock 150
munlock 151
mlockall 152
@@ -258,7 +257,6 @@ utimes 313
fallocate 314
utimensat 315
signalfd 316
-timerfd 317
eventfd 318
timerfd_create 319
timerfd_settime 320
diff --git a/include/lapi/syscalls/sh.in b/include/lapi/syscalls/sh.in
index 29cd8f8df..584d36a4e 100644
--- a/include/lapi/syscalls/sh.in
+++ b/include/lapi/syscalls/sh.in
@@ -128,7 +128,6 @@ readv 145
writev 146
getsid 147
fdatasync 148
-_sysctl 149
mlock 150
munlock 151
mlockall 152
diff --git a/include/lapi/syscalls/sparc.in b/include/lapi/syscalls/sparc.in
index ea2bfc9c1..a48c5b365 100644
--- a/include/lapi/syscalls/sparc.in
+++ b/include/lapi/syscalls/sparc.in
@@ -246,7 +246,6 @@ sched_get_priority_min 247
sched_rr_get_interval 248
nanosleep 249
mremap 250
-_sysctl 251
getsid 252
fdatasync 253
nfsservctl 254
@@ -402,6 +401,7 @@ fsconfig 431
fsmount 432
fspick 433
pidfd_open 434
+clone3 435
close_range 436
openat2 437
pidfd_getfd 438
diff --git a/include/lapi/syscalls/sparc64.in b/include/lapi/syscalls/sparc64.in
index 692652c3b..9f3e825a9 100644
--- a/include/lapi/syscalls/sparc64.in
+++ b/include/lapi/syscalls/sparc64.in
@@ -228,7 +228,6 @@ sched_get_priority_min 247
sched_rr_get_interval 248
nanosleep 249
mremap 250
-_sysctl 251
getsid 252
fdatasync 253
nfsservctl 254
@@ -365,6 +364,7 @@ fsconfig 431
fsmount 432
fspick 433
pidfd_open 434
+clone3 435
close_range 436
openat2 437
pidfd_getfd 438
diff --git a/include/lapi/syscalls/x86_64.in b/include/lapi/syscalls/x86_64.in
index 7d3d8163c..1996c26b8 100644
--- a/include/lapi/syscalls/x86_64.in
+++ b/include/lapi/syscalls/x86_64.in
@@ -154,7 +154,6 @@ munlockall 152
vhangup 153
modify_ldt 154
pivot_root 155
-_sysctl 156
prctl 157
arch_prctl 158
adjtimex 159
@@ -334,6 +333,7 @@ statx 332
io_pgetevents 333
rseq 334
uretprobe 335
+uprobe 336
pidfd_send_signal 424
io_uring_setup 425
io_uring_enter 426
--
2.51.0
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply related [flat|nested] 21+ messages in thread* [LTP] [PATCH 03/16] lapi: fallback fchroot() parameters
2026-08-19 21:24 [LTP] [PATCH 00/16] fchroot: add fchroot() testing suite Andrea Cervesato
2026-08-19 21:24 ` [LTP] [PATCH 01/16] syscalls: add v7.3 syscall numbers Andrea Cervesato
2026-08-19 21:24 ` [LTP] [PATCH 02/16] syscalls: update outdated syscall entries Andrea Cervesato
@ 2026-08-19 21:24 ` Andrea Cervesato
2026-08-19 22:27 ` [LTP] syscalls: add v7.3 syscall numbers linuxtestproject.agent
2026-08-19 21:24 ` [LTP] [PATCH STAGING 04/16] fchroot01: test fchroot() with a directory fd Andrea Cervesato
` (12 subsequent siblings)
15 siblings, 1 reply; 21+ messages in thread
From: Andrea Cervesato @ 2026-08-19 21:24 UTC (permalink / raw)
To: Linux Test Project
From: Andrea Cervesato <andrea.cervesato@suse.com>
Add to fcntl.h fallback definitions:
- FD_PIDFS_ROOT
- FD_NSFS_ROOT
- FD_FAILFS_ROOT
Signed-off-by: Andrea Cervesato <andrea.cervesato@suse.com>
---
include/lapi/fcntl.h | 13 +++++++++++++
1 file changed, 13 insertions(+)
diff --git a/include/lapi/fcntl.h b/include/lapi/fcntl.h
index 6a9ef11ce..d70954ea7 100644
--- a/include/lapi/fcntl.h
+++ b/include/lapi/fcntl.h
@@ -144,6 +144,19 @@
# endif
#endif
+/* Roots of kernel internal pseudo filesystems, usable as fd sentinels */
+#ifndef FD_PIDFS_ROOT
+# define FD_PIDFS_ROOT -10002
+#endif
+
+#ifndef FD_NSFS_ROOT
+# define FD_NSFS_ROOT -10003
+#endif
+
+#ifndef FD_FAILFS_ROOT
+# define FD_FAILFS_ROOT -10004
+#endif
+
#ifndef FALLOC_FL_KEEP_SIZE
# define FALLOC_FL_KEEP_SIZE 1
#endif
--
2.51.0
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply related [flat|nested] 21+ messages in thread* Re: [LTP] syscalls: add v7.3 syscall numbers
2026-08-19 21:24 ` [LTP] [PATCH 03/16] lapi: fallback fchroot() parameters Andrea Cervesato
@ 2026-08-19 22:27 ` linuxtestproject.agent
2026-08-20 6:03 ` Andrea Cervesato via ltp
0 siblings, 1 reply; 21+ messages in thread
From: linuxtestproject.agent @ 2026-08-19 22:27 UTC (permalink / raw)
To: Andrea Cervesato; +Cc: ltp
Hi Andrea,
On Aug 19, 2026, Andrea Cervesato wrote:
> syscalls: add v7.3 syscall numbers
--- [PATCH 3/16] ---
> /* Roots of kernel internal pseudo filesystems, usable as fd sentinels */
> #ifndef FD_PIDFS_ROOT
> # define FD_PIDFS_ROOT -10002
Could these new public macros use kernel-doc comments? Public macros added
under include/lapi must be documented in syntax recognized by linuxdoc.
--- [PATCH 4/16] ---
> fchroot01: test fchroot() with a directory fd
Could the subject carry the "[STAGING]" prefix? Linux 7.2 is the latest
stable release, while this test targets fchroot() from the 7.3 development
cycle and is placed in runtest/staging.
--- [PATCH 5/16] ---
> fchroot02: test fchroot() invalid arguments
Could the subject carry the "[STAGING]" prefix? Linux 7.2 is the latest
stable release, while this test targets fchroot() from the 7.3 development
cycle and is placed in runtest/staging.
--- [PATCH 6/16] ---
> fchroot03: test fchroot() permission checks
Could the subject carry the "[STAGING]" prefix? Linux 7.2 is the latest
stable release, while this test targets fchroot() from the 7.3 development
cycle and is placed in runtest/staging.
--- [PATCH 7/16] ---
> fchroot04: test fchroot() into failfs as root
Could the subject carry the "[STAGING]" prefix? Linux 7.2 is the latest
stable release, while this test targets fchroot() and failfs from the 7.3
development cycle and is placed in runtest/staging.
--- [PATCH 8/16] ---
> fchroot05: test failfs root can not be referenced
Could the subject carry the "[STAGING]" prefix? Linux 7.2 is the latest
stable release, while this test targets fchroot() and failfs from the 7.3
development cycle and is placed in runtest/staging.
--- [PATCH 9/16] ---
> fchroot06: test path walks under failfs root
Could the subject carry the "[STAGING]" prefix? Linux 7.2 is the latest
stable release, while this test targets fchroot() and failfs from the 7.3
development cycle and is placed in runtest/staging.
> #define UPWARDS "../../../../../../../../../.."
Could this walk continue until the inode stops changing instead? LTP accepts
any absolute TMPDIR, so a path more than ten components deep leaves this
lookup at an intermediate directory and fails the root inode comparison on a
correct kernel.
--- [PATCH 10/16] ---
> fchroot07: test execve blocked by failfs root
Could the subject carry the "[STAGING]" prefix? Linux 7.2 is the latest
stable release, while this test targets fchroot() and failfs from the 7.3
development cycle and is placed in runtest/staging.
--- [PATCH 11/16] ---
> fchroot08: test failfs root fork inheritance
Could the subject carry the "[STAGING]" prefix? Linux 7.2 is the latest
stable release, while this test targets fchroot() and failfs from the 7.3
development cycle and is placed in runtest/staging.
--- [PATCH 12/16] ---
> fchroot09: test setns escape from failfs root
Could the subject carry the "[STAGING]" prefix? Linux 7.2 is the latest
stable release, while this test targets fchroot() and failfs from the 7.3
development cycle and is placed in runtest/staging.
> SAFE_STAT(".", &st);
> TST_EXP_EXPR(st.st_dev == realcwd.st_dev &&
> st.st_ino == realcwd.st_ino,
Should this compare "." with realroot instead? mntns_install() resolves the
target namespace's "/" and assigns it to both fs->root and fs->pwd; it does
not restore the previous working directory. Unless the test starts in "/",
this comparison fails on a correct kernel.
--- [PATCH 13/16] ---
> fchroot10: test failfs entry without no_new_privs
Could the subject carry the "[STAGING]" prefix? Linux 7.2 is the latest
stable release, while this test targets fchroot() and failfs from the 7.3
development cycle and is placed in runtest/staging.
> static void setup(void)
> {
> ltpuser = SAFE_GETPWNAM("nobody");
> }
Could setup query PR_GET_NO_NEW_PRIVS and return TCONF when it is already
set? The bit is inherited and cannot be cleared, so such a launcher makes
this test exercise the allowed-entry path and report a kernel failure.
--- [PATCH 14/16] ---
> fchroot11: test failfs entry with no_new_privs
Could the subject carry the "[STAGING]" prefix? Linux 7.2 is the latest
stable release, while this test targets fchroot() and failfs from the 7.3
development cycle and is placed in runtest/staging.
> TST_EXP_FAIL(unshare(CLONE_NEWUSER), EPERM,
> "user namespace creation blocked by the failfs root");
Could a separate unprivileged child first prove that user-namespace creation
works, with TCONF when the environment blocks it? Runtime policy, seccomp, or
an LSM can return EPERM even with CONFIG_USER_NS=y, making this assertion pass
without testing the failfs restriction.
--- [PATCH 15/16] ---
> fchroot12: test failfs entry with shared fs_struct
Could the subject carry the "[STAGING]" prefix? Linux 7.2 is the latest
stable release, while this test targets fchroot() and failfs from the 7.3
development cycle and is placed in runtest/staging.
--- [PATCH 16/16] ---
> fchroot13: test failfs entry when chrooted
Could the subject carry the "[STAGING]" prefix? Linux 7.2 is the latest
stable release, while this test targets fchroot() and failfs from the 7.3
development cycle and is placed in runtest/staging.
Verdict - Needs revision
---
Note:
The agent can sometimes produce false positives although often its
findings are genuine. If you find issues with the review, please
comment this email or ignore the suggestions.
Regards,
LTP AI Reviewer
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply [flat|nested] 21+ messages in thread* Re: [LTP] syscalls: add v7.3 syscall numbers
2026-08-19 22:27 ` [LTP] syscalls: add v7.3 syscall numbers linuxtestproject.agent
@ 2026-08-20 6:03 ` Andrea Cervesato via ltp
0 siblings, 0 replies; 21+ messages in thread
From: Andrea Cervesato via ltp @ 2026-08-20 6:03 UTC (permalink / raw)
To: linuxtestproject.agent; +Cc: ltp
> > /* Roots of kernel internal pseudo filesystems, usable as fd sentinels */
> > #ifndef FD_PIDFS_ROOT
> > # define FD_PIDFS_ROOT -10002
>
> Could these new public macros use kernel-doc comments? Public macros added
> under include/lapi must be documented in syntax recognized by linuxdoc.
Not needed.
>
> --- [PATCH 4/16] ---
>
> > fchroot01: test fchroot() with a directory fd
>
> Could the subject carry the "[STAGING]" prefix? Linux 7.2 is the latest
> stable release, while this test targets fchroot() from the 7.3 development
> cycle and is placed in runtest/staging.
This is correct for all the patches.
> > #define UPWARDS "../../../../../../../../../.."
>
> Could this walk continue until the inode stops changing instead? LTP accepts
> any absolute TMPDIR, so a path more than ten components deep leaves this
> lookup at an intermediate directory and fails the root inode comparison on a
> correct kernel.
Hard to happen but correct.
> > SAFE_STAT(".", &st);
> > TST_EXP_EXPR(st.st_dev == realcwd.st_dev &&
> > st.st_ino == realcwd.st_ino,
>
> Should this compare "." with realroot instead? mntns_install() resolves the
> target namespace's "/" and assigns it to both fs->root and fs->pwd; it does
> not restore the previous working directory. Unless the test starts in "/",
> this comparison fails on a correct kernel.
I need to verify.
> > static void setup(void)
> > {
> > ltpuser = SAFE_GETPWNAM("nobody");
> > }
>
> Could setup query PR_GET_NO_NEW_PRIVS and return TCONF when it is already
> set? The bit is inherited and cannot be cleared, so such a launcher makes
> this test exercise the allowed-entry path and report a kernel failure.
Need to verify.
>
> --- [PATCH 14/16] ---
>
> > fchroot11: test failfs entry with no_new_privs
>
> Could the subject carry the "[STAGING]" prefix? Linux 7.2 is the latest
> stable release, while this test targets fchroot() and failfs from the 7.3
> development cycle and is placed in runtest/staging.
>
> > TST_EXP_FAIL(unshare(CLONE_NEWUSER), EPERM,
> > "user namespace creation blocked by the failfs root");
>
> Could a separate unprivileged child first prove that user-namespace creation
> works, with TCONF when the environment blocks it? Runtime policy, seccomp, or
> an LSM can return EPERM even with CONFIG_USER_NS=y, making this assertion pass
> without testing the failfs restriction.
Will try.
Will send a v2.
--
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] 21+ messages in thread
* [LTP] [PATCH STAGING 04/16] fchroot01: test fchroot() with a directory fd
2026-08-19 21:24 [LTP] [PATCH 00/16] fchroot: add fchroot() testing suite Andrea Cervesato
` (2 preceding siblings ...)
2026-08-19 21:24 ` [LTP] [PATCH 03/16] lapi: fallback fchroot() parameters Andrea Cervesato
@ 2026-08-19 21:24 ` Andrea Cervesato
2026-08-19 21:24 ` [LTP] [PATCH STAGING 05/16] fchroot02: test fchroot() invalid arguments Andrea Cervesato
` (11 subsequent siblings)
15 siblings, 0 replies; 21+ messages in thread
From: Andrea Cervesato @ 2026-08-19 21:24 UTC (permalink / raw)
To: Linux Test Project
From: Andrea Cervesato <andrea.cervesato@suse.com>
Verify that fchroot() with a directory fd moves the process root to
the directory referenced by the fd.
fchroot() was introduced in Linux v7.3, so the test is added to the
staging runtest file.
Signed-off-by: Andrea Cervesato <andrea.cervesato@suse.com>
---
runtest/staging | 2 +
testcases/kernel/syscalls/fchroot/.gitignore | 1 +
testcases/kernel/syscalls/fchroot/Makefile | 7 ++++
testcases/kernel/syscalls/fchroot/fchroot01.c | 57 +++++++++++++++++++++++++++
4 files changed, 67 insertions(+)
diff --git a/runtest/staging b/runtest/staging
index ef1cdea15..733d5609a 100644
--- a/runtest/staging
+++ b/runtest/staging
@@ -1 +1,3 @@
# Tests for features that are not yet in the stable kernel ABI
+
+fchroot01 fchroot01
diff --git a/testcases/kernel/syscalls/fchroot/.gitignore b/testcases/kernel/syscalls/fchroot/.gitignore
new file mode 100644
index 000000000..03ebdbe7a
--- /dev/null
+++ b/testcases/kernel/syscalls/fchroot/.gitignore
@@ -0,0 +1 @@
+fchroot01
diff --git a/testcases/kernel/syscalls/fchroot/Makefile b/testcases/kernel/syscalls/fchroot/Makefile
new file mode 100644
index 000000000..137550149
--- /dev/null
+++ b/testcases/kernel/syscalls/fchroot/Makefile
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (C) 2026 SUSE LLC Andrea Cervesato <andrea.cervesato@suse.com>
+
+top_srcdir ?= ../../../..
+
+include $(top_srcdir)/include/mk/testcases.mk
+include $(top_srcdir)/include/mk/generic_leaf_target.mk
diff --git a/testcases/kernel/syscalls/fchroot/fchroot01.c b/testcases/kernel/syscalls/fchroot/fchroot01.c
new file mode 100644
index 000000000..fb4894519
--- /dev/null
+++ b/testcases/kernel/syscalls/fchroot/fchroot01.c
@@ -0,0 +1,57 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2026 SUSE LLC Andrea Cervesato <andrea.cervesato@suse.com>
+ */
+
+/*\
+ * Test that :manpage:`fchroot(2)` with a regular directory fd moves the
+ * process root to the directory referenced by the fd.
+ *
+ * This is the fd-based counterpart of :manpage:`chroot(2)`, introduced in
+ * Linux v7.3. Root is required because fchroot() with a regular directory
+ * fd requires CAP_SYS_CHROOT.
+ *
+ * The syscall runs in a forked child so that the root of the parent
+ * process, which the test framework needs for its cleanup, is left
+ * untouched.
+ */
+
+#include <sys/stat.h>
+#include <fcntl.h>
+#include "tst_test.h"
+#include "lapi/fcntl.h"
+#include "lapi/syscalls.h"
+
+#define JAILDIR "jail"
+#define CANARY JAILDIR "/canary"
+
+static void run(void)
+{
+ struct stat st;
+
+ if (!SAFE_FORK()) {
+ int dfd = SAFE_OPEN(JAILDIR, O_PATH | O_DIRECTORY);
+
+ TST_EXP_PASS(tst_syscall(__NR_fchroot, dfd, 0),
+ "fchroot() with a directory fd");
+
+ TST_EXP_PASS(stat("/canary", &st),
+ "canary file visible under the new root");
+
+ exit(0);
+ }
+}
+
+static void setup(void)
+{
+ SAFE_MKDIR(JAILDIR, 0755);
+ SAFE_TOUCH(CANARY, 0644, NULL);
+}
+
+static struct tst_test test = {
+ .setup = setup,
+ .test_all = run,
+ .needs_root = 1,
+ .needs_tmpdir = 1,
+ .forks_child = 1,
+};
--
2.51.0
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply related [flat|nested] 21+ messages in thread* [LTP] [PATCH STAGING 05/16] fchroot02: test fchroot() invalid arguments
2026-08-19 21:24 [LTP] [PATCH 00/16] fchroot: add fchroot() testing suite Andrea Cervesato
` (3 preceding siblings ...)
2026-08-19 21:24 ` [LTP] [PATCH STAGING 04/16] fchroot01: test fchroot() with a directory fd Andrea Cervesato
@ 2026-08-19 21:24 ` Andrea Cervesato
2026-08-19 21:24 ` [LTP] [PATCH STAGING 06/16] fchroot03: test fchroot() permission checks Andrea Cervesato
` (10 subsequent siblings)
15 siblings, 0 replies; 21+ messages in thread
From: Andrea Cervesato @ 2026-08-19 21:24 UTC (permalink / raw)
To: Linux Test Project
From: Andrea Cervesato <andrea.cervesato@suse.com>
Verify the fchroot() error paths: a non-zero flags argument fails with
EINVAL before anything else, an invalid fd fails with EBADF, including
the FD_PIDFS_ROOT and FD_NSFS_ROOT sentinels, and a fd referring to a
regular file fails with ENOTDIR. All these checks happen before the
CAP_SYS_CHROOT check, so no privileges are needed.
Signed-off-by: Andrea Cervesato <andrea.cervesato@suse.com>
---
runtest/staging | 1 +
testcases/kernel/syscalls/fchroot/.gitignore | 1 +
testcases/kernel/syscalls/fchroot/fchroot02.c | 80 +++++++++++++++++++++++++++
3 files changed, 82 insertions(+)
diff --git a/runtest/staging b/runtest/staging
index 733d5609a..e12230218 100644
--- a/runtest/staging
+++ b/runtest/staging
@@ -1,3 +1,4 @@
# Tests for features that are not yet in the stable kernel ABI
fchroot01 fchroot01
+fchroot02 fchroot02
diff --git a/testcases/kernel/syscalls/fchroot/.gitignore b/testcases/kernel/syscalls/fchroot/.gitignore
index 03ebdbe7a..fb3287612 100644
--- a/testcases/kernel/syscalls/fchroot/.gitignore
+++ b/testcases/kernel/syscalls/fchroot/.gitignore
@@ -1 +1,2 @@
fchroot01
+fchroot02
diff --git a/testcases/kernel/syscalls/fchroot/fchroot02.c b/testcases/kernel/syscalls/fchroot/fchroot02.c
new file mode 100644
index 000000000..6e8aec820
--- /dev/null
+++ b/testcases/kernel/syscalls/fchroot/fchroot02.c
@@ -0,0 +1,80 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2026 SUSE LLC Andrea Cervesato <andrea.cervesato@suse.com>
+ */
+
+/*\
+ * Test the :manpage:`fchroot(2)` error paths for invalid arguments.
+ *
+ * fchroot() was introduced in Linux v7.3. The syscall checks its arguments
+ * in this order:
+ *
+ * - a non-zero flags argument fails with EINVAL before anything else,
+ * including with the FD_FAILFS_ROOT sentinel and with an invalid fd
+ * - an invalid fd fails with EBADF, including the FD_PIDFS_ROOT and
+ * FD_NSFS_ROOT sentinels which fchroot() does not accept
+ * - a fd referring to a regular file fails with ENOTDIR
+ *
+ * All these checks happen before the CAP_SYS_CHROOT check, so the test
+ * needs no privileges.
+ */
+
+#include <fcntl.h>
+#include "tst_test.h"
+#include "lapi/fcntl.h"
+#include "lapi/syscalls.h"
+
+#define FILENAME "file.txt"
+
+static int dir_fd = -1;
+static int file_fd = -1;
+static int bad_fd = -1;
+static int failfs_root = FD_FAILFS_ROOT;
+static int pidfs_root = FD_PIDFS_ROOT;
+static int nsfs_root = FD_NSFS_ROOT;
+
+static struct tcase {
+ int *fd;
+ unsigned int flags;
+ int exp_errno;
+ const char *desc;
+} tcases[] = {
+ {&dir_fd, 1, EINVAL, "non-zero flags with a directory fd"},
+ {&failfs_root, 1, EINVAL, "non-zero flags with FD_FAILFS_ROOT"},
+ {&bad_fd, 1, EINVAL, "non-zero flags with an invalid fd"},
+ {&bad_fd, 0, EBADF, "invalid fd"},
+ {&pidfs_root, 0, EBADF, "FD_PIDFS_ROOT sentinel"},
+ {&nsfs_root, 0, EBADF, "FD_NSFS_ROOT sentinel"},
+ {&file_fd, 0, ENOTDIR, "fd referring to a regular file"},
+};
+
+static void run(unsigned int i)
+{
+ struct tcase *tc = &tcases[i];
+
+ TST_EXP_FAIL(tst_syscall(__NR_fchroot, *tc->fd, tc->flags),
+ tc->exp_errno, "fchroot() with %s", tc->desc);
+}
+
+static void setup(void)
+{
+ dir_fd = SAFE_OPEN(".", O_PATH | O_DIRECTORY);
+ file_fd = SAFE_OPEN(FILENAME, O_CREAT | O_EXCL | O_WRONLY, 0644);
+}
+
+static void cleanup(void)
+{
+ if (dir_fd != -1)
+ SAFE_CLOSE(dir_fd);
+
+ if (file_fd != -1)
+ SAFE_CLOSE(file_fd);
+}
+
+static struct tst_test test = {
+ .test = run,
+ .tcnt = ARRAY_SIZE(tcases),
+ .setup = setup,
+ .cleanup = cleanup,
+ .needs_tmpdir = 1,
+};
--
2.51.0
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply related [flat|nested] 21+ messages in thread* [LTP] [PATCH STAGING 06/16] fchroot03: test fchroot() permission checks
2026-08-19 21:24 [LTP] [PATCH 00/16] fchroot: add fchroot() testing suite Andrea Cervesato
` (4 preceding siblings ...)
2026-08-19 21:24 ` [LTP] [PATCH STAGING 05/16] fchroot02: test fchroot() invalid arguments Andrea Cervesato
@ 2026-08-19 21:24 ` Andrea Cervesato
2026-08-19 21:24 ` [LTP] [PATCH STAGING 07/16] fchroot04: test fchroot() into failfs as root Andrea Cervesato
` (9 subsequent siblings)
15 siblings, 0 replies; 21+ messages in thread
From: Andrea Cervesato @ 2026-08-19 21:24 UTC (permalink / raw)
To: Linux Test Project
From: Andrea Cervesato <andrea.cervesato@suse.com>
Verify that with a regular directory fd the kernel first checks the
execute permission on the directory and then CAP_SYS_CHROOT: an
unprivileged process with an accessible directory fails with EPERM and
a process without execute permission fails with EACCES.
Signed-off-by: Andrea Cervesato <andrea.cervesato@suse.com>
---
runtest/staging | 1 +
testcases/kernel/syscalls/fchroot/.gitignore | 1 +
testcases/kernel/syscalls/fchroot/fchroot03.c | 77 +++++++++++++++++++++++++++
3 files changed, 79 insertions(+)
diff --git a/runtest/staging b/runtest/staging
index e12230218..13635037b 100644
--- a/runtest/staging
+++ b/runtest/staging
@@ -2,3 +2,4 @@
fchroot01 fchroot01
fchroot02 fchroot02
+fchroot03 fchroot03
diff --git a/testcases/kernel/syscalls/fchroot/.gitignore b/testcases/kernel/syscalls/fchroot/.gitignore
index fb3287612..235befd99 100644
--- a/testcases/kernel/syscalls/fchroot/.gitignore
+++ b/testcases/kernel/syscalls/fchroot/.gitignore
@@ -1,2 +1,3 @@
fchroot01
fchroot02
+fchroot03
diff --git a/testcases/kernel/syscalls/fchroot/fchroot03.c b/testcases/kernel/syscalls/fchroot/fchroot03.c
new file mode 100644
index 000000000..4c1350e21
--- /dev/null
+++ b/testcases/kernel/syscalls/fchroot/fchroot03.c
@@ -0,0 +1,77 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2026 SUSE LLC Andrea Cervesato <andrea.cervesato@suse.com>
+ */
+
+/*\
+ * Test the :manpage:`fchroot(2)` permission checks with a regular directory
+ * fd.
+ *
+ * fchroot() was introduced in Linux v7.3. With a regular directory fd the
+ * kernel first checks that the caller has execute permission on the
+ * directory, then that it holds CAP_SYS_CHROOT:
+ *
+ * - an unprivileged process with an accessible directory fails with EPERM
+ * - a process without execute permission on the directory fails with
+ * EACCES, proving the permission check comes before the capability
+ * check
+ *
+ * Root is required to open the directory file descriptors before dropping
+ * to an unprivileged user in forked children.
+ */
+
+#include <fcntl.h>
+#include <pwd.h>
+#include "tst_test.h"
+#include "lapi/fcntl.h"
+#include "lapi/syscalls.h"
+
+static struct tcase {
+ const char *dir;
+ mode_t mode;
+ int exp_errno;
+ const char *desc;
+} tcases[] = {
+ {"pubdir", 0755, EPERM, "no CAP_SYS_CHROOT"},
+ {"privdir", 0600, EACCES, "no execute permission"},
+};
+
+static struct passwd *ltpuser;
+
+static void run(unsigned int i)
+{
+ struct tcase *tc = &tcases[i];
+
+ if (!SAFE_FORK()) {
+ int dfd = SAFE_OPEN(tc->dir, O_PATH | O_DIRECTORY);
+
+ SAFE_SETRESUID(ltpuser->pw_uid, ltpuser->pw_uid,
+ ltpuser->pw_uid);
+
+ TST_EXP_FAIL(tst_syscall(__NR_fchroot, dfd, 0),
+ tc->exp_errno, "fchroot() with %s", tc->desc);
+
+ exit(0);
+ }
+}
+
+static void setup(void)
+{
+ unsigned int i;
+
+ ltpuser = SAFE_GETPWNAM("nobody");
+
+ for (i = 0; i < ARRAY_SIZE(tcases); i++) {
+ SAFE_MKDIR(tcases[i].dir, tcases[i].mode);
+ SAFE_CHMOD(tcases[i].dir, tcases[i].mode);
+ }
+}
+
+static struct tst_test test = {
+ .test = run,
+ .tcnt = ARRAY_SIZE(tcases),
+ .setup = setup,
+ .needs_root = 1,
+ .needs_tmpdir = 1,
+ .forks_child = 1,
+};
--
2.51.0
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply related [flat|nested] 21+ messages in thread* [LTP] [PATCH STAGING 07/16] fchroot04: test fchroot() into failfs as root
2026-08-19 21:24 [LTP] [PATCH 00/16] fchroot: add fchroot() testing suite Andrea Cervesato
` (5 preceding siblings ...)
2026-08-19 21:24 ` [LTP] [PATCH STAGING 06/16] fchroot03: test fchroot() permission checks Andrea Cervesato
@ 2026-08-19 21:24 ` Andrea Cervesato
2026-08-19 21:24 ` [LTP] [PATCH STAGING 08/16] fchroot05: test failfs root can not be referenced Andrea Cervesato
` (8 subsequent siblings)
15 siblings, 0 replies; 21+ messages in thread
From: Andrea Cervesato @ 2026-08-19 21:24 UTC (permalink / raw)
To: Linux Test Project
From: Andrea Cervesato <andrea.cervesato@suse.com>
Verify that entering failfs with the FD_FAILFS_ROOT sentinel succeeds
with CAP_SYS_CHROOT and that every absolute path lookup then fails
with EOPNOTSUPP, while the working directory is reported as
unreachable by getcwd().
Signed-off-by: Andrea Cervesato <andrea.cervesato@suse.com>
---
runtest/staging | 1 +
testcases/kernel/syscalls/fchroot/.gitignore | 1 +
testcases/kernel/syscalls/fchroot/fchroot04.c | 66 +++++++++++++++++++++++++++
3 files changed, 68 insertions(+)
diff --git a/runtest/staging b/runtest/staging
index 13635037b..91dfdba08 100644
--- a/runtest/staging
+++ b/runtest/staging
@@ -3,3 +3,4 @@
fchroot01 fchroot01
fchroot02 fchroot02
fchroot03 fchroot03
+fchroot04 fchroot04
diff --git a/testcases/kernel/syscalls/fchroot/.gitignore b/testcases/kernel/syscalls/fchroot/.gitignore
index 235befd99..9270c1408 100644
--- a/testcases/kernel/syscalls/fchroot/.gitignore
+++ b/testcases/kernel/syscalls/fchroot/.gitignore
@@ -1,3 +1,4 @@
fchroot01
fchroot02
fchroot03
+fchroot04
diff --git a/testcases/kernel/syscalls/fchroot/fchroot04.c b/testcases/kernel/syscalls/fchroot/fchroot04.c
new file mode 100644
index 000000000..5a56e84bd
--- /dev/null
+++ b/testcases/kernel/syscalls/fchroot/fchroot04.c
@@ -0,0 +1,66 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2026 SUSE LLC Andrea Cervesato <andrea.cervesato@suse.com>
+ */
+
+/*\
+ * Test :manpage:`fchroot(2)` with the FD_FAILFS_ROOT sentinel as a
+ * privileged process.
+ *
+ * fchroot() was introduced in Linux v7.3 together with failfs, a kernel
+ * internal filesystem where every operation fails with EOPNOTSUPP. The
+ * FD_FAILFS_ROOT sentinel moves the process root there without needing a
+ * file descriptor: it is the ``fs_struct`` equivalent of RESOLVE_BENEATH.
+ *
+ * The test verifies that entering failfs succeeds with CAP_SYS_CHROOT and
+ * that every absolute path lookup then fails with EOPNOTSUPP. The working
+ * directory, left behind in the real filesystem, is consequently reported
+ * as unreachable by :manpage:`getcwd(2)`.
+ *
+ * The syscall runs in a forked child because leaving failfs requires a
+ * mount namespace file descriptor and the parent needs its root for the
+ * test framework cleanup.
+ */
+
+#define _GNU_SOURCE
+#include <fcntl.h>
+#include <limits.h>
+#include <sys/stat.h>
+#include "tst_test.h"
+#include "lapi/fcntl.h"
+#include "lapi/syscalls.h"
+
+static void run(void)
+{
+ if (!SAFE_FORK()) {
+ char buf[PATH_MAX];
+
+ TST_EXP_PASS(tst_syscall(__NR_fchroot, FD_FAILFS_ROOT, 0),
+ "fchroot() with the FD_FAILFS_ROOT sentinel");
+
+ TST_EXP_FAIL2(open("/etc/passwd", O_RDONLY), EOPNOTSUPP,
+ "absolute file open");
+
+ TST_EXP_FAIL(mkdir("/foo", 0700), EOPNOTSUPP,
+ "absolute directory creation");
+
+ /*
+ * The libc getcwd() wrapper rejects the "(unreachable)"
+ * prefix produced by the kernel, so call the raw syscall.
+ */
+ TEST(tst_syscall(__NR_getcwd, buf, sizeof(buf)));
+ if (TST_RET > 0)
+ TST_EXP_EQ_STRN(buf, "(unreachable)", 13);
+ else
+ tst_res(TFAIL | TTERRNO, "getcwd() failed");
+
+ exit(0);
+ }
+}
+
+static struct tst_test test = {
+ .test_all = run,
+ .needs_root = 1,
+ .needs_tmpdir = 1,
+ .forks_child = 1,
+};
--
2.51.0
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply related [flat|nested] 21+ messages in thread* [LTP] [PATCH STAGING 08/16] fchroot05: test failfs root can not be referenced
2026-08-19 21:24 [LTP] [PATCH 00/16] fchroot: add fchroot() testing suite Andrea Cervesato
` (6 preceding siblings ...)
2026-08-19 21:24 ` [LTP] [PATCH STAGING 07/16] fchroot04: test fchroot() into failfs as root Andrea Cervesato
@ 2026-08-19 21:24 ` Andrea Cervesato
2026-08-19 21:24 ` [LTP] [PATCH STAGING 09/16] fchroot06: test path walks under failfs root Andrea Cervesato
` (7 subsequent siblings)
15 siblings, 0 replies; 21+ messages in thread
From: Andrea Cervesato @ 2026-08-19 21:24 UTC (permalink / raw)
To: Linux Test Project
From: Andrea Cervesato <andrea.cervesato@suse.com>
Verify that once fchroot() moved the root into failfs, the root
directory can not be opened anymore, not even with O_PATH, nor pinned
by following the /proc/self/root magic link, although readlink()
still names it as "failfs:/" since it does not follow the link.
Signed-off-by: Andrea Cervesato <andrea.cervesato@suse.com>
---
runtest/staging | 1 +
testcases/kernel/syscalls/fchroot/.gitignore | 1 +
testcases/kernel/syscalls/fchroot/fchroot05.c | 75 +++++++++++++++++++++++++++
3 files changed, 77 insertions(+)
diff --git a/runtest/staging b/runtest/staging
index 91dfdba08..49f51d637 100644
--- a/runtest/staging
+++ b/runtest/staging
@@ -4,3 +4,4 @@ fchroot01 fchroot01
fchroot02 fchroot02
fchroot03 fchroot03
fchroot04 fchroot04
+fchroot05 fchroot05
diff --git a/testcases/kernel/syscalls/fchroot/.gitignore b/testcases/kernel/syscalls/fchroot/.gitignore
index 9270c1408..0697f10eb 100644
--- a/testcases/kernel/syscalls/fchroot/.gitignore
+++ b/testcases/kernel/syscalls/fchroot/.gitignore
@@ -2,3 +2,4 @@ fchroot01
fchroot02
fchroot03
fchroot04
+fchroot05
diff --git a/testcases/kernel/syscalls/fchroot/fchroot05.c b/testcases/kernel/syscalls/fchroot/fchroot05.c
new file mode 100644
index 000000000..3046594ed
--- /dev/null
+++ b/testcases/kernel/syscalls/fchroot/fchroot05.c
@@ -0,0 +1,75 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2026 SUSE LLC Andrea Cervesato <andrea.cervesato@suse.com>
+ */
+
+/*\
+ * Test that the failfs root can not be referenced once it is the process
+ * root.
+ *
+ * After :manpage:`fchroot(2)` moved the root into failfs, the root
+ * directory can not be opened anymore, not even with O_PATH, because the
+ * walk lands on the failfs root as its terminal. The root also can not
+ * be pinned by following the /proc/self/root magic link into it, although
+ * :manpage:`readlink(2)` still names it as "failfs:/" since it does not
+ * follow the link.
+ *
+ * Root is required because entering failfs with the FD_FAILFS_ROOT
+ * sentinel requires CAP_SYS_CHROOT.
+ *
+ * /proc must be opened before entering failfs because every absolute path
+ * lookup fails once the root is unreachable. The test runs in a forked
+ * child so the root of the parent process is left untouched.
+ */
+
+#define _GNU_SOURCE
+#include <fcntl.h>
+#include <limits.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include "tst_test.h"
+#include "lapi/fcntl.h"
+#include "lapi/syscalls.h"
+
+static void run(void)
+{
+ if (!SAFE_FORK()) {
+ char buf[PATH_MAX];
+ struct stat st;
+ int procfd, len;
+
+ procfd = SAFE_OPEN("/proc", O_PATH | O_DIRECTORY);
+
+ TST_EXP_PASS(tst_syscall(__NR_fchroot, FD_FAILFS_ROOT, 0),
+ "fchroot() with the FD_FAILFS_ROOT sentinel");
+
+ TST_EXP_FAIL2(open("/", O_RDONLY | O_DIRECTORY), EOPNOTSUPP,
+ "open() of the failfs root");
+
+ TST_EXP_FAIL2(open("/", O_PATH), EOPNOTSUPP,
+ "O_PATH open() of the failfs root");
+
+ TST_EXP_FAIL2(openat(procfd, "self/root", O_PATH), EOPNOTSUPP,
+ "pin of the root via /proc/self/root");
+
+ TST_EXP_FAIL(fstatat(procfd, "self/root", &st, 0), EOPNOTSUPP,
+ "stat of the root via /proc/self/root");
+
+ len = readlinkat(procfd, "self/root", buf, sizeof(buf) - 1);
+ if (len < 0) {
+ tst_res(TFAIL | TTERRNO,
+ "readlinkat() of /proc/self/root");
+ } else {
+ buf[len] = '\0';
+ TST_EXP_EQ_STR(buf, "failfs:/");
+ }
+
+ exit(0);
+ }
+}
+
+static struct tst_test test = {
+ .test_all = run,
+ .needs_root = 1,
+ .forks_child = 1,
+};
--
2.51.0
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply related [flat|nested] 21+ messages in thread* [LTP] [PATCH STAGING 09/16] fchroot06: test path walks under failfs root
2026-08-19 21:24 [LTP] [PATCH 00/16] fchroot: add fchroot() testing suite Andrea Cervesato
` (7 preceding siblings ...)
2026-08-19 21:24 ` [LTP] [PATCH STAGING 08/16] fchroot05: test failfs root can not be referenced Andrea Cervesato
@ 2026-08-19 21:24 ` Andrea Cervesato
2026-08-19 21:24 ` [LTP] [PATCH STAGING 10/16] fchroot07: test execve blocked by " Andrea Cervesato
` (6 subsequent siblings)
15 siblings, 0 replies; 21+ messages in thread
From: Andrea Cervesato @ 2026-08-19 21:24 UTC (permalink / raw)
To: Linux Test Project
From: Andrea Cervesato <andrea.cervesato@suse.com>
Verify that once the root is in failfs only lookups anchored at a
file descriptor keep working: relative lookups from the working
directory and from a pre-opened directory fd succeed, absolute
symlinks fail with EOPNOTSUPP and ".." walks clamp at the top of the
mount tree, landing on the real root.
Signed-off-by: Andrea Cervesato <andrea.cervesato@suse.com>
---
runtest/staging | 1 +
testcases/kernel/syscalls/fchroot/.gitignore | 1 +
testcases/kernel/syscalls/fchroot/fchroot06.c | 90 +++++++++++++++++++++++++++
3 files changed, 92 insertions(+)
diff --git a/runtest/staging b/runtest/staging
index 49f51d637..8b6b1ecd9 100644
--- a/runtest/staging
+++ b/runtest/staging
@@ -5,3 +5,4 @@ fchroot02 fchroot02
fchroot03 fchroot03
fchroot04 fchroot04
fchroot05 fchroot05
+fchroot06 fchroot06
diff --git a/testcases/kernel/syscalls/fchroot/.gitignore b/testcases/kernel/syscalls/fchroot/.gitignore
index 0697f10eb..12151270a 100644
--- a/testcases/kernel/syscalls/fchroot/.gitignore
+++ b/testcases/kernel/syscalls/fchroot/.gitignore
@@ -3,3 +3,4 @@ fchroot02
fchroot03
fchroot04
fchroot05
+fchroot06
diff --git a/testcases/kernel/syscalls/fchroot/fchroot06.c b/testcases/kernel/syscalls/fchroot/fchroot06.c
new file mode 100644
index 000000000..a1293b932
--- /dev/null
+++ b/testcases/kernel/syscalls/fchroot/fchroot06.c
@@ -0,0 +1,90 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2026 SUSE LLC Andrea Cervesato <andrea.cervesato@suse.com>
+ */
+
+/*\
+ * Test path walks under the failfs root.
+ *
+ * Once :manpage:`fchroot(2)` moved the process root into failfs, only
+ * lookups anchored at a file descriptor keep working:
+ *
+ * - lookups relative to the working directory, which stays in the real
+ * filesystem, keep working
+ * - lookups anchored at a pre-opened directory fd keep working, including
+ * resolution of relative symlinks
+ * - absolute symlinks restart the walk at the failfs root and fail with
+ * EOPNOTSUPP
+ * - ".." walks clamp at the top of the mount tree, not at the failfs
+ * root, so walking up from the working directory lands on the real
+ * root
+ *
+ * Root is required because entering failfs with the FD_FAILFS_ROOT
+ * sentinel requires CAP_SYS_CHROOT.
+ *
+ * The test runs in a forked child so the root of the parent process is
+ * left untouched.
+ */
+
+#define _GNU_SOURCE
+#include <fcntl.h>
+#include <sys/stat.h>
+#include "tst_test.h"
+#include "lapi/fcntl.h"
+#include "lapi/syscalls.h"
+#include "tst_safe_file_at.h"
+
+#define UPWARDS "../../../../../../../../../.."
+
+static void run(void)
+{
+ if (!SAFE_FORK()) {
+ struct stat realroot, st;
+ int dfd, fd;
+
+ SAFE_STAT("/", &realroot);
+ dfd = SAFE_OPEN(".", O_RDONLY | O_DIRECTORY);
+
+ TST_EXP_PASS(tst_syscall(__NR_fchroot, FD_FAILFS_ROOT, 0),
+ "fchroot() with the FD_FAILFS_ROOT sentinel");
+
+ fd = SAFE_OPENAT(AT_FDCWD, ".", O_RDONLY | O_DIRECTORY);
+ SAFE_CLOSE(fd);
+
+ fd = SAFE_OPENAT(dfd, "canary", O_WRONLY | O_CREAT, 0600);
+ SAFE_WRITE(SAFE_WRITE_ALL, fd, "x", 1);
+ SAFE_CLOSE(fd);
+
+ fd = SAFE_OPENAT(dfd, "rel", O_RDONLY);
+ SAFE_CLOSE(fd);
+
+ TST_EXP_FAIL2(openat(dfd, "abs", O_RDONLY), EOPNOTSUPP,
+ "resolution of an absolute symlink");
+
+ fd = SAFE_OPENAT(AT_FDCWD, UPWARDS, O_PATH);
+ SAFE_FSTAT(fd, &st);
+ TST_EXP_EXPR(st.st_dev == realroot.st_dev &&
+ st.st_ino == realroot.st_ino,
+ "'..' walk clamps at the top of the mount tree");
+ SAFE_CLOSE(fd);
+
+ SAFE_CLOSE(dfd);
+
+ exit(0);
+ }
+}
+
+static void setup(void)
+{
+ SAFE_TOUCH("target", 0644, NULL);
+ SAFE_SYMLINK("target", "rel");
+ SAFE_SYMLINK("/etc", "abs");
+}
+
+static struct tst_test test = {
+ .setup = setup,
+ .test_all = run,
+ .needs_root = 1,
+ .needs_tmpdir = 1,
+ .forks_child = 1,
+};
--
2.51.0
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply related [flat|nested] 21+ messages in thread* [LTP] [PATCH STAGING 10/16] fchroot07: test execve blocked by failfs root
2026-08-19 21:24 [LTP] [PATCH 00/16] fchroot: add fchroot() testing suite Andrea Cervesato
` (8 preceding siblings ...)
2026-08-19 21:24 ` [LTP] [PATCH STAGING 09/16] fchroot06: test path walks under failfs root Andrea Cervesato
@ 2026-08-19 21:24 ` Andrea Cervesato
2026-08-19 21:24 ` [LTP] [PATCH STAGING 11/16] fchroot08: test failfs root fork inheritance Andrea Cervesato
` (5 subsequent siblings)
15 siblings, 0 replies; 21+ messages in thread
From: Andrea Cervesato @ 2026-08-19 21:24 UTC (permalink / raw)
To: Linux Test Project
From: Andrea Cervesato <andrea.cervesato@suse.com>
Verify that after fchroot() moved the root into failfs, loading a
binary by absolute path fails with EOPNOTSUPP. The exec runs in a
grandchild so a wrongly successful exec is still detected through
the exit code.
Signed-off-by: Andrea Cervesato <andrea.cervesato@suse.com>
---
runtest/staging | 1 +
testcases/kernel/syscalls/fchroot/.gitignore | 1 +
testcases/kernel/syscalls/fchroot/fchroot07.c | 71 +++++++++++++++++++++++++++
3 files changed, 73 insertions(+)
diff --git a/runtest/staging b/runtest/staging
index 8b6b1ecd9..b8954bb5a 100644
--- a/runtest/staging
+++ b/runtest/staging
@@ -6,3 +6,4 @@ fchroot03 fchroot03
fchroot04 fchroot04
fchroot05 fchroot05
fchroot06 fchroot06
+fchroot07 fchroot07
diff --git a/testcases/kernel/syscalls/fchroot/.gitignore b/testcases/kernel/syscalls/fchroot/.gitignore
index 12151270a..b68069d9a 100644
--- a/testcases/kernel/syscalls/fchroot/.gitignore
+++ b/testcases/kernel/syscalls/fchroot/.gitignore
@@ -4,3 +4,4 @@ fchroot03
fchroot04
fchroot05
fchroot06
+fchroot07
diff --git a/testcases/kernel/syscalls/fchroot/fchroot07.c b/testcases/kernel/syscalls/fchroot/fchroot07.c
new file mode 100644
index 000000000..d7b5d159b
--- /dev/null
+++ b/testcases/kernel/syscalls/fchroot/fchroot07.c
@@ -0,0 +1,71 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2026 SUSE LLC Andrea Cervesato <andrea.cervesato@suse.com>
+ */
+
+/*\
+ * Test that :manpage:`execve(2)` is blocked under the failfs root.
+ *
+ * After :manpage:`fchroot(2)` moved the process root into failfs, loading
+ * a binary by absolute path fails with EOPNOTSUPP.
+ *
+ * Root is required because entering failfs with the FD_FAILFS_ROOT
+ * sentinel requires CAP_SYS_CHROOT.
+ *
+ * The exec runs in a grandchild: a wrongly successful exec would replace
+ * the test image, so the outcome can only be reported when the exec call
+ * returns, and the grandchild exit code tells the parent whether the
+ * image was replaced.
+ */
+
+#define _GNU_SOURCE
+#include <sys/wait.h>
+#include <unistd.h>
+#include "tst_test.h"
+#include "lapi/fcntl.h"
+#include "lapi/syscalls.h"
+
+/* Marker exit code proving the grandchild image was not replaced. */
+#define EXEC_NOT_REPLACED 42
+
+static void check_exec_blocked(void)
+{
+ pid_t pid = SAFE_FORK();
+ int status;
+
+ if (!pid) {
+ TST_EXP_FAIL(execl("/bin/true", "true", NULL), EOPNOTSUPP,
+ "absolute exec blocked by the failfs root");
+
+ exit(EXEC_NOT_REPLACED);
+ }
+
+ SAFE_WAITPID(pid, &status, 0);
+ if (!WIFEXITED(status) || WEXITSTATUS(status) != EXEC_NOT_REPLACED)
+ tst_res(TFAIL, "exec replaced the test image");
+}
+
+static void run(void)
+{
+ if (!SAFE_FORK()) {
+ TST_EXP_PASS(tst_syscall(__NR_fchroot, FD_FAILFS_ROOT, 0),
+ "fchroot() with the FD_FAILFS_ROOT sentinel");
+
+ check_exec_blocked();
+
+ exit(0);
+ }
+}
+
+static void setup(void)
+{
+ if (access("/bin/true", X_OK))
+ tst_brk(TCONF | TERRNO, "/bin/true is not available");
+}
+
+static struct tst_test test = {
+ .setup = setup,
+ .test_all = run,
+ .needs_root = 1,
+ .forks_child = 1,
+};
--
2.51.0
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply related [flat|nested] 21+ messages in thread* [LTP] [PATCH STAGING 11/16] fchroot08: test failfs root fork inheritance
2026-08-19 21:24 [LTP] [PATCH 00/16] fchroot: add fchroot() testing suite Andrea Cervesato
` (9 preceding siblings ...)
2026-08-19 21:24 ` [LTP] [PATCH STAGING 10/16] fchroot07: test execve blocked by " Andrea Cervesato
@ 2026-08-19 21:24 ` Andrea Cervesato
2026-08-19 21:24 ` [LTP] [PATCH STAGING 12/16] fchroot09: test setns escape from failfs root Andrea Cervesato
` (4 subsequent siblings)
15 siblings, 0 replies; 21+ messages in thread
From: Andrea Cervesato @ 2026-08-19 21:24 UTC (permalink / raw)
To: Linux Test Project
From: Andrea Cervesato <andrea.cervesato@suse.com>
Verify that the root moved into failfs by fchroot() lives in the
fs_struct which is duplicated on fork, so a child of a process with
the failfs root also fails every absolute path lookup with
EOPNOTSUPP.
Signed-off-by: Andrea Cervesato <andrea.cervesato@suse.com>
---
runtest/staging | 1 +
testcases/kernel/syscalls/fchroot/.gitignore | 1 +
testcases/kernel/syscalls/fchroot/fchroot08.c | 52 +++++++++++++++++++++++++++
3 files changed, 54 insertions(+)
diff --git a/runtest/staging b/runtest/staging
index b8954bb5a..8dfe95c4a 100644
--- a/runtest/staging
+++ b/runtest/staging
@@ -7,3 +7,4 @@ fchroot04 fchroot04
fchroot05 fchroot05
fchroot06 fchroot06
fchroot07 fchroot07
+fchroot08 fchroot08
diff --git a/testcases/kernel/syscalls/fchroot/.gitignore b/testcases/kernel/syscalls/fchroot/.gitignore
index b68069d9a..c099b4b3a 100644
--- a/testcases/kernel/syscalls/fchroot/.gitignore
+++ b/testcases/kernel/syscalls/fchroot/.gitignore
@@ -5,3 +5,4 @@ fchroot04
fchroot05
fchroot06
fchroot07
+fchroot08
diff --git a/testcases/kernel/syscalls/fchroot/fchroot08.c b/testcases/kernel/syscalls/fchroot/fchroot08.c
new file mode 100644
index 000000000..4593a24d3
--- /dev/null
+++ b/testcases/kernel/syscalls/fchroot/fchroot08.c
@@ -0,0 +1,52 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2026 SUSE LLC Andrea Cervesato <andrea.cervesato@suse.com>
+ */
+
+/*\
+ * Test that the failfs root is inherited across :manpage:`fork(2)`.
+ *
+ * The root moved into failfs by :manpage:`fchroot(2)` lives in the
+ * fs_struct which is duplicated on fork, so a child of a process with the
+ * failfs root also fails every absolute path lookup with EOPNOTSUPP.
+ *
+ * Root is required because entering failfs with the FD_FAILFS_ROOT
+ * sentinel requires CAP_SYS_CHROOT.
+ *
+ * The test runs in a forked child so the root of the parent process is
+ * left untouched.
+ */
+
+#define _GNU_SOURCE
+#include <fcntl.h>
+#include <sys/wait.h>
+#include "tst_test.h"
+#include "lapi/fcntl.h"
+#include "lapi/syscalls.h"
+
+static void run(void)
+{
+ if (!SAFE_FORK()) {
+ pid_t pid;
+
+ TST_EXP_PASS(tst_syscall(__NR_fchroot, FD_FAILFS_ROOT, 0),
+ "fchroot() with the FD_FAILFS_ROOT sentinel");
+
+ pid = SAFE_FORK();
+ if (!pid) {
+ TST_EXP_FAIL2(open("/etc", O_PATH), EOPNOTSUPP,
+ "absolute lookup in a forked child");
+ exit(0);
+ }
+
+ SAFE_WAITPID(pid, NULL, 0);
+
+ exit(0);
+ }
+}
+
+static struct tst_test test = {
+ .test_all = run,
+ .needs_root = 1,
+ .forks_child = 1,
+};
--
2.51.0
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply related [flat|nested] 21+ messages in thread* [LTP] [PATCH STAGING 12/16] fchroot09: test setns escape from failfs root
2026-08-19 21:24 [LTP] [PATCH 00/16] fchroot: add fchroot() testing suite Andrea Cervesato
` (10 preceding siblings ...)
2026-08-19 21:24 ` [LTP] [PATCH STAGING 11/16] fchroot08: test failfs root fork inheritance Andrea Cervesato
@ 2026-08-19 21:24 ` Andrea Cervesato
2026-08-19 21:24 ` [LTP] [PATCH STAGING 13/16] fchroot10: test failfs entry without no_new_privs Andrea Cervesato
` (3 subsequent siblings)
15 siblings, 0 replies; 21+ messages in thread
From: Andrea Cervesato @ 2026-08-19 21:24 UTC (permalink / raw)
To: Linux Test Project
From: Andrea Cervesato <andrea.cervesato@suse.com>
Verify that entering failfs with fchroot() is hard to undo: a process
inside counts as chrooted, so the remaining way out is a pre-opened
mount namespace fd. setns() into it resets both the root and the
working directory.
Signed-off-by: Andrea Cervesato <andrea.cervesato@suse.com>
---
runtest/staging | 1 +
testcases/kernel/syscalls/fchroot/.gitignore | 1 +
testcases/kernel/syscalls/fchroot/fchroot09.c | 70 +++++++++++++++++++++++++++
3 files changed, 72 insertions(+)
diff --git a/runtest/staging b/runtest/staging
index 8dfe95c4a..23f6c6a20 100644
--- a/runtest/staging
+++ b/runtest/staging
@@ -8,3 +8,4 @@ fchroot05 fchroot05
fchroot06 fchroot06
fchroot07 fchroot07
fchroot08 fchroot08
+fchroot09 fchroot09
diff --git a/testcases/kernel/syscalls/fchroot/.gitignore b/testcases/kernel/syscalls/fchroot/.gitignore
index c099b4b3a..e803fa2b7 100644
--- a/testcases/kernel/syscalls/fchroot/.gitignore
+++ b/testcases/kernel/syscalls/fchroot/.gitignore
@@ -6,3 +6,4 @@ fchroot05
fchroot06
fchroot07
fchroot08
+fchroot09
diff --git a/testcases/kernel/syscalls/fchroot/fchroot09.c b/testcases/kernel/syscalls/fchroot/fchroot09.c
new file mode 100644
index 000000000..196a6844d
--- /dev/null
+++ b/testcases/kernel/syscalls/fchroot/fchroot09.c
@@ -0,0 +1,70 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2026 SUSE LLC Andrea Cervesato <andrea.cervesato@suse.com>
+ */
+
+/*\
+ * Test that :manpage:`setns(2)` escapes the failfs root.
+ *
+ * Entering failfs with :manpage:`fchroot(2)` is hard to undo: a process
+ * inside counts as chrooted, so :manpage:`chroot(2)` and fchroot() back
+ * out require CAP_SYS_CHROOT. The remaining way out is a pre-opened mount
+ * namespace file descriptor: setns() into it resets both the root and the
+ * working directory.
+ *
+ * Root is required because entering failfs requires CAP_SYS_CHROOT and
+ * :manpage:`setns(2)` into the mount namespace requires CAP_SYS_ADMIN.
+ *
+ * The test runs in a forked child so the root of the parent process is
+ * left untouched.
+ */
+
+#define _GNU_SOURCE
+#include <fcntl.h>
+#include <sys/stat.h>
+#include "tst_test.h"
+#include "lapi/fcntl.h"
+#include "lapi/sched.h"
+#include "lapi/setns.h"
+#include "lapi/syscalls.h"
+
+static void run(void)
+{
+ if (!SAFE_FORK()) {
+ struct stat realroot, realcwd, st;
+ int nsfd;
+
+ SAFE_STAT("/", &realroot);
+ SAFE_STAT(".", &realcwd);
+ nsfd = SAFE_OPEN("/proc/self/ns/mnt", O_RDONLY);
+
+ TST_EXP_PASS(tst_syscall(__NR_fchroot, FD_FAILFS_ROOT, 0),
+ "fchroot() with the FD_FAILFS_ROOT sentinel");
+
+ TST_EXP_FAIL2(open("/etc", O_PATH), EOPNOTSUPP,
+ "absolute lookup after entering failfs");
+
+ TST_EXP_PASS(setns(nsfd, CLONE_NEWNS),
+ "setns() back into the mount namespace");
+
+ SAFE_CLOSE(nsfd);
+
+ SAFE_STAT("/", &st);
+ TST_EXP_EXPR(st.st_dev == realroot.st_dev &&
+ st.st_ino == realroot.st_ino,
+ "root restored after setns()");
+
+ SAFE_STAT(".", &st);
+ TST_EXP_EXPR(st.st_dev == realcwd.st_dev &&
+ st.st_ino == realcwd.st_ino,
+ "working directory restored after setns()");
+
+ exit(0);
+ }
+}
+
+static struct tst_test test = {
+ .test_all = run,
+ .needs_root = 1,
+ .forks_child = 1,
+};
--
2.51.0
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply related [flat|nested] 21+ messages in thread* [LTP] [PATCH STAGING 13/16] fchroot10: test failfs entry without no_new_privs
2026-08-19 21:24 [LTP] [PATCH 00/16] fchroot: add fchroot() testing suite Andrea Cervesato
` (11 preceding siblings ...)
2026-08-19 21:24 ` [LTP] [PATCH STAGING 12/16] fchroot09: test setns escape from failfs root Andrea Cervesato
@ 2026-08-19 21:24 ` Andrea Cervesato
2026-08-19 21:24 ` [LTP] [PATCH STAGING 14/16] fchroot11: test failfs entry with no_new_privs Andrea Cervesato
` (2 subsequent siblings)
15 siblings, 0 replies; 21+ messages in thread
From: Andrea Cervesato @ 2026-08-19 21:24 UTC (permalink / raw)
To: Linux Test Project
From: Andrea Cervesato <andrea.cervesato@suse.com>
Verify that unprivileged fchroot() into failfs is refused without
no_new_privs: without it a setuid binary on a regular mount is still
reachable via an inherited directory fd, and executing it with an
unusable root directory is the classic confused deputy, so the kernel
refuses the syscall with EPERM.
Signed-off-by: Andrea Cervesato <andrea.cervesato@suse.com>
---
runtest/staging | 1 +
testcases/kernel/syscalls/fchroot/.gitignore | 1 +
testcases/kernel/syscalls/fchroot/fchroot10.c | 49 +++++++++++++++++++++++++++
3 files changed, 51 insertions(+)
diff --git a/runtest/staging b/runtest/staging
index 23f6c6a20..9ec2a7897 100644
--- a/runtest/staging
+++ b/runtest/staging
@@ -9,3 +9,4 @@ fchroot06 fchroot06
fchroot07 fchroot07
fchroot08 fchroot08
fchroot09 fchroot09
+fchroot10 fchroot10
diff --git a/testcases/kernel/syscalls/fchroot/.gitignore b/testcases/kernel/syscalls/fchroot/.gitignore
index e803fa2b7..570da2b98 100644
--- a/testcases/kernel/syscalls/fchroot/.gitignore
+++ b/testcases/kernel/syscalls/fchroot/.gitignore
@@ -7,3 +7,4 @@ fchroot06
fchroot07
fchroot08
fchroot09
+fchroot10
diff --git a/testcases/kernel/syscalls/fchroot/fchroot10.c b/testcases/kernel/syscalls/fchroot/fchroot10.c
new file mode 100644
index 000000000..42b343f64
--- /dev/null
+++ b/testcases/kernel/syscalls/fchroot/fchroot10.c
@@ -0,0 +1,49 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2026 SUSE LLC Andrea Cervesato <andrea.cervesato@suse.com>
+ */
+
+/*\
+ * Test that unprivileged :manpage:`fchroot(2)` into failfs is refused
+ * without no_new_privs.
+ *
+ * Without no_new_privs a setuid binary on a regular mount is still
+ * reachable via an inherited directory file descriptor, and executing it
+ * with an unusable root directory is the classic confused deputy, so the
+ * kernel refuses the syscall with EPERM.
+ *
+ * Root is required to drop to an unprivileged user in the forked child.
+ */
+
+#define _GNU_SOURCE
+#include <pwd.h>
+#include "tst_test.h"
+#include "lapi/fcntl.h"
+#include "lapi/syscalls.h"
+
+static struct passwd *ltpuser;
+
+static void run(void)
+{
+ if (!SAFE_FORK()) {
+ SAFE_SETRESUID(ltpuser->pw_uid, ltpuser->pw_uid,
+ ltpuser->pw_uid);
+
+ TST_EXP_FAIL(tst_syscall(__NR_fchroot, FD_FAILFS_ROOT, 0),
+ EPERM, "unprivileged fchroot() without no_new_privs");
+
+ exit(0);
+ }
+}
+
+static void setup(void)
+{
+ ltpuser = SAFE_GETPWNAM("nobody");
+}
+
+static struct tst_test test = {
+ .setup = setup,
+ .test_all = run,
+ .needs_root = 1,
+ .forks_child = 1,
+};
--
2.51.0
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply related [flat|nested] 21+ messages in thread* [LTP] [PATCH STAGING 14/16] fchroot11: test failfs entry with no_new_privs
2026-08-19 21:24 [LTP] [PATCH 00/16] fchroot: add fchroot() testing suite Andrea Cervesato
` (12 preceding siblings ...)
2026-08-19 21:24 ` [LTP] [PATCH STAGING 13/16] fchroot10: test failfs entry without no_new_privs Andrea Cervesato
@ 2026-08-19 21:24 ` Andrea Cervesato
2026-08-19 21:24 ` [LTP] [PATCH STAGING 15/16] fchroot12: test failfs entry with shared fs_struct Andrea Cervesato
2026-08-19 21:24 ` [LTP] [PATCH STAGING 16/16] fchroot13: test failfs entry when chrooted Andrea Cervesato
15 siblings, 0 replies; 21+ messages in thread
From: Andrea Cervesato @ 2026-08-19 21:24 UTC (permalink / raw)
To: Linux Test Project
From: Andrea Cervesato <andrea.cervesato@suse.com>
Verify that an unprivileged process may enter failfs when it
committed to no_new_privs, since setuid binaries then pose no
confused deputy risk anymore. The test also verifies that a process
which entered failfs counts as chrooted: it can no longer create a
user namespace with unshare() to regain CAP_SYS_CHROOT.
Signed-off-by: Andrea Cervesato <andrea.cervesato@suse.com>
---
runtest/staging | 1 +
testcases/kernel/syscalls/fchroot/.gitignore | 1 +
testcases/kernel/syscalls/fchroot/fchroot11.c | 74 +++++++++++++++++++++++++++
3 files changed, 76 insertions(+)
diff --git a/runtest/staging b/runtest/staging
index 9ec2a7897..137e3f99a 100644
--- a/runtest/staging
+++ b/runtest/staging
@@ -10,3 +10,4 @@ fchroot07 fchroot07
fchroot08 fchroot08
fchroot09 fchroot09
fchroot10 fchroot10
+fchroot11 fchroot11
diff --git a/testcases/kernel/syscalls/fchroot/.gitignore b/testcases/kernel/syscalls/fchroot/.gitignore
index 570da2b98..0eca20548 100644
--- a/testcases/kernel/syscalls/fchroot/.gitignore
+++ b/testcases/kernel/syscalls/fchroot/.gitignore
@@ -8,3 +8,4 @@ fchroot07
fchroot08
fchroot09
fchroot10
+fchroot11
diff --git a/testcases/kernel/syscalls/fchroot/fchroot11.c b/testcases/kernel/syscalls/fchroot/fchroot11.c
new file mode 100644
index 000000000..114f89da7
--- /dev/null
+++ b/testcases/kernel/syscalls/fchroot/fchroot11.c
@@ -0,0 +1,74 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2026 SUSE LLC Andrea Cervesato <andrea.cervesato@suse.com>
+ */
+
+/*\
+ * Test unprivileged :manpage:`fchroot(2)` into failfs with no_new_privs
+ * set.
+ *
+ * An unprivileged process may enter failfs when it committed to
+ * no_new_privs, since setuid binaries then pose no confused deputy risk
+ * anymore. The test also verifies that a process which entered failfs
+ * counts as chrooted: it can no longer create a user namespace with
+ * :manpage:`unshare(2)` to regain CAP_SYS_CHROOT.
+ *
+ * The user namespace check requires CONFIG_USER_NS, which is enforced
+ * with .needs_kconfigs.
+ *
+ * Root is required to drop to an unprivileged user in the forked child.
+ */
+
+#define _GNU_SOURCE
+#include <fcntl.h>
+#include <pwd.h>
+#include <sched.h>
+#include "tst_test.h"
+#include "lapi/fcntl.h"
+#include "lapi/prctl.h"
+#include "lapi/sched.h"
+#include "lapi/syscalls.h"
+
+static struct passwd *ltpuser;
+
+static void check_userns_blocked(void)
+{
+ TST_EXP_FAIL(unshare(CLONE_NEWUSER), EPERM,
+ "user namespace creation blocked by the failfs root");
+}
+
+static void run(void)
+{
+ if (!SAFE_FORK()) {
+ SAFE_SETRESUID(ltpuser->pw_uid, ltpuser->pw_uid,
+ ltpuser->pw_uid);
+
+ SAFE_PRCTL(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
+
+ TST_EXP_PASS(tst_syscall(__NR_fchroot, FD_FAILFS_ROOT, 0),
+ "unprivileged fchroot() with no_new_privs");
+
+ TST_EXP_FAIL2(open("/etc/passwd", O_RDONLY), EOPNOTSUPP,
+ "absolute lookup after entering failfs");
+
+ check_userns_blocked();
+
+ exit(0);
+ }
+}
+
+static void setup(void)
+{
+ ltpuser = SAFE_GETPWNAM("nobody");
+}
+
+static struct tst_test test = {
+ .setup = setup,
+ .test_all = run,
+ .needs_root = 1,
+ .forks_child = 1,
+ .needs_kconfigs = (const char *[]) {
+ "CONFIG_USER_NS=y",
+ NULL,
+ }
+};
--
2.51.0
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply related [flat|nested] 21+ messages in thread* [LTP] [PATCH STAGING 15/16] fchroot12: test failfs entry with shared fs_struct
2026-08-19 21:24 [LTP] [PATCH 00/16] fchroot: add fchroot() testing suite Andrea Cervesato
` (13 preceding siblings ...)
2026-08-19 21:24 ` [LTP] [PATCH STAGING 14/16] fchroot11: test failfs entry with no_new_privs Andrea Cervesato
@ 2026-08-19 21:24 ` Andrea Cervesato
2026-08-19 21:24 ` [LTP] [PATCH STAGING 16/16] fchroot13: test failfs entry when chrooted Andrea Cervesato
15 siblings, 0 replies; 21+ messages in thread
From: Andrea Cervesato @ 2026-08-19 21:24 UTC (permalink / raw)
To: Linux Test Project
From: Andrea Cervesato <andrea.cervesato@suse.com>
Verify that unprivileged fchroot() into failfs is refused with a
shared fs_struct: a CLONE_FS sibling without no_new_privs could
execute a setuid binary with the failfs root, so entry requires
fs->users == 1, the same restriction setns() applies for the mount
namespace. The sibling bumps fs->users to 2, making the syscall fail
with EINVAL.
Signed-off-by: Andrea Cervesato <andrea.cervesato@suse.com>
---
runtest/staging | 1 +
testcases/kernel/syscalls/fchroot/.gitignore | 1 +
testcases/kernel/syscalls/fchroot/fchroot12.c | 82 +++++++++++++++++++++++++++
3 files changed, 84 insertions(+)
diff --git a/runtest/staging b/runtest/staging
index 137e3f99a..1655b6813 100644
--- a/runtest/staging
+++ b/runtest/staging
@@ -11,3 +11,4 @@ fchroot08 fchroot08
fchroot09 fchroot09
fchroot10 fchroot10
fchroot11 fchroot11
+fchroot12 fchroot12
diff --git a/testcases/kernel/syscalls/fchroot/.gitignore b/testcases/kernel/syscalls/fchroot/.gitignore
index 0eca20548..de8ca0688 100644
--- a/testcases/kernel/syscalls/fchroot/.gitignore
+++ b/testcases/kernel/syscalls/fchroot/.gitignore
@@ -9,3 +9,4 @@ fchroot08
fchroot09
fchroot10
fchroot11
+fchroot12
diff --git a/testcases/kernel/syscalls/fchroot/fchroot12.c b/testcases/kernel/syscalls/fchroot/fchroot12.c
new file mode 100644
index 000000000..97fefb9fa
--- /dev/null
+++ b/testcases/kernel/syscalls/fchroot/fchroot12.c
@@ -0,0 +1,82 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2026 SUSE LLC Andrea Cervesato <andrea.cervesato@suse.com>
+ */
+
+/*\
+ * Test that unprivileged :manpage:`fchroot(2)` into failfs is refused
+ * with a shared fs_struct.
+ *
+ * no_new_privs is checked on the calling thread, but the root lives in
+ * the fs_struct shared with a :manpage:`clone(2)` CLONE_FS sibling. A
+ * sibling without no_new_privs could execute a setuid binary with the
+ * failfs root, so entry requires fs->users == 1, the same restriction
+ * :manpage:`setns(2)` applies for the mount namespace. The sibling bumps
+ * fs->users to 2, making the syscall fail with EINVAL.
+ *
+ * Root is required to drop to an unprivileged user in the forked child.
+ */
+
+#define _GNU_SOURCE
+#include <sys/wait.h>
+#include <pwd.h>
+#include <sched.h>
+#include <signal.h>
+#include <unistd.h>
+#include "tst_test.h"
+#include "lapi/fcntl.h"
+#include "lapi/prctl.h"
+#include "lapi/sched.h"
+#include "lapi/syscalls.h"
+
+static struct passwd *ltpuser;
+
+static void run(void)
+{
+ if (!SAFE_FORK()) {
+ struct tst_clone_args args = {
+ .flags = CLONE_FS,
+ .exit_signal = SIGCHLD,
+ };
+ pid_t parent = getpid();
+ pid_t sib;
+
+ SAFE_SETRESUID(ltpuser->pw_uid, ltpuser->pw_uid,
+ ltpuser->pw_uid);
+
+ sib = SAFE_CLONE(&args);
+ if (!sib) {
+ /*
+ * Parked sibling sharing the fs_struct with the
+ * caller. It must die with the parent, otherwise it
+ * would outlive the test.
+ */
+ SAFE_PRCTL(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0);
+ if (getppid() == parent)
+ pause();
+ exit(0);
+ }
+
+ SAFE_PRCTL(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
+
+ TST_EXP_FAIL(tst_syscall(__NR_fchroot, FD_FAILFS_ROOT, 0),
+ EINVAL, "fchroot() with a shared fs_struct");
+
+ SAFE_KILL(sib, SIGKILL);
+ SAFE_WAITPID(sib, NULL, 0);
+
+ exit(0);
+ }
+}
+
+static void setup(void)
+{
+ ltpuser = SAFE_GETPWNAM("nobody");
+}
+
+static struct tst_test test = {
+ .setup = setup,
+ .test_all = run,
+ .needs_root = 1,
+ .forks_child = 1,
+};
--
2.51.0
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply related [flat|nested] 21+ messages in thread* [LTP] [PATCH STAGING 16/16] fchroot13: test failfs entry when chrooted
2026-08-19 21:24 [LTP] [PATCH 00/16] fchroot: add fchroot() testing suite Andrea Cervesato
` (14 preceding siblings ...)
2026-08-19 21:24 ` [LTP] [PATCH STAGING 15/16] fchroot12: test failfs entry with shared fs_struct Andrea Cervesato
@ 2026-08-19 21:24 ` Andrea Cervesato
15 siblings, 0 replies; 21+ messages in thread
From: Andrea Cervesato @ 2026-08-19 21:24 UTC (permalink / raw)
To: Linux Test Project
From: Andrea Cervesato <andrea.cervesato@suse.com>
Verify that unprivileged fchroot() into failfs is refused when the
process is already chrooted: moving the root of a chrooted task into
failfs would allow it to escape its chroot via openat(fd, "..") with
a pre-opened directory fd, so the kernel refuses the syscall with
EPERM.
Signed-off-by: Andrea Cervesato <andrea.cervesato@suse.com>
---
runtest/staging | 1 +
testcases/kernel/syscalls/fchroot/.gitignore | 1 +
testcases/kernel/syscalls/fchroot/fchroot13.c | 62 +++++++++++++++++++++++++++
3 files changed, 64 insertions(+)
diff --git a/runtest/staging b/runtest/staging
index 1655b6813..95e777ffb 100644
--- a/runtest/staging
+++ b/runtest/staging
@@ -12,3 +12,4 @@ fchroot09 fchroot09
fchroot10 fchroot10
fchroot11 fchroot11
fchroot12 fchroot12
+fchroot13 fchroot13
diff --git a/testcases/kernel/syscalls/fchroot/.gitignore b/testcases/kernel/syscalls/fchroot/.gitignore
index de8ca0688..0c4461611 100644
--- a/testcases/kernel/syscalls/fchroot/.gitignore
+++ b/testcases/kernel/syscalls/fchroot/.gitignore
@@ -10,3 +10,4 @@ fchroot09
fchroot10
fchroot11
fchroot12
+fchroot13
diff --git a/testcases/kernel/syscalls/fchroot/fchroot13.c b/testcases/kernel/syscalls/fchroot/fchroot13.c
new file mode 100644
index 000000000..4bedd0180
--- /dev/null
+++ b/testcases/kernel/syscalls/fchroot/fchroot13.c
@@ -0,0 +1,62 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2026 SUSE LLC Andrea Cervesato <andrea.cervesato@suse.com>
+ */
+
+/*\
+ * Test that unprivileged :manpage:`fchroot(2)` into failfs is refused
+ * when the process is already chrooted.
+ *
+ * The root directory is what confines ".." resolution and the failfs root
+ * can never be reached by walking up a real mount tree. Moving the root
+ * of a chrooted task into failfs would allow it to escape its chroot via
+ * openat(fd, "..") with a pre-opened directory fd, so the kernel refuses
+ * the syscall with EPERM.
+ *
+ * Root is required to create the chroot jail before dropping to an
+ * unprivileged user in the forked child.
+ */
+
+#define _GNU_SOURCE
+#include <pwd.h>
+#include <unistd.h>
+#include "tst_test.h"
+#include "lapi/fcntl.h"
+#include "lapi/prctl.h"
+#include "lapi/syscalls.h"
+
+#define JAILDIR "jail"
+
+static struct passwd *ltpuser;
+
+static void run(void)
+{
+ if (!SAFE_FORK()) {
+ SAFE_CHROOT(JAILDIR);
+ SAFE_CHDIR("/");
+
+ SAFE_SETRESUID(ltpuser->pw_uid, ltpuser->pw_uid,
+ ltpuser->pw_uid);
+
+ SAFE_PRCTL(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
+
+ TST_EXP_FAIL(tst_syscall(__NR_fchroot, FD_FAILFS_ROOT, 0),
+ EPERM, "fchroot() from a chrooted process");
+
+ exit(0);
+ }
+}
+
+static void setup(void)
+{
+ ltpuser = SAFE_GETPWNAM("nobody");
+ SAFE_MKDIR(JAILDIR, 0755);
+}
+
+static struct tst_test test = {
+ .setup = setup,
+ .test_all = run,
+ .needs_root = 1,
+ .needs_tmpdir = 1,
+ .forks_child = 1,
+};
--
2.51.0
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply related [flat|nested] 21+ messages in thread