The implementation of spawnveg() in src/lib/libast/comp/spawnveg.c, when
_real_vfork is defined and non-zero, relies on vfork() being implemented
with the following properties:
1) The parent process is suspended until the child process successfully
calls one of the exec*() functions or _exit().
2) The parent and child processes share the same address space (until
the child process successfully calls an exec*() function or _exit())
Reliance on these properties imparts a performance advantage to ksh that
is desirable in most cases and for most users. However, these reliances
also result in undefined behavior according to the SUSv3/POSIX 2004
specification:
http://pubs.opengroup.org/onlinepubs/009695399/functions/vfork.html
(Note that vfork() was withdrawn in SUSv4/POSIX 2008)
Specifically, reliance on these properties causes ksh to behave
unexpectedly when vfork() is redirected to fork() via library
interposition. For example, on Linux:
$ cat vfork-interposer.c
#include <unistd.h>
pid_t vfork() {
return fork();
}
$ gcc -g -fPIC -shared vfork-interposer.c -o libvfork-interposer.so
$ cat script.ksh
echo "Script executed!"
$ ksh -c "./script.ksh; echo 'done'"
Script executed!
done
$ LD_PRELOAD=./libvfork-interposer.so ksh -c "./script.ksh; echo 'done'"
done
The extra "echo 'done'" command is used above to side-step a ksh
optimization - ksh elides calls to spawn a new process for the last
command to be executed.
Note that "Script executed!" is not echoed in the second ksh invocation
(the one with LD_PRELOAD set). The reason is that ksh attempts to call
execve() on 'script.ksh' and, since 'script.ksh' does not specify an
interpreter, the call to execve() fails with ENOEXEC. In the first
invocation, the execve() failure is detected and ksh interprets the
script directly. In the second invocation, the execve() failure is not
detected, ksh assumes the executable was run successfully, and does not
interpret the script. The reason that ksh does not detect the failed
execve() failure in the second invocation is because spawnveg() calls
vfork() in order to execute the script, and the child process
communicates any failures to the parent process by writing into the
shared address space (via the stack). If the communication of an
execve() failure with ENOEXEC is successful, the parent process will
attempt to interpret the script directly (after checking file modes).
When the communication fails (when the parent and child processes do not
share the same address space), the parent process is not informed that
the call to execve() in the child process failed and proceeds as though
the call in the child process succeeded.
Library interposition such as that demonstrated is important for
numerous tracing programs. The following URLs link to other cases of
tracing of ksh failing due to this issue. I've been told that some
versions of strace on Linux trigger this issue, though I don't have any
details and have not seen this myself.
-
http://markmail.org/message/6w2nvm3rdurntmk5#query:+page:1+mid:6w2nvm3rdurntmk5+state:results
- https://bugzilla.redhat.com/show_bug.cgi?id=464520
- See comments 7 and 9
There are several options for addressing this issue such that library
interposition of vfork() with fork() will result in the expected behavior:
1) ksh could use a different mechanism for communicating between the
child and parent processes when vfork() is used (ie, a pipe instead of
assuming a shared address space)
2) ksh could call fork() instead of vfork() when strict POSIX compliance
is requested (when the POSIXLY_CORRECT environment variable is set)
Per the following email thread, I surmise that ksh's use of vfork() on
systems that support posix_spawn() is unintentional. It seems the glibc
posix_spawn() implementation had a defect that caused ksh to avoid using
it.
https://mailman.research.att.com/pipermail/ast-developers/2012q3/001495.html
(The glibc defect is described at
https://mailman.research.att.com/pipermail/ast-developers/2012q3/001601.html)
Tom.
_______________________________________________
ast-developers mailing list
ast-developers@research.att.com
https://mailman.research.att.com/mailman/listinfo/ast-developers