Skip to content

Commit

Permalink
mingw: when trying to execute non-existing /bin/<cmd>, try an applet
Browse files Browse the repository at this point in the history
If BusyBox is installed as /mingw64/bin/busybox.exe, and there are no
Unix-y tools installed (not even a /bin/sh), it is pretty clear what we
want BusyBox to do when a script asks to execute /bin/sh <args>: BusyBox
should pass those args to its own ash.

So what we do is to detect when /bin/<command> does not exist and
<command> is an applet, and replace /bin/<command> by BusyBox' own
executable path.

This works as long as argv[0] still refers to the <command>.

However, when we wanted to execute /bin/sh and argv[0] referred to a
script's path, we need to be careful to shift "sh" into argv so that
BusyBox will know to what applet it is expected to pass the arguments.

To accomplish that, we simply redo the argv -> wargv conversion (it is
by far the most robust way).

Technically, that latter case does not even happen right now, because we
only ever call scripts with argv[0] being set to "sh" due to the way
parse_interpreter simply strips away the path from the interpreter. But
this is the current state of affairs, and it is better to safeguard,
especially when it is this easy.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
  • Loading branch information
dscho committed Sep 7, 2017
1 parent 7dc2796 commit 735da41
Showing 1 changed file with 31 additions and 3 deletions.
34 changes: 31 additions & 3 deletions win32/process.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

#define WARGV_OOM ((void *)(intptr_t)-1ll)

static wchar_t **argv_to_wargv(char *const *argv)
static wchar_t **argv_to_wargv(char *const *argv, const char *prepend)
{
size_t size = 0, count = 1;
wchar_t **w0, *w1, **wargv;
Expand All @@ -12,6 +12,10 @@ static wchar_t **argv_to_wargv(char *const *argv)
if (!argv)
return NULL;

if (prepend) {
count++;
size += MultiByteToWideChar(CP_UTF8, 0, prepend, -1, NULL, 0);
}
for (i = 0; argv[i]; i++) {
count++;
size += MultiByteToWideChar(CP_UTF8, 0, argv[i], -1, NULL, 0);
Expand All @@ -21,6 +25,10 @@ static wchar_t **argv_to_wargv(char *const *argv)
return WARGV_OOM;
w0 = wargv;
w1 = (void *)(w0 + count);
if (prepend) {
*(w0++) = w1;
w1 += MultiByteToWideChar(CP_UTF8, 0, prepend, -1, w1, size);
}
for (i = 0; argv[i]; i++) {
*(w0++) = w1;
w1 += MultiByteToWideChar(CP_UTF8, 0, argv[i], -1, w1, size);
Expand Down Expand Up @@ -122,13 +130,33 @@ static intptr_t mingw_spawnve(int mode,
intptr_t ret;

wcmd = mingw_pathconv(cmd);
wargv = argv_to_wargv(argv);
wenv = argv_to_wargv(env);
wargv = argv_to_wargv(argv, NULL);
wenv = argv_to_wargv(env, NULL);
if (!wcmd || wargv == WARGV_OOM || wenv == WARGV_OOM) {
errno = ENOMEM;
return -1;
}

/*
* When /bin/<command> does not exist, and <command> is an applet,
* run that applet instead.
*/
if (!strncmp(cmd, "/bin/", 5) &&
GetFileAttributesW(wcmd) == INVALID_FILE_ATTRIBUTES &&
find_applet_by_name(cmd + 5) >= 0) {
if (!argv[0] || (strcmp(argv[0], cmd) &&
strcmp(argv[0], cmd + 5))) {
/* argv[0] is different from cmd, let's shift in cmd */
free(wargv);
wargv = argv_to_wargv(argv, cmd + 5);
if (wargv == WARGV_OOM) {
errno = ENOMEM;
return -1;
}
}
wcmd = mingw_pathconv(get_busybox_exec_path());
}

/*
* It can be easily verified that _wspawnve() has no problems with a
* wcmd that has the \\?\ prefix and it may be a long path.
Expand Down

0 comments on commit 735da41

Please sign in to comment.