From 3b51a07e478d64a84e40b3a7c026b2f8566b194b Mon Sep 17 00:00:00 2001 From: Rob Landley Date: Sun, 27 Sep 2015 09:03:41 -0500 Subject: Another chunk of nommu support, replacing toys.recursion with toys.stacktop. --- lib/lib.h | 7 ++++--- lib/xwrap.c | 18 +++++++++++++----- main.c | 63 +++++++++++++++++++++++++++++++++++++++---------------------- toys.h | 26 ++++++++++++------------- 4 files changed, 70 insertions(+), 44 deletions(-) diff --git a/lib/lib.h b/lib/lib.h index cbac3aa5..d76dcc0c 100644 --- a/lib/lib.h +++ b/lib/lib.h @@ -98,6 +98,7 @@ void xputc(char c); void xflush(void); void xexec(char **argv); pid_t xpopen_both(char **argv, int *pipes); +int xwaitpid(pid_t pid); int xpclose_both(pid_t pid, int *pipes); pid_t xpopen(char **argv, int *pipe, int stdout); pid_t xpclose(pid_t pid, int pipe); @@ -248,12 +249,12 @@ void names_to_pid(char **names, int (*callback)(pid_t pid, char *name)); // Returning from a function can modify a potentially shared stack, // so this has to always inline. - static inline pid_t xvfork(void) { - pid_t p = vfork(); + pid_t p; - if (p == -1) perror_exit("vfork"); + toys.stacktop = 0; + if ((p = vfork()) == -1) perror_exit("vfork"); return p; } diff --git a/lib/xwrap.c b/lib/xwrap.c index 80862828..e5dc4826 100644 --- a/lib/xwrap.c +++ b/lib/xwrap.c @@ -133,7 +133,8 @@ void xflush(void) // with a path isn't a builtin, so /bin/sh won't match the builtin sh. void xexec(char **argv) { - if (CFG_TOYBOX && !CFG_TOYBOX_NORECURSE) toy_exec(argv); + // Only recurse to builtin when we have multiplexer and !vfork context. + if (CFG_TOYBOX && !CFG_TOYBOX_NORECURSE && toys.stacktop) toy_exec(argv); execvp(argv[0], argv); perror_msg("exec %s", argv[0]); @@ -198,17 +199,24 @@ pid_t xpopen_both(char **argv, int *pipes) return pid; } -int xpclose_both(pid_t pid, int *pipes) +// Wait for child process to exit, then return adjusted exit code. +int xwaitpid(pid_t pid) { - int rc = 127; + int status; + + while (-1 == waitpid(pid, &status, 0) && errno == EINTR); + + return WIFEXITED(status) ? WEXITSTATUS(status) : WTERMSIG(status)+127; +} +int xpclose_both(pid_t pid, int *pipes) +{ if (pipes) { close(pipes[0]); close(pipes[1]); } - waitpid(pid, &rc, 0); - return WIFEXITED(rc) ? WEXITSTATUS(rc) : WTERMSIG(rc) + 127; + return xwaitpid(pid); } // Wrapper to xpopen with a pipe for just one of stdin/stdout diff --git a/main.c b/main.c index dcd486eb..1e562b06 100644 --- a/main.c +++ b/main.c @@ -67,7 +67,7 @@ static const int NEED_OPTIONS = #include "generated/newtoys.h" 0; // Ends the opts || opts || opts... -// Subset of init needed by singlemain +// Setup toybox global state for this command. static void toy_singleinit(struct toy_list *which, char *argv[]) { toys.which = which; @@ -85,7 +85,7 @@ static void toy_singleinit(struct toy_list *which, char *argv[]) if (NEED_OPTIONS && which->options) get_optflags(); else { toys.optargs = argv+1; - for (toys.optc=0; toys.optargs[toys.optc]; toys.optc++); + for (toys.optc = 0; toys.optargs[toys.optc]; toys.optc++); } toys.old_umask = umask(0); if (!(which->flags & TOYFLAG_UMASK)) umask(toys.old_umask); @@ -93,8 +93,7 @@ static void toy_singleinit(struct toy_list *which, char *argv[]) toys.toycount = ARRAY_LEN(toy_list); } -// Setup toybox global state for this command. - +// Full init needed by multiplexer or reentrant calls, calls singleinit at end void toy_init(struct toy_list *which, char *argv[]) { // Drop permissions for non-suid commands. @@ -114,12 +113,12 @@ void toy_init(struct toy_list *which, char *argv[]) } // Free old toys contents (to be reentrant), but leave rebound if any - - if (toys.optargs != toys.argv+1) free(toys.optargs); + // don't blank old optargs if our new argc lives in the old optargs. + if (argvtoys.optargs+toys.optc) free(toys.optargs); memset(&toys, 0, offsetof(struct toy_context, rebound)); - if (toys.recursion > 1) memset(&this, 0, sizeof(this)); + if (toys.which) memset(&this, 0, sizeof(this)); - // Subset of init needed by singlemain. + // Continue to portion of init needed by standalone commands toy_singleinit(which, argv); } @@ -129,14 +128,15 @@ void toy_exec(char *argv[]) { struct toy_list *which; - // Return if we can't find it, or need to re-exec to acquire root, - // or if stack depth is getting silly. - if (!(which = toy_find(argv[0]))) return; - if (toys.recursion && (which->flags & TOYFLAG_ROOTONLY) && getuid()) return; - if (toys.recursion++ > 5) return; + // Return if we can't find it (which includes no multiplexer case), + if (!(which = toy_find(*argv))) return; - // don't blank old optargs if our new argc lives in the old optargs. - if (argv>=toys.optargs && argv<=toys.optargs+toys.optc) toys.optargs = 0; + // Return if stack depth getting noticeable (proxy for leaked heap, etc). + if (toys.stacktop && labs((char *)toys.stacktop-(char *)&which)>6000) + return; + + // Return if we need to re-exec to acquire root via suid bit. + if (toys.which && (which->flags&TOYFLAG_ROOTONLY) && getuid()) return; // Run command toy_init(which, argv); @@ -146,16 +146,19 @@ void toy_exec(char *argv[]) // Multiplexer command, first argument is command to run, rest are args to that. // If first argument starts with - output list of command install paths. - void toybox_main(void) { static char *toy_paths[]={"usr/","bin/","sbin/",0}; int i, len = 0; + // fast path: try to exec immediately. + // (Leave toys.which null to disable suid return logic.) + if (toys.argv[1]) toy_exec(toys.argv+1); + + // For early error reporting toys.which = toy_list; + if (toys.argv[1]) { - toys.optc = toys.recursion = 0; - toy_exec(toys.argv+1); if (!strcmp("--version", toys.argv[1])) { xputs(TOYBOX_VERSION); xexit(); @@ -185,13 +188,27 @@ void toybox_main(void) int main(int argc, char *argv[]) { - // We check our own stdout errors, disable sigpipe killer - signal(SIGPIPE, SIG_IGN); + if (!*argv) return 127; - if (CFG_TOYBOX) { - // Trim path off of command name - *argv = basename(*argv); + // Snapshot stack location so we can detect recursion depth later. + // This is its own block so probe doesn't permanently consume stack. + else { + int stack; + + toys.stacktop = &stack; + } + *argv = basename_r(*argv); + + // If nommu can't fork, special reentry path. + // Use !stacktop to signal "vfork happened", both before and after xexec() + if (!CFG_TOYBOX_FORK) { + if (0x80 & **argv) { + **argv &= 0x7f; + toys.stacktop = 0; + } + } + if (CFG_TOYBOX) { // Call the multiplexer, adjusting this argv[] to be its' argv[1]. // (It will adjust it back before calling toy_exec().) toys.argv = argv-1; diff --git a/toys.h b/toys.h index 6b539554..d8efa667 100644 --- a/toys.h +++ b/toys.h @@ -67,18 +67,6 @@ #include #include -#include "lib/lib.h" -#include "lib/lsm.h" -#include "toys/e2fs.h" - -// Get list of function prototypes for all enabled command_main() functions. - -#define NEWTOY(name, opts, flags) void name##_main(void); -#define OLDTOY(name, oldname, flags) void oldname##_main(void); -#include "generated/newtoys.h" -#include "generated/flags.h" -#include "generated/globals.h" - // These live in main.c struct toy_list *toy_find(char *name); @@ -132,7 +120,7 @@ extern struct toy_context { // This is at the end so toy_init() doesn't zero it. jmp_buf *rebound; // longjmp here instead of exit when do_rebound set - int recursion; // How many nested calls to toy_exec() + void *stacktop; // nested toy_exec() call count, or -1 if vforked } toys; // Two big temporary buffers: one for use by commands, one for library functions @@ -144,3 +132,15 @@ extern char **environ; #define GLOBALS(...) #define ARRAY_LEN(array) (sizeof(array)/sizeof(*array)) + +#include "lib/lib.h" +#include "lib/lsm.h" +#include "toys/e2fs.h" + +// Get list of function prototypes for all enabled command_main() functions. + +#define NEWTOY(name, opts, flags) void name##_main(void); +#define OLDTOY(name, oldname, flags) void oldname##_main(void); +#include "generated/newtoys.h" +#include "generated/flags.h" +#include "generated/globals.h" -- cgit v1.2.3