Hi all,

I’m working on making BusyBox an embeddable, self-contained shell interpreter
with applets as built-in commands.

This enhancement would allow users to link directly with BusyBox to provide
a complete shell capable of executing pre-built applets as commands.
It could be particularly useful for embedding limited shell functionality
in other programs that expose a shell-like environment or that require executing
shell scripts in a controlled, curated environment.

I’ve already developed some of the patches required to implement this,
and will continue working on it.
For now, I’ve attached patches that introduce some of the changes I’m proposing.

Looking forward to your feedback!

Kind regards,
Nadav
>From d79421a35d94eae4eeaecc385cf77716d4b0c245 Mon Sep 17 00:00:00 2001
From: Nadav Tasher <[email protected]>
Date: Mon, 13 Jan 2025 22:26:35 +0200
Subject: [PATCH 1/6] libbb.h: change BB_EXECLP macro to use BB_EXECVP

This change reduces code duplication between the implementation of BB_EXECVP and the BB_EXECLP macro.

Signed-off-by: Nadav Tasher <[email protected]>
---
 include/libbb.h | 7 +------
 1 file changed, 1 insertion(+), 6 deletions(-)

diff --git a/include/libbb.h b/include/libbb.h
index 4d6193795..496b57724 100644
--- a/include/libbb.h
+++ b/include/libbb.h
@@ -1235,12 +1235,7 @@ int executable_exists(const char *filename) FAST_FUNC;
  */
 #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)
+#define BB_EXECLP(prog,cmd,...) BB_EXECVP(prog, (char *[]){cmd, __VA_ARGS__, NULL})
 #else
 #define BB_EXECVP(prog,cmd)     execvp(prog,cmd)
 #define BB_EXECLP(prog,cmd,...) execlp(prog,cmd,__VA_ARGS__)
-- 
2.43.0

>From b73fc92e0f2a753f2172d4cfcacf67d3cb9f673c Mon Sep 17 00:00:00 2001
From: Nadav Tasher <[email protected]>
Date: Mon, 13 Jan 2025 22:50:46 +0200
Subject: [PATCH 2/6] Config.in: introduce FEATURE_DIRECTLY_CALL_APPLETS
 options

This config option will allow applets to be called directly,
bypassing any NOEXEC restrictions.

Signed-off-by: Nadav Tasher <[email protected]>
---
 Config.in | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/Config.in b/Config.in
index ad0cd1e26..98345f6ca 100644
--- a/Config.in
+++ b/Config.in
@@ -310,6 +310,18 @@ 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_DIRECTLY_CALL_APPLETS
+	bool "call applets directly"
+	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 BUSYBOX_EXEC_PATH
 	string "Path to busybox executable"
 	default "/proc/self/exe"
-- 
2.43.0

>From 7686d75c85ffb4b34cce10c72fe40cdef22c93f2 Mon Sep 17 00:00:00 2001
From: Nadav Tasher <[email protected]>
Date: Mon, 13 Jan 2025 22:53:24 +0200
Subject: [PATCH 3/6] executable.c: allow calling applets directly in BB_EXECVP

This change allows BB_EXECVP to call applets directly,
instead of using execvp when possible.

This change is optional and can be enabled using
'FEATURE_DIRECTLY_CALL_APPLETS'.

Signed-off-by: Nadav Tasher <[email protected]>
---
 libbb/executable.c | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/libbb/executable.c b/libbb/executable.c
