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

Reply via email to