[PATCH v4 3/8] executable.c: introduce BB_EXECVPE function to handle applet execution and replace BB_EXECVP with a macro
Nadav Tasher
tashernadav at gmail.com
Mon Jan 27 23:04:16 UTC 2025
This patch makes BB_EXECVPE 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 busybox
binary. This feature takes NOEXEC definitions into account
by checking NOEXEC with the APPLET_IS_NOEXEC function.
When FEATURE_FORCE_APPLETS is enabled, BB_EXECVPE will
fail when trying to execute external binaries, and only
pre-compiled applets will be able to run. This allows for
better control over the programs executed by busybox.
BB_EXECVP is redefined as a macro to BB_EXECVPE that uses
environ as the default envp argument.
Signed-off-by: Nadav Tasher <tashernadav at gmail.com>
---
Config.in | 10 +++++++++
include/libbb.h | 28 +++++++++++--------------
libbb/executable.c | 51 +++++++++++++++++++++++++++++++++++++---------
3 files changed, 63 insertions(+), 26 deletions(-)
diff --git a/Config.in b/Config.in
index dfab102bb..9d40431ee 100644
--- a/Config.in
+++ b/Config.in
@@ -311,6 +311,16 @@ 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_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..39ca6d811 100644
--- a/include/libbb.h
+++ b/include/libbb.h
@@ -1230,22 +1230,18 @@ 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.
- */
-#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
-void BB_EXECVP_or_die(char **argv) NORETURN FAST_FUNC;
+/* BB_EXECVP is just a macro to use BB_EXECVPE
+ * with a default environ argument. */
+#define BB_EXECVP(file, argv) BB_EXECVPE(file, argv, environ)
+#define BB_EXECVP_or_die(argv) BB_EXECVP_or_die_msg(argv, "can't execute '%s'")
+
+/* BB_EXECVPE is called instead of using exec directly.
+ * this allows implementation of NOEXEC applet logic. */
+int BB_EXECVPE(const char *file, char *const argv[], char *const envp[]) FAST_FUNC;
+
+/* BB_EXECVP_or_die is commonly used in functions that must
+ * exit if the execution of the desired program fails. */
+void BB_EXECVP_or_die_msg(char **argv, const char *msg) NORETURN FAST_FUNC;
/* xvfork() can't be a _function_, return after vfork in child mangles stack
* in the parent. It must be a macro. */
diff --git a/libbb/executable.c b/libbb/executable.c
index 09bed1eaf..2f27cb9fa 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,20 +79,50 @@ 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 */
-int FAST_FUNC BB_EXECVP(const char *file, char *const argv[])
+/* just like the real execvpe, but we might try to launch an applet named 'file' first */
+int FAST_FUNC BB_EXECVPE(const char *file, char *const argv[], char *const envp[])
{
- if (find_applet_by_name(file) >= 0)
- execvp(bb_busybox_exec_path, argv);
- return execvp(file, argv);
-}
+#if ENABLE_FEATURE_PREFER_APPLETS
+ int applet = find_applet_by_name(file);
+ if (applet >= 0) {
+ if (APPLET_IS_NOEXEC(applet)) {
+ /* if non-default environ was passed, replace environ */
+ if (envp != environ) {
+ clearenv();
+
+ /* envp is NULL terminated. */
+ while (*envp)
+ putenv(*envp++);
+ }
+
+ /* this should never return. */
+ run_noexec_applet_and_exit(applet, file, (char **) argv);
+
+ /* if this is reached, error out */
+ errno = ENOEXEC;
+ return -1;
+ } else {
+ /* applet has to be executed using an exec syscall */
+ return execvpe(bb_busybox_exec_path, argv, envp);
+ }
+ }
+# if ENABLE_FEATURE_FORCE_APPLETS
+ else {
+ /* no external programs are allowed, error out */
+ errno = ENOENT;
+ return -1;
+ }
+# endif
#endif
-void FAST_FUNC BB_EXECVP_or_die(char **argv)
+ /* we can always fall back to execvpe */
+ return execvpe(file, argv, envp);
+}
+
+void FAST_FUNC BB_EXECVP_or_die_msg(char **argv, const char *msg)
{
BB_EXECVP(argv[0], argv);
/* SUSv3-mandated exit codes */
xfunc_error_retval = (errno == ENOENT) ? 127 : 126;
- bb_perror_msg_and_die("can't execute '%s'", argv[0]);
-}
+ bb_perror_msg_and_die(msg, argv[0]);
+}
\ No newline at end of file
--
2.43.0
More information about the busybox
mailing list