diff options
| author | Ron Yorston <rmy@pobox.com> | 2026-05-06 13:57:04 +0100 |
|---|---|---|
| committer | Ron Yorston <rmy@pobox.com> | 2026-05-06 13:57:04 +0100 |
| commit | 169694ebdba292f6533f78aeea678bfbb907396c (patch) | |
| tree | 4ac3721afe009fcb4a428f3b8fdf0376519a108e /shell | |
| parent | 9351ea0819bfb05da32389d93964acc56b076c8d (diff) | |
| download | busybox-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.c | 84 |
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 | ||
| 6946 | static const struct builtincmd *find_builtin(const char *name); | ||
| 6947 | static void | 6946 | static void |
| 6948 | tryexec(const char *cmd, const char *path, int noexec, char **argv, char **envp) | 6947 | tryexec(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 | ||
| 7049 | static const struct builtincmd *find_builtin(const char *name); | ||
| 7050 | #endif | ||
| 7055 | static void shellexec(char *prog, char **argv, const char *path, int idx, int noexec) | 7051 | static 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) { |
