Skip to content

fs,sched: add chroot() filesystem jail - #19900

Open
Abhishekmishra2808 wants to merge 4 commits into
apache:masterfrom
Abhishekmishra2808:feature/fs-chroot
Open

fs,sched: add chroot() filesystem jail#19900
Abhishekmishra2808 wants to merge 4 commits into
apache:masterfrom
Abhishekmishra2808:feature/fs-chroot

Conversation

@Abhishekmishra2808

@Abhishekmishra2808 Abhishekmishra2808 commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

Summary

CONFIG_FS_CHROOT adds POSIX chroot() so a task group can pin a directory as its filesystem root. Absolute lookups start at that inode, children inherit the jail, and PWD is rewritten so relative paths cannot walk out. This is a filesystem jail, not a container: descriptors opened before chroot() that already point outside the tree remain usable.

Impact

The option is off by default. When enabled, chroot() is a new syscall and, with SCHED_USER_IDENTITY, requires effective UID 0. On CONFIG_BUILD_FLAT the euid == 0 gate and tg_root share the same trust boundary as credentials. Companion apps change: apache/nuttx-apps#3735.

Testing

Host: WSL2 x86_64. Board: sim (CONFIG_FS_CHROOT=y). Companion apps PR: apache/nuttx-apps#3735.

$ ./tools/checkpatch.sh -f fs/inode/fs_inodesearch.c fs/vfs/fs_chroot.c \
    include/nuttx/sched.h include/unistd.h sched/group/group_create.c \
    sched/group/group_leave.c include/nuttx/fs/fs.h syscall/syscall.csv \
    fs/Kconfig fs/vfs/Make.defs fs/vfs/CMakeLists.txt \
    Documentation/implementation/chroot.rst \
    Documentation/implementation/user_identity.rst \
    Documentation/applications/nsh/commands.rst
✔️ All checks pass.
$ cd Documentation && make html
build succeeded.

The HTML pages are in _build/html.

