aboutsummaryrefslogtreecommitdiff
path: root/shell
diff options
context:
space:
mode:
authorRon Yorston <rmy@pobox.com>2026-05-06 13:57:04 +0100
committerRon Yorston <rmy@pobox.com>2026-05-06 13:57:04 +0100
commit169694ebdba292f6533f78aeea678bfbb907396c (patch)
tree4ac3721afe009fcb4a428f3b8fdf0376519a108e /shell
parent9351ea0819bfb05da32389d93964acc56b076c8d (diff)
downloadbusybox-w32-169694ebdba292f6533f78aeea678bfbb907396c.tar.gz
busybox-w32-169694ebdba292f6533f78aeea678bfbb907396c.tar.bz2
busybox-w32-169694ebdba292f6533f78aeea678bfbb907396c.zip
ash: further fixes to merge of upstream changesFRP-6075-g169694ebd
Commits e23652908 and 686a0803f (ash: fix execution of applets via Unix-style path) were necessary following a major revision of the shell upstream. Another problem was found: $ sh $ PATH="/usr/bin;$PATH" exec non-existent resulted in a segfault. This happened because during a PATH search tryexec() modified argv[0] when it detected a Unix-style path (in this case '/usr/bin/non-existent') but failed to restore it if the execution failed. There were other issues: - The logic of the new code failed to match the original: the test for a Unix-style path should only have happened if an attempt to execute the full path had already failed. - The test for a script running an interpreter which is an applet also modified argv. If the execution failed argv should have been restored to its original state. - During a PATH search the 'path' pointer walked through the elements of the path. This pointer was also used to determine if an applet was overridden by an executable. This is wrong: the full PATH variable should have been used. The code around tryexec()/shellexec() has been rewritten to take these issues into account. Adds 32-48 bytes. (GitHub issue #584)
Diffstat (limited to 'shell')
-rw-r--r--shell/ash.c84
1 files changed, 60 insertions, 24 deletions
diff --git a/shell/ash.c b/shell/ash.c
index 560effa4a..0a63c434f 100644
--- a/shell/ash.c
+++ b/shell/ash.c
@@ -6943,27 +6943,13 @@ tryexec_applet(int applet_no, int noexec, const char *cmd, char **argv, char **e
6943# define tryexec(c, p, n, a, e) tryexec(c, a, e) 6943# define tryexec(c, p, n, a, e) tryexec(c, a, e)
6944# endif 6944# endif
6945 6945
6946static const struct builtincmd *find_builtin(const char *name);
6947static void 6946static void
6948tryexec(const char *cmd, const char *path, int noexec, char **argv, char **envp) 6947tryexec(const char *cmd, const char *path, int noexec, char **argv, char **envp)
6949{ 6948{
6950#if ENABLE_FEATURE_SH_STANDALONE 6949#if ENABLE_FEATURE_SH_STANDALONE
6951 interp_t interp; 6950 interp_t interp;
6952 int applet_no;
6953#endif 6951#endif
6954 6952
6955 if (unix_path(cmd)) {
6956 const char *name = bb_basename(cmd);
6957# if ENABLE_FEATURE_SH_STANDALONE
6958 if ((applet_no = find_applet_by_name_for_sh(name, path)) >= 0) {
6959 tryexec_applet(applet_no, noexec, name, argv, envp);
6960 }
6961# endif
6962 if (!find_builtin(name)) {
6963 argv[0] = (char *)name;
6964 }
6965 }
6966
6967 /* Workaround for libtool, which assumes the host is an MSYS2 6953 /* Workaround for libtool, which assumes the host is an MSYS2
6968 * environment and requires special-case escaping for cmd.exe. 6954 * environment and requires special-case escaping for cmd.exe.
6969 * https://github.com/skeeto/w64devkit/issues/50 */ 6955 * https://github.com/skeeto/w64devkit/issues/50 */
@@ -6981,9 +6967,12 @@ tryexec(const char *cmd, const char *path, int noexec, char **argv, char **envp)
6981 /* If the command is a script with an interpreter which is an 6967 /* If the command is a script with an interpreter which is an
6982 * applet, we can run it as if it were a noexec applet. */ 6968 * applet, we can run it as if it were a noexec applet. */
6983 if (parse_interpreter(cmd, &interp)) { 6969 if (parse_interpreter(cmd, &interp)) {
6984 applet_no = find_applet_by_name_for_sh(interp.name, path); 6970 int applet_no = find_applet_by_name_for_sh(interp.name, path);
6985 6971
6986 if (applet_no >= 0) { 6972 if (applet_no >= 0) {
6973 char **argv_old = argv;
6974 char *argv0 = argv[0];
6975
6987 argv[0] = (char *)cmd; 6976 argv[0] = (char *)cmd;
6988 /* evalcommand()/spawn_forkshell() add two elements before argv */ 6977 /* evalcommand()/spawn_forkshell() add two elements before argv */
6989 if (interp.opts) { 6978 if (interp.opts) {
@@ -6995,6 +6984,10 @@ tryexec(const char *cmd, const char *path, int noexec, char **argv, char **envp)
6995 /* Identify the index of the script file in argv */ 6984 /* Identify the index of the script file in argv */
6996 set_interp(1 + (interp.opts != NULL)); 6985 set_interp(1 + (interp.opts != NULL));
6997 tryexec_applet(applet_no, noexec, cmd, argv, envp); 6986 tryexec_applet(applet_no, noexec, cmd, argv, envp);
6987
6988 // Something went wrong, restore state of argv
6989 argv = argv_old;
6990 cmd = argv[0] = argv0;
6998 } 6991 }
6999 } 6992 }
7000# endif 6993# endif
@@ -7052,12 +7045,18 @@ tryexec(const char *cmd, char **argv, char **envp)
7052 * have to change the find_command routine as well. 7045 * have to change the find_command routine as well.
7053 * argv[-1] must exist and be writable! See tryexec() for why. 7046 * argv[-1] must exist and be writable! See tryexec() for why.
7054 */ 7047 */
7048#if ENABLE_PLATFORM_MINGW32
7049static const struct builtincmd *find_builtin(const char *name);
7050#endif
7055static void shellexec(char *prog, char **argv, const char *path, int idx, int noexec) 7051static void shellexec(char *prog, char **argv, const char *path, int idx, int noexec)
7056{ 7052{
7057 char *cmdname; 7053 char *cmdname;
7058 int e; 7054 int e;
7059 char **envp; 7055 char **envp;
7060 int exerrno; 7056 int exerrno;
7057#if ENABLE_PLATFORM_MINGW32 && ENABLE_FEATURE_SH_STANDALONE
7058 int path_fail = FALSE;
7059#endif
7061 7060
7062 envp = listvars(VEXPORT, VUNSET, /*strlist:*/ NULL, /*end:*/ NULL); 7061 envp = listvars(VEXPORT, VUNSET, /*strlist:*/ NULL, /*end:*/ NULL);
7063#if ENABLE_FEATURE_SH_STANDALONE && ENABLE_PLATFORM_MINGW32 && defined(_UCRT) 7062#if ENABLE_FEATURE_SH_STANDALONE && ENABLE_PLATFORM_MINGW32 && defined(_UCRT)
@@ -7068,13 +7067,9 @@ static void shellexec(char *prog, char **argv, const char *path, int idx, int no
7068 putenv(*envp++); 7067 putenv(*envp++);
7069 envp = NULL; 7068 envp = NULL;
7070#endif 7069#endif
7071#if ENABLE_PLATFORM_MINGW32 7070#if !ENABLE_PLATFORM_MINGW32
7072 if (has_path(prog)) {
7073 tryexec(stack_add_ext_space(prog), path, noexec, argv, envp);
7074#else
7075 if (strchr(prog, '/') != NULL) { 7071 if (strchr(prog, '/') != NULL) {
7076 tryexec(prog, argv, envp); 7072 tryexec(prog, argv, envp);
7077#endif
7078 e = errno; 7073 e = errno;
7079 } else { 7074 } else {
7080#if ENABLE_FEATURE_SH_STANDALONE 7075#if ENABLE_FEATURE_SH_STANDALONE
@@ -7089,16 +7084,57 @@ static void shellexec(char *prog, char **argv, const char *path, int idx, int no
7089 while (padvance(&path, argv[0]) >= 0) { 7084 while (padvance(&path, argv[0]) >= 0) {
7090 cmdname = stackblock(); 7085 cmdname = stackblock();
7091 if (--idx < 0 && pathopt == NULL) { 7086 if (--idx < 0 && pathopt == NULL) {
7092#if ENABLE_PLATFORM_MINGW32
7093 tryexec(cmdname, path, noexec, argv, envp);
7094#else
7095 tryexec(cmdname, argv, envp); 7087 tryexec(cmdname, argv, envp);
7096#endif
7097 if (errno != ENOENT && errno != ENOTDIR) 7088 if (errno != ENOENT && errno != ENOTDIR)
7098 e = errno; 7089 e = errno;
7099 } 7090 }
7100 } 7091 }
7101 } 7092 }
7093#else /* ENABLE_PLATFORM_MINGW32 */
7094 if (has_path(prog)) {
7095 tryexec(stack_add_ext_space(prog), path, noexec, argv, envp);
7096 e = errno;
7097# if ENABLE_FEATURE_SH_STANDALONE
7098 path_fail = TRUE;
7099# endif
7100 }
7101 {
7102# if ENABLE_FEATURE_SH_STANDALONE
7103 const char *path0 = path;
7104 int applet_no = find_applet_by_name_for_sh(prog, path);
7105 if (applet_no >= 0) {
7106 tryexec_applet(applet_no, noexec, prog, argv, envp);
7107 /* We tried execing ourself, but it didn't work.
7108 * Maybe /proc/self/exe doesn't exist?
7109 */
7110 goto try_PATH;
7111 }
7112 if (unix_path(prog)) {
7113 const char *name = bb_basename(prog);
7114 if ((applet_no = find_applet_by_name_for_sh(name, path)) >= 0) {
7115 tryexec_applet(applet_no, noexec, name, argv, envp);
7116 }
7117 if (!find_builtin(name)) {
7118 argv[0] = (char *)name;
7119 }
7120 }
7121 if (!path_fail) {
7122 try_PATH:
7123# endif
7124 e = ENOENT;
7125 while (padvance(&path, argv[0]) >= 0) {
7126 cmdname = stackblock();
7127 if (--idx < 0 && pathopt == NULL) {
7128 tryexec(cmdname, path0, noexec, argv, envp);
7129 if (errno != ENOENT && errno != ENOTDIR)
7130 e = errno;
7131 }
7132 }
7133# if ENABLE_FEATURE_SH_STANDALONE
7134 }
7135# endif
7136 }
7137#endif
7102 7138
7103 /* Map to POSIX errors */ 7139 /* Map to POSIX errors */
7104 switch (e) { 7140 switch (e) {