Skip to content

Commit 749bf75

Browse files
bors[bot]asomers
andauthored
Merge #1821
1821: Fix UB in the SO_TYPE sockopt r=rtzoeller a=asomers 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 Co-authored-by: Alan Somers <asomers@gmail.com>
2 parents 12fb354 + 8e91b28 commit 749bf75

File tree

4 files changed

+59
-4
lines changed

4 files changed

+59
-4
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ This project adheres to [Semantic Versioning](https://semver.org/).
77
### Added
88
### Changed
99
### Fixed
10+
- Fix UB with `sys::socket::sockopt::SockType` using `SOCK_PACKET`.
11+
([#1821](https://github.com/nix-rust/nix/pull/1821))
12+
1013
### Removed
1114

1215
## [0.26.0] - 2022-11-29

src/sys/socket/mod.rs

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use libc::{
1212
self, c_int, c_void, iovec, size_t, socklen_t, CMSG_DATA, CMSG_FIRSTHDR,
1313
CMSG_LEN, CMSG_NXTHDR,
1414
};
15-
use std::convert::TryInto;
15+
use std::convert::{TryFrom, TryInto};
1616
use std::io::{IoSlice, IoSliceMut};
1717
#[cfg(feature = "net")]
1818
use std::net;
@@ -107,6 +107,24 @@ pub enum SockType {
107107
#[cfg(not(any(target_os = "haiku")))]
108108
Rdm = libc::SOCK_RDM,
109109
}
110+
// The TryFrom impl could've been derived using libc_enum!. But for
111+
// backwards-compatibility with Nix-0.25.0 we manually implement it, so as to
112+
// keep the old variant names.
113+
impl TryFrom<i32> for SockType {
114+
type Error = crate::Error;
115+
116+
fn try_from(x: i32) -> Result<Self> {
117+
match x {
118+
libc::SOCK_STREAM => Ok(Self::Stream),
119+
libc::SOCK_DGRAM => Ok(Self::Datagram),
120+
libc::SOCK_SEQPACKET => Ok(Self::SeqPacket),
121+
libc::SOCK_RAW => Ok(Self::Raw),
122+
#[cfg(not(any(target_os = "haiku")))]
123+
libc::SOCK_RDM => Ok(Self::Rdm),
124+
_ => Err(Errno::EINVAL)
125+
}
126+
}
127+
}
110128

111129
/// Constants used in [`socket`](fn.socket.html) and [`socketpair`](fn.socketpair.html)
112130
/// to specify the protocol to use.

src/sys/socket/sockopt.rs

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,10 @@ use crate::Result;
66
use cfg_if::cfg_if;
77
use libc::{self, c_int, c_void, socklen_t};
88
use std::ffi::{OsStr, OsString};
9-
use std::mem::{self, MaybeUninit};
9+
use std::{
10+
convert::TryFrom,
11+
mem::{self, MaybeUninit}
12+
};
1013
#[cfg(target_family = "unix")]
1114
use std::os::unix::ffi::OsStrExt;
1215
use std::os::unix::io::RawFd;
@@ -102,7 +105,10 @@ macro_rules! getsockopt_impl {
102105
);
103106
Errno::result(res)?;
104107

105-
Ok(getter.assume_init())
108+
match <$ty>::try_from(getter.assume_init()) {
109+
Err(_) => Err(Errno::EINVAL),
110+
Ok(r) => Ok(r)
111+
}
106112
}
107113
}
108114
}
@@ -629,7 +635,8 @@ sockopt_impl!(
629635
GetOnly,
630636
libc::SOL_SOCKET,
631637
libc::SO_TYPE,
632-
super::SockType
638+
super::SockType,
639+
GetStruct<i32>
633640
);
634641
sockopt_impl!(
635642
/// Returns a value indicating whether or not this socket has been marked to

test/sys/test_sockopt.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,33 @@ fn test_so_tcp_maxseg() {
151151
close(ssock).unwrap();
152152
}
153153

154+
#[test]
155+
fn test_so_type() {
156+
let sockfd = socket(
157+
AddressFamily::Inet,
158+
SockType::Stream,
159+
SockFlag::empty(),
160+
None,
161+
)
162+
.unwrap();
163+
164+
assert_eq!(Ok(SockType::Stream), getsockopt(sockfd, sockopt::SockType));
165+
}
166+
167+
/// getsockopt(_, sockopt::SockType) should gracefully handle unknown socket
168+
/// types. Regression test for https://github.com/nix-rust/nix/issues/1819
169+
#[cfg(any(target_os = "android", target_os = "linux",))]
170+
#[test]
171+
fn test_so_type_unknown() {
172+
use nix::errno::Errno;
173+
174+
require_capability!("test_so_type", CAP_NET_RAW);
175+
let sockfd = unsafe { libc::socket(libc::AF_PACKET, libc::SOCK_PACKET, 0) };
176+
assert!(sockfd >= 0, "Error opening socket: {}", nix::Error::last());
177+
178+
assert_eq!(Err(Errno::EINVAL), getsockopt(sockfd, sockopt::SockType));
179+
}
180+
154181
// The CI doesn't supported getsockopt and setsockopt on emulated processors.
155182
// It's believed that a QEMU issue, the tests run ok on a fully emulated system.
156183
// Current CI just run the binary with QEMU but the Kernel remains the same as the host.

0 commit comments

Comments
 (0)