Skip to content

Commit 91aeecf

Browse files
garethgareth
gareth
authored and
gareth
committed
Fix issue #5976 - HANDLE leaks and undefined/bad behavour
on windows.
1 parent d9896d5 commit 91aeecf

File tree

4 files changed

+119
-40
lines changed

4 files changed

+119
-40
lines changed

src/libcore/libc.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1725,6 +1725,7 @@ pub mod funcs {
17251725
findFileData: HANDLE)
17261726
-> BOOL;
17271727
unsafe fn FindClose(findFile: HANDLE) -> BOOL;
1728+
unsafe fn CloseHandle(hObject: HANDLE) -> BOOL;
17281729
unsafe fn TerminateProcess(hProcess: HANDLE, uExitCode: c_uint) -> BOOL;
17291730
}
17301731
}

src/libcore/os.rs

+6-2
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ pub mod rustrt {
6060
unsafe fn rust_get_argv() -> **c_char;
6161
unsafe fn rust_path_is_dir(path: *libc::c_char) -> c_int;
6262
unsafe fn rust_path_exists(path: *libc::c_char) -> c_int;
63-
unsafe fn rust_process_wait(handle: c_int) -> c_int;
63+
unsafe fn rust_process_wait(pid: c_int) -> c_int;
6464
unsafe fn rust_set_exit_status(code: libc::intptr_t);
6565
}
6666
}
@@ -356,7 +356,11 @@ pub fn fsync_fd(fd: c_int, _l: io::fsync::Level) -> c_int {
356356
#[cfg(windows)]
357357
pub fn waitpid(pid: pid_t) -> c_int {
358358
unsafe {
359-
return rustrt::rust_process_wait(pid);
359+
let status = rustrt::rust_process_wait(pid);
360+
if status < 0 {
361+
fail!(fmt!("failure in rust_process_wait: %s", last_os_error()));
362+
}
363+
return status;
360364
}
361365
}
362366

src/libcore/run.rs

+67-24
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,9 @@ use task;
2424
use vec;
2525

2626
pub mod rustrt {
27-
use libc::{c_int, c_void, pid_t};
27+
use libc::{c_int, c_void};
2828
use libc;
29+
use run;
2930

3031
#[abi = "cdecl"]
3132
pub extern {
@@ -34,11 +35,18 @@ pub mod rustrt {
3435
dir: *libc::c_char,
3536
in_fd: c_int,
3637
out_fd: c_int,
37-
err_fd: c_int)
38-
-> pid_t;
38+
err_fd: c_int) -> run::RunProgramResult;
3939
}
4040
}
4141

42+
pub struct RunProgramResult {
43+
// the process id of the program, or -1 if in case of errors
44+
pid: pid_t,
45+
// a handle to the process - on unix this will always be NULL, but on windows it will be a
46+
// HANDLE to the process, which will prevent the pid being re-used until the handle is closed.
47+
handle: *(),
48+
}
49+
4250
/// A value representing a child process
4351
pub trait Program {
4452
/// Returns the process id of the program
@@ -100,16 +108,24 @@ pub trait Program {
100108
* The process id of the spawned process
101109
*/
102110
pub fn spawn_process(prog: &str, args: &[~str],
103-
env: &Option<~[(~str,~str)]>,
104-
dir: &Option<~str>,
105-
in_fd: c_int, out_fd: c_int, err_fd: c_int)
106-
-> pid_t {
111+
env: &Option<~[(~str,~str)]>,
112+
dir: &Option<~str>,
113+
in_fd: c_int, out_fd: c_int, err_fd: c_int) -> pid_t {
114+
115+
let res = spawn_process_internal(prog, args, env, dir, in_fd, out_fd, err_fd);
116+
free_handle(res.handle);
117+
return res.pid;
118+
}
119+
120+
fn spawn_process_internal(prog: &str, args: &[~str],
121+
env: &Option<~[(~str,~str)]>,
122+
dir: &Option<~str>,
123+
in_fd: c_int, out_fd: c_int, err_fd: c_int) -> RunProgramResult {
107124
unsafe {
108125
do with_argv(prog, args) |argv| {
109126
do with_envp(env) |envp| {
110127
do with_dirp(dir) |dirp| {
111-
rustrt::rust_run_program(argv, envp, dirp,
112-
in_fd, out_fd, err_fd)
128+
rustrt::rust_run_program(argv, envp, dirp, in_fd, out_fd, err_fd)
113129
}
114130
}
115131
}
@@ -195,6 +211,18 @@ priv unsafe fn fclose_and_null(f: &mut *libc::FILE) {
195211
}
196212
}
197213

214+
#[cfg(windows)]
215+
priv fn free_handle(handle: *()) {
216+
unsafe {
217+
libc::funcs::extra::kernel32::CloseHandle(cast::transmute(handle));
218+
}
219+
}
220+
221+
#[cfg(unix)]
222+
priv fn free_handle(_handle: *()) {
223+
// unix has no process handle object, just a pid
224+
}
225+
198226
/**
199227
* Spawns a process and waits for it to terminate
200228
*
@@ -208,10 +236,13 @@ priv unsafe fn fclose_and_null(f: &mut *libc::FILE) {
208236
* The process's exit code
209237
*/
210238
pub fn run_program(prog: &str, args: &[~str]) -> int {
211-
let pid = spawn_process(prog, args, &None, &None,
212-
0i32, 0i32, 0i32);
213-
if pid == -1 as pid_t { fail!(); }
214-
return waitpid(pid);
239+
let res = spawn_process_internal(prog, args, &None, &None,
240+
0i32, 0i32, 0i32);
241+
if res.pid == -1 as pid_t { fail!(); }
242+
243+
let code = waitpid(res.pid);
244+
free_handle(res.handle);
245+
return code;
215246
}
216247

217248
/**
@@ -234,20 +265,21 @@ pub fn start_program(prog: &str, args: &[~str]) -> @Program {
234265
let pipe_input = os::pipe();
235266
let pipe_output = os::pipe();
236267
let pipe_err = os::pipe();
237-
let pid =
238-
spawn_process(prog, args, &None, &None,
239-
pipe_input.in, pipe_output.out,
240-
pipe_err.out);
268+
let res =
269+
spawn_process_internal(prog, args, &None, &None,
270+
pipe_input.in, pipe_output.out,
271+
pipe_err.out);
241272

242273
unsafe {
243-
if pid == -1 as pid_t { fail!(); }
274+
if res.pid == -1 as pid_t { fail!(); }
244275
libc::close(pipe_input.in);
245276
libc::close(pipe_output.out);
246277
libc::close(pipe_err.out);
247278
}
248279

249280
struct ProgRepr {
250281
pid: pid_t,
282+
handle: *(),
251283
in_fd: c_int,
252284
out_file: *libc::FILE,
253285
err_file: *libc::FILE,
@@ -317,6 +349,7 @@ pub fn start_program(prog: &str, args: &[~str]) -> @Program {
317349
finish_repr(cast::transmute(&self.r));
318350
close_repr_outputs(cast::transmute(&self.r));
319351
}
352+
free_handle(self.r.handle);
320353
}
321354
}
322355

@@ -343,8 +376,9 @@ pub fn start_program(prog: &str, args: &[~str]) -> @Program {
343376
fn force_destroy(&mut self) { destroy_repr(&mut self.r, true); }
344377
}
345378

346-
let repr = ProgRepr {
347-
pid: pid,
379+
let mut repr = ProgRepr {
380+
pid: res.pid,
381+
handle: res.handle,
348382
in_fd: pipe_input.out,
349383
out_file: os::fdopen(pipe_output.in),
350384
err_file: os::fdopen(pipe_err.in),
@@ -385,13 +419,13 @@ pub fn program_output(prog: &str, args: &[~str]) -> ProgramOutput {
385419
let pipe_in = os::pipe();
386420
let pipe_out = os::pipe();
387421
let pipe_err = os::pipe();
388-
let pid = spawn_process(prog, args, &None, &None,
389-
pipe_in.in, pipe_out.out, pipe_err.out);
422+
let res = spawn_process_internal(prog, args, &None, &None,
423+
pipe_in.in, pipe_out.out, pipe_err.out);
390424

391425
os::close(pipe_in.in);
392426
os::close(pipe_out.out);
393427
os::close(pipe_err.out);
394-
if pid == -1i32 {
428+
if res.pid == -1i32 {
395429
os::close(pipe_in.out);
396430
os::close(pipe_out.in);
397431
os::close(pipe_err.in);
@@ -415,7 +449,10 @@ pub fn program_output(prog: &str, args: &[~str]) -> ProgramOutput {
415449
let output = readclose(pipe_out.in);
416450
ch_clone.send((1, output));
417451
};
418-
let status = run::waitpid(pid);
452+
453+
let status = waitpid(res.pid);
454+
free_handle(res.handle);
455+
419456
let mut errs = ~"";
420457
let mut outs = ~"";
421458
let mut count = 2;
@@ -563,6 +600,12 @@ mod tests {
563600
assert!(status == 1);
564601
}
565602

603+
#[test]
604+
#[should_fail]
605+
fn waitpid_non_existant_pid() {
606+
run::waitpid(123456789); // assume that this pid doesn't exist
607+
}
608+
566609
#[test]
567610
fn test_destroy_once() {
568611
let mut p = run::start_program("echo", []);

src/rt/rust_run_program.cpp

+45-14
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,11 @@
1515
#include <crt_externs.h>
1616
#endif
1717

18+
struct RunProgramResult {
19+
pid_t pid;
20+
void* handle;
21+
};
22+
1823
#if defined(__WIN32__)
1924

2025
#include <process.h>
@@ -78,7 +83,7 @@ void append_arg(char *& buf, char const *arg, bool last) {
7883
}
7984
}
8085

81-
extern "C" CDECL int
86+
extern "C" CDECL RunProgramResult
8287
rust_run_program(const char* argv[],
8388
void* envp,
8489
const char* dir,
@@ -88,19 +93,21 @@ rust_run_program(const char* argv[],
8893
si.cb = sizeof(STARTUPINFO);
8994
si.dwFlags = STARTF_USESTDHANDLES;
9095

96+
RunProgramResult result = {-1, NULL};
97+
9198
HANDLE curproc = GetCurrentProcess();
9299
HANDLE origStdin = (HANDLE)_get_osfhandle(in_fd ? in_fd : 0);
93100
if (!DuplicateHandle(curproc, origStdin,
94101
curproc, &si.hStdInput, 0, 1, DUPLICATE_SAME_ACCESS))
95-
return -1;
102+
return result;
96103
HANDLE origStdout = (HANDLE)_get_osfhandle(out_fd ? out_fd : 1);
97104
if (!DuplicateHandle(curproc, origStdout,
98105
curproc, &si.hStdOutput, 0, 1, DUPLICATE_SAME_ACCESS))
99-
return -1;
106+
return result;
100107
HANDLE origStderr = (HANDLE)_get_osfhandle(err_fd ? err_fd : 2);
101108
if (!DuplicateHandle(curproc, origStderr,
102109
curproc, &si.hStdError, 0, 1, DUPLICATE_SAME_ACCESS))
103-
return -1;
110+
return result;
104111

105112
size_t cmd_len = 0;
106113
for (const char** arg = argv; *arg; arg++) {
@@ -124,18 +131,39 @@ rust_run_program(const char* argv[],
124131
CloseHandle(si.hStdError);
125132
free(cmd);
126133

127-
if (!created) return -1;
128-
return (int)pi.hProcess;
134+
if (!created) {
135+
return result;
136+
}
137+
138+
// We close the thread handle because we don't care about keeping the thread id valid,
139+
// and we aren't keeping the thread handle around to be able to close it later. We don't
140+
// close the process handle however because we want the process id to stay valid at least
141+
// until the calling rust code closes the process handle.
142+
CloseHandle(pi.hThread);
143+
result.pid = pi.dwProcessId;
144+
result.handle = pi.hProcess;
145+
return result;
129146
}
130147

131148
extern "C" CDECL int
132-
rust_process_wait(int proc) {
149+
rust_process_wait(int pid) {
150+
151+
HANDLE proc = OpenProcess(SYNCHRONIZE | PROCESS_QUERY_INFORMATION, FALSE, pid);
152+
if (proc == NULL) {
153+
return -1;
154+
}
155+
133156
DWORD status;
134157
while (true) {
135-
if (GetExitCodeProcess((HANDLE)proc, &status) &&
136-
status != STILL_ACTIVE)
137-
return (int)status;
138-
WaitForSingleObject((HANDLE)proc, INFINITE);
158+
if (!GetExitCodeProcess(proc, &status)) {
159+
CloseHandle(proc);
160+
return -1;
161+
}
162+
if (status != STILL_ACTIVE) {
163+
CloseHandle(proc);
164+
return (int) status;
165+
}
166+
WaitForSingleObject(proc, INFINITE);
139167
}
140168
}
141169

@@ -151,13 +179,16 @@ rust_process_wait(int proc) {
151179
extern char **environ;
152180
#endif
153181

154-
extern "C" CDECL int
182+
extern "C" CDECL RunProgramResult
155183
rust_run_program(const char* argv[],
156184
void* envp,
157185
const char* dir,
158186
int in_fd, int out_fd, int err_fd) {
159187
int pid = fork();
160-
if (pid != 0) return pid;
188+
if (pid != 0) {
189+
RunProgramResult result = {pid, NULL};
190+
return result;
191+
}
161192

162193
sigset_t sset;
163194
sigemptyset(&sset);
@@ -187,7 +218,7 @@ rust_run_program(const char* argv[],
187218
}
188219

189220
extern "C" CDECL int
190-
rust_process_wait(int proc) {
221+
rust_process_wait(int pid) {
191222
// FIXME: stub; exists to placate linker. (#2692)
192223
return 0;
193224
}

0 commit comments

Comments
 (0)