CHROOT(2)

๐Ÿ“ฆ version 15.1, in freebsd 15.1

โœจ chroot(2), fchroot(2) โ€” change root directory โœจ

okay bestie so this one's from the System Calls Manual, section 2, and the whole thing ships in the Standard C Library (libc, -lc). buckle up, we're moving the root of the filesystem today ๐Ÿ’…


๐ŸŒธ NAME ๐ŸŒธ

chroot, fchroot โ€” change root directory

๐ŸŽ€ LIBRARY ๐ŸŽ€

Standard C Library (libc, -lc) โ€” she's built in, no extra credit needed ๐Ÿ’–


๐Ÿฆ‹ SYNOPSIS ๐Ÿฆ‹

#include <unistd.h>

int
chroot(const char *dirname);

int
fchroot(int fd);

โœจ DESCRIPTION โœจ

so dirname is the address of the pathname of a directory, and it's terminated by an ASCII NUL โ€” classic string behaviour, no notes ๐Ÿ™ˆ. The chroot() system call makes dirname become the root directory, like, the actual starting point for path searches of any pathname that begins with /. very main character energy for a directory ngl ๐Ÿ‘‘

For a directory to become the root directory, a process must have execute (search) access on that directory. no search access, no root, periodt.

โš ๏ธ also lowkey important: chroot() has no effect on the process's current directory. she does not touch it. do not @ me.

This call is restricted to the super-user โ€” unless the security.bsd.unprivileged_chroot sysctl variable is set to 1 and the process has enabled the PROC_NO_NEW_PRIVS_CTL procctl(2). both. it's an and situation, bestie ๐Ÿซถ

Depending on how kern.chroot_allow_open_directories is set, open file descriptors that reference directories will make chroot() fail like this:

  • If kern.chroot_allow_open_directories is set to zero, chroot() will always fail with EPERM if there are any directories open. any. at all. ๐Ÿ’€
  • If kern.chroot_allow_open_directories is set to one โ€” and hey, that's the default โ€” chroot() will fail with EPERM if there are any directories open and the process is already subject to the chroot() system call.
  • Any other value for kern.chroot_allow_open_directories will bypass the check for open directories entirely, mimicking the historic insecure behavior of chroot() that's still present on other systems. iykyk, that's the sketchy one ๐Ÿšฉ

The fchroot() system call is identical to chroot() except it takes a file descriptor instead of a path. same vibes, different argument โœจ


๐Ÿ’œ RETURN VALUES ๐Ÿ’œ

Upon successful completion, the value 0 is returned; otherwise the value -1 is returned and the global variable errno is set to indicate the error. zero = slay, negative one = check errno, that's the whole deal ๐ŸŽ€


๐Ÿ”ฅ ERRORS ๐Ÿ”ฅ

The chroot() and fchroot() system calls will fail and the root directory will be unchanged if:

  • [EPERM] โ€” the effective user ID is not the super-user and the security.bsd.unprivileged_chroot sysctl is 0. sorry bestie, no root for you ๐Ÿ™ˆ
  • [EPERM] โ€” the effective user ID is not the super-user and the process has not enabled the PROC_NO_NEW_PRIVS_CTL procctl(2).
  • [EPERM] โ€” one or more file descriptors are open directories and the kern.chroot_allow_open_directories sysctl is not set to permit this.
  • [EIO] โ€” an I/O error occurred while reading from or writing to the file system. ow ๐Ÿ˜ญ
  • [EINTEGRITY] โ€” corrupted data was detected while reading from the file system.

The chroot() system call will fail and the root directory will be unchanged if:

  • [ENOTDIR] โ€” a component of the path name is not a directory.
  • [ENAMETOOLONG] โ€” a component of a pathname exceeded 255 characters, or an entire path name exceeded 1023 characters. that's a Lot of characters ๐Ÿ’€
  • [ENOENT] โ€” the named directory does not exist. she's not there, girl.
  • [EACCES] โ€” search permission is denied for any component of the path name.
  • [ELOOP] โ€” too many symbolic links were encountered in translating the pathname. symlink spiral, we've all been there ๐ŸŒ€
  • [EFAULT] โ€” the dirname argument points outside the process's allocated address space.

The fchroot() system call will fail and the root directory will be unchanged if:

  • [EACCES] โ€” search permission is denied for the directory referenced by the file descriptor.
  • [EBADF] โ€” the argument fd is not a valid file descriptor.
  • [ENOTDIR] โ€” the file descriptor does not reference a directory.

๐Ÿซถ SEE ALSO ๐Ÿซถ

chdir(2), jail(2)

like yeah go read those too, they're literally the neighbours ๐Ÿ’–


๐Ÿ‘‘ HISTORY ๐Ÿ‘‘

The chroot() system call appeared in Version 7 AT&T UNIX โ€” ancient. iconic. she's been here a minute ๐Ÿ•ฐ๏ธ. It was marked as โ€œlegacyโ€ in Version 2 of the Single UNIX Specification (โ€œSUSv2โ€), and was removed in subsequent standards. rip bozo-ish behaviour, but she's still here ๐Ÿ˜Œ. The fchroot() system call first appeared in FreeBSD 15.0. fresh out the box โœจ


๐Ÿ’€ BUGS ๐Ÿ’€

okay so this is a weird one: if the process is able to change its working directory to the target directory, but another access control check fails โ€” like a check for open directories, or a MAC check โ€” it is possible that this system call may return an error, with the working directory of the process left changed. so you get a fail and a side effect. rude. ๐Ÿ™ˆ


๐ŸŒˆ SECURITY CONSIDERATIONS ๐ŸŒˆ

The system has many hardcoded paths to files which it may load after the process starts. so like, it is generally recommended to drop privileges immediately after a successful chroot call, and to restrict write access to a limited subtree of the chroot root. for instance, set up the sandbox so the sandboxed user will have no write access to any well-known system directories. please. for me. ๐Ÿฅบ

For complete isolation from the rest of the system, use jail(2) instead. that's the actual final form, no cap ๐Ÿ‘‘


๐Ÿ’… that's the whole page bestie, go forth and chroot responsibly ๐Ÿ’…

and this little beauty comes to you via Debian, dated April 27, 2026 โ€” mwah, take care of her ๐ŸŽ€๐Ÿฆ‹๐Ÿ’–

๐Ÿ“ฆ where this page comes from
  • freebsd 15.1: base 15.1
  • ๐Ÿ’– girlified by deepseek-ai/DeepSeek-V4.1-Flash-1017 on : a language model's rewrite of the original, under its terms