Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 60ddaa6

Browse files
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 ab97e9c commit 60ddaa6

File tree

6 files changed

+110
-89
lines changed

6 files changed

+110
-89
lines changed

‎library/std/src/io/mod.rs

Lines changed: 54 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -297,7 +297,6 @@
297297
#[cfg(test)]
298298
mod tests;
299299

300-
use core::intrinsics;
301300
#[unstable(feature = "read_buf", issue = "78485")]
302301
pub use core::io::{BorrowedBuf, BorrowedCursor};
303302
use core::slice::memchr;
@@ -1389,25 +1388,6 @@ impl<'a> IoSliceMut<'a> {
13891388
}
13901389
}
13911390

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-
#[allow(dead_code)] // Not used on all platforms
1397-
#[inline]
1398-
pub(crate) fn limit_slices(bufs: &mut &mut [IoSliceMut<'a>], n: usize) {
1399-
if intrinsics::unlikely(bufs.len() > n) {
1400-
for (i, buf) in bufs.iter().enumerate() {
1401-
if !buf.is_empty() {
1402-
let len = cmp::min(bufs.len() - i, n);
1403-
*bufs = &mut take(bufs)[i..i + len];
1404-
return;
1405-
}
1406-
}
1407-
*bufs = &mut take(bufs)[..0];
1408-
}
1409-
}
1410-
14111391
/// Get the underlying bytes as a mutable slice with the original lifetime.
14121392
///
14131393
/// # Examples
@@ -1569,25 +1549,6 @@ impl<'a> IoSlice<'a> {
15691549
}
15701550
}
15711551

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

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

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,8 @@ impl FileDesc {
3737
Ok(())
3838
}
3939

