Hook arbitrary function on to _exit(2) - c

I'm interested in whether I can call an arbitrary function on _exit(2) call, which bypasses other hooking architectures, so it doesn't seem easy to me.
If this is an ordinary exit(3) or return statement, obviously it's possible by atexit(3), on_exit(3), or __attribute__((destructor)) with gcc extension
It is possible by overriding _exit(2) with LD_PRELOAD; which I wish to avoid
Is there a way to do it without LD_PRELOAD, say, overriding _exit(2)?
Edit: The problem I'm facing is fork(2)ed Perl programs with CoW. The program's children processes run destructors on exit(3) call, in which they touch many memory locations, to cause large memory copy, in spite they will exit.
It's hard to bypass destructors with ordinary exit call in Perl, so an idea is call POSIX::_exit instead.
However, there is a dynamically loaded library with LD_PRELOAD, and I want to call a function in it on process exit.

AFAIU, it is simply not possible without LD_PRELOAD tricks, or ptrace(2) with PTRACE_SYSCALLfrom another process (e.g. the parent process running gdb). At the lowest level, _exit(2) is a system call so is an "atomic" operation using the SYSENTER machine instruction, e.g. thru vdso(7).
Notice that a C program could use some asm to invoke the _exit syscall (or use the indirect syscall(2))
Assuming a dynamically linked executable to GNU libc or musl-libc, your only way is to catch exit(3) library function (not the _exit(2) syscall!) using atexit(3)
You could redefine _exit and hope that the dynamic linker would call your _exit, not the one in libc. I won't play such tricks.
Alternatively, write a small wrapping C program which fork, execve and waitpid the original program.

could you make use of the 'atexit()' function call?
Near the beginning of main() call atexit() with a parameter of the function that you want executed when the program exits.
You can call atexit() numerous time, thereby stacking several things to be executed when the application exits.

Related

GCC C: Is there an equivalent to exit() that calls __attribute__(cleanup) functions?

I read that the functions registered with cleanup will not be called when the program terminates early through exit() or longjmp(). Is there any function that I can call that works similarly to exit() (meaning I can use it to terminate the program from functions other than main(), such as signal handlers) but which will call all of the registered cleanup functions? If I was using C++ I could just use throw() (since cleanup functions are called during stack unwinding), but I need this to be in C.
In the glibc implementation, pthread_exit performs stack unwinding and terminates the program if the current thread is the only thread. The downside is that you cannot specify the exit status.
To get cleanup functions to execute during stack unwinding, you need to build with -fexceptions.
(For glibc before 2.34, you may have to link with -lpthread to get the stack-unwinding variant of pthread_exit.)

Shared library unloading and atexit handling - what order?

I'm debugging a sort of weird issue where it looks like a thread that's killed in an atexit handler is accessing a shared library, and is segfaulting because that shared library is unloaded before the exit handler runs. I'm not sure this is actually the issue, but it's a hunch on what might be happening.
When a process terminates (main exits or exit() is called), is the atexit handler the immediate next thing to run? My mind says so, but the segfault I'm seeing seems to say otherwise.
Is there any difference (with regards to exit handling) between main returning (either end of function or with return) and calling exit() directly?
When a process terminates (main exits or exit() is called), is the atexit handler the immediate next thing to run? My mind says so, but the segfault I'm seeing seems to say otherwise.
Not necessarily, you're guaranteed that atexit handlers will run. But that's it, atexit handlers may even be called concurrently with other things. While you're using c remember that c++ may be in the same process. C++ says that atexit may be called concurrently to destructors being run for objects of static duration. This means that atexit is very dangerous and you need to ensure you're being very careful what you call.
Is there any difference (with regards to exit handling) between main returning (either end of function or with return) and calling exit() directly?
According to the documentation: No.
The safest thing to do when tearing down the house: Nothing. Just get out and let the house be torn down. Closing the drapes on the way out isn't worth the time so to speak.

Syscall implementation of exit()

