Skip to content

Commit 0960099

Browse files
alyssaislauralt
authored andcommitted
sock_ctrl_msg: mark recv_with_fds as unsafe
Writes to arbitrary pointers are unsafe in Rust. It's the caller's job to ensure that it's safe for the memory they are writing to can contain whatever arbitrary bytes are received over the socket. For example, it would be unsafe to have an iovec pointing to the return value of str::as_mut_ptr, because strings can only contain byte sequences that are valid UTF-8. Because it's on the caller to make sure they're passing pointers safely, any function that writes to iovecs has to be marked as unsafe. Signed-off-by: Alyssa Ross <[email protected]>
1 parent e39373f commit 0960099

File tree

1 file changed

+43
-29
lines changed

1 file changed

+43
-29
lines changed

src/linux/sock_ctrl_msg.rs

Lines changed: 43 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,11 @@ fn raw_sendmsg<D: IntoIovec>(fd: RawFd, out_data: &[D], out_fds: &[RawFd]) -> Re
198198
}
199199
}
200200

201-
fn raw_recvmsg(fd: RawFd, iovecs: &mut [iovec], in_fds: &mut [RawFd]) -> Result<(usize, usize)> {
201+
unsafe fn raw_recvmsg(
202+
fd: RawFd,
203+
iovecs: &mut [iovec],
204+
in_fds: &mut [RawFd],
205+
) -> Result<(usize, usize)> {
202206
let cmsg_capacity = CMSG_SPACE!(size_of::<RawFd>() * in_fds.len());
203207
let mut cmsg_buffer = CmsgBuffer::with_capacity(cmsg_capacity);
204208
let mut msg = new_msghdr(iovecs);
@@ -212,7 +216,7 @@ fn raw_recvmsg(fd: RawFd, iovecs: &mut [iovec], in_fds: &mut [RawFd]) -> Result<
212216
// Safe because the msghdr was properly constructed from valid (or null) pointers of the
213217
// indicated length and we check the return value.
214218
// TODO: Should we handle MSG_TRUNC in a specific way?
215-
let total_read = unsafe { recvmsg(fd, &mut msg, 0) };
219+
let total_read = recvmsg(fd, &mut msg, 0);
216220
if total_read == -1 {
217221
return Err(Error::last());
218222
}
@@ -233,7 +237,7 @@ fn raw_recvmsg(fd: RawFd, iovecs: &mut [iovec], in_fds: &mut [RawFd]) -> Result<
233237
// Safe because we checked that cmsg_ptr was non-null, and the loop is constructed such
234238
// that it only happens when there is at least sizeof(cmsghdr) space after the pointer to
235239
// read.
236-
let cmsg = unsafe { (cmsg_ptr as *mut cmsghdr).read_unaligned() };
240+
let cmsg = (cmsg_ptr as *mut cmsghdr).read_unaligned();
237241
if cmsg.cmsg_level == SOL_SOCKET && cmsg.cmsg_type == SCM_RIGHTS {
238242
let fds_count = ((cmsg.cmsg_len - CMSG_LEN!(0)) as usize) / size_of::<RawFd>();
239243
// The sender can transmit more data than we can buffer. If a message is too long to
@@ -250,22 +254,18 @@ fn raw_recvmsg(fd: RawFd, iovecs: &mut [iovec], in_fds: &mut [RawFd]) -> Result<
250254
let raw_fds_ptr = CMSG_DATA(cmsg_ptr);
251255
// The cmsg_ptr is valid here because is checked at the beginning of the
252256
// loop and it is assured to have `fds_count` fds available.
253-
unsafe {
254-
let raw_fd = *(raw_fds_ptr.wrapping_add(fd_offset)) as c_int;
255-
libc::close(raw_fd)
256-
};
257+
let raw_fd = *(raw_fds_ptr.wrapping_add(fd_offset)) as c_int;
258+
libc::close(raw_fd);
257259
}
258260
} else {
259261
// Safe because `cmsg_ptr` is checked against null and we copy from `cmesg_buffer` to
260262
// `in_fds` according to their current capacity.
261-
unsafe {
262-
copy_nonoverlapping(
263-
CMSG_DATA(cmsg_ptr),
264-
in_fds[copied_fds_count..(copied_fds_count + fds_to_be_copied_count)]
265-
.as_mut_ptr(),
266-
fds_to_be_copied_count,
267-
);
268-
}
263+
copy_nonoverlapping(
264+
CMSG_DATA(cmsg_ptr),
265+
in_fds[copied_fds_count..(copied_fds_count + fds_to_be_copied_count)]
266+
.as_mut_ptr(),
267+
fds_to_be_copied_count,
268+
);
269269

270270
copied_fds_count += fds_to_be_copied_count;
271271
}
@@ -276,7 +276,7 @@ fn raw_recvmsg(fd: RawFd, iovecs: &mut [iovec], in_fds: &mut [RawFd]) -> Result<
276276
for fd in in_fds.iter().take(copied_fds_count) {
277277
// This is safe because we close only the previously copied fds. We do not care
278278
// about `close` return code.
279-
unsafe { libc::close(*fd) };
279+
libc::close(*fd);
280280
}
281281

282282
return Err(Error::new(libc::ENOBUFS));
@@ -319,9 +319,10 @@ fn raw_recvmsg(fd: RawFd, iovecs: &mut [iovec], in_fds: &mut [RawFd]) -> Result<
319319
/// iov_base: buf.as_mut_ptr() as *mut c_void,
320320
/// iov_len: buf.len(),
321321
/// }];
322-
/// let (read_count, file_count) = s2
323-
/// .recv_with_fds(&mut iovecs[..], &mut files)
324-
/// .expect("failed to recv fd");
322+
/// let (read_count, file_count) = unsafe {
323+
/// s2.recv_with_fds(&mut iovecs[..], &mut files)
324+
/// .expect("failed to recv fd")
325+
/// };
325326
///
326327
/// let mut file = unsafe { File::from_raw_fd(files[0]) };
327328
/// file.write(unsafe { from_raw_parts(&1203u64 as *const u64 as *const u8, 8) })
@@ -370,7 +371,9 @@ pub trait ScmSocket {
370371
iov_len: buf.len(),
371372
}];
372373

