@@ -18,7 +18,7 @@ use env;
1818use ffi:: { OsString , OsStr } ;
1919use fmt;
2020use fs;
21- use io:: { self , Error } ;
21+ use io:: { self , Error , ErrorKind } ;
2222use libc:: c_void;
2323use mem;
2424use os:: windows:: ffi:: OsStrExt ;
@@ -43,13 +43,21 @@ fn mk_key(s: &OsStr) -> OsString {
4343 } )
4444}
4545
46+ fn ensure_no_nuls < T : AsRef < OsStr > > ( str : T ) -> io:: Result < T > {
47+ if str. as_ref ( ) . encode_wide ( ) . any ( |b| b == 0 ) {
48+ Err ( io:: Error :: new ( ErrorKind :: InvalidInput , "nul byte found in provided data" ) )
49+ } else {
50+ Ok ( str)
51+ }
52+ }
53+
4654#[ derive( Clone ) ]
4755pub struct Command {
48- pub program : OsString ,
49- pub args : Vec < OsString > ,
50- pub env : Option < HashMap < OsString , OsString > > ,
51- pub cwd : Option < OsString > ,
52- pub detach : bool , // not currently exposed in std::process
56+ program : OsString ,
57+ args : Vec < OsString > ,
58+ env : Option < HashMap < OsString , OsString > > ,
59+ cwd : Option < OsString > ,
60+ detach : bool , // not currently exposed in std::process
5361}
5462
5563impl Command {
@@ -92,6 +100,16 @@ impl Command {
92100 }
93101}
94102
103+ impl fmt:: Debug for Command {
104+ fn fmt ( & self , f : & mut fmt:: Formatter ) -> fmt:: Result {
105+ try!( write ! ( f, "{:?}" , self . program) ) ;
106+ for arg in & self . args {
107+ try!( write ! ( f, " {:?}" , arg) ) ;
108+ }
109+ Ok ( ( ) )
110+ }
111+ }
112+
95113////////////////////////////////////////////////////////////////////////////////
96114// Processes
97115////////////////////////////////////////////////////////////////////////////////
@@ -153,7 +171,7 @@ impl Process {
153171 si. hStdError = stderr. raw ( ) ;
154172
155173 let program = program. as_ref ( ) . unwrap_or ( & cfg. program ) ;
156- let mut cmd_str = make_command_line ( program, & cfg. args ) ;
174+ let mut cmd_str = try! ( make_command_line ( program, & cfg. args ) ) ;
157175 cmd_str. push ( 0 ) ; // add null terminator
158176
159177 // stolen from the libuv code.
@@ -162,8 +180,8 @@ impl Process {
162180 flags |= c:: DETACHED_PROCESS | c:: CREATE_NEW_PROCESS_GROUP ;
163181 }
164182
165- let ( envp, _data) = make_envp ( cfg. env . as_ref ( ) ) ;
166- let ( dirp, _data) = make_dirp ( cfg. cwd . as_ref ( ) ) ;
183+ let ( envp, _data) = try! ( make_envp ( cfg. env . as_ref ( ) ) ) ;
184+ let ( dirp, _data) = try! ( make_dirp ( cfg. cwd . as_ref ( ) ) ) ;
167185 let mut pi = zeroed_process_information ( ) ;
168186 try!( unsafe {
169187 // `CreateProcess` is racy!
@@ -265,22 +283,24 @@ fn zeroed_process_information() -> c::PROCESS_INFORMATION {
265283 }
266284}
267285
268- // Produces a wide string *without terminating null*
269- fn make_command_line ( prog : & OsStr , args : & [ OsString ] ) -> Vec < u16 > {
286+ // Produces a wide string *without terminating null*; returns an error if
287+ // `prog` or any of the `args` contain a nul.
288+ fn make_command_line ( prog : & OsStr , args : & [ OsString ] ) -> io:: Result < Vec < u16 > > {
270289 // Encode the command and arguments in a command line string such
271290 // that the spawned process may recover them using CommandLineToArgvW.
272291 let mut cmd: Vec < u16 > = Vec :: new ( ) ;
273- append_arg ( & mut cmd, prog) ;
292+ try! ( append_arg ( & mut cmd, prog) ) ;
274293 for arg in args {
275294 cmd. push ( ' ' as u16 ) ;
276- append_arg ( & mut cmd, arg) ;
295+ try! ( append_arg ( & mut cmd, arg) ) ;
277296 }
278- return cmd;
297+ return Ok ( cmd) ;
279298
280- fn append_arg ( cmd : & mut Vec < u16 > , arg : & OsStr ) {
299+ fn append_arg ( cmd : & mut Vec < u16 > , arg : & OsStr ) -> io :: Result < ( ) > {
281300 // If an argument has 0 characters then we need to quote it to ensure
282301 // that it actually gets passed through on the command line or otherwise
283302 // it will be dropped entirely when parsed on the other end.
303+ try!( ensure_no_nuls ( arg) ) ;
284304 let arg_bytes = & arg. as_inner ( ) . inner . as_inner ( ) ;
285305 let quote = arg_bytes. iter ( ) . any ( |c| * c == b' ' || * c == b'\t' )
286306 || arg_bytes. is_empty ( ) ;
@@ -312,11 +332,12 @@ fn make_command_line(prog: &OsStr, args: &[OsString]) -> Vec<u16> {
312332 }
313333 cmd. push ( '"' as u16 ) ;
314334 }
335+ Ok ( ( ) )
315336 }
316337}
317338
318339fn make_envp ( env : Option < & collections:: HashMap < OsString , OsString > > )
319- -> ( * mut c_void , Vec < u16 > ) {
340+ -> io :: Result < ( * mut c_void , Vec < u16 > ) > {
320341 // On Windows we pass an "environment block" which is not a char**, but
321342 // rather a concatenation of null-terminated k=v\0 sequences, with a final
322343 // \0 to terminate.
@@ -325,26 +346,27 @@ fn make_envp(env: Option<&collections::HashMap<OsString, OsString>>)
325346 let mut blk = Vec :: new ( ) ;
326347
327348 for pair in env {
328- blk. extend ( pair. 0 . encode_wide ( ) ) ;
349+ blk. extend ( try! ( ensure_no_nuls ( pair. 0 ) ) . encode_wide ( ) ) ;
329350 blk. push ( '=' as u16 ) ;
330- blk. extend ( pair. 1 . encode_wide ( ) ) ;
351+ blk. extend ( try! ( ensure_no_nuls ( pair. 1 ) ) . encode_wide ( ) ) ;
331352 blk. push ( 0 ) ;
332353 }
333354 blk. push ( 0 ) ;
334- ( blk. as_mut_ptr ( ) as * mut c_void , blk)
355+ Ok ( ( blk. as_mut_ptr ( ) as * mut c_void , blk) )
335356 }
336- _ => ( ptr:: null_mut ( ) , Vec :: new ( ) )
357+ _ => Ok ( ( ptr:: null_mut ( ) , Vec :: new ( ) ) )
337358 }
338359}
339360
340- fn make_dirp ( d : Option < & OsString > ) -> ( * const u16 , Vec < u16 > ) {
361+ fn make_dirp ( d : Option < & OsString > ) -> io:: Result < ( * const u16 , Vec < u16 > ) > {
362+
341363 match d {
342364 Some ( dir) => {
343- let mut dir_str: Vec < u16 > = dir. encode_wide ( ) . collect ( ) ;
365+ let mut dir_str: Vec < u16 > = try! ( ensure_no_nuls ( dir) ) . encode_wide ( ) . collect ( ) ;
344366 dir_str. push ( 0 ) ;
345- ( dir_str. as_ptr ( ) , dir_str)
367+ Ok ( ( dir_str. as_ptr ( ) , dir_str) )
346368 } ,
347- None => ( ptr:: null ( ) , Vec :: new ( ) )
369+ None => Ok ( ( ptr:: null ( ) , Vec :: new ( ) ) )
348370 }
349371}
350372
@@ -397,11 +419,12 @@ mod tests {
397419 #[ test]
398420 fn test_make_command_line ( ) {
399421 fn test_wrapper ( prog : & str , args : & [ & str ] ) -> String {
400- String :: from_utf16 (
401- & make_command_line ( OsStr :: new ( prog) ,
402- & args. iter ( )
403- . map ( |a| OsString :: from ( a) )
404- . collect :: < Vec < OsString > > ( ) ) ) . unwrap ( )
422+ let command_line = & make_command_line ( OsStr :: new ( prog) ,
423+ & args. iter ( )
424+ . map ( |a| OsString :: from ( a) )
425+ . collect :: < Vec < OsString > > ( ) )
426+ . unwrap ( ) ;
427+ String :: from_utf16 ( command_line) . unwrap ( )
405428 }
406429
407430 assert_eq ! (
0 commit comments