I wrote a simple C program which just calls the exit() function, however strace says that the binary is actually calling exit_group, is exit() a exit_group() wrapper? Are these two functions equivalent? If so why would the compiler choose exit_group() over exit()?
The Linux and glibc man pages document all of this (See especially the "C library/kernel differences" in the NOTES section).
_exit(2): In glibc 2.3 and later, this wrapper function actually uses the Linux SYS_exit_group system call to exit all threads. Before glibc2.3, it was a wrapper for SYS_exit to exit just the current thread.
exit_group(2): glibc wrapper for SYS_exit_group, which exits all threads.
exit(3): The ISO C89 function which flushes buffers and then exits the whole process. (It always uses exit_group() because there's no benefit to checking if the process was single-threaded and deciding to use SYS_exit vs. SYS_exit_group). As #Matteo points out, recent ISO C / POSIX standards are thread-aware and one or both probably require this behaviour.
But apparently exit(3) itself is not thread-safe (in the C library cleanup parts), so I guess don't call it from multiple threads at once.
syscall / int 0x80 with SYS_exit: terminates just the current thread, leaving others running. AFAIK, modern glibc has no thin wrapper function for this Linux system call, but I think pthread_exit() uses it if this isn't the last thread. (Otherwise exit(3) -> exit_group(2).)
Only exit(), not _exit() or exit_group(), flushes stdout, leading to "printf doesn't print anything" problems in newbie asm programs if writing to a pipe (which makes stdout full-buffered instead of line-buffered), or if you forgot the \n in the format string. For example, How come _exit(0) (exiting by syscall) prevents me from receiving any stdout content?. If you use any buffered I/O functions, or at_exit, or anything like that, it's usually a good idea to call the libc exit(3) function instead of the system call directly. But of course you can call fflush before SYS_exit_group.
(Also related: On x64 Linux, what is the difference between syscall, int 0x80 and ret to exit a program? - ret from main is equivalent to calling exit(3))
It's not of course the compiler that chose anything, it's libc. When you include headers and write read(fd, buf, 123) or exit(1), the C compiler just sees an ordinary function call.
Some C libraries (e.g. musl, but not glibc) may use inline asm to inline a syscall instruction into your binary, but still the headers are part of the C library, not the compiler.

When I call vfork(), can I call any exec*() function, or must I call execve()?

From the Linux man page:
The vfork() function has the same effect as fork(2), except that the behavior is undefined if the process [...] calls any other function before successfully calling [...] one of the exec(3) family of functions.
This suggests that calling any exec*() function after vfork() is acceptable. However, later in the man page it says specifically:
In particular, the programmer cannot rely on the parent remaining blocked until the child [...] calls execve(2) [...].
execve(2) is used repeatedly in the man page, and its usage suggests that it's the only exec-type function that is acceptable after vfork().
So why is execve being singled out here, and am I safe to call other exec-type functions (like execlp)?
You must call execve. There is no guarantee that any of the other exec-family functions do not perform actions which would be unsafe after vfork. For example:
execl may allocate memory for the argument list. It's required to be async-signal-safe, which means it's unlikely to use malloc, but even if it doesn't, there's no way it could free the allocated memory (which exists in the parent's memory space) after the underlying execve takes place, so it would (at best) leak memory in the parent unless it manages to construct the argument list on the stack.
execvp needs to access the environment to perform a path search, and also needs to construct concatenated pathnames to pass to execve. The latter may require allocation and the former may do all sorts of unsafe-after-vfork things (note: execvp is not even async-signal-safe).
etc.
Really you should simply not use vfork. It's almost impossible to make its use safe. In particular it's unsafe in any program that's using signal handlers since the signal handler could run in the child while it's sharing the parent's memory unless you block all signals (and in that case the child would inherit a fully-blocked signal mask after exec, which is almost surely not what you want).
If you're looking for a more-efficient alternative to fork, use posix_spawn.
On Linux all exec* functions are actually wrapper library functions on top of execve syscall. So by calling execlp you are actually also calling execve.
After reading over the manual again, it becomes clear that there are two descriptions of vfork:
The POSIX Standard Description says that after vfork, one of the exec(3) functions must be called.
The Linux Description says that after vfork, execve(2) (and only execve) must be called.
It's not clear to me whether the POSIX Standard Description requires a conforming implementation to allow any one of the exec functions to be called. One possible reading of the Standard Description is that the implementation may decide which exec functions are allowed (and only requires that at least one of them be allowed after a vfork).
Either way, it's clear that Linux allows execve (and only execve*) to be called after a vfork. The POSIX standard might allow other exec functions, but Linux does not.
*Well, sure, it can call _exit too, but I'm ignoring _exit in this Q&A.

Is it possible to change the exit code in a function registered with atexit()?

The man page for atexit(3) says the following:
POSIX.1-2001 says that the result of calling exit(3) more than once (i.e., calling exit(3) within a function registered using atexit()) is undefined. On some systems (but not Linux), this can result in an infinite recursion; portable programs should not invoke exit(3) inside a function registered using atexit().
However, I'm interested in modifying the exit code in a finalizer for my program. The only way I've managed to do this is by calling exit() from within my finalization function, but the man page explicitly warns against that.
Is there any practical danger against doing this? Are there any implementations where that approach might cause problems? Even better, is there another way of doing this?
You can call _exit() instead.
Within the Notes section of the man page:
The function _exit() is like exit(), but does not call any functions registered with atexit() or on_exit().
This should avoid the "recursive" issue that is being warned about in the POSIX spec. If you are somehow able to guarantee that your "exit code changing" exit handler runs last, this should work perfectly, modulo the caveats also listed in the Notes:
Whether it flushes standard I/O buffers and removes temporary files created with tmpfile(3) is implementation-dependent. On the other hand, _exit() does close open file descriptors, and this may cause an unknown delay, waiting for pending output to finish. If the delay is undesired, it may be useful to call functions like tcflush(3) before calling _exit(). Whether any pending I/O is canceled, and which pending I/O may be canceled upon _exit(), is implementation-dependent.

Resources