Index: source/Plugins/Process/Linux/NativeProcessLinux.cpp =================================================================== --- source/Plugins/Process/Linux/NativeProcessLinux.cpp +++ source/Plugins/Process/Linux/NativeProcessLinux.cpp @@ -1545,6 +1545,11 @@ if (args->m_error.Fail()) exit(ePtraceFailed); + // terminal has already dupped the tty descriptors to stdin/out/err. + // This closes original fd from which they were copied (and avoids + // leaking descriptors to the debugged process. + terminal.CloseSlaveFileDescriptor(); + // Do not inherit setgid powers. if (setgid(getgid()) != 0) exit(eSetGidFailed); @@ -3532,7 +3537,10 @@ if (target_fd == -1) return false; - return (dup2(target_fd, fd) == -1) ? false : true; + if (dup2(target_fd, fd) == -1) + return false; + + return (close(target_fd) == -1) ? false : true; } void Index: source/Plugins/Process/Linux/ProcessMonitor.cpp =================================================================== --- source/Plugins/Process/Linux/ProcessMonitor.cpp +++ source/Plugins/Process/Linux/ProcessMonitor.cpp @@ -1356,6 +1356,11 @@ if (PTRACE(PTRACE_TRACEME, 0, NULL, NULL, 0) < 0) exit(ePtraceFailed); + // terminal has already dupped the tty descriptors to stdin/out/err. + // This closes original fd from which they were copied (and avoids + // leaking descriptors to the debugged process. + terminal.CloseSlaveFileDescriptor(); + // Do not inherit setgid powers. if (setgid(getgid()) != 0) exit(eSetGidFailed); @@ -2319,7 +2324,10 @@ if (target_fd == -1) return false; - return (dup2(target_fd, fd) == -1) ? false : true; + if (dup2(target_fd, fd) == -1) + return false; + + return (close(target_fd) == -1) ? false : true; } void Index: source/Target/ProcessLaunchInfo.cpp =================================================================== --- source/Target/ProcessLaunchInfo.cpp +++ source/Target/ProcessLaunchInfo.cpp @@ -344,7 +344,7 @@ log->Printf ("ProcessLaunchInfo::%s default_to_use_pty is set, and at least one stdin/stderr/stdout is unset, so generating a pty to use for it", __FUNCTION__); - if (m_pty->OpenFirstAvailableMaster(O_RDWR| O_NOCTTY, NULL, 0)) + if (m_pty->OpenFirstAvailableMaster(O_RDWR | O_NOCTTY | O_CLOEXEC, NULL, 0)) { const char *slave_path = m_pty->GetSlaveName(NULL, 0); Index: test/dosep.py =================================================================== --- test/dosep.py +++ test/dosep.py @@ -57,8 +57,8 @@ """Run command with a timeout if possible.""" if timeout_command and timeout != "0": return subprocess.call([timeout_command, timeout] + command, - stdin=subprocess.PIPE) - return (ePassed if subprocess.call(command, stdin=subprocess.PIPE) == 0 + stdin=subprocess.PIPE, close_fds=True) + return (ePassed if subprocess.call(command, stdin=subprocess.PIPE, close_fds=True) == 0 else eFailed) def process_dir(root, files, test_root, dotest_options): Index: test/functionalities/avoids-fd-leak/Makefile =================================================================== --- /dev/null +++ test/functionalities/avoids-fd-leak/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../make + +C_SOURCES := main.c + +include $(LEVEL)/Makefile.rules Index: test/functionalities/avoids-fd-leak/TestFdLeak.py =================================================================== --- /dev/null +++ test/functionalities/avoids-fd-leak/TestFdLeak.py @@ -0,0 +1,32 @@ +""" +Test whether a process started by lldb has no extra file descriptors open. +""" + +import os +import unittest2 +import lldb +from lldbtest import * +import lldbutil + +class AvoidsFdLeakTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @expectedFailureWindows("The check for descriptor leakage needs to be implemented differently") + def test_fd_leak (self): + self.buildDefault() + exe = os.path.join (os.getcwd(), "a.out") + + target = self.dbg.CreateTarget(exe) + + process = target.LaunchSimple (None, None, self.get_process_working_directory()) + self.assertTrue(process, PROCESS_IS_VALID) + + self.assertTrue(process.GetState() == lldb.eStateExited) + self.assertTrue(process.GetExitStatus() == 0) + +if __name__ == '__main__': + import atexit + lldb.SBDebugger.Initialize() + atexit.register(lambda: lldb.SBDebugger.Terminate()) + unittest2.main() Index: test/functionalities/avoids-fd-leak/main.c =================================================================== --- /dev/null +++ test/functionalities/avoids-fd-leak/main.c @@ -0,0 +1,28 @@ +#include +#include +#include +#include +#include + +int +main (int argc, char const **argv) +{ + struct stat buf; + int i; + + // Make sure stdin/stdout/stderr exist. + for (i = 0; i <= 2; ++i) { + if (fstat(i, &buf) != 0) + return 1; + } + + // Make sure no other file descriptors are open. + for (i = 3; i <= 256; ++i) { + if (fstat(i, &buf) == 0 || errno != EBADF) { + fprintf(stderr, "File descriptor %d is open.\n", i); + return 2; + } + } + + return 0; +}