user10714665
user10714665

Reputation:

Exec function in c is not returning -1 when it should

I am using an execv function to run a program called code.x. code.x has a part where it guarantees its failure by Assertion. My code that runs execl is:

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h> 
#include <errno.h>
#include <string.h>

int main()
{
    pid_t pid;
    char *args[] = { "./code.x",NULL };
    pid = fork();
    if (pid > 0) {
        wait(NULL);
        printf("%s\n", strerror(errno));
        printf("done\n");
    }
    else if (pid == 0) {
        printf("%s\n", strerror(errno));
        execv(args[0], args);
        printf("should fail");
    }
    else {
        printf("forkfail");
    }
    return 1;
}

the code prints

Success
code.x: code.c:15: main: Assertion '0 == 1' failed.
Success
done

"should fail" is never printed and WEXITSTATUS(status) shows that the exit status is 0.

Upvotes: 4

Views: 1317

Answers (4)

contrapants
contrapants

Reputation: 793

execv successfully did its job. The process ./code.x executed, then exited because of an assertiong.

The exec family of functions don't care about the process's return value. Once the process starts, the calling process is effectively terminated and gone.

Exec will only return if for some reason the process couldn't be started at all. Specifically, only these errors (taken from the man page) will cause exec to return and set errno to one of these values:

  • E2BIG The total number of bytes in the environment (envp) and argument list (argv) is too large.

  • EACCES Search permission is denied on a component of the path prefix of filename or the name of a script interpreter. (See also path_resolution(7).)

  • EACCES The file or a script interpreter is not a regular file.

  • EACCES Execute permission is denied for the file or a script or ELF interpreter.

  • EACCES The filesystem is mounted noexec.

  • EAGAIN (since Linux 3.1) Having changed its real UID using one of the set*uid() calls, the caller was—and is now still—above its RLIMIT_NPROC resource limit (see setrlimit(2)). For a more detailed explanation of this error, see NOTES.

  • EFAULT filename or one of the pointers in the vectors argv or envp points outside your accessible address space.

  • EINVAL An ELF executable had more than one PT_INTERP segment (i.e., tried to name more than one interpreter).

  • EIO An I/O error occurred.

  • EISDIR An ELF interpreter was a directory.

  • ELIBBAD An ELF interpreter was not in a recognized format.

  • ELOOP Too many symbolic links were encountered in resolving filename or the name of a script or ELF interpreter.

  • ELOOP The maximum recursion limit was reached during recursive script interpretation (see "Interpreter scripts", above). Before Linux 3.8, the error produced for this case was ENOEXEC.

  • EMFILE The per-process limit on the number of open file descriptors has been reached.

  • ENAMETOOLONG filename is too long.

  • ENFILE The system-wide limit on the total number of open files has been reached.

  • ENOENT The file filename or a script or ELF interpreter does not exist, or a shared library needed for the file or interpreter cannot be found.

  • ENOEXEC An executable is not in a recognized format, is for the wrong architecture, or has some other format error that means it cannot be executed.

  • ENOMEM Insufficient kernel memory was available.

  • ENOTDIR A component of the path prefix of filename or a script or ELF interpreter is not a directory.

  • EPERM The filesystem is mounted nosuid, the user is not the superuser, and the file has the set-user-ID or set-group-ID bit set.

  • EPERM The process is being traced, the user is not the superuser and the file has the set-user-ID or set-group-ID bit set.

  • EPERM A "capability-dumb" applications would not obtain the full set of permitted capabilities granted by the executable file. See capabilities(7).

  • ETXTBSY The specified executable was open for writing by one or more processes.

Upvotes: 1

armitus
armitus

Reputation: 748

The Exec function family replaces the existing process image with a new process image. This is why it is required to fork before spawning another process, because the currently running process is completely replaced, this includes the program counter, which keeps track of the next instruction to execute.

printf("should fail");

is never excecuted because the instant you called execv(args[0], args), the program counter was moved to execute args[0], thus leaving behind the execution path that would have resulted in that print statement.

Exec returns -1 on the condition that it encountered an error while replacing the image, and has absolutely no relation to the return value of the program being executed. This is because the two processes, after Exec is called, are not coordinating with each other at all. Remember: the fork() command created a new address space, which means that these processes are now running in separate domains on separate executables.

Some documentation may be of help:

http://man7.org/linux/man-pages/man3/exec.3.html

Hope this helped.

Upvotes: 0

exec* functions succeed if the program starts running. Your program did start running.

An assertion failure causes the program to abort, exit with a signal. The Linux manual page wait(2) explains that:

WEXITSTATUS(wstatus)

returns the exit status of the child. This consists of the least significant 8 bits of the status argument that the child specified in a call to exit(3) or _exit(2) or as the argument for a return statement in main(). This macro should be employed only if WIFEXITED returned true.

If you didn't check that WIFEXITED(status) is true, then WEXITSTATUS(status) is garbage.

Instead, check WIFSIGNALED(status) and if true, get the signal - WTERMSIG(status), which should equal to SIGABRT.

Upvotes: 1

R.. GitHub STOP HELPING ICE
R.. GitHub STOP HELPING ICE

Reputation: 215193

The exec family of functions replace the calling process with a new program in its initial state loaded from an executable file. They can only fail if this replacement fails, e.g. due to the requested file not existing or the invoking user not having permissions to access/execute it.

If an assertion failure in the program ./code.x you're invoking happens, this is long past the point where execv could have failed; at this point, the original program state where execv was performed no longer exists, because it was already replaced. The parent process will see it exit via a wait-family function, and can inspect the status reported by the wait-family function to determine why it exited.

Upvotes: 1

Related Questions