This patch makes BB_EXECVP the gateway to the exec syscall family. When called, it first looks for a matching applet, and executes it directly of indirectly by re-executing the binary. This new behaviour is configurable by the new FEATURE_FORCE_NOEXEC option.
When FEATURE_FORCE_APPLETS is enabled, BB_EXECVP will fail when trying to execute things that are not busybox applets. This allows more control over the executed processes. BB_EXECVPE is introduced to support passing seperate environment to a BB_EXECVP call, and uses the new copy_terminated_string_array function to backup the current environ. This mimics the behaviour of execvpe. Signed-off-by: Nadav Tasher <[email protected]> --- Config.in | 22 +++++++++++ include/libbb.h | 19 +++------- libbb/executable.c | 92 +++++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 115 insertions(+), 18 deletions(-) diff --git a/Config.in b/Config.in index dfab102bb..e5d08b33f 100644 --- a/Config.in +++ b/Config.in @@ -311,6 +311,28 @@ config FEATURE_PREFER_APPLETS problems in chroot jails without mounted /proc and with ps/top (command name can be shown as 'exe' for applets started this way). +config FEATURE_FORCE_NOEXEC + bool "call applets without exec" + default n + depends on FEATURE_PREFER_APPLETS + help + This is an experimental option which allows calling applets directly + and bypassing NOEXEC restrictions, instead of exec'ing /proc/self/exe. + This reduces the amount of exec syscalls used when running applets, + especially in shells. + + This feature extends the "exec prefers applets" feature. + +config FEATURE_FORCE_APPLETS + bool "only use applets" + default n + depends on FEATURE_PREFER_APPLETS + help + This is an experimental option which makes exec calls fail when trying + to execute external binaries that are not part of busybox. + + This feature extends the "exec prefers applets" feature. + config BUSYBOX_EXEC_PATH string "Path to busybox executable" default "/proc/self/exe" diff --git a/include/libbb.h b/include/libbb.h index 4d6193795..bb9261626 100644 --- a/include/libbb.h +++ b/include/libbb.h @@ -1230,21 +1230,14 @@ int file_is_executable(const char *name) FAST_FUNC; char *find_executable(const char *filename, const char **PATHp) FAST_FUNC; int executable_exists(const char *filename) FAST_FUNC; -/* BB_EXECxx always execs (it's not doing NOFORK/NOEXEC stuff), - * but it may exec busybox and call applet instead of searching PATH. +/* used to copy or backup NULL terminated char arrays */ +char** copy_terminated_string_array(char **array) FAST_FUNC; + +/* BB_EXECxx are called instead of using execXX directly, to allow + * implementation of NOFORK/NOEXEC applet logic if required. */ -#if ENABLE_FEATURE_PREFER_APPLETS int BB_EXECVP(const char *file, char *const argv[]) FAST_FUNC; -#define BB_EXECLP(prog,cmd,...) \ - do { \ - if (find_applet_by_name(prog) >= 0) \ - execlp(bb_busybox_exec_path, cmd, __VA_ARGS__); \ - execlp(prog, cmd, __VA_ARGS__); \ - } while (0) -#else -#define BB_EXECVP(prog,cmd) execvp(prog,cmd) -#define BB_EXECLP(prog,cmd,...) execlp(prog,cmd,__VA_ARGS__) -#endif +int BB_EXECVPE(const char *file, char *const argv[], char *const envp[]) FAST_FUNC; void BB_EXECVP_or_die(char **argv) NORETURN FAST_FUNC; /* xvfork() can't be a _function_, return after vfork in child mangles stack diff --git a/libbb/executable.c b/libbb/executable.c index 09bed1eaf..bfd8ce0f7 100644 --- a/libbb/executable.c +++ b/libbb/executable.c @@ -7,6 +7,7 @@ * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ #include "libbb.h" +#include "busybox.h" /* for APPLET_IS_NOEXEC */ /* check if path points to an executable file; * return 1 if found; @@ -78,15 +79,96 @@ int FAST_FUNC executable_exists(const char *name) return ret != NULL; } -#if ENABLE_FEATURE_PREFER_APPLETS -/* just like the real execvp, but try to launch an applet named 'file' first */ +char** copy_terminated_string_array(char **array) { + size_t array_length = 0, string_length = 0, index = 0; + char **output, **temporary; + + /* calculate array size */ + for (temporary = array; *temporary; ++temporary) + ++array_length; + + /* allocate memory for a new array */ + output = xmalloc(sizeof(char*) * (array_length + 1)); + + /* copy all strings */ + for (index = 0; index < array_length; ++index) { + /* calculate length of string */ + string_length = strlen(array[index]); + + /* allocate memory for the new string */ + output[index] = xmalloc(string_length + 1); + + /* copy the string */ + strncpy(output[index], array[index], string_length); + + /* terminate string manually */ + output[index][string_length] = '\0'; + } + + /* terminate the array */ + output[array_length] = NULL; + + return output; +} + +/* just like the real execvp, but we might try to launch an applet named 'file' first */ int FAST_FUNC BB_EXECVP(const char *file, char *const argv[]) { - if (find_applet_by_name(file) >= 0) - execvp(bb_busybox_exec_path, argv); +#if ENABLE_FEATURE_PREFER_APPLETS + int applet = find_applet_by_name(file); + if (applet >= 0) { + if (ENABLE_FEATURE_FORCE_NOEXEC || APPLET_IS_NOEXEC(applet)) + run_noexec_applet_and_exit(applet, file, (char **) argv); + else + execvp(bb_busybox_exec_path, argv); + } +# if ENABLE_FEATURE_FORCE_APPLETS + else { + /* set errno accordingly */ + errno = ENOENT; + return -1; + } +# endif +#endif + return execvp(file, argv); } -#endif + +int FAST_FUNC BB_EXECVPE(const char *file, char *const argv[], char *const envp[]) +{ + /* for storing BB_EXECVP return value */ + int return_value = -1; + + /* copy the environ global */ + char **original_environ = copy_terminated_string_array(environ), **temporary = original_environ; + + /* we can now safely use clearenv */ + clearenv(); + + /* envp is NULL terminated, so we add all of its values */ + while (*envp) + putenv(*envp++); + + /* execute using BB_EXECVP and store return value */ + return_value = BB_EXECVP(file, argv); + + /* we have to clear the environ before restoring the original + * environment, otherwise we'll have values from envp and + * from the process executed by BB_EXECVP. + */ + clearenv(); + + /* we can now restore all values from our original environ copy */ + while (*temporary) { + printf("pppp %s\n", *temporary); + putenv(*temporary++); + } + + /* free the array */ + free(original_environ); + + return return_value; +} void FAST_FUNC BB_EXECVP_or_die(char **argv) { -- 2.43.0 _______________________________________________ busybox mailing list [email protected] https://lists.busybox.net/mailman/listinfo/busybox
