Skip to content

Commit de0672f

Browse files
committed
Ensure at least one buffer for vectored I/O
POSIX requires at least one buffer passed to readv and writev, but we allow the user to pass an empty slice of buffers. In this case, return a zero-length read or write.
1 parent 82ebd46 commit de0672f

File tree

5 files changed

+86
-84
lines changed

5 files changed

+86
-84
lines changed

library/std/src/io/mod.rs

+54-36
Original file line numberDiff line numberDiff line change
@@ -1389,24 +1389,6 @@ impl<'a> IoSliceMut<'a> {
13891389
}
13901390
}
13911391

1392-
/// Limits a slice of buffers to at most `n` buffers.
1393-
///
1394-
/// When the slice contains over `n` buffers, ensure that at least one
1395-
/// non-empty buffer is in the truncated slice, if there is one.
1396-
#[inline]
1397-
pub(crate) fn limit_slices(bufs: &mut &mut [IoSliceMut<'a>], n: usize) {
1398-
if intrinsics::unlikely(bufs.len() > n) {
1399-
for (i, buf) in bufs.iter().enumerate() {
1400-
if !buf.is_empty() {
1401-
let len = cmp::min(bufs.len() - i, n);
1402-
*bufs = &mut take(bufs)[i..i + len];
1403-
return;
1404-
}
1405-
}
1406-
*bufs = &mut take(bufs)[..0];
1407-
}
1408-
}
1409-
14101392
/// Get the underlying bytes as a mutable slice with the original lifetime.
14111393
///
14121394
/// # Examples
@@ -1568,24 +1550,6 @@ impl<'a> IoSlice<'a> {
15681550
}
15691551
}
15701552

1571-
/// Limits a slice of buffers to at most `n` buffers.
1572-
///
1573-
/// When the slice contains over `n` buffers, ensure that at least one
1574-
/// non-empty buffer is in the truncated slice, if there is one.
1575-
#[inline]
1576-
pub(crate) fn limit_slices(bufs: &mut &[IoSlice<'a>], n: usize) {
1577-
if intrinsics::unlikely(bufs.len() > n) {
1578-
for (i, buf) in bufs.iter().enumerate() {
1579-
if !buf.is_empty() {
1580-
let len = cmp::min(bufs.len() - i, n);
1581-
*bufs = &bufs[i..i + len];
1582-
return;
1583-
}
1584-
}
1585-
*bufs = &bufs[..0];
1586-
}
1587-
}
1588-
15891553
/// Get the underlying bytes as a slice with the original lifetime.
15901554
///
15911555
/// This doesn't borrow from `self`, so is less restrictive than calling
@@ -1623,6 +1587,60 @@ impl<'a> Deref for IoSlice<'a> {
16231587
}
16241588
}
16251589

1590+
/// Limits a slice of buffers to at most `n` buffers and ensures that it has at
1591+
/// least one buffer, even if empty.
1592+
///
1593+
/// When the slice contains over `n` buffers, ensure that at least one non-empty
1594+
/// buffer is in the truncated slice, if there is one.
1595+
#[allow(unused)] // Not used on all platforms
1596+
pub(crate) macro limit_slices($bufs:expr, $n:expr) {
1597+
'slices: {
1598+
let bufs: &[IoSlice<'_>] = $bufs;
1599+
let n: usize = $n;
1600+
// if bufs.len() > n || bufs.is_empty()
1601+
if intrinsics::unlikely(bufs.len().wrapping_sub(1) > n - 1) {
1602+
for (i, buf) in bufs.iter().enumerate() {
1603+
if !buf.is_empty() {
1604+
let len = cmp::min(bufs.len() - i, n);
1605+
break 'slices &bufs[i..i + len];
1606+
}
1607+
}
1608+
// All buffers are empty. Since POSIX requires at least one buffer
1609+
// for [writev], but possibly bufs.is_empty(), return an empty write.
1610+
// [writev]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/writev.html
1611+
return Ok(0);
1612+
}
1613+
bufs
1614+
}
1615+
}
1616+
1617+
/// Limits a slice of buffers to at most `n` buffers and ensures that it has at
1618+
/// least one buffer, even if empty.
1619+
///
1620+
/// When the slice contains over `n` buffers, ensure that at least one non-empty
1621+
/// buffer is in the truncated slice, if there is one.
1622+
#[allow(unused)] // Not used on all platforms
1623+
pub(crate) macro limit_slices_mut($bufs:expr, $n:expr) {
1624+
'slices: {
1625+
let bufs: &mut [IoSliceMut<'_>] = $bufs;
1626+
let n: usize = $n;
1627+
// if bufs.len() > n || bufs.is_empty()
1628+
if intrinsics::unlikely(bufs.len().wrapping_sub(1) > n - 1) {
1629+
for (i, buf) in bufs.iter().enumerate() {
1630+
if !buf.is_empty() {
1631+
let len = cmp::min(bufs.len() - i, n);
1632+
break 'slices &mut bufs[i..i + len];
1633+
}
1634+
}
1635+
// All buffers are empty. Since POSIX requires at least one buffer
1636+
// for [readv], but possibly bufs.is_empty(), return an empty read.
1637+
// [readv]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/readv.html
1638+
return Ok(0);
1639+
}
1640+
bufs
1641+
}
1642+
}
1643+
16261644
/// A trait for objects which are byte-oriented sinks.
16271645
///
16281646
/// Implementors of the `Write` trait are sometimes called 'writers'.

