mirror of
https://gitee.com/openharmony/third_party_rust_nix
synced 2024-11-23 07:20:09 +00:00
Fix UB in the SO_TYPE sockopt
When reading a value into an enum from getsockopt, we must validate it. Failing to do so can lead to UB for example with SOCK_PACKET on Linux. Perform the validation in GetSockOpt::get. Currently SockType is the only type that requires validation. Fixes #1819
This commit is contained in:
parent
12fb35434f
commit
8e91b28b64
@ -7,6 +7,9 @@ This project adheres to [Semantic Versioning](https://semver.org/).
|
|||||||
### Added
|
### Added
|
||||||
### Changed
|
### Changed
|
||||||
### Fixed
|
### Fixed
|
||||||
|
- Fix UB with `sys::socket::sockopt::SockType` using `SOCK_PACKET`.
|
||||||
|
([#1821](https://github.com/nix-rust/nix/pull/1821))
|
||||||
|
|
||||||
### Removed
|
### Removed
|
||||||
|
|
||||||
## [0.26.0] - 2022-11-29
|
## [0.26.0] - 2022-11-29
|
||||||
|
@ -12,7 +12,7 @@ use libc::{
|
|||||||
self, c_int, c_void, iovec, size_t, socklen_t, CMSG_DATA, CMSG_FIRSTHDR,
|
self, c_int, c_void, iovec, size_t, socklen_t, CMSG_DATA, CMSG_FIRSTHDR,
|
||||||
CMSG_LEN, CMSG_NXTHDR,
|
CMSG_LEN, CMSG_NXTHDR,
|
||||||
};
|
};
|
||||||
use std::convert::TryInto;
|
use std::convert::{TryFrom, TryInto};
|
||||||
use std::io::{IoSlice, IoSliceMut};
|
use std::io::{IoSlice, IoSliceMut};
|
||||||
#[cfg(feature = "net")]
|
#[cfg(feature = "net")]
|
||||||
use std::net;
|
use std::net;
|
||||||
@ -107,6 +107,24 @@ pub enum SockType {
|
|||||||
#[cfg(not(any(target_os = "haiku")))]
|
#[cfg(not(any(target_os = "haiku")))]
|
||||||
Rdm = libc::SOCK_RDM,
|
Rdm = libc::SOCK_RDM,
|
||||||
}
|
}
|
||||||
|
// The TryFrom impl could've been derived using libc_enum!. But for
|
||||||
|
// backwards-compatibility with Nix-0.25.0 we manually implement it, so as to
|
||||||
|
// keep the old variant names.
|
||||||
|
impl TryFrom<i32> for SockType {
|
||||||
|
type Error = crate::Error;
|
||||||
|
|
||||||
|
fn try_from(x: i32) -> Result<Self> {
|
||||||
|
match x {
|
||||||
|
libc::SOCK_STREAM => Ok(Self::Stream),
|
||||||
|
libc::SOCK_DGRAM => Ok(Self::Datagram),
|
||||||
|
libc::SOCK_SEQPACKET => Ok(Self::SeqPacket),
|
||||||
|
libc::SOCK_RAW => Ok(Self::Raw),
|
||||||
|
#[cfg(not(any(target_os = "haiku")))]
|
||||||
|
libc::SOCK_RDM => Ok(Self::Rdm),
|
||||||
|
_ => Err(Errno::EINVAL)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Constants used in [`socket`](fn.socket.html) and [`socketpair`](fn.socketpair.html)
|
/// Constants used in [`socket`](fn.socket.html) and [`socketpair`](fn.socketpair.html)
|
||||||
/// to specify the protocol to use.
|
/// to specify the protocol to use.
|
||||||
|
@ -6,7 +6,10 @@ use crate::Result;
|
|||||||
use cfg_if::cfg_if;
|
use cfg_if::cfg_if;
|
||||||
use libc::{self, c_int, c_void, socklen_t};
|
use libc::{self, c_int, c_void, socklen_t};
|
||||||
use std::ffi::{OsStr, OsString};
|
use std::ffi::{OsStr, OsString};
|
||||||
use std::mem::{self, MaybeUninit};
|
use std::{
|
||||||
|
convert::TryFrom,
|
||||||
|
mem::{self, MaybeUninit}
|
||||||
|
};
|
||||||
#[cfg(target_family = "unix")]
|
#[cfg(target_family = "unix")]
|
||||||
use std::os::unix::ffi::OsStrExt;
|
use std::os::unix::ffi::OsStrExt;
|
||||||
use std::os::unix::io::RawFd;
|
use std::os::unix::io::RawFd;
|
||||||
@ -102,7 +105,10 @@ macro_rules! getsockopt_impl {
|
|||||||
);
|
);
|
||||||
Errno::result(res)?;
|
Errno::result(res)?;
|
||||||
|
|
||||||
Ok(getter.assume_init())
|
match <$ty>::try_from(getter.assume_init()) {
|
||||||
|
Err(_) => Err(Errno::EINVAL),
|
||||||
|
Ok(r) => Ok(r)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -629,7 +635,8 @@ sockopt_impl!(
|
|||||||
GetOnly,
|
GetOnly,
|
||||||
libc::SOL_SOCKET,
|
libc::SOL_SOCKET,
|
||||||
libc::SO_TYPE,
|
libc::SO_TYPE,
|
||||||
super::SockType
|
super::SockType,
|
||||||
|
GetStruct<i32>
|
||||||
);
|
);
|
||||||
sockopt_impl!(
|
sockopt_impl!(
|
||||||
/// Returns a value indicating whether or not this socket has been marked to
|
/// Returns a value indicating whether or not this socket has been marked to
|
||||||
|
@ -151,6 +151,33 @@ fn test_so_tcp_maxseg() {
|
|||||||
close(ssock).unwrap();
|
close(ssock).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_so_type() {
|
||||||
|
let sockfd = socket(
|
||||||
|
AddressFamily::Inet,
|
||||||
|
SockType::Stream,
|
||||||
|
SockFlag::empty(),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
assert_eq!(Ok(SockType::Stream), getsockopt(sockfd, sockopt::SockType));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// getsockopt(_, sockopt::SockType) should gracefully handle unknown socket
|
||||||
|
/// types. Regression test for https://github.com/nix-rust/nix/issues/1819
|
||||||
|
#[cfg(any(target_os = "android", target_os = "linux",))]
|
||||||
|
#[test]
|
||||||
|
fn test_so_type_unknown() {
|
||||||
|
use nix::errno::Errno;
|
||||||
|
|
||||||
|
require_capability!("test_so_type", CAP_NET_RAW);
|
||||||
|
let sockfd = unsafe { libc::socket(libc::AF_PACKET, libc::SOCK_PACKET, 0) };
|
||||||
|
assert!(sockfd >= 0, "Error opening socket: {}", nix::Error::last());
|
||||||
|
|
||||||
|
assert_eq!(Err(Errno::EINVAL), getsockopt(sockfd, sockopt::SockType));
|
||||||
|
}
|
||||||
|
|
||||||
// The CI doesn't supported getsockopt and setsockopt on emulated processors.
|
// The CI doesn't supported getsockopt and setsockopt on emulated processors.
|
||||||
// It's believed that a QEMU issue, the tests run ok on a fully emulated system.
|
// It's believed that a QEMU issue, the tests run ok on a fully emulated system.
|
||||||
// Current CI just run the binary with QEMU but the Kernel remains the same as the host.
|
// Current CI just run the binary with QEMU but the Kernel remains the same as the host.
|
||||||
|
Loading…
Reference in New Issue
Block a user