Skip to content

Commit d1c7a93

Browse files
committed
Auto merge of #29913 - tbu-:pr_windows_path_error_on_nul, r=alexcrichton
On Windows: Previously these paths were silently truncated at these NUL characters, now they fail with `ErrorKind::InvalidInput`.
2 parents 79b7a9e + 71dccf8 commit d1c7a93

File tree

4 files changed

+85
-30
lines changed

4 files changed

+85
-30
lines changed

src/libstd/sys/windows/fs.rs

+20-22
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@ use sync::Arc;
2222
use sys::handle::Handle;
2323
use sys::{c, cvt};
2424
use sys_common::FromInner;
25-
use vec::Vec;
25+
26+
use super::to_u16s;
2627

2728
pub struct File { handle: Handle }
2829

@@ -226,7 +227,7 @@ impl File {
226227
}
227228

228229
pub fn open(path: &Path, opts: &OpenOptions) -> io::Result<File> {
229-
let path = to_utf16(path);
230+
let path = try!(to_u16s(path));
230231
let handle = unsafe {
231232
c::CreateFileW(path.as_ptr(),
232233
opts.get_desired_access(),
@@ -377,10 +378,6 @@ impl fmt::Debug for File {
377378
}
378379
}
379380

380-
pub fn to_utf16(s: &Path) -> Vec<u16> {
381-
s.as_os_str().encode_wide().chain(Some(0)).collect()
382-
}
383-
384381
impl FileAttr {
385382
pub fn size(&self) -> u64 {
386383
((self.data.nFileSizeHigh as u64) << 32) | (self.data.nFileSizeLow as u64)
@@ -449,7 +446,7 @@ impl DirBuilder {
449446
pub fn new() -> DirBuilder { DirBuilder }
450447

451448
pub fn mkdir(&self, p: &Path) -> io::Result<()> {
452-
let p = to_utf16(p);
449+
let p = try!(to_u16s(p));
453450
try!(cvt(unsafe {
454451
c::CreateDirectoryW(p.as_ptr(), ptr::null_mut())
455452
}));
@@ -460,7 +457,7 @@ impl DirBuilder {
460457
pub fn readdir(p: &Path) -> io::Result<ReadDir> {
461458
let root = p.to_path_buf();
462459
let star = p.join("*");
463-
let path = to_utf16(&star);
460+
let path = try!(to_u16s(&star));
464461

465462
unsafe {
466463
let mut wfd = mem::zeroed();
@@ -478,22 +475,22 @@ pub fn readdir(p: &Path) -> io::Result<ReadDir> {
478475
}
479476

480477
pub fn unlink(p: &Path) -> io::Result<()> {
481-
let p_utf16 = to_utf16(p);
482-
try!(cvt(unsafe { c::DeleteFileW(p_utf16.as_ptr()) }));
478+
let p_u16s = try!(to_u16s(p));
479+
try!(cvt(unsafe { c::DeleteFileW(p_u16s.as_ptr()) }));
483480
Ok(())
484481
}
485482

486483
pub fn rename(old: &Path, new: &Path) -> io::Result<()> {
487-
let old = to_utf16(old);
488-
let new = to_utf16(new);
484+
let old = try!(to_u16s(old));
485+
let new = try!(to_u16s(new));
489486
try!(cvt(unsafe {
490487
c::MoveFileExW(old.as_ptr(), new.as_ptr(), c::MOVEFILE_REPLACE_EXISTING)
491488
}));
492489
Ok(())
493490
}
494491

495492
pub fn rmdir(p: &Path) -> io::Result<()> {
496-
let p = to_utf16(p);
493+
let p = try!(to_u16s(p));
497494
try!(cvt(unsafe { c::RemoveDirectoryW(p.as_ptr()) }));
498495
Ok(())
499496
}
@@ -508,8 +505,8 @@ pub fn symlink(src: &Path, dst: &Path) -> io::Result<()> {
508505
}
509506

510507
pub fn symlink_inner(src: &Path, dst: &Path, dir: bool) -> io::Result<()> {
511-
let src = to_utf16(src);
512-
let dst = to_utf16(dst);
508+
let src = try!(to_u16s(src));
509+
let dst = try!(to_u16s(dst));
513510
let flags = if dir { c::SYMBOLIC_LINK_FLAG_DIRECTORY } else { 0 };
514511
try!(cvt(unsafe {
515512
c::CreateSymbolicLinkW(dst.as_ptr(), src.as_ptr(), flags) as c::BOOL
@@ -518,8 +515,8 @@ pub fn symlink_inner(src: &Path, dst: &Path, dir: bool) -> io::Result<()> {
518515
}
519516

520517
pub fn link(src: &Path, dst: &Path) -> io::Result<()> {
521-
let src = to_utf16(src);
522-
let dst = to_utf16(dst);
518+
let src = try!(to_u16s(src));
519+
let dst = try!(to_u16s(dst));
523520
try!(cvt(unsafe {
524521
c::CreateHardLinkW(dst.as_ptr(), src.as_ptr(), ptr::null_mut())
525522
}));
@@ -545,10 +542,10 @@ pub fn stat(p: &Path) -> io::Result<FileAttr> {
545542
}
546543

547544
pub fn lstat(p: &Path) -> io::Result<FileAttr> {
548-
let utf16 = to_utf16(p);
545+
let u16s = try!(to_u16s(p));
549546
unsafe {
550547
let mut attr: FileAttr = mem::zeroed();
551-
try!(cvt(c::GetFileAttributesExW(utf16.as_ptr(),
548+
try!(cvt(c::GetFileAttributesExW(u16s.as_ptr(),
552549
c::GetFileExInfoStandard,
553550
&mut attr.data as *mut _ as *mut _)));
554551
if attr.is_reparse_point() {
@@ -562,7 +559,7 @@ pub fn lstat(p: &Path) -> io::Result<FileAttr> {
562559
}
563560

564561
pub fn set_perm(p: &Path, perm: FilePermissions) -> io::Result<()> {
565-
let p = to_utf16(p);
562+
let p = try!(to_u16s(p));
566563
unsafe {
567564
try!(cvt(c::SetFileAttributesW(p.as_ptr(), perm.attrs)));
568565
Ok(())
@@ -602,8 +599,8 @@ pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
602599
*(lpData as *mut i64) = TotalBytesTransferred;
603600
c::PROGRESS_CONTINUE
604601
}
605-
let pfrom = to_utf16(from);
606-
let pto = to_utf16(to);
602+
let pfrom = try!(to_u16s(from));
603+
let pto = try!(to_u16s(to));
607604
let mut size = 0i64;
608605
try!(cvt(unsafe {
609606
c::CopyFileExW(pfrom.as_ptr(), pto.as_ptr(), Some(callback),
@@ -617,6 +614,7 @@ fn directory_junctions_are_directories() {
617614
use ffi::OsStr;
618615
use env;
619616
use rand::{self, StdRng, Rng};
617+
use vec::Vec;
620618

621619
macro_rules! t {
622620
($e:expr) => (match $e {

src/libstd/sys/windows/mod.rs

+11-4
Original file line numberDiff line numberDiff line change
@@ -72,10 +72,17 @@ pub fn decode_error_kind(errno: i32) -> ErrorKind {
7272
}
7373
}
7474

75-
fn to_utf16_os(s: &OsStr) -> Vec<u16> {
76-
let mut v: Vec<_> = s.encode_wide().collect();
77-
v.push(0);
78-
v
75+
pub fn to_u16s<S: AsRef<OsStr>>(s: S) -> io::Result<Vec<u16>> {
76+
fn inner(s: &OsStr) -> io::Result<Vec<u16>> {
77+
let mut maybe_result: Vec<u16> = s.encode_wide().collect();
78+
if maybe_result.iter().any(|&u| u == 0) {
79+
return Err(io::Error::new(io::ErrorKind::InvalidInput,
80+
"strings passed to WinAPI cannot contain NULs"));
81+
}
82+
maybe_result.push(0);
83+
Ok(maybe_result)
84+
}
85+
inner(s.as_ref())
7986
}
8087

8188
// Many Windows APIs follow a pattern of where we hand a buffer and then they

src/libstd/sys/windows/os.rs

+6-4
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ use slice;
2828
use sys::{c, cvt};
2929
use sys::handle::Handle;
3030

31+
use super::to_u16s;
32+
3133
pub fn errno() -> i32 {
3234
unsafe { c::GetLastError() as i32 }
3335
}
@@ -237,7 +239,7 @@ pub fn chdir(p: &path::Path) -> io::Result<()> {
237239
}
238240

239241
pub fn getenv(k: &OsStr) -> io::Result<Option<OsString>> {
240-
let k = super::to_utf16_os(k);
242+
let k = try!(to_u16s(k));
241243
let res = super::fill_utf16_buf(|buf, sz| unsafe {
242244
c::GetEnvironmentVariableW(k.as_ptr(), buf, sz)
243245
}, |buf| {
@@ -256,16 +258,16 @@ pub fn getenv(k: &OsStr) -> io::Result<Option<OsString>> {
256258
}
257259

258260
pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
259-
let k = super::to_utf16_os(k);
260-
let v = super::to_utf16_os(v);
261+
let k = try!(to_u16s(k));
262+
let v = try!(to_u16s(v));
261263

262264
cvt(unsafe {
263265
c::SetEnvironmentVariableW(k.as_ptr(), v.as_ptr())
264266
}).map(|_| ())
265267
}
266268

267269
pub fn unsetenv(n: &OsStr) -> io::Result<()> {
268-
let v = super::to_utf16_os(n);
270+
let v = try!(to_u16s(n));
269271
cvt(unsafe {
270272
c::SetEnvironmentVariableW(v.as_ptr(), ptr::null())
271273
}).map(|_| ())
+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
use std::fs;
12+
use std::io;
13+
14+
fn assert_invalid_input<T>(on: &str, result: io::Result<T>) {
15+
fn inner(on: &str, result: io::Result<()>) {
16+
match result {
17+
Ok(()) => panic!("{} didn't return an error on a path with NUL", on),
18+
Err(e) => assert!(e.kind() == io::ErrorKind::InvalidInput,
19+
"{} returned a strange {:?} on a path with NUL", on, e.kind()),
20+
}
21+
}
22+
inner(on, result.map(|_| ()))
23+
}
24+
25+
fn main() {
26+
assert_invalid_input("File::open", fs::File::open("\0"));
27+
assert_invalid_input("File::create", fs::File::create("\0"));
28+
assert_invalid_input("remove_file", fs::remove_file("\0"));
29+
assert_invalid_input("metadata", fs::metadata("\0"));
30+
assert_invalid_input("symlink_metadata", fs::symlink_metadata("\0"));
31+
assert_invalid_input("rename1", fs::rename("\0", "a"));
32+
assert_invalid_input("rename2", fs::rename("a", "\0"));
33+
assert_invalid_input("copy1", fs::copy("\0", "a"));
34+
assert_invalid_input("copy2", fs::copy("a", "\0"));
35+
assert_invalid_input("hard_link1", fs::hard_link("\0", "a"));
36+
assert_invalid_input("hard_link2", fs::hard_link("a", "\0"));
37+
assert_invalid_input("soft_link1", fs::soft_link("\0", "a"));
38+
assert_invalid_input("soft_link2", fs::soft_link("a", "\0"));
39+
assert_invalid_input("read_link", fs::read_link("\0"));
40+
assert_invalid_input("canonicalize", fs::canonicalize("\0"));
41+
assert_invalid_input("create_dir", fs::create_dir("\0"));
42+
assert_invalid_input("create_dir_all", fs::create_dir_all("\0"));
43+
assert_invalid_input("remove_dir", fs::remove_dir("\0"));
44+
assert_invalid_input("remove_dir_all", fs::remove_dir_all("\0"));
45+
assert_invalid_input("read_dir", fs::read_dir("\0"));
46+
assert_invalid_input("set_permissions",
47+
fs::set_permissions("\0", fs::metadata(".").unwrap().permissions()));
48+
}

0 commit comments

Comments
 (0)