Quantcast
Channel: Active questions tagged gcc - Stack Overflow
Viewing all articles
Browse latest Browse all 22213

Is this unstable behavior of execve after vfork a gcc bug?

$
0
0

So the object of this question is to determine, if possible, whether or not I'm looking at the signature of a compiler bug from oddball code brokenness. I have done as much as possible to eliminate undefined behavior from the surrounding code; however, bisection of the code into a standalone reproduction is not possible. I think I need a bunch of variables on the stack to get it to reproduce.

Broken code:

pid_t pid;ultoa(uid, buf);volatile int execve_error = 0;int status;if ((pid = vfork()) == 0) {    /* call useradd */    if (setuid(0)) { execve_error = -254; _exit(0); }    if (dup2(3, 0)) { execve_error = -254; _exit(0); }    const char *argv[13] = { "/usr/sbin/useradd", "-md", namebuf,"-k", skelbufbase, "-s", shell,"-u", buf, "-g", create_group, username, NULL };    execve_error = execve(argv[0], argv, NULL);    _exit(0);} else if (pid < 0) {    writeblock(2, "can't fork\n", 11);    r = pid;    goto cleanup4;}wait4(pid, &status, 0, NULL);if (execve_error) { r = execve_error; goto cleanup4; } /* execve failed */

Working code:

pid_t pid;ultoa(uid, buf);volatile int execve_error = 0;int status;if ((pid = vfork()) == 0) {    /* call useradd */    if (setuid(0)) { execve_error = -254; _exit(0); }    if (dup2(3, 0)) { execve_error = -254; _exit(0); }    const char *argv[13] = { "/usr/sbin/useradd", "-md", namebuf,"-k", skelbufbase, "-s", shell,"-u", buf, "-g", create_group, username, NULL };    writeblock(3, "exec?\n", 6); /* Compiler bug? If this nop is omitted, execve() never happens */    execve_error = execve(argv[0], argv, NULL);    _exit(0);} else if (pid < 0) {    writeblock(2, "can't fork\n", 11);    r = pid;    goto cleanup4;}wait4(pid, &status, 0, NULL);if (execve_error) { r = execve_error; goto cleanup4; } /* execve failed */

Other working code:

pid_t pid;ultoa(uid, buf);volatile int execve_error = 0;int status;const char *argv[13] = { "/usr/sbin/useradd", "-md", namebuf,"-k", skelbufbase, "-s", shell,"-u", buf, "-g", create_group, username, NULL };if ((pid = vfork()) == 0) {    /* call useradd */    if (setuid(0)) { execve_error = -254; _exit(0); }    if (dup2(3, 0)) { execve_error = -254; _exit(0); }    execve_error = execve(argv[0], argv, NULL);    _exit(0);} else if (pid < 0) {    writeblock(2, "can't fork\n", 11);    r = pid;    goto cleanup4;}wait4(pid, &status, 0, NULL);if (execve_error) { r = execve_error; goto cleanup4; } /* execve failed */

This wasn't all that frustrating to get the code to work; but it defies normal sensibilities for why it works or does not work. writeblock() just calls write() in a loop in case of part writes. If I take out the call to writeblock() on null (handle 3 is /dev/null here) the code stops working. It's not just malfunctioning on the second return from vfork(); it's already messed up before then because execve() doesn't succeed.

On debugging with strace: argv[0] is trashed in the argv array (points to unmapped memory) but the correct first argument is passed to execve().

The code is also fixed by moving the argv array creation to outside the vfork() call. Declaring arrays with static sizes is not one of the things listed as can't do under vfork() or setjmp().

Trying to add trace statements reliably makes the code work so I'm having a hard time getting a line on it. Furthermore, the problem resists bisection. I've got a dozen or so spawn a child process routines that look similar that don't fail in this strange manner.


Viewing all articles
Browse latest Browse all 22213

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>