Skip to content

Commit

Permalink
Fix unsoundness in FdSet methods
Browse files Browse the repository at this point in the history
Ensure file descriptors are nonnegative and less than `FD_SETSIZE`.
(Fixes #1572.)
  • Loading branch information
taylordotfish committed Oct 22, 2021
1 parent 103b29f commit dc80b4c
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 0 deletions.
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,19 @@
All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](https://semver.org/).

## [Unreleased] - ReleaseDate

### Added
### Changed
### Fixed

- Fixed soundness issues in `FdSet::insert`, `FdSet::remove`, and
`FdSet::contains` involving file descriptors outside of the range
`0..FD_SETSIZE`.
(#[1575](https://github.com/nix-rust/nix/pull/1575))

### Removed

## [0.23.0] - 2021-09-28
### Added

Expand Down
11 changes: 11 additions & 0 deletions src/sys/select.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
//! Portably monitor a group of file descriptors for readiness.
use std::convert::TryFrom;
use std::iter::FusedIterator;
use std::mem;
use std::ops::Range;
Expand All @@ -17,6 +18,13 @@ pub use libc::FD_SETSIZE;
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct FdSet(libc::fd_set);

fn assert_fd_valid(fd: RawFd) {
assert!(
usize::try_from(fd).map_or(false, |fd| fd < FD_SETSIZE),
"fd must be in the range 0..FD_SETSIZE",
);
}

impl FdSet {
/// Create an empty `FdSet`
pub fn new() -> FdSet {
Expand All @@ -29,16 +37,19 @@ impl FdSet {

/// Add a file descriptor to an `FdSet`
pub fn insert(&mut self, fd: RawFd) {
assert_fd_valid(fd);
unsafe { libc::FD_SET(fd, &mut self.0) };
}

/// Remove a file descriptor from an `FdSet`
pub fn remove(&mut self, fd: RawFd) {
assert_fd_valid(fd);
unsafe { libc::FD_CLR(fd, &mut self.0) };
}

/// Test an `FdSet` for the presence of a certain file descriptor.
pub fn contains(&self, fd: RawFd) -> bool {
assert_fd_valid(fd);
unsafe { libc::FD_ISSET(fd, &self.0) }
}

Expand Down
28 changes: 28 additions & 0 deletions test/sys/test_select.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,31 @@ pub fn test_pselect_nfds2() {
assert!(fd_set.contains(r1));
assert!(!fd_set.contains(r2));
}

macro_rules! generate_fdset_bad_fd_tests {
($fd:expr, $($method:ident),* $(,)?) => {
$(
#[test]
#[should_panic]
fn $method() {
FdSet::new().$method($fd);
}
)*
}
}

mod test_fdset_negative_fd {
use super::*;
generate_fdset_bad_fd_tests!(-1, insert, remove, contains);
}

mod test_fdset_too_large_fd {
use super::*;
use std::convert::TryInto;
generate_fdset_bad_fd_tests!(
FD_SETSIZE.try_into().unwrap(),
insert,
remove,
contains,
);
}

0 comments on commit dc80b4c

Please sign in to comment.