使用pivot_root切换根目录后exec无法找到文件的问题排查
Linux Namespaces与pivot_root环境中execvp执行失败问题
我正在实验Linux namespaces与chroot模拟,已成功创建仅包含新根的环境(通过目录遍历确认),但无法在其中执行任何程序。
最小可复现示例
第一步:创建目录并复制bash
mkdir /jail mkdir /jail/bin mkdir /jail/usr mkdir /jail/lib cp /bin/bash /jail/bin
第二步:isoroot.c 代码
/* isoroot.c */ #define _GNU_SOURCE #include <stdlib.h> #include <stdio.h> #include <string.h> #include <unistd.h> #include <errno.h> #include <signal.h> #include <sys/stat.h> #include <sys/types.h> #include <sys/resource.h> #include <sched.h> /* use clone */ #include <syscall.h> #include <sys/mount.h> #include <sched.h> #ifndef pivot_root #define pivot_root(new, old) syscall(SYS_pivot_root, new, old) #endif int main(int argc, char *argv[]) { int res = 0; const char *new_root = "/jail"; const char *old_root = "/oldroot"; (void) argc; if (unshare(CLONE_NEWNS)) { /* 必须以特权执行,否则会破坏真实挂载点 */ fprintf(stderr, "unshare: %s\n", strerror(errno)); _exit(errno); } if (mount(NULL, "/", NULL, MS_REC | MS_PRIVATE, NULL)) { /* 阻止挂载传播 */ fprintf(stderr, "mount: %s\n", strerror(errno)); _exit(errno); } if (mount(new_root, new_root, NULL, MS_BIND, NULL)) { /* 确保new_root是挂载点 */ fprintf(stderr, "mount: %s\n", strerror(errno)); _exit(errno); } if (mkdir("/jail/oldroot", 0777)) { /* 创建旧根挂载点 */ fprintf(stderr, "mkdir: %s\n", strerror(errno)); _exit(errno); } /* 卸载旧根前挂载必需的目录 */ /* /usr包含/bin、/sbin、/lib和/lib64 */ res = mount("/usr", "/jail/usr", NULL, MS_BIND | MS_REC | MS_RDONLY, NULL); if (res) { fprintf(stderr, "mount usr failed: %s\n", strerror(errno)); } if (pivot_root(new_root, "/jail/oldroot")) { /* 切换到新根 */ fprintf(stderr, "pivot_root: %s\n", strerror(errno)); _exit(errno); } if (chdir("/")) { /* 切换工作目录到新根 */ fprintf(stderr, "chdir failed: %s\n", strerror(errno)); _exit(errno); } if (umount2(old_root, MNT_DETACH)) { /* 卸载旧根 */ fprintf(stderr, "umount2 failed: %s\n", strerror(errno)); _exit(errno); } if (rmdir(old_root)) { /* 删除旧挂载点 */ fprintf(stderr, "rmdir failed: %s\n", strerror(errno)); _exit(errno); } if (access("/bin/bash", X_OK)) { fprintf(stderr, "bash4 not found: %s\n", strerror(errno)); _exit(errno); } printf("Got all the way here\n"); execvp("/bin/bash", argv); fprintf(stderr, "%s\n", strerror(errno)); exit(errno); }
编译命令
gcc -Wall -Werror -Wunused -Wextra -Wmaybe-uninitialized -Wstrict-prototypes -Wmissing-prototypes -Wdeclaration-after-statement -Wmissing-declarations -Wmissing-format-attribute -Wnull-dereference -Wformat=2 -Wshadow -Wsizeof-pointer-memaccess -std=gnu99 -pthread -O0 -g -Wstack-protector -fno-omit-frame-pointer -fwrapv -D_FORTIFY_SOURCE=2 -c isoroot.c gcc -Wall -Werror -Wunused -Wextra -Wmaybe-uninitialized -Wstrict-prototypes -Wmissing-prototypes -Wdeclaration-after-statement -Wmissing-declarations -Wmissing-format-attribute -Wnull-dereference -Wformat=2 -Wshadow -Wsizeof-pointer-memaccess -std=gnu99 -pthread -O0 -g -Wstack-protector -fno-omit-frame-pointer -fwrapv -D_FORTIFY_SOURCE=2 -o isoroot *.o
核心问题
特别奇怪的是,最后一个access调用成功,表明能找到/bin/bash,但execvp始终报错“No such file or directory”。我认为access已使用新命名空间,exec也应如此,但实际并非如此。切换根目录后,让exec能找到文件有什么技巧吗?
对比示例(可执行但未真正切换根)
以下代码的exec可成功执行,但并未真正切换根目录,原根目录的所有内容仍可见:
chdir("/jail"); unshare(CLONE_NEWNS); mount("/jail", "/jail", NULL, MS_BIND, NULL); pivot_root("/jail", "/jail/old_root"); chdir("/"); mount("/old_root/bin", "bin", NULL, MS_BIND, NULL); mount("/old_root/usr", "usr", NULL, MS_BIND, NULL); mount("/old_root/lib", "lib", NULL, MS_BIND, NULL); umount2("/old_root", MNT_DETACH); exec("/busybox");
依赖检查结果
通过ldd确认运行/bin/bash所需的依赖:
# ldd /bin/bash linux-vdso.so.1 (0x00007ffc99116000) libtinfo.so.6 => /lib/x86_64-linux-gnu/libtinfo.so.6 (0x00007fd041cb9000) libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007fd041cb3000) libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fd041ade000) /lib64/ld-linux-x86-64.so.2 (0x00007fd041e3a000)
已尝试的解决方法
- 手动添加软链接,但在exec之前执行时提示“Error: File already exists”:
if (symlink("/bin", "/usr/bin")) { fprintf(stderr, "symlink failed: %s\n", strerror(errno)); } if (symlink("/lib", "/usr/lib")) { fprintf(stderr, "symlink failed: %s\n", strerror(errno)); } if (symlink("/lib64", "/usr/lib64")) { fprintf(stderr, "symlink failed: %s\n", strerror(errno)); }
- 尝试挂载
/proc和/lib64失败,但/bin和/lib挂载成功(除/proc外,其余均为指向/usr/<dir>的软链接)。
内容的提问来源于stack exchange,提问作者InterLinked
相关产品推荐
相关产品推荐

