set-uid script with -p in magic number gets Exec format error
[Originally filed as http://bugs.opensolaris.org/bugdatabase/view_bug.do?bug_id=6934836]
The following script, when run as a set-uid script, fails to execute,
reporting an "Exec format error" instead:
$ cat ok.ksh
This is odd because ksh -p is documented as doing nothing more than suppressing the processing of the user's $HOME/.profile and using
/etc/suid_profile instead of the ENV variable. Nothing in available ksh93 documentation suggests that -p is incompatible with set-uid scripts.
Remove the options from the #! line and use a "set" line instead to set the same options. For instance, the following line:
can be replaced by these lines:
Does not seem to have anything to do with the "-p" option specifically. The script fails in exactly the same way if we use any options, "-x", "-v", etc.
The difference between the way a suid script is executed and a regular one is that in case of a suid script, the script to be executed is passed as a /dev/fd parameter to close the "replace script between exec and open by shell" hole (usr/src/uts/common/exec/shbin/shbin.c). This, with ksh93, seems to have a problem only if there are options specified on the command line.
The program below demonstrates this:
$ cat testexec.c
int fd = -1;
char *script = "/tmp/ok.ksh";
fd = open(script, O_RDONLY);
sprintf(devfd, "/dev/fd/%d", fd);
execl("/usr/bin/sparcv9/ksh93", "ksh", "-v", devfd, NULL);
//execl("/usr/bin/sparcv9/ksh93", "ksh", "-v", script, NULL);
//execl("/usr/bin/bash", "bash", "-v", devfd, NULL);
//execl("/usr/bin/sparcv9/ksh93", "ksh", devfd, NULL);
/usr/bin/ksh: /usr/bin/ksh: cannot execute [Exec format error]
This fails irrespective of whether the script is suid or not. And the other commented execl() lines in the source (using the script directly instead of the /dev/fd path, bash with command line options and the devfd path, or ksh93 without command line options) all work perfectly.
A few interesting twists:
$ cp testexec kshexec
(Renaming the script to something starting with 'ksh' makes it start working.)
$ SHELL=/bin/ksh ./testexec
ksh: ksh: cannot execute [Exec format error]
$ SHELL=/bin/bash ./testexec
/usr/bin/ksh: /usr/bin/ksh: cannot execute binary file
(Though we are invoking ksh93 directly, it does an exec of itself when we use /dev/fd/XX scripts where it ends up using the SHELL environment variable.)
The culprit seems to be the code below:
Here, we are overwriting one of the arguments of argv (because shp->st.dolv indexes into the argv vector).
In this particular case, argv which originally looked like this:
ksh, -v, /dev/fd/3
ends up looking like this:
ksh, ksh, /dev/fd/3
We then pass the mangled argv to execv():
298 /* exec to change $0 for ps */
As a consequence, ksh tries to load the ksh binary as a shell script and fails with an "Exec format" error.