From 9e9a924877a8e0abd80d2873e245a0c55c5925a5 Mon Sep 17 00:00:00 2001 From: Alexander Larsson Date: Thu, 12 Oct 2023 13:29:31 +0200 Subject: [PATCH 1/4] tools: Move sandboxing code to separate file This will help with reuse. Signed-off-by: Alexander Larsson --- tools/Makefile.am | 2 +- tools/composefs-from-json.c | 194 +------------------------------ tools/sandbox.c | 221 ++++++++++++++++++++++++++++++++++++ tools/sandbox.h | 23 ++++ 4 files changed, 246 insertions(+), 194 deletions(-) create mode 100644 tools/sandbox.c create mode 100644 tools/sandbox.h diff --git a/tools/Makefile.am b/tools/Makefile.am index 871e359f..f54e7621 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -20,7 +20,7 @@ mkcomposefs_LDADD = ../libcomposefs/libcomposefs.la $(LIBCRYPTO_LIBS) mount_composefs_SOURCES = mountcomposefs.c mount_composefs_LDADD = ../libcomposefs/libcomposefs.la $(LIBCRYPTO_LIBS) -composefs_from_json_SOURCES = composefs-from-json.c read-file.c read-file.h +composefs_from_json_SOURCES = composefs-from-json.c read-file.c read-file.h sandbox.c sandbox.h composefs_from_json_LDADD = ../libcomposefs/libcomposefs.la $(LIBS_YAJL) $(LIBCRYPTO_LIBS) $(LIBS_SECCOMP) composefs_info_SOURCES = composefs-info.c ../libcomposefs/hash.c diff --git a/tools/composefs-from-json.c b/tools/composefs-from-json.c index 43db1894..17d8d10c 100644 --- a/tools/composefs-from-json.c +++ b/tools/composefs-from-json.c @@ -22,6 +22,7 @@ #include "libcomposefs/lcfs-writer.h" #include "libcomposefs/lcfs-utils.h" #include "read-file.h" +#include "sandbox.h" #include #include @@ -29,205 +30,12 @@ #include #include #include -#include #include #include #include #include -#include -#include -#include -#ifdef HAVE_SYS_CAPABILITY_H -#include -#endif -#ifdef HAVE_LIBSECCOMP -#include -#include -#endif #include -static void do_seccomp_sandbox(void) -{ -#ifdef HAVE_LIBSECCOMP - scmp_filter_ctx ctx; - int ret; - size_t i; - int syscalls[] = { - SCMP_SYS(brk), SCMP_SYS(close), SCMP_SYS(exit), - SCMP_SYS(exit_group), SCMP_SYS(fstat), SCMP_SYS(lseek), - SCMP_SYS(mmap), SCMP_SYS(mremap), SCMP_SYS(munmap), - SCMP_SYS(newfstatat), SCMP_SYS(read), SCMP_SYS(readv), - SCMP_SYS(sysinfo), SCMP_SYS(write), SCMP_SYS(writev), - }; - - ctx = seccomp_init(SCMP_ACT_ERRNO(ENOSYS)); - if (ctx == NULL) - err(EXIT_FAILURE, "seccomp_init"); - - for (i = 0; i < sizeof(syscalls) / sizeof(syscalls[0]); i++) { - ret = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, syscalls[i], 0); - if (ret < 0) { - errno = -ret; - err(EXIT_FAILURE, "seccomp_rule_add"); - } - } - - ret = seccomp_load(ctx); - if (ret < 0) { - errno = -ret; - err(EXIT_FAILURE, "seccomp_load"); - } -#endif -} - -#ifdef __NR_pivot_root -static int pivot_root(const char *new_root, const char *put_old) -{ - return syscall(__NR_pivot_root, new_root, put_old); -} -#endif - -static void do_namespace_sandbox(void) -{ - uid_t uid = geteuid(); - gid_t gid = getegid(); - int ret, fd; -#ifdef __NR_pivot_root - int old_root; - char *cwd; -#endif - - ret = unshare(CLONE_NEWUSER | CLONE_NEWNS | CLONE_NEWUTS | - CLONE_NEWIPC | CLONE_NEWNET); - if (ret < 0) - return; - - fd = open("/proc/self/setgroups", O_WRONLY | O_CLOEXEC); - if (fd < 0) - err(EXIT_FAILURE, "open /proc/self/setgroups"); - ret = write(fd, "deny", 4); - if (ret < 0) - err(EXIT_FAILURE, "write to /proc/self/gid_map"); - close(fd); - - fd = open("/proc/self/gid_map", O_WRONLY | O_CLOEXEC); - if (fd < 0) - err(EXIT_FAILURE, "open /proc/self/gid_map"); - ret = dprintf(fd, "0 %d 1\n", gid); - if (ret < 0) - err(EXIT_FAILURE, "write to /proc/self/gid_map"); - close(fd); - - fd = open("/proc/self/uid_map", O_WRONLY | O_CLOEXEC); - if (fd < 0) - err(EXIT_FAILURE, "open /proc/self/uid_map"); - ret = dprintf(fd, "0 %d 1\n", uid); - if (ret < 0) - err(EXIT_FAILURE, "write to /proc/self/uid_map"); - close(fd); - -#ifdef __NR_pivot_root - ret = mount(NULL, "/", NULL, MS_REC | MS_PRIVATE, NULL); - if (ret < 0) - err(EXIT_FAILURE, "mount /"); - - cwd = get_current_dir_name(); - if (!cwd) - err(EXIT_FAILURE, "get_current_dir_name"); - - ret = mount(NULL, cwd, "tmpfs", 0, NULL); - if (ret < 0) - err(EXIT_FAILURE, "mount tmpfs"); - - old_root = open("/", O_PATH | O_DIRECTORY | O_CLOEXEC); - if (old_root < 0) - err(EXIT_FAILURE, "open /"); - - ret = chdir(cwd); - if (ret < 0) - err(EXIT_FAILURE, "chdir cwd"); - - free(cwd); - cwd = NULL; - - ret = pivot_root(".", "."); - if (ret < 0) - err(EXIT_FAILURE, "pivot_root"); - - ret = fchdir(old_root); - if (ret < 0) - err(EXIT_FAILURE, "fchdir"); - close(old_root); - - ret = umount2(".", MNT_DETACH); - if (ret < 0) - err(EXIT_FAILURE, "umount2"); - - ret = chdir("/"); - if (ret < 0) - err(EXIT_FAILURE, "fchdir"); -#endif -} - -static void drop_caps(void) -{ -#ifdef HAVE_SYS_CAPABILITY_H - struct __user_cap_header_struct hdr = { _LINUX_CAPABILITY_VERSION_3, 0 }; - struct __user_cap_data_struct data[2] = { { 0 } }; -#endif - int ret, cap; - ret = prctl(PR_SET_KEEPCAPS, 0, 0, 0, 0); - if (ret < 0) - err(EXIT_FAILURE, "prctl(PR_SET_KEEPCAPS)"); - - for (cap = 0;; cap++) { - ret = prctl(PR_CAPBSET_DROP, cap, 0, 0, 0); - if (ret < 0 && errno != EINVAL) - err(EXIT_FAILURE, "prctl(PR_CAPBSET_DROP)"); - if (ret < 0) - break; - } - -#ifdef PR_CAP_AMBIENT - ret = prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_CLEAR_ALL, 0, 0, 0); - if (ret < 0 && errno != EINVAL) - err(EXIT_FAILURE, "prctl(PR_CAP_AMBIENT)"); -#endif - -#ifdef HAVE_SYS_CAPABILITY_H - ret = capset(&hdr, data); - if (ret < 0 && errno != EINVAL) - err(EXIT_FAILURE, "capset"); -#endif -} - -static void do_set_oom_score_adj(void) -{ - int fd, ret; - - fd = open("/proc/self/oom_score_adj", O_WRONLY); - if (fd < 0) - err(EXIT_FAILURE, "open /proc/self/oom_score_adj"); - - ret = write(fd, "1000", 4); - if (ret < 0) - err(EXIT_FAILURE, "write to /proc/self/oom_score_adj"); - - close(fd); -} - -static void sandbox(void) -{ - do_set_oom_score_adj(); - do_namespace_sandbox(); - drop_caps(); - - if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) < 0) - err(EXIT_FAILURE, "prctl(PR_SET_NO_NEW_PRIVS)"); - - do_seccomp_sandbox(); -} - /* Adapted from mailutils 0.6.91(distributed under LGPL 2.0+) */ static int b64_input(char c) { diff --git a/tools/sandbox.c b/tools/sandbox.c new file mode 100644 index 00000000..888e2a21 --- /dev/null +++ b/tools/sandbox.c @@ -0,0 +1,221 @@ +/* lcfs + Copyright (C) 2021 Giuseppe Scrivano + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ +#define _GNU_SOURCE + +#include "config.h" + +#include "sandbox.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_SYS_CAPABILITY_H +#include +#endif +#ifdef HAVE_LIBSECCOMP +#include +#include +#endif + +static void do_seccomp_sandbox(void) +{ +#ifdef HAVE_LIBSECCOMP + scmp_filter_ctx ctx; + int ret; + size_t i; + int syscalls[] = { + SCMP_SYS(brk), SCMP_SYS(close), SCMP_SYS(exit), + SCMP_SYS(exit_group), SCMP_SYS(fstat), SCMP_SYS(lseek), + SCMP_SYS(mmap), SCMP_SYS(mremap), SCMP_SYS(munmap), + SCMP_SYS(newfstatat), SCMP_SYS(read), SCMP_SYS(readv), + SCMP_SYS(sysinfo), SCMP_SYS(write), SCMP_SYS(writev), + }; + + ctx = seccomp_init(SCMP_ACT_ERRNO(ENOSYS)); + if (ctx == NULL) + err(EXIT_FAILURE, "seccomp_init"); + + for (i = 0; i < sizeof(syscalls) / sizeof(syscalls[0]); i++) { + ret = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, syscalls[i], 0); + if (ret < 0) { + errno = -ret; + err(EXIT_FAILURE, "seccomp_rule_add"); + } + } + + ret = seccomp_load(ctx); + if (ret < 0) { + errno = -ret; + err(EXIT_FAILURE, "seccomp_load"); + } +#endif +} + +#ifdef __NR_pivot_root +static int pivot_root(const char *new_root, const char *put_old) +{ + return syscall(__NR_pivot_root, new_root, put_old); +} +#endif + +static void do_namespace_sandbox(void) +{ + uid_t uid = geteuid(); + gid_t gid = getegid(); + int ret, fd; +#ifdef __NR_pivot_root + int old_root; + char *cwd; +#endif + + ret = unshare(CLONE_NEWUSER | CLONE_NEWNS | CLONE_NEWUTS | + CLONE_NEWIPC | CLONE_NEWNET); + if (ret < 0) + return; + + fd = open("/proc/self/setgroups", O_WRONLY | O_CLOEXEC); + if (fd < 0) + err(EXIT_FAILURE, "open /proc/self/setgroups"); + ret = write(fd, "deny", 4); + if (ret < 0) + err(EXIT_FAILURE, "write to /proc/self/gid_map"); + close(fd); + + fd = open("/proc/self/gid_map", O_WRONLY | O_CLOEXEC); + if (fd < 0) + err(EXIT_FAILURE, "open /proc/self/gid_map"); + ret = dprintf(fd, "0 %d 1\n", gid); + if (ret < 0) + err(EXIT_FAILURE, "write to /proc/self/gid_map"); + close(fd); + + fd = open("/proc/self/uid_map", O_WRONLY | O_CLOEXEC); + if (fd < 0) + err(EXIT_FAILURE, "open /proc/self/uid_map"); + ret = dprintf(fd, "0 %d 1\n", uid); + if (ret < 0) + err(EXIT_FAILURE, "write to /proc/self/uid_map"); + close(fd); + +#ifdef __NR_pivot_root + ret = mount(NULL, "/", NULL, MS_REC | MS_PRIVATE, NULL); + if (ret < 0) + err(EXIT_FAILURE, "mount /"); + + cwd = get_current_dir_name(); + if (!cwd) + err(EXIT_FAILURE, "get_current_dir_name"); + + ret = mount(NULL, cwd, "tmpfs", 0, NULL); + if (ret < 0) + err(EXIT_FAILURE, "mount tmpfs"); + + old_root = open("/", O_PATH | O_DIRECTORY | O_CLOEXEC); + if (old_root < 0) + err(EXIT_FAILURE, "open /"); + + ret = chdir(cwd); + if (ret < 0) + err(EXIT_FAILURE, "chdir cwd"); + + free(cwd); + cwd = NULL; + + ret = pivot_root(".", "."); + if (ret < 0) + err(EXIT_FAILURE, "pivot_root"); + + ret = fchdir(old_root); + if (ret < 0) + err(EXIT_FAILURE, "fchdir"); + close(old_root); + + ret = umount2(".", MNT_DETACH); + if (ret < 0) + err(EXIT_FAILURE, "umount2"); + + ret = chdir("/"); + if (ret < 0) + err(EXIT_FAILURE, "fchdir"); +#endif +} + +static void drop_caps(void) +{ +#ifdef HAVE_SYS_CAPABILITY_H + struct __user_cap_header_struct hdr = { _LINUX_CAPABILITY_VERSION_3, 0 }; + struct __user_cap_data_struct data[2] = { { 0 } }; +#endif + int ret, cap; + ret = prctl(PR_SET_KEEPCAPS, 0, 0, 0, 0); + if (ret < 0) + err(EXIT_FAILURE, "prctl(PR_SET_KEEPCAPS)"); + + for (cap = 0;; cap++) { + ret = prctl(PR_CAPBSET_DROP, cap, 0, 0, 0); + if (ret < 0 && errno != EINVAL) + err(EXIT_FAILURE, "prctl(PR_CAPBSET_DROP)"); + if (ret < 0) + break; + } + +#ifdef PR_CAP_AMBIENT + ret = prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_CLEAR_ALL, 0, 0, 0); + if (ret < 0 && errno != EINVAL) + err(EXIT_FAILURE, "prctl(PR_CAP_AMBIENT)"); +#endif + +#ifdef HAVE_SYS_CAPABILITY_H + ret = capset(&hdr, data); + if (ret < 0 && errno != EINVAL) + err(EXIT_FAILURE, "capset"); +#endif +} + +static void do_set_oom_score_adj(void) +{ + int fd, ret; + + fd = open("/proc/self/oom_score_adj", O_WRONLY); + if (fd < 0) + err(EXIT_FAILURE, "open /proc/self/oom_score_adj"); + + ret = write(fd, "1000", 4); + if (ret < 0) + err(EXIT_FAILURE, "write to /proc/self/oom_score_adj"); + + close(fd); +} + +void sandbox(void) +{ + do_set_oom_score_adj(); + do_namespace_sandbox(); + drop_caps(); + + if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) < 0) + err(EXIT_FAILURE, "prctl(PR_SET_NO_NEW_PRIVS)"); + + do_seccomp_sandbox(); +} diff --git a/tools/sandbox.h b/tools/sandbox.h new file mode 100644 index 00000000..a05ccb73 --- /dev/null +++ b/tools/sandbox.h @@ -0,0 +1,23 @@ +/* lcfs + Copyright (C) 2021 Giuseppe Scrivano + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef SANDBOX_H +#define SANDBOX_H + +void sandbox(void); + +#endif From fdd7bf5ac969acbc6cf36f1c1164b2a8483b3c54 Mon Sep 17 00:00:00 2001 From: Alexander Larsson Date: Thu, 12 Oct 2023 16:37:56 +0200 Subject: [PATCH 2/4] mkcomposefs: Use sandboxing when --from-file is used This helps protecting the parsing code if some external process is feeding data to mkcomposefs to create an image. Its not really useful to use a sandbox when a directory is specified though, as it needs to do way to many different kinds of operations, as well as reading actual uid/gid:s etc. Signed-off-by: Alexander Larsson --- man/mkcomposefs.md | 4 ++++ tools/Makefile.am | 4 ++-- tools/mkcomposefs.c | 18 +++++++++++++++++- 3 files changed, 23 insertions(+), 3 deletions(-) diff --git a/man/mkcomposefs.md b/man/mkcomposefs.md index fd307a65..d539e3bd 100644 --- a/man/mkcomposefs.md +++ b/man/mkcomposefs.md @@ -60,6 +60,10 @@ will be a mountable composefs image. : The source is a file in the **composefs-dump(5)** format. If the specified file is "-", the data is read from stdin. +**\-\-no-sandbox** +: Normally, when using **--from-file** a sandbox is set up to protect + the parsing code. This option disables this. + # SEE ALSO **composefs-info(1)**, **mount.composefs(1)**, **composefs-dump(5)** diff --git a/tools/Makefile.am b/tools/Makefile.am index f54e7621..8b972df4 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -14,8 +14,8 @@ endif AM_CFLAGS = $(WARN_CFLAGS) -I$(top_srcdir)/ -mkcomposefs_SOURCES = mkcomposefs.c -mkcomposefs_LDADD = ../libcomposefs/libcomposefs.la $(LIBCRYPTO_LIBS) +mkcomposefs_SOURCES = mkcomposefs.c sandbox.c sandbox.h +mkcomposefs_LDADD = ../libcomposefs/libcomposefs.la $(LIBCRYPTO_LIBS) $(LIBS_SECCOMP) mount_composefs_SOURCES = mountcomposefs.c mount_composefs_LDADD = ../libcomposefs/libcomposefs.la $(LIBCRYPTO_LIBS) diff --git a/tools/mkcomposefs.c b/tools/mkcomposefs.c index 0f273f78..919acd25 100644 --- a/tools/mkcomposefs.c +++ b/tools/mkcomposefs.c @@ -22,6 +22,7 @@ #include "libcomposefs/lcfs-writer.h" #include "libcomposefs/lcfs-utils.h" #include "libcomposefs/lcfs-internal.h" +#include "sandbox.h" #include #include @@ -325,7 +326,8 @@ static void usage(const char *argv0) " --user-xattrs Only store user.* xattrs\n" " --print-digest Print the digest of the image\n" " --print-digest-only Print the digest of the image, don't write image\n" - " --from-file The source is a dump file, not a directory\n", + " --from-file The source is a dump file, not a directory\n" + " --no-sandbox Disable sandboxing code\n", bin); } @@ -337,6 +339,7 @@ static void usage(const char *argv0) #define OPT_PRINT_DIGEST_ONLY 111 #define OPT_USER_XATTRS 112 #define OPT_FROM_FILE 113 +#define OPT_NO_SANDBOX 114 static ssize_t write_cb(void *_file, void *buf, size_t count) { @@ -878,6 +881,12 @@ int main(int argc, char **argv) flag: NULL, val: OPT_FROM_FILE }, + { + name: "no-sandbox", + has_arg: no_argument, + flag: NULL, + val: OPT_NO_SANDBOX + }, {}, }; struct lcfs_write_options_s options = { 0 }; @@ -886,6 +895,7 @@ int main(int argc, char **argv) bool print_digest = false; bool print_digest_only = false; bool from_file = false; + bool no_sandbox = false; struct lcfs_node_s *root; const char *out = NULL; const char *src_path = NULL; @@ -925,6 +935,9 @@ int main(int argc, char **argv) case OPT_FROM_FILE: from_file = true; break; + case OPT_NO_SANDBOX: + no_sandbox = true; + break; case ':': fprintf(stderr, "option needs a value\n"); exit(EXIT_FAILURE); @@ -994,6 +1007,9 @@ int main(int argc, char **argv) close_input = true; } + if (!no_sandbox) + sandbox(); + root = tree_from_dump(input); if (root == NULL) errx(EXIT_FAILURE, "No files in dump file"); From 8a88bfa3cbcaf5976cd81d47f2fdfeccb0eaadff Mon Sep 17 00:00:00 2001 From: Alexander Larsson Date: Thu, 12 Oct 2023 16:40:50 +0200 Subject: [PATCH 3/4] sandbox: Add some alternative syscalls Add some extra syscalls that are used on other arches: fstate: fstat64 newfstatat: fstatat64 mmap: mmap2 Signed-off-by: Alexander Larsson --- tools/sandbox.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tools/sandbox.c b/tools/sandbox.c index 888e2a21..751eb7bb 100644 --- a/tools/sandbox.c +++ b/tools/sandbox.c @@ -46,8 +46,9 @@ static void do_seccomp_sandbox(void) size_t i; int syscalls[] = { SCMP_SYS(brk), SCMP_SYS(close), SCMP_SYS(exit), - SCMP_SYS(exit_group), SCMP_SYS(fstat), SCMP_SYS(lseek), - SCMP_SYS(mmap), SCMP_SYS(mremap), SCMP_SYS(munmap), + SCMP_SYS(exit_group), SCMP_SYS(fstat), SCMP_SYS(fstat64), + SCMP_SYS(fstatat64), SCMP_SYS(lseek), SCMP_SYS(mmap), + SCMP_SYS(mmap2), SCMP_SYS(mremap), SCMP_SYS(munmap), SCMP_SYS(newfstatat), SCMP_SYS(read), SCMP_SYS(readv), SCMP_SYS(sysinfo), SCMP_SYS(write), SCMP_SYS(writev), }; From d79fd6033c57b5451779989f18cb6d80235d1e49 Mon Sep 17 00:00:00 2001 From: Alexander Larsson Date: Thu, 12 Oct 2023 17:16:40 +0200 Subject: [PATCH 4/4] sandbox: Unshare pids and mount a minimal /proc Signed-off-by: Alexander Larsson --- tools/sandbox.c | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/tools/sandbox.c b/tools/sandbox.c index 751eb7bb..d019b6da 100644 --- a/tools/sandbox.c +++ b/tools/sandbox.c @@ -30,6 +30,8 @@ #include #include #include +#include +#include #ifdef HAVE_SYS_CAPABILITY_H #include #endif @@ -91,10 +93,25 @@ static void do_namespace_sandbox(void) #endif ret = unshare(CLONE_NEWUSER | CLONE_NEWNS | CLONE_NEWUTS | - CLONE_NEWIPC | CLONE_NEWNET); + CLONE_NEWIPC | CLONE_NEWNET | CLONE_NEWPID); if (ret < 0) return; + pid_t pid = fork(); + if (pid < 0) + err(EXIT_FAILURE, "fork"); + if (pid != 0) { + int wstatus; + int res = waitpid(pid, &wstatus, 0); + if (res == -1) + err(EXIT_FAILURE, "waitpid"); + + if (!WIFEXITED(wstatus)) + err(EXIT_FAILURE, "sandbox process died"); + + exit(WEXITSTATUS(wstatus)); + } + fd = open("/proc/self/setgroups", O_WRONLY | O_CLOEXEC); if (fd < 0) err(EXIT_FAILURE, "open /proc/self/setgroups"); @@ -143,6 +160,14 @@ static void do_namespace_sandbox(void) free(cwd); cwd = NULL; + ret = mkdir("proc", 0755); + if (ret < 0) + err(EXIT_FAILURE, "mkdir /proc"); + + if (mount("proc", "proc", "proc", MS_NOSUID | MS_NOEXEC | MS_NODEV, + "subset=pid,hidepid=noaccess") != 0) + err(EXIT_FAILURE, "mount /proc"); + ret = pivot_root(".", "."); if (ret < 0) err(EXIT_FAILURE, "pivot_root");