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

Reply via email to