373-
let (read_count, fd_count) = self.recv_with_fds(&mut iovecs[..], &mut fd)?;
374+
// Safe because we have mutably borrowed buf and it's safe to write arbitrary data
375+
// to a slice.
376+
let (read_count, fd_count) = unsafe { self.recv_with_fds(&mut iovecs[..], &mut fd)? };
374377
let file = if fd_count == 0 {
375378
None
376379
} else {
@@ -394,7 +397,16 @@ pub trait ScmSocket {
394397
/// returned tuple. The caller owns these file descriptors, but they will not be
395398
/// closed on drop like a `File`-like type would be. It is recommended that each valid
396399
/// file descriptor gets wrapped in a drop type that closes it after this returns.
397-
fn recv_with_fds(&self, iovecs: &mut [iovec], fds: &mut [RawFd]) -> Result<(usize, usize)> {
400+
///
401+
/// # Safety
402+
///
403+
/// It is the callers responsibility to ensure it is safe for arbitrary data to be
404+
/// written to the iovec pointers.
405+
unsafe fn recv_with_fds(
406+
&self,
407+
iovecs: &mut [iovec],
408+
fds: &mut [RawFd],
409+
) -> Result<(usize, usize)> {
398410
raw_recvmsg(self.socket_fd(), iovecs, fds)
399411
}
400412
}
@@ -503,9 +515,10 @@ mod tests {
503515
iov_base: buf.as_mut_ptr() as *mut c_void,
504516
iov_len: buf.len(),
505517
}];
506-
let (read_count, file_count) = s2
507-
.recv_with_fds(&mut iovecs[..], &mut files)
508-
.expect("failed to recv data");
518+
let (read_count, file_count) = unsafe {
519+
s2.recv_with_fds(&mut iovecs[..], &mut files)
520+
.expect("failed to recv data")
521+
};
509522

510523
assert_eq!(read_count, 6);
511524
assert_eq!(file_count, 0);
@@ -556,9 +569,10 @@ mod tests {
556569
iov_base: buf.as_mut_ptr() as *mut c_void,
557570
iov_len: buf.len(),
558571
}];
559-
let (read_count, file_count) = s2
560-
.recv_with_fds(&mut iovecs[..], &mut files)
561-
.expect("failed to recv fd");
572+
let (read_count, file_count) = unsafe {
573+
s2.recv_with_fds(&mut iovecs[..], &mut files)
574+
.expect("failed to recv fd")
575+
};
562576

563577
assert_eq!(read_count, 1);
564578
assert_eq!(buf[0], 237);
@@ -606,7 +620,7 @@ mod tests {
606620
iov_base: buf.as_mut_ptr() as *mut c_void,
607621
iov_len: buf.len(),
608622
}];
609-
assert!(s2.recv_with_fds(&mut iovecs[..], &mut files).is_err());
623+
assert!(unsafe { s2.recv_with_fds(&mut iovecs[..], &mut files).is_err() });
610624
}
611625

612626
// Exercise the code paths that activate the issue of receiving part of the sent ancillary
@@ -639,6 +653,6 @@ mod tests {
639653
iov_base: buf.as_mut_ptr() as *mut c_void,
640654
iov_len: buf.len(),
641655
}];
642-
assert!(s2.recv_with_fds(&mut iovecs[..], &mut files).is_err());
656+
assert!(unsafe { s2.recv_with_fds(&mut iovecs[..], &mut files).is_err() });
643657
}
644658
}

0 commit comments

Comments
 (0)