NSH no-command form (syscall from userspace; chroot builtin is in apache/nuttx-apps#3735):

login: root
User Logged-in!
nsh> mkdir /tmp/jail
nsh> echo hello > /tmp/jail/marker
nsh> echo secret > /tmp/secret
nsh> chroot /tmp/jail
nsh> pwd
/
nsh> cat /marker
hello
nsh> ls /dev
nsh: ls: stat failed: 2
nsh> cat /tmp/secret
nsh: cat: open failed: 2

ostest (kernel jail, leftover fd, inheritance):

nsh> ostest
...
user_main: chroot test
chroot_test: Starting test
chroot_test: /marker is visible inside the jail
chroot_test: host paths are not visible inside the jail
chroot_test: pre-opened host fd still usable after chroot
chroot_test: grandchild still sees the jail
chroot_test: PASSED

Comment thread Documentation/applications/nsh/commands.rst
Comment thread Documentation/implementation/chroot.rst Outdated
Comment thread Documentation/implementation/chroot.rst
Comment thread Documentation/implementation/chroot.rst Outdated
Comment thread Documentation/implementation/user_identity.rst Outdated
Comment thread fs/inode/fs_inodesearch.c
@acassis

acassis commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

Nice work @Abhishekmishra2808 !
@xiaoxiang781216 @raiden00pl maybe we could use it to create separated jails/arenas to use with telnet / ssh to avoid user to damage the original root filesystems. But I think in this case we need to be able to mount a limited /dev, /proc, etc

@github-actions

github-actions Bot commented Aug 19, 2026

Copy link
Copy Markdown

MemBrowse Memory Report

arduino-mega2560

  • flash: .text +166 B (+0.3%, 65,258 B / 262,144 B, total: 25% used)

hifive1-revb

  • flash: .text +104 B (+0.1%, 83,820 B / 4,194,304 B, total: 2% used)

mirtoo

  • kseg0_progmem: .text +96 B (+0.1%, 67,848 B / 131,072 B, total: 52% used)

qemu-armv8a

  • Code: .text.file_mq_unlink -4 B, .text.file_mq_vopen -8 B, .text.file_vopen -4 B, .text.inode_find +12 B, .text.inode_reserve +8 B, .text.inode_search -496 B, .text.mkdir -4 B, .text.nx_stat -4 B, .text.nx_umount2 +16 B, .text.nx_unlink -20 B, .text.rmdir -20 B (+0.0%, 319,936 B)

qemu-intel64

  • Code: .text -316 B (-0.0%, 8,659,398 B)
  • Data: .rodata +64 B (+0.1%, 120,209 B)

rx65n-rsk2mb

  • ROM: .text -32 B (-0.0%, 86,848 B / 2,097,152 B, total: 4% used)

s698pm-dkit

  • Code: .text +336 B (+0.1%, 365,504 B)

stm32-nucleo-f103rb

  • flash: .text +64 B (+0.2%, 34,464 B / 131,072 B, total: 26% used)
    No memory changes detected for:
  • esp32-devkitc

Comment thread include/nuttx/fs/fs.h Outdated
Comment thread sched/group/group_create.c
Comment thread sched/group/group_create.c Outdated
Comment thread fs/vfs/fs_chroot.c Outdated
Comment thread sched/group/group_create.c Outdated
Comment thread fs/vfs/fs_chroot.c Outdated
Comment thread fs/inode/fs_inodesearch.c Outdated
Comment thread include/nuttx/sched.h Outdated
Comment thread fs/vfs/fs_chroot.c Outdated
Comment thread fs/inode/fs_inodesearch.c Outdated
@Abhishekmishra2808
Abhishekmishra2808 force-pushed the feature/fs-chroot branch 2 times, most recently from 5b250cc to d3ea539 Compare August 24, 2026 03:39
Comment thread sched/group/group_create.c Outdated
Comment thread sched/group/group_create.c Outdated
@Abhishekmishra2808

Copy link
Copy Markdown
Contributor Author

@acassis PTAL !

acassis
acassis previously approved these changes Aug 24, 2026
@acassis

acassis commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

Comment thread fs/vfs/fs_chroot.c Outdated
Comment thread fs/vfs/fs_chroot.c Outdated
Comment thread fs/vfs/fs_chroot.c Outdated
Comment thread fs/inode/fs_inodesearch.c Outdated
Comment thread fs/inode/fs_inodesearch.c Outdated
@xiaoxiang781216

xiaoxiang781216 commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

ci was fixed, please rebase your change to the last master. @Abhishekmishra2808, many code can be simplified with #19991.

@Abhishekmishra2808

Copy link
Copy Markdown
Contributor Author

@xiaoxiang781216 done

@xiaoxiang781216

Copy link
Copy Markdown
Contributor

@xiaoxiang781216 done

@Abhishekmishra2808 please fix the conflict.

Comment thread fs/inode/fs_inodesearch.c Outdated
Comment thread fs/inode/fs_inodesearch.c Outdated
Comment thread fs/inode/fs_inodesearch.c Outdated
Comment thread fs/inode/fs_inodesearch.c Outdated
Comment thread fs/inode/fs_inodesearch.c Outdated
Comment thread fs/inode/fs_inodesearch.c Outdated
Comment thread fs/inode/fs_inodesearch.c
Comment thread fs/inode/fs_inodesearch.c Outdated
Comment thread fs/inode/fs_inodesearch.c Outdated
Comment thread fs/inode/fs_inodesearch.c Outdated
@github-actions github-actions Bot added Size: XL The size of the change in this PR is very large. Consider breaking down the PR into smaller pieces. and removed Size: L The size of the change in this PR is large labels Aug 29, 2026
Comment thread fs/driver/fs_findblockdriver.c Outdated
Comment thread fs/driver/fs_finddriver.c Outdated
Comment thread fs/driver/fs_findmtddriver.c Outdated
Comment thread fs/event/event_open.c Outdated
Comment thread fs/fat/fs_fat32attrib.c Outdated
Comment thread fs/mount/fs_automount.c Outdated
Comment thread fs/inode/inode.h Outdated
Comment thread fs/inode/inode.h Outdated
Comment thread fs/inode/fs_inodesearch.c Outdated
Comment thread fs/vfs/fs_chroot.c Outdated
Store the jail as an absolute path on the task group, copy it to
children, and free it when the last member leaves.

Signed-off-by: Abhishek Mishra <mishra.abhishek2808@gmail.com>
Add CONFIG_FS_CHROOT and POSIX chroot(). Store the jail as an
absolute path on the task group, and require euid 0 when user
identity is enabled.

Signed-off-by: Abhishek Mishra <mishra.abhishek2808@gmail.com>
Prepare paths in inode_search_setup(): prepend tg_root, canonicalize
with a jail-floor dst_min, then walk from g_root_inode. Replace
SETUP_SEARCH / RELEASE_SEARCH with inode_search_setup() /
inode_search_release().

Signed-off-by: Abhishek Mishra <mishra.abhishek2808@gmail.com>
Describe the jail, leftover pre-opened fds, the NSH command-form scrub,
and the flat-build trust boundary shared with credentials.

Signed-off-by: Abhishek Mishra <mishra.abhishek2808@gmail.com>
if (ret < 0)
{
ferr("ERROR: Failed to find %s\n", pathname);
ret = -ENOENT;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove

if (ret < 0)
{
ferr("ERROR: Failed to find %s\n", pathname);
ret = -ENOENT;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove

Comment thread fs/inode/fs_inodefind.c
inode_rlock();
ret = inode_search(desc);
ret = inode_search(desc, inode);
if (ret >= 0)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

need check inode isnot NULL

Comment thread fs/vfs/fs_symlink.c
ret = inode_search_setup(&desc, path2, false);
if (ret < 0)
{
errcode = -ret;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove errcode too

Comment thread fs/vfs/fs_symlink.c
ret = desc.node->u.i_mops->symlink(desc.node, path1,
desc.relpath);
ret = inode->u.i_mops->symlink(inode, path1,
desc.relpath);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add one space

Comment thread fs/vfs/fs_rename.c
}

ret = inode_search(&olddesc, &oldfound);
inode_search_release(&olddesc);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

release oldfound

Comment thread fs/inode/fs_inodesearch.c
@@ -155,7 +157,8 @@ static int _inode_compare(FAR const char *fname, FAR struct inode *inode)

#ifdef CONFIG_FS_LINKS
static int _inode_linktarget(FAR struct inode *inode,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let change inode to ** directly

Comment thread fs/inode/fs_inodesearch.c
****************************************************************************/

int inode_search(FAR struct inode_search_s *desc)
int inode_search_setup(FAR struct inode_search_s *desc,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Move before inode_search to reduce the difference

Comment thread fs/inode/fs_inodesearch.c

if (inodep != NULL)
{
*inodep = inode;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why update in the fail path

Comment thread fs/inode/fs_inodesearch.c
*
****************************************************************************/

void inode_search_release(FAR struct inode_search_s *desc)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Move before inode_search_setup

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Area: Documentation Improvements or additions to documentation Size: XL The size of the change in this PR is very large. Consider breaking down the PR into smaller pieces.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants