Bug #500


set-uid script with -p in magic number gets Exec format error

Added by Roland Mainz almost 13 years ago. Updated almost 13 years ago.

cmd - userland programs
Start date:
Due date:
% Done:


Estimated time:
Gerrit CR:
External Bug:


[Originally filed as]
The following script, when run as a set-uid script, fails to execute,
reporting an "Exec format error" instead:

$ cat ok.ksh
#!/bin/ksh -p

print "okay"

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.

Work Around

Remove the options from the #! line and use a "set" line instead to set the same options. For instance, the following line:

#!/usr/bin/ksh -p

can be replaced by these lines:

set -p

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
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>

main() {
int fd = -1;
char devfd32;
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);
perror("exec failed");
$ ./testexec
/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
$ ./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:

1216 shp->st.dolv=argv+(argc-1)-shp->st.dolc;
1217 shp->st.dolv0 = argv0;

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 */
299 execv(pathshell(),av);

As a consequence, ksh tries to load the ksh binary as a shell script and fails with an "Exec format" error.



Also available in: Atom PDF