0

I have a program which forks child processes, but never invokes any exec-style function. This is by design. The problem is that the address sanitizer does not report anything it finds (even when issues are obvious) when the child terminates via _exit().

As I understand it, calling _exit or _Exit is the correct way to end a child process which never invokes exec. If I change the logic for the child to use return or exit, ASan is able to report all issues found in the child process.

Is there a way to make ASan report what it finds when _exit is used? Is this behavior expected?

Below is the example code I've been using to test this:

// file: main.cpp

#include <cstdio>
#include <cstdlib>
#include <iostream>

#include <unistd.h>
#include <sys/wait.h>

int main()
{
    const auto pid = fork();

    if (pid > 0) {
        std::cout << "parent: " << getpid() << '\n';
        int status;
        wait(&status);
    }
    else if (pid == 0) {
        std::cout << "child: " << getpid() << '\n';
        auto* p = new int;
        p = nullptr;

        _exit(0);   // No ASan output.
        //_Exit(0); // No ASan output.
        //exit(0);  // ASan reports leaks.
    }
    else {
        std::perror("fork");
    }

    return 0;
}

Godbolt link: https://gcc.godbolt.org/z/dfsaTc7rz

  • Platform: Ubuntu 20.04
  • Compiler: Clang 13.0.0 (compiled from source)

Here is the command used to compile the code:

clang++ -std=c++20 -g -O0 -fsanitize=address -fno-omit-frame-pointer main.cpp

I haven't found anything in the ASan documentation that points to a solution for this.

Kory
  • 133
  • 1
  • 5
  • 2
    _As I understand it, calling _exit or _Exit is the correct way to end a child process which never invokes exec_ No, [almost] always use `exit`. The choice has nothing to do with whether `exec` has been used or not. There are callbacks that are invoked when doing `exit` (See: `atexit`), probably one of which is something for asan. Using `_exit` which does an EXIT _syscall_ stops the program immediately – Craig Estey Oct 20 '22 at 21:30
  • So in most cases, `exit` is preferred. Doesn't calling `exit` also run the risk of corrupting the parent's memory? When would someone choose to use `_exit`? – Kory Oct 20 '22 at 22:08
  • No, once you do `fork`, the two processes have _separate_ [although similar] address spaces. Child could zero out all of its memory and _not_ a _single_ byte of parent's memory would be affected. `_exit` is largely for the library functions to use (e.g. `_exit` is the last thing that `exit` calls). Or, an `atexit` handler. That is, if I do: `atexit(myhandler)`, then when `exit` is called, `myhandler` determines that the program is in an _unsafe_ state (e.g. another handler would try to cleanup a temp file but actually delete a database), `myhandler` would do `_exit` to prevent further damage. – Craig Estey Oct 20 '22 at 22:17
  • 1
    Try this program: `#include #include int main(void) { printf("hello"); exit(0); return 0; }` Then, change `exit` to `_exit`. Note that there is _no_ newline on the `printf` – Craig Estey Oct 20 '22 at 22:25
  • Will do. I think I arrived at that statement about using `_exit` because of statements made in the book, `The Linux Programming Interface`. One such statement made in the book is _The exit() library function is layered on top of the _exit() system call. In Chapter 25, we explain the difference between the two interfaces. In the meantime, we’ll just note that, after a fork(), generally only one of the parent and child termi- nate by calling exit(); the other process should terminate using _exit()._ Switching to `_exit` resolved an issue in a project I work on, but now I'm second guessing it. – Kory Oct 20 '22 at 22:37
  • I added `#include ` since it complained about not recognizing `_exit`. And now `"hello"` isn't flushed to the terminal, which makes sense given that `_exit` doesn't flush stdio buffers. My guess is that ASan really depends on the behavior of `exit`. – Kory Oct 20 '22 at 22:41
  • Yes, it does something like (e.g.) `atexit(asan_exit);` on or before the beginning of `main`. Then, all the analysis you've come to know and love occurs on the callback to `asan_exit` from `exit` ;-) – Craig Estey Oct 20 '22 at 23:01
  • @CraigEstey this should be an answer – yugr Oct 21 '22 at 04:47
  • Here is one such post which lead to my use of `_exit`: https://stackoverflow.com/a/5423108/5818611 - Do you have any thoughts about the question and accepted answer? – Kory Oct 21 '22 at 21:30
  • @CraigEstey: I'm also confused by your advice. In most of the cases I can think of, the child should *not* execute any `atexit()` handlers that the parent may have registered. Since there isn't any way to unregister an `atexit` handler, the only solution is to have the child call `_exit()`. The current case is an exceptional situation. – Nate Eldredge Oct 23 '22 at 05:26

0 Answers0