Avoid using environ on FreeBSD

WebKit passes -Wl,--no-undefined to the compiler when linking shared
libraries. This is usually a good practice, but it causes gtest to fail
to link because of the use of environ. FreeBSD puts environ symbol in
crt1.o instead of libc.so, so the linker is unable to find environ when
linking a shared library.

Fortunately, there is an easy way to fix it. Since the only use of
environ in gtest is to pass it to execve, we can simply replace execve
with execv to avoid the need of environ. execv is usually a simple
wrapper around execve, so it should be safe to use.

This problem was found and reported more than three years ago. The same
fix is already committed to WebKit and we don't see any problem reports
caused by the change.

https://bugs.webkit.org/show_bug.cgi?id=138420
https://trac.webkit.org/changeset/194501/webkit
https://bugs.webkit.org/show_bug.cgi?id=208409
https://trac.webkit.org/changeset/257691/webkit
https://groups.google.com/forum/#!topic/googletestframework/wrrMj_fmXMc
This commit is contained in:
Ting-Wei Lan 2018-05-01 17:04:43 +08:00
parent 8567b09290
commit 2e8ebe69dc

View File

@ -1225,21 +1225,9 @@ struct ExecDeathTestArgs {
int close_fd; // File descriptor to close; the read end of a pipe int close_fd; // File descriptor to close; the read end of a pipe
}; };
# if GTEST_OS_MAC # if GTEST_OS_QNX
inline char** GetEnviron() {
// When Google Test is built as a framework on MacOS X, the environ variable
// is unavailable. Apple's documentation (man environ) recommends using
// _NSGetEnviron() instead.
return *_NSGetEnviron();
}
# else
// Some POSIX platforms expect you to declare environ. extern "C" makes
// it reside in the global namespace.
extern "C" char** environ; extern "C" char** environ;
inline char** GetEnviron() { return environ; } # else // GTEST_OS_QNX
# endif // GTEST_OS_MAC
# if !GTEST_OS_QNX
// The main function for a threadsafe-style death test child process. // The main function for a threadsafe-style death test child process.
// This function is called in a clone()-ed process and thus must avoid // This function is called in a clone()-ed process and thus must avoid
// any potentially unsafe operations like malloc or libc functions. // any potentially unsafe operations like malloc or libc functions.
@ -1259,18 +1247,18 @@ static int ExecDeathTestChildMain(void* child_arg) {
return EXIT_FAILURE; return EXIT_FAILURE;
} }
// We can safely call execve() as it's a direct system call. We // We can safely call execv() as it's almost a direct system call. We
// cannot use execvp() as it's a libc function and thus potentially // cannot use execvp() as it's a libc function and thus potentially
// unsafe. Since execve() doesn't search the PATH, the user must // unsafe. Since execv() doesn't search the PATH, the user must
// invoke the test program via a valid path that contains at least // invoke the test program via a valid path that contains at least
// one path separator. // one path separator.
execve(args->argv[0], args->argv, GetEnviron()); execv(args->argv[0], args->argv);
DeathTestAbort(std::string("execve(") + args->argv[0] + ", ...) in " + DeathTestAbort(std::string("execv(") + args->argv[0] + ", ...) in " +
original_dir + " failed: " + original_dir + " failed: " +
GetLastErrnoDescription()); GetLastErrnoDescription());
return EXIT_FAILURE; return EXIT_FAILURE;
} }
# endif // !GTEST_OS_QNX # endif // GTEST_OS_QNX
# if GTEST_HAS_CLONE # if GTEST_HAS_CLONE
// Two utility routines that together determine the direction the stack // Two utility routines that together determine the direction the stack
@ -1339,8 +1327,7 @@ static pid_t ExecDeathTestSpawnChild(char* const* argv, int close_fd) {
fd_flags | FD_CLOEXEC)); fd_flags | FD_CLOEXEC));
struct inheritance inherit = {0}; struct inheritance inherit = {0};
// spawn is a system call. // spawn is a system call.
child_pid = child_pid = spawn(args.argv[0], 0, nullptr, &inherit, args.argv, environ);
spawn(args.argv[0], 0, nullptr, &inherit, args.argv, GetEnviron());
// Restores the current working directory. // Restores the current working directory.
GTEST_DEATH_TEST_CHECK_(fchdir(cwd_fd) != -1); GTEST_DEATH_TEST_CHECK_(fchdir(cwd_fd) != -1);
GTEST_DEATH_TEST_CHECK_SYSCALL_(close(cwd_fd)); GTEST_DEATH_TEST_CHECK_SYSCALL_(close(cwd_fd));