One type of change that tends to accumulate in my tree are things I implement enough of to know how to finish it, but am not sure I SHOULD do?
Here's a change I had lying around in my tree that I just cleaned up so it works, adding a new "-f" option to chroot that uses execveat() to run a binary that lives outside the chroot in the chroot. This is a useful option for _me_ (something I've wanted chroot to be able to do for years), but I don't know how representative I am in this? The problem is, of course, that it has to be a _static_ binary, because although the executable is available via the fd, the shared libraries aren't. Debian's qemu-user-static package for the binfmt_misc stuff is static for the same reason: $ cat /proc/sys/fs/binfmt_misc/qemu-sh4 enabled interpreter /usr/bin/qemu-sh4-static flags: OCF offset 0 magic 7f454c4601010100000000000000000002002a00 mask fffffffffffffffcfffffffffffffffffeffffff Which is why I can "chroot root/sh4/fs" after "scripts/mkroot.sh CROSS=sh4" and it works. The interpreter is static because launching a binary filched out of an adjacent namespace is NOT the same as the dynamic linker being able to straddle namespaces at runtime: exec(fd) after the chroot works fine, even when the fd is CLOEXEC, the libraries and dynamic linker itself would need to be in the chroot. Making glibc's dynamic linker work with this would involve a child process doing LD_BIND_NOW and using ptrace() to set a breakpoint and inject the chroot right before the call to main, but of course bionic ignores the environment variable and instead uses an ELF flag to indicate this for some reason? (Why? If you're making this decision at compile time you could just link it statically instead? Bionic still listens to LD_PRELOAD, so it's not conceptually objecting to dynamic linker control variables. Meanwhile, musl never does lazy binding so setting the variable for it is a NOP and it just needs the chroot before main()...) Anyway, anything like THAT would belongs in the "contain" command I've got in the post-1.0 todo heap instead, and possibly this does too, but for the record here's the patch... Rob diff --git a/toys/other/chroot.c b/toys/other/chroot.c index d791f34a..210afc09 100644 --- a/toys/other/chroot.c +++ b/toys/other/chroot.c @@ -7,27 +7,46 @@ * The container guys use pivot_root() to deal with this, which does actually * edit mount tree. (New option? Kernel patch?) -USE_CHROOT(NEWTOY(chroot, "^<1", TOYFLAG_USR|TOYFLAG_SBIN|TOYFLAG_ARGFAIL(125))) +USE_CHROOT(NEWTOY(chroot, "^<1f", TOYFLAG_USR|TOYFLAG_SBIN|TOYFLAG_ARGFAIL(125))) config CHROOT bool "chroot" default y help - usage: chroot NEWROOT [COMMAND [ARG...]] + usage: chroot [-f] NEWROOT [COMMAND [ARG...]] Run command within a new root directory. If no command, run /bin/sh. + + -f Run (static) COMMAND from outside of chroot. */ +#define FOR_chroot #include "toys.h" +// Try to be less impolite to BSD than a straight inline syscall() +#ifdef __NR_execveat +#define execveat(...) syscall(__NR_execveat, __VA_ARGS__) +#else +#define execveat(...) +#undef FLAG_f +#define FLAG_f 0 +#endif + void chroot_main(void) { - char *binsh[] = {"/bin/sh", "-i", 0}; + char *binsh[] = {"/bin/sh", "-i", 0}, + **ss = toys.optargs[1] ? toys.optargs+1 : binsh; + int fd = FLAG(f) ? xopen(*ss, O_RDONLY|O_CLOEXEC) : -1; if (chdir(*toys.optargs) || chroot(".")) { toys.exitval = 125; perror_exit_raw(*toys.optargs); } - if (toys.optargs[1]) xexec(toys.optargs+1); - else xexec(binsh); + + if (fd!=-1) { + execveat(fd, "", ss, environ, 0x1000); + error_exit("could not run '%s' (is it statically linked?)", *ss); + } + + xexec(ss); } _______________________________________________ Toybox mailing list Toybox@lists.landley.net http://lists.landley.net/listinfo.cgi/toybox-landley.net