You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.04 15:00:42