/// This finalizer is called automatically as the auditor library /// is being unmapped, but only if interception is on (aka active) /// and only on "normal" exit as described in Interposer/exit.h. /*static*/ void interposed_finalize(void) { if (_auditor_isActiveByRequest()) { pid_t pid; // This is a really cute trick I got from a guy named Nate Eldredge. // The exit status is not given to the library finalizer but we // need it, so what can we do? This is the answer: do a meaningless // fork. The child just returns so that the parent can wait for // it and gets its exit status. There's a slight chance that // the extra fork will cause non-beneficial side effects but // that should be quite rare. // IDEA Would it be better to use vfork() here? A big IDE // like Eclipse might have a large memory footprint and forking // it only to exit might be costly and/or have an OOM failure. // Answer, no, vfork is not allowed to return. There could be // an answer within posix_spawn but I haven't looked into it. if ((pid = fork_real()) == -1) { putil_syserr(0, "fork"); } else if (pid == 0) { // The child just returns so the parent can wait for it. } else { int wstat, status = 7; if (waitpid(pid, &wstat, 0) == pid) { if (WIFEXITED(wstat)) { status = WEXITSTATUS(wstat); } } else { putil_syserr(0, "waitpid"); } _audit_end("exit", EXITING, status); // Do an explicit _exit( to abort any remaining library // finalizers since they will have run in the child. I.e. // any *previous* finalizers will have run in the // original (now parent) copy and any *subsequent* // ones will have run in the child, so continuing with // exit processing here would run the latter set twice. _exit_real(status); } } else if (_auditor_isActiveByDefault()) { _audit_end("exit", EXITING, 0); } /* * The cleanups below would be done by exit anyway but we try to * free things explicitly so that leak detectors such as valgrind * can more easily pinpoint real leaks. Perhaps we could have an * optimized "production mode" which skips the explicit cleanup. */ code_fini(); prop_fini(); vb_fini(); }
void _exit(int status) { static int loaded=0; static void (*_exit_real)(int status); if(!loaded) { _exit_real=0; _exit_real = (void (*)(int status)) dlsym(RTLD_NEXT, "_exit"); if(!dlerror()) loaded=1; else { IPMERR("Intercepting _exit(): load failed\n"); } } if( ipm_state==STATE_ACTIVE || ipm_state==STATE_NOTACTIVE ) ipm_finalize(0); _exit_real(status); }