Skip to content

Commit 82db31d

Browse files
committed
windows: More defensive receive buffer handling
Extend the buffer's exposed length to cover its entire allocated capacity before using it in receive calls, so we can use safe slice operations rather than manual pointer arithmetic for determining addresses and lengths to be passed to the system calls. To keep the effects localised, we reset the length to the actually filled part again after the system calls, rather than keeping it at the full allocated size permanently. I'm not entirely sure yet whether to consider that more or less defensive than the other option -- but at least I'm confident that it's more robust than the original approach.
1 parent 350a9b2 commit 82db31d

File tree

1 file changed

+48
-21
lines changed

1 file changed

+48
-21
lines changed

src/platform/windows/mod.rs

Lines changed: 48 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -390,25 +390,43 @@ impl MessageReader {
390390

391391
win32_trace!("[$ {:?}] start_read", self.handle);
392392

393+
if self.read_buf.len() == self.read_buf.capacity() {
394+
self.read_buf.reserve(PIPE_BUFFER_SIZE);
395+
}
396+
393397
unsafe {
398+
// Temporarily extend the vector to span its entire capacity,
399+
// so we can safely sub-slice it for the actual read.
394400
let buf_len = self.read_buf.len();
395-
let mut buf_cap = self.read_buf.capacity();
396-
let mut bytes_read: u32 = 0;
397-
398-
if buf_len == buf_cap {
399-
self.read_buf.reserve(PIPE_BUFFER_SIZE);
400-
buf_cap = self.read_buf.capacity();
401-
}
401+
let buf_cap = self.read_buf.capacity();
402+
self.read_buf.set_len(buf_cap);
402403

403404
// issue the read to the buffer, at the current length offset
404405
*self.ov.deref_mut() = mem::zeroed();
405-
let buf_ptr = self.read_buf.as_mut_ptr() as LPVOID;
406-
let max_read_bytes = buf_cap - buf_len;
407-
let ok = kernel32::ReadFile(*self.handle,
408-
buf_ptr.offset(buf_len as isize),
409-
max_read_bytes as u32,
410-
&mut bytes_read,
411-
self.ov.deref_mut());
406+
let mut bytes_read: u32 = 0;
407+
let ok = {
408+
let remaining_buf = &mut self.read_buf[buf_len..];
409+
kernel32::ReadFile(*self.handle,
410+
remaining_buf.as_mut_ptr() as LPVOID,
411+
remaining_buf.len() as u32,
412+
&mut bytes_read,
413+
self.ov.deref_mut())
414+
}
415+
416+
// Reset the vector to only expose the already filled part.
417+
//
418+
// This means that the async read
419+
// will actually fill memory beyond the exposed part of the vector.
420+
// While this use of a vector is officially sanctioned for such cases,
421+
// it still feel rather icky to me...
422+
//
423+
// On the other hand, this way we make sure
424+
// the buffer never appears to have more valid data
425+
// than what is actually present,
426+
// which could pose a potential danger in its own right.
427+
// Also, it avoids the need to keep a separate state variable --
428+
// which would bear some risk of getting out of sync.
429+
self.read_buf.set_len(buf_len);
412430

413431
// ReadFile can return TRUE; if it does, an IO completion
414432
// packet is still posted to any port, and the OVERLAPPED
@@ -583,17 +601,26 @@ impl MessageReader {
583601
let ov = self.ov.deref_mut();
584602
*ov = mem::zeroed();
585603

604+
// Temporarily extend the vector to span its entire capacity,
605+
// so we can safely sub-slice it for the actual read.
586606
let buf_len = buf.len();
587-
let dest_ptr = buf.as_mut_ptr().offset(buf_len as isize) as LPVOID;
607+
let buf_cap = buf.capacity();
608+
buf.set_len(buf_cap);
588609

589-
let bytes_left = (size - buf_len) as u32;
590610
let mut bytes_read: u32 = 0;
611+
let ok = {
612+
let remaining_buf = &mut buf[buf_len..];
613+
kernel32::ReadFile(*self.handle,
614+
remaining_buf.as_mut_ptr() as LPVOID,
615+
remaining_buf.len() as u32,
616+
&mut bytes_read,
617+
ov)
618+
}
619+
620+
// Restore the original size before error handling,
621+
// so we never leave the function with the buffer exposing uninitialized data.
622+
buf.set_len(buf_len);
591623

592-
let ok = kernel32::ReadFile(*self.handle,
593-
dest_ptr,
594-
bytes_left,
595-
&mut bytes_read,
596-
ov);
597624
if ok == winapi::FALSE && GetLastError() != winapi::ERROR_IO_PENDING {
598625
return Err(WinError::last("ReadFile"));
599626
}

0 commit comments

Comments
 (0)