library/std/src/sys/net/connection/socket/solid.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -222,8 +222,8 @@ impl Socket {
222222
self.recv_with_flags(buf, 0)
223223
}
224224

225-
pub fn read_vectored(&self, mut bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
226-
IoSliceMut::limit_slices(&mut bufs, max_iov());
225+
pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
226+
let bufs = io::limit_slices_mut!(bufs, max_iov());
227227
let ret = cvt(unsafe {
228228
netc::readv(self.as_raw_fd(), bufs.as_ptr() as *const netc::iovec, bufs.len() as c_int)
229229
})?;
@@ -264,8 +264,8 @@ impl Socket {
264264
self.recv_from_with_flags(buf, MSG_PEEK)
265265
}
266266

267-
pub fn write_vectored(&self, mut bufs: &[IoSlice<'_>]) -> io::Result<usize> {
268-
IoSlice::limit_slices(&mut bufs, max_iov());
267+
pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
268+
let bufs = io::limit_slices!(bufs, max_iov());
269269
let ret = cvt(unsafe {
270270
netc::writev(self.as_raw_fd(), bufs.as_ptr() as *const netc::iovec, bufs.len() as c_int)
271271
})?;

library/std/src/sys/net/connection/socket/windows.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -333,8 +333,8 @@ impl Socket {
333333
self.recv_with_flags(buf, 0)
334334
}
335335

336-
pub fn read_vectored(&self, mut bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
337-
IoSliceMut::limit_slices(&mut bufs, u32::MAX as usize);
336+
pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
337+
let bufs = io::limit_slices_mut!(bufs, u32::MAX as usize);
338338
let mut nread = 0;
339339
let mut flags = 0;
340340
let result = unsafe {
@@ -422,8 +422,8 @@ impl Socket {
422422
self.recv_from_with_flags(buf, c::MSG_PEEK)
423423
}
424424

425-
pub fn write_vectored(&self, mut bufs: &[IoSlice<'_>]) -> io::Result<usize> {
426-
IoSlice::limit_slices(&mut bufs, u32::MAX as usize);
425+
pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
426+
let bufs = io::limit_slices!(bufs, u32::MAX as usize);
427427
let mut nwritten = 0;
428428
let result = unsafe {
429429
c::WSASend(

library/std/src/sys/pal/hermit/fd.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,8 @@ impl FileDesc {
3737
Ok(())
3838
}
3939

40-
pub fn read_vectored(&self, mut bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
41-
IoSliceMut::limit_slices(&mut bufs, max_iov());
40+
pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
41+
let bufs = io::limit_slices_mut!(bufs, max_iov());
4242
let ret = cvt(unsafe {
4343
hermit_abi::readv(
4444
self.as_raw_fd(),
@@ -65,8 +65,8 @@ impl FileDesc {
6565
Ok(result as usize)
6666
}
6767

68-
pub fn write_vectored(&self, mut bufs: &[IoSlice<'_>]) -> io::Result<usize> {
69-
IoSlice::limit_slices(&mut bufs, max_iov());
68+
pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
69+
let bufs = io::limit_slices!(bufs, max_iov());
7070
let ret = cvt(unsafe {
7171
hermit_abi::writev(
7272
self.as_raw_fd(),

library/std/src/sys/pal/unix/fd.rs

+20-36
Original file line numberDiff line numberDiff line change
@@ -104,8 +104,8 @@ impl FileDesc {
104104
target_os = "vita",
105105
target_os = "nuttx"
106106
)))]
107-
pub fn read_vectored(&self, mut bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
108-
IoSliceMut::limit_slices(&mut bufs, max_iov());
107+
pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
108+
let bufs = io::limit_slices_mut!(bufs, max_iov());
109109
let ret = cvt(unsafe {
110110
libc::readv(
111111
self.as_raw_fd(),
@@ -195,12 +195,8 @@ impl FileDesc {
195195
target_os = "netbsd",
196196
target_os = "openbsd", // OpenBSD 2.7
197197
))]
198-
pub fn read_vectored_at(
199-
&self,
200-
mut bufs: &mut [IoSliceMut<'_>],
201-
offset: u64,
202-
) -> io::Result<usize> {
203-
IoSliceMut::limit_slices(&mut bufs, max_iov());
198+
pub fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result<usize> {
199+
let bufs = io::limit_slices_mut!(bufs, max_iov());
204200
let ret = cvt(unsafe {
205201
libc::preadv(
206202
self.as_raw_fd(),
@@ -237,11 +233,7 @@ impl FileDesc {
237233
// passing 64-bits parameters to syscalls, so we fallback to the default
238234
// implementation if `preadv` is not available.
239235
#[cfg(all(target_os = "android", target_pointer_width = "64"))]
240-
pub fn read_vectored_at(
241-
&self,
242-
mut bufs: &mut [IoSliceMut<'_>],
243-
offset: u64,
244-
) -> io::Result<usize> {
236+
pub fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result<usize> {
245237
super::weak::syscall!(
246238
fn preadv(
247239
fd: libc::c_int,
@@ -251,7 +243,7 @@ impl FileDesc {
251243
) -> isize;
252244
);
253245

254-
IoSliceMut::limit_slices(&mut bufs, max_iov());
246+
let bufs = io::limit_slices_mut!(bufs, max_iov());
255247
let ret = cvt(unsafe {
256248
preadv(
257249
self.as_raw_fd(),
@@ -267,11 +259,7 @@ impl FileDesc {
267259
// FIXME(#115199): Rust currently omits weak function definitions
268260
// and its metadata from LLVM IR.
269261
#[no_sanitize(cfi)]
270-
pub fn read_vectored_at(
271-
&self,
272-
mut bufs: &mut [IoSliceMut<'_>],
273-
offset: u64,
274-
) -> io::Result<usize> {
262+
pub fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result<usize> {
275263
super::weak::weak!(
276264
fn preadv64(
277265
fd: libc::c_int,
@@ -283,7 +271,7 @@ impl FileDesc {
283271

284272
match preadv64.get() {
285273
Some(preadv) => {
286-
IoSliceMut::limit_slices(&mut bufs, max_iov());
274+
let bufs = io::limit_slices_mut!(bufs, max_iov());
287275
let ret = cvt(unsafe {
288276
preadv(
289277
self.as_raw_fd(),
@@ -308,11 +296,7 @@ impl FileDesc {
308296
// These versions may be newer than the minimum supported versions of OS's we support so we must
309297
// use "weak" linking.
310298
#[cfg(target_vendor = "apple")]
311-
pub fn read_vectored_at(
312-
&self,
313-
mut bufs: &mut [IoSliceMut<'_>],
314-
offset: u64,
315-
) -> io::Result<usize> {
299+
pub fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result<usize> {
316300
super::weak::weak!(
317301
fn preadv(
318302
fd: libc::c_int,
@@ -324,7 +308,7 @@ impl FileDesc {
324308

325309
match preadv.get() {
326310
Some(preadv) => {
327-
IoSliceMut::limit_slices(&mut bufs, max_iov());
311+
let bufs = io::limit_slices_mut!(bufs, max_iov());
328312
let ret = cvt(unsafe {
329313
preadv(
330314
self.as_raw_fd(),
@@ -356,8 +340,8 @@ impl FileDesc {
356340
target_os = "vita",
357341
target_os = "nuttx"
358342
)))]
359-
pub fn write_vectored(&self, mut bufs: &[IoSlice<'_>]) -> io::Result<usize> {
360-
IoSlice::limit_slices(&mut bufs, max_iov());
343+
pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
344+
let bufs = io::limit_slices!(bufs, max_iov());
361345
let ret = cvt(unsafe {
362346
libc::writev(
363347
self.as_raw_fd(),
@@ -426,8 +410,8 @@ impl FileDesc {
426410
target_os = "netbsd",
427411
target_os = "openbsd", // OpenBSD 2.7
428412
))]
429-
pub fn write_vectored_at(&self, mut bufs: &[IoSlice<'_>], offset: u64) -> io::Result<usize> {
430-
IoSlice::limit_slices(&mut bufs, max_iov());
413+
pub fn write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result<usize> {
414+
let bufs = io::limit_slices!(bufs, max_iov());
431415
let ret = cvt(unsafe {
432416
libc::pwritev(
433417
self.as_raw_fd(),
@@ -464,7 +448,7 @@ impl FileDesc {
464448
// passing 64-bits parameters to syscalls, so we fallback to the default
465449
// implementation if `pwritev` is not available.
466450
#[cfg(all(target_os = "android", target_pointer_width = "64"))]
467-
pub fn write_vectored_at(&self, mut bufs: &[IoSlice<'_>], offset: u64) -> io::Result<usize> {
451+
pub fn write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result<usize> {
468452
super::weak::syscall!(
469453
fn pwritev(
470454
fd: libc::c_int,
@@ -474,7 +458,7 @@ impl FileDesc {
474458
) -> isize;
475459
);
476460

477-
IoSlice::limit_slices(&mut bufs, max_iov());
461+
let bufs = io::limit_slices!(bufs, max_iov());
478462
let ret = cvt(unsafe {
479463
pwritev(
480464
self.as_raw_fd(),
@@ -487,7 +471,7 @@ impl FileDesc {
487471
}
488472

489473
#[cfg(all(target_os = "android", target_pointer_width = "32"))]
490-
pub fn write_vectored_at(&self, mut bufs: &[IoSlice<'_>], offset: u64) -> io::Result<usize> {
474+
pub fn write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result<usize> {
491475
super::weak::weak!(
492476
fn pwritev64(
493477
fd: libc::c_int,
@@ -499,7 +483,7 @@ impl FileDesc {
499483

500484
match pwritev64.get() {
501485
Some(pwritev) => {
502-
IoSlice::limit_slices(&mut bufs, max_iov());
486+
let bufs = io::limit_slices!(bufs, max_iov());
503487
let ret = cvt(unsafe {
504488
pwritev(
505489
self.as_raw_fd(),
@@ -524,7 +508,7 @@ impl FileDesc {
524508
// These versions may be newer than the minimum supported versions of OS's we support so we must
525509
// use "weak" linking.
526510
#[cfg(target_vendor = "apple")]
527-
pub fn write_vectored_at(&self, mut bufs: &[IoSlice<'_>], offset: u64) -> io::Result<usize> {
511+
pub fn write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result<usize> {
528512
super::weak::weak!(
529513
fn pwritev(
530514
fd: libc::c_int,
@@ -536,7 +520,7 @@ impl FileDesc {
536520

537521
match pwritev.get() {
538522
Some(pwritev) => {
539-
IoSlice::limit_slices(&mut bufs, max_iov());
523+
let bufs = io::limit_slices!(bufs, max_iov());
540524
let ret = cvt(unsafe {
541525
pwritev(
542526
self.as_raw_fd(),

0 commit comments

Comments
 (0)