40-
pub fn read_vectored(&self, mutbufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
41-
IoSliceMut::limit_slices(&mutbufs, 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, mutbufs: &[IoSlice<'_>]) -> io::Result<usize> {
69-
IoSlice::limit_slices(&mutbufs, 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/fd/unix.rs

Lines changed: 20 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -108,8 +108,8 @@ impl FileDesc {
108108
target_os = "vita",
109109
target_os = "nuttx"
110110
)))]
111-
pub fn read_vectored(&self, mutbufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
112-
IoSliceMut::limit_slices(&mutbufs, max_iov());
111+
pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
112+
let bufs = io::limit_slices_mut!(bufs, max_iov());
113113
let ret = cvt(unsafe {
114114
libc::readv(
115115
self.as_raw_fd(),
@@ -199,12 +199,8 @@ impl FileDesc {
199199
target_os = "netbsd",
200200
target_os = "openbsd", // OpenBSD 2.7
201201
))]
202-
pub fn read_vectored_at(
203-
&self,
204-
mut bufs: &mut [IoSliceMut<'_>],
205-
offset: u64,
206-
) -> io::Result<usize> {
207-
IoSliceMut::limit_slices(&mut bufs, max_iov());
202+
pub fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result<usize> {
203+
let bufs = io::limit_slices_mut!(bufs, max_iov());
208204
let ret = cvt(unsafe {
209205
libc::preadv(
210206
self.as_raw_fd(),
@@ -241,11 +237,7 @@ impl FileDesc {
241237
// passing 64-bits parameters to syscalls, so we fallback to the default
242238
// implementation if `preadv` is not available.
243239
#[cfg(all(target_os = "android", target_pointer_width = "64"))]
244-
pub fn read_vectored_at(
245-
&self,
246-
mut bufs: &mut [IoSliceMut<'_>],
247-
offset: u64,
248-
) -> io::Result<usize> {
240+
pub fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result<usize> {
249241
syscall!(
250242
fn preadv(
251243
fd: libc::c_int,
@@ -255,7 +247,7 @@ impl FileDesc {
255247
) -> isize;
256248
);
257249

258-
IoSliceMut::limit_slices(&mutbufs, max_iov());
250+
let bufs = io::limit_slices_mut!(bufs, max_iov());
259251
let ret = cvt(unsafe {
260252
preadv(
261253
self.as_raw_fd(),
@@ -271,11 +263,7 @@ impl FileDesc {
271263
// FIXME(#115199): Rust currently omits weak function definitions
272264
// and its metadata from LLVM IR.
273265
#[no_sanitize(cfi)]
274-
pub fn read_vectored_at(
275-
&self,
276-
mut bufs: &mut [IoSliceMut<'_>],
277-
offset: u64,
278-
) -> io::Result<usize> {
266+
pub fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result<usize> {
279267
weak!(
280268
fn preadv64(
281269
fd: libc::c_int,
@@ -287,7 +275,7 @@ impl FileDesc {
287275

288276
match preadv64.get() {
289277
Some(preadv) => {
290-
IoSliceMut::limit_slices(&mutbufs, max_iov());
278+
let bufs = io::limit_slices_mut!(bufs, max_iov());
291279
let ret = cvt(unsafe {
292280
preadv(
293281
self.as_raw_fd(),
@@ -312,11 +300,7 @@ impl FileDesc {
312300
// These versions may be newer than the minimum supported versions of OS's we support so we must
313301
// use "weak" linking.
314302
#[cfg(target_vendor = "apple")]
315-
pub fn read_vectored_at(
316-
&self,
317-
mut bufs: &mut [IoSliceMut<'_>],
318-
offset: u64,
319-
) -> io::Result<usize> {
303+
pub fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result<usize> {
320304
weak!(
321305
fn preadv(
322306
fd: libc::c_int,
@@ -328,7 +312,7 @@ impl FileDesc {
328312

329313
match preadv.get() {
330314
Some(preadv) => {
331-
IoSliceMut::limit_slices(&mutbufs, max_iov());
315+
let bufs = io::limit_slices_mut!(bufs, max_iov());
332316
let ret = cvt(unsafe {
333317
preadv(
334318
self.as_raw_fd(),
@@ -360,8 +344,8 @@ impl FileDesc {
360344
target_os = "vita",
361345
target_os = "nuttx"
362346
)))]
363-
pub fn write_vectored(&self, mutbufs: &[IoSlice<'_>]) -> io::Result<usize> {
364-
IoSlice::limit_slices(&mutbufs, max_iov());
347+
pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
348+
let bufs = io::limit_slices!(bufs, max_iov());
365349
let ret = cvt(unsafe {
366350
libc::writev(
367351
self.as_raw_fd(),
@@ -430,8 +414,8 @@ impl FileDesc {
430414
target_os = "netbsd",
431415
target_os = "openbsd", // OpenBSD 2.7
432416
))]
433-
pub fn write_vectored_at(&self, mutbufs: &[IoSlice<'_>], offset: u64) -> io::Result<usize> {
434-
IoSlice::limit_slices(&mutbufs, max_iov());
417+
pub fn write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result<usize> {
418+
let bufs = io::limit_slices!(bufs, max_iov());
435419
let ret = cvt(unsafe {
436420
libc::pwritev(
437421
self.as_raw_fd(),
@@ -468,7 +452,7 @@ impl FileDesc {
468452
// passing 64-bits parameters to syscalls, so we fallback to the default
469453
// implementation if `pwritev` is not available.
470454
#[cfg(all(target_os = "android", target_pointer_width = "64"))]
471-
pub fn write_vectored_at(&self, mutbufs: &[IoSlice<'_>], offset: u64) -> io::Result<usize> {
455+
pub fn write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result<usize> {
472456
syscall!(
473457
fn pwritev(
474458
fd: libc::c_int,
@@ -478,7 +462,7 @@ impl FileDesc {
478462
) -> isize;
479463
);
480464

481-
IoSlice::limit_slices(&mutbufs, max_iov());
465+
let bufs = io::limit_slices!(bufs, max_iov());
482466
let ret = cvt(unsafe {
483467
pwritev(
484468
self.as_raw_fd(),
@@ -491,7 +475,7 @@ impl FileDesc {
491475
}
492476

493477
#[cfg(all(target_os = "android", target_pointer_width = "32"))]
494-
pub fn write_vectored_at(&self, mutbufs: &[IoSlice<'_>], offset: u64) -> io::Result<usize> {
478+
pub fn write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result<usize> {
495479
weak!(
496480
fn pwritev64(
497481
fd: libc::c_int,
@@ -503,7 +487,7 @@ impl FileDesc {
503487

504488
match pwritev64.get() {
505489
Some(pwritev) => {
506-
IoSlice::limit_slices(&mutbufs, max_iov());
490+
let bufs = io::limit_slices!(bufs, max_iov());
507491
let ret = cvt(unsafe {
508492
pwritev(
509493
self.as_raw_fd(),
@@ -528,7 +512,7 @@ impl FileDesc {
528512
// These versions may be newer than the minimum supported versions of OS's we support so we must
529513
// use "weak" linking.
530514
#[cfg(target_vendor = "apple")]
531-
pub fn write_vectored_at(&self, mutbufs: &[IoSlice<'_>], offset: u64) -> io::Result<usize> {
515+
pub fn write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result<usize> {
532516
weak!(
533517
fn pwritev(
534518
fd: libc::c_int,
@@ -540,7 +524,7 @@ impl FileDesc {
540524

541525
match pwritev.get() {
542526
Some(pwritev) => {
543-
IoSlice::limit_slices(&mutbufs, max_iov());
527+
let bufs = io::limit_slices!(bufs, max_iov());
544528
let ret = cvt(unsafe {
545529
pwritev(
546530
self.as_raw_fd(),

‎library/std/src/sys/fd/unix/tests.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,12 @@ fn limit_vector_count() {
2121
bufs[IOV_MAX * 2] = IoSlice::new(b"world!");
2222
assert_eq!(stdout.write_vectored(&bufs).unwrap(), b"hello".len())
2323
}
24+
25+
#[test]
26+
fn empty_vector() {
27+
let stdin = ManuallyDrop::new(unsafe { FileDesc::from_raw_fd(0) });
28+
assert_eq!(stdin.read_vectored(&mut []).unwrap(), 0);
29+
30+
let stdout = ManuallyDrop::new(unsafe { FileDesc::from_raw_fd(1) });
31+
assert_eq!(stdout.write_vectored(&[]).unwrap(), 0);
32+
}

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

Lines changed: 4 additions & 4 deletions
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, mutbufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
226-
IoSliceMut::limit_slices(&mutbufs, 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, mutbufs: &[IoSlice<'_>]) -> io::Result<usize> {
268-
IoSlice::limit_slices(&mutbufs, 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
})?;

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /