Skip to content

Commit

Permalink
Auto merge of #38622 - alexcrichton:read-lengths, r=sfackler
Browse files Browse the repository at this point in the history
std: Clamp max read/write sizes on Unix

Turns out that even though all these functions take a `size_t` they don't
actually work that well with anything larger than the maximum value of
`ssize_t`, the return value. Furthermore it looks like OSX rejects any
read/write requests larger than `INT_MAX - 1`. Handle all these cases by just
clamping the maximum size of a read/write on Unix to a platform-specific value.

Closes #38590
  • Loading branch information
bors committed Dec 29, 2016
2 parents e7c788a + 917a9af commit b1efc75
Show file tree
Hide file tree
Showing 2 changed files with 24 additions and 12 deletions.
27 changes: 22 additions & 5 deletions src/libstd/sys/unix/fd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@

#![unstable(reason = "not public", issue = "0", feature = "fd")]

use cmp;
use io::{self, Read};
use libc::{self, c_int, c_void};
use libc::{self, c_int, c_void, ssize_t};
use mem;
use sync::atomic::{AtomicBool, Ordering};
use sys::cvt;
Expand All @@ -23,6 +24,22 @@ pub struct FileDesc {
fd: c_int,
}

fn max_len() -> usize {
// The maximum read limit on most posix-like systems is `SSIZE_MAX`,
// with the man page quoting that if the count of bytes to read is
// greater than `SSIZE_MAX` the result is "unspecified".
//
// On OSX, however, apparently the 64-bit libc is either buggy or
// intentionally showing odd behavior by rejecting any read with a size
// larger than or equal to INT_MAX. To handle both of these the read
// size is capped on both platforms.
if cfg!(target_os = "macos") {
<c_int>::max_value() as usize - 1
} else {
<ssize_t>::max_value() as usize
}
}

impl FileDesc {
pub fn new(fd: c_int) -> FileDesc {
FileDesc { fd: fd }
Expand All @@ -41,7 +58,7 @@ impl FileDesc {
let ret = cvt(unsafe {
libc::read(self.fd,
buf.as_mut_ptr() as *mut c_void,
buf.len())
cmp::min(buf.len(), max_len()))
})?;
Ok(ret as usize)
}
Expand Down Expand Up @@ -69,7 +86,7 @@ impl FileDesc {
unsafe {
cvt_pread64(self.fd,
buf.as_mut_ptr() as *mut c_void,
buf.len(),
cmp::min(buf.len(), max_len()),
offset as i64)
.map(|n| n as usize)
}
Expand All @@ -79,7 +96,7 @@ impl FileDesc {
let ret = cvt(unsafe {
libc::write(self.fd,
buf.as_ptr() as *const c_void,
buf.len())
cmp::min(buf.len(), max_len()))
})?;
Ok(ret as usize)
}
Expand All @@ -102,7 +119,7 @@ impl FileDesc {
unsafe {
cvt_pwrite64(self.fd,
buf.as_ptr() as *const c_void,
buf.len(),
cmp::min(buf.len(), max_len()),
offset as i64)
.map(|n| n as usize)
}
Expand Down
9 changes: 2 additions & 7 deletions src/libstd/sys/windows/handle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ use ptr;
use sys::c;
use sys::cvt;
use sys_common::io::read_to_end_uninitialized;
use u32;

/// An owned container for `HANDLE` object, closing them on Drop.
///
Expand Down Expand Up @@ -83,9 +82,7 @@ impl RawHandle {

pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
let mut read = 0;
// ReadFile takes a DWORD (u32) for the length so it only supports
// reading u32::MAX bytes at a time.
let len = cmp::min(buf.len(), u32::MAX as usize) as c::DWORD;
let len = cmp::min(buf.len(), <c::DWORD>::max_value() as usize) as c::DWORD;
let res = cvt(unsafe {
c::ReadFile(self.0, buf.as_mut_ptr() as c::LPVOID,
len, &mut read, ptr::null_mut())
Expand Down Expand Up @@ -181,9 +178,7 @@ impl RawHandle {

pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
let mut amt = 0;
// WriteFile takes a DWORD (u32) for the length so it only supports
// writing u32::MAX bytes at a time.
let len = cmp::min(buf.len(), u32::MAX as usize) as c::DWORD;
let len = cmp::min(buf.len(), <c::DWORD>::max_value() as usize) as c::DWORD;
cvt(unsafe {
c::WriteFile(self.0, buf.as_ptr() as c::LPVOID,
len, &mut amt, ptr::null_mut())
Expand Down

0 comments on commit b1efc75

Please sign in to comment.