index 09bed1eaf..f31505777 100644
--- a/libbb/executable.c
+++ b/libbb/executable.c
@@ -82,8 +82,13 @@ int FAST_FUNC executable_exists(const char *name)
 /* 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[])
 {
-	if (find_applet_by_name(file) >= 0)
+	int applet = find_applet_by_name(file);
+	if (applet >= 0)
+# if ENABLED_FEATURE_DIRECTLY_CALL_APPLETS
+		run_applet_no_and_exit(applet, file, argv);
+# else
 		execvp(bb_busybox_exec_path, argv);
+# endif
 	return execvp(file, argv);
 }
 #endif
-- 
2.43.0

>From 27bb2928f4030cbc73b9008607a22fc3627dfa94 Mon Sep 17 00:00:00 2001
From: Nadav Tasher <[email protected]>
Date: Mon, 13 Jan 2025 23:00:43 +0200
Subject: [PATCH 4/6] ash.c: allow bypassing NOEXEC check using
 FEATURE_DIRECTLY_CALL_APPLETS

This feature allows the FEATURE_DIRECTLY_CALL_APPLETS option to
bypass the NOEXEC setting of the applet, essentially making every
applet NOEXEC.

Signed-off-by: Nadav Tasher <[email protected]>
---
 shell/ash.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/shell/ash.c b/shell/ash.c
index 9173b8608..312c3daee 100644
--- a/shell/ash.c
+++ b/shell/ash.c
@@ -8275,7 +8275,7 @@ tryexec(IF_FEATURE_SH_STANDALONE(int applet_no,) const char *cmd, char **argv, c
 {
 #if ENABLE_FEATURE_SH_STANDALONE
 	if (applet_no >= 0) {
-		if (APPLET_IS_NOEXEC(applet_no)) {
+		if (ENABLE_FEATURE_DIRECTLY_CALL_APPLETS || APPLET_IS_NOEXEC(applet_no)) {
 			clearenv();
 			while (*envp)
 				putenv(*envp++);
-- 
2.43.0

>From 0e662e3f405c037b19d3a4a07e3c1453719d3158 Mon Sep 17 00:00:00 2001
From: Nadav Tasher <[email protected]>
Date: Mon, 13 Jan 2025 23:03:10 +0200
Subject: [PATCH 5/6] vfork_daemon_rexec.c: allow bypassing NOEXEC check using
 FEATURE_DIRECTLY_CALL_APPLETS

This feature allows the FEATURE_DIRECTLY_CALL_APPLETS option to
bypass the NOEXEC setting of the applet, essentially making every
applet NOEXEC.

Signed-off-by: Nadav Tasher <[email protected]>
---
 libbb/vfork_daemon_rexec.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libbb/vfork_daemon_rexec.c b/libbb/vfork_daemon_rexec.c
index 2055c4b71..bff21ae93 100644
--- a/libbb/vfork_daemon_rexec.c
+++ b/libbb/vfork_daemon_rexec.c
@@ -232,7 +232,7 @@ int FAST_FUNC spawn_and_wait(char **argv)
 		if (APPLET_IS_NOFORK(a))
 			return run_nofork_applet(a, argv);
 # if BB_MMU /* NOEXEC needs fork(), thus this is done only on MMU machines: */
-		if (APPLET_IS_NOEXEC(a)) {
+		if (ENABLED_FEATURE_DIRECTLY_CALL_APPLETS || APPLET_IS_NOEXEC(a)) {
 			fflush_all();
 			rc = fork();
 			if (rc) /* parent or error */
-- 
2.43.0

>From 30a1c2829393ba13d53fdf80391bb6cdfcb425e0 Mon Sep 17 00:00:00 2001
From: Nadav Tasher <[email protected]>
Date: Mon, 13 Jan 2025 23:04:08 +0200
Subject: [PATCH 6/6] hush.c: allow bypassing NOEXEC check using
 FEATURE_DIRECTLY_CALL_APPLETS

This feature allows the FEATURE_DIRECTLY_CALL_APPLETS option to
bypass the NOEXEC setting of the applet, essentially making every
applet NOEXEC.

Signed-off-by: Nadav Tasher <[email protected]>
---
 shell/hush.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/shell/hush.c b/shell/hush.c
index 4a97293cc..f8c86c922 100644
--- a/shell/hush.c
+++ b/shell/hush.c
@@ -8806,7 +8806,7 @@ static NOINLINE void pseudo_exec_argv(nommu_save_t *nommu_save,
 		if (a >= 0) {
 			if_command_vV_print_and_exit(opt_vV, argv[0], "an applet");
 # if BB_MMU /* see above why on NOMMU it is not allowed */
-			if (APPLET_IS_NOEXEC(a)) {
+			if (ENABLED_FEATURE_DIRECTLY_CALL_APPLETS || APPLET_IS_NOEXEC(a)) {
 				/* Do not leak open fds from opened script files etc.
 				 * Testcase: interactive "ls -l /proc/self/fd"
 				 * should not show tty fd open.
-- 
2.43.0

_______________________________________________
busybox mailing list
[email protected]
https://lists.busybox.net/mailman/listinfo/busybox

Reply via email to