Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save keith-bennett-gbg/680d3e82a732471becfd97d171f5b7ea to your computer and use it in GitHub Desktop.
Save keith-bennett-gbg/680d3e82a732471becfd97d171f5b7ea to your computer and use it in GitHub Desktop.
cloexec-does-not-close-in-parent
extern "C" {
#include <sys/types.h> // fork(), open()
#include <sys/stat.h> // open()
#include <sys/wait.h> // waitpid()
#include <unistd.h> // fork(), fcntl(), read(), execve()
#include <fcntl.h> // fcntl(), open()
#include <signal.h> // signal()
} // extern "C"
#include <iostream>// std::cerr
#include <fstream> // std::fstream
#include <cstdlib> // EXIT_FAILURE, EXIT_SUCCESS
#include <cstring> // std::strerror, std::strcpy
#include <cerrno> // errno
void child_action(int signum)
{
// do nothing.
}
int main(int argc, char **argv)
{
if ( argc == 1 )
{
std::cerr << "./a.out child-file\n";
return EXIT_FAILURE;
}
errno = 0;
int fd = open(argv[0], O_CLOEXEC | O_RDONLY);
if ( -1 == fd )
{
int err = errno;
std::cerr << "open(" << argv[0] << "): " << std::strerror(err) << std::endl;
return EXIT_FAILURE;
}
errno = 0;
sighandler_t prev_handler = signal(SIGCHLD, child_action);
if ( SIG_ERR == prev_handler )
{
int err = errno;
std::cerr << "signal(SIGCHLD): " << std::strerror(err) << std::endl;
return EXIT_FAILURE;
}
errno = 0;
pid_t pid = fork();
if ( -1 == pid )
{
int err = errno;
std::cerr << "fork(): " << std::strerror(err) << std::endl;
return EXIT_FAILURE;
}
if ( 0 == pid )
{
// I am child. Check fd, write status to file named by argv[1], and exit.
std::fstream f(argv[1], std::ios_base::out | std::ios_base::trunc);
if ( false == f.good() )
{
return EXIT_FAILURE;
}
char buffer[16];
// read from fd.
errno = 0;
int result = read(fd, buffer, sizeof(buffer));
int err = errno;
f << fd << '\t' << result << '\t' << err << '\t' << std::strerror(err) << '\n' << std::flush;
if ( false == f.good() )
{
return EXIT_FAILURE;
}
// try a bad execve()
std::strcpy(buffer, "blah blah blah");
char * argv_[2] = {buffer, nullptr};
errno = 0;
// reusing argv_ for argp_. this will break most apps. oh well, I just need the app to die quick.
result = execve("no-such-executable", argv_, argv_);
err = errno;
f << "no-such-executable\t" << result << '\t' << err << '\t' << std::strerror(err) << '\n';
// try reading fd some more.
errno = 0;
result = read(fd, buffer, sizeof(buffer));
err = errno;
f << fd << '\t' << result << '\t' << err << '\t' << std::strerror(err) << '\n' << std::flush;
if ( false == f.good() )
{
return EXIT_FAILURE;
}
// child is done.
return EXIT_SUCCESS;
}
else
{
// I am parent. Wait for child to exit, then reap.
int status = 0;
int result = waitpid(pid, &status, 0);
// Child has exited. Is our fd still open?
char buffer[16];
errno = 0;
result = read(fd, buffer, sizeof(buffer));
int err = errno;
std::cout << fd << '\t' << result << '\t' << err << '\t' << std::strerror(err) << '\n' << std::flush;
if ( -1 != result || EBADF != err )
{
std::cerr << "fd appears to still be open even after execve should have failed in child\n" << std::flush;
return EXIT_FAILURE;
}
}
return EXIT_SUCCESS;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment