Skip to content

Commit 50493d2

Browse files
committed
Rework to AncillaryData<'a> / AncillaryDataBuf for stack alloc.
1 parent d57ff25 commit 50493d2

File tree

1 file changed

+178
-91
lines changed

1 file changed

+178
-91
lines changed

text/3430-unix-socket-ancillary-data-v2.md

Lines changed: 178 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -92,14 +92,16 @@ general knowledge of ancillary data and the Unix sockets API?
9292
Sending a file descriptor:
9393

9494
```rust
95-
use std::os::unix::net::{AncillaryData, MessageSender, UnixStream};
95+
use std::fs::File;
96+
use std::os::unix::net::{AncillaryDataBuf, MessageSender, UnixStream};
9697
use std::os::fd::AsFd;
9798

98-
fn send_file(stream: &UnixStream, file: std::fs::File) -> std::io::Result<()> {
99-
let mut ancillary = AncillaryData::new();
100-
ancillary.add_file_descriptors(&[
99+
fn send_file(stream: &UnixStream, file: File) -> std::io::Result<()> {
100+
let mut ancillary_buf = AncillaryDataBuf::new();
101+
ancillary_buf.add_file_descriptors(&[
101102
file.as_fd(),
102103
]);
104+
let mut ancillary = ancillary_buf.to_ancillary_data();
103105

104106
MessageSender::new(stream, b"\x00")
105107
.ancillary_data(&mut ancillary)
@@ -111,22 +113,28 @@ fn send_file(stream: &UnixStream, file: std::fs::File) -> std::io::Result<()> {
111113
Receiving a file descriptor:
112114

113115
```rust
114-
use std::os::unix::net::{AncillaryData, MessageReceiver, UnixStream};
116+
use std::fs::File;
117+
use std::os::unix::net::{AncillaryDataBuf, MessageReceiver, UnixStream};
115118

116-
fn recv_file(stream: &UnixStream) -> std::io::Result<std::fs::File> {
119+
fn recv_file(stream: &UnixStream) -> std::io::Result<File> {
117120
// TODO: expose CMSG_SPACE() in a user-friendly way.
118121
const ANCILLARY_CAPACITY: usize = 100;
119122

120-
let mut ancillary = AncillaryData::with_capacity(ANCILLARY_CAPACITY);
123+
let mut ancillary_buf = AncillaryDataBuf::with_capacity(ANCILLARY_CAPACITY);
124+
let mut ancillary = ancillary_buf.to_ancillary_data();
125+
121126
let mut buf = [0u8; 1];
122127
MessageReceiver::new(stream, &mut buf)
123128
.ancillary_data(&mut ancillary)
124129
.recv()?;
125130

126-
// TODO: error handling (std::io::Error if not enough FDs returned)
127-
let mut owned_fds = ancillary.take_owned_fds().unwrap();
128-
let sent_fd = owned_fds.swap_remove(0);
129-
Ok(sent_fd.into())
131+
let mut received_fds: Vec<_> = ancillary.received_fds().collect();
132+
if received_fds.len() != 1 {
133+
// TODO: error handling (std::io::Error if not enough FDs returned)
134+
panic!("didn't receive enough FDs");
135+
}
136+
let received_fd = received_fds.pop().unwrap();
137+
Ok(File::from(received_fd))
130138
}
131139
```
132140

@@ -166,7 +174,7 @@ inspected and iterated over to obtain `ControlMessage` values.
166174

167175

168176
```rust
169-
struct ControlMessages { ... };
177+
struct ControlMessages;
170178

171179
impl ControlMessages {
172180
fn from_bytes(bytes: &[u8]) -> &ControlMessages;
@@ -180,7 +188,7 @@ impl<'a> IntoIterator for &'a ControlMessages {
180188
type IntoIter = ControlMessagesIter<'a>;
181189
}
182190

183-
struct ControlMessagesIter<'a> { ... }
191+
struct ControlMessagesIter<'a>;
184192

185193
impl<'a> Iterator for ControlMessagesIter<'a> {
186194
type Item = ControlMessage<'a>;
@@ -192,106 +200,187 @@ impl ControlMessagesIter<'a> {
192200
}
193201
```
194202

195-
A `ControlMessagesBuf` is the owned variant of `ControlMessages`. It exposes a
196-
subset of the `Vec` capacity management functions, with a public API that only
197-
allows operations that won't reduce its length (since that would risk losing
198-
information about received file descriptors).
203+
## Ancillary data
204+
205+
### `struct AncillaryData`
206+
207+
An `AncillaryData` is responsible for combining the serialized control messages
208+
with a notion of file descriptor ownership, ensuring that (1) borrowed FDs live
209+
long enough to be sent, and (2) received FDs aren't leaked.
199210

200211
```rust
201-
struct ControlMessagesBuf;
212+
struct AncillaryData<'a, 'fd>;
202213

203-
impl ControlMessagesBuf {
204-
fn new() -> ControlMessagesBuf;
205-
fn with_capacity(capacity: usize) -> ControlMessagesBuf;
214+
impl Drop for AncillaryData;
206215

207-
fn capacity(&self) -> usize;
216+
impl AncillaryData<'a, 'fd> {
217+
fn new(
218+
control_messages_buf: &'a mut [MaybeUninit<u8>],
219+
) -> AncillaryData<'a, 'fd>;
208220

209-
fn push(&mut self, message: impl Into<ControlMessage<'_>>);
221+
// returns initialized portion of `control_messages_buf`.
222+
fn control_messages(&self) -> &ControlMessages;
210223

211-
fn reserve(&mut self, additional: usize);
224+
// copy a control message into the ancillary data; error on out-of-capacity.
225+
fn add_control_message(
226+
&mut self,
227+
control_message: impl Into<ControlMessage<'_>>,
228+
) -> Result<(), AncillaryDataNoCapacity>;
212229

213-
fn try_reserve(
230+
// Add an `SCM_RIGHTS` control message with given borrowed FDs.
231+
fn add_file_descriptors(
214232
&mut self,
215-
additional: usize,
216-
) -> Result<(), TryReserveError>;
233+
borrowed_fds: &[BorrowedFd<'fd>],
234+
) -> Result<(), AncillaryDataNoCapacity>;
217235

218-
fn reserve_exact(&mut self, additional: usize);
236+
// Transfers ownership of received FDs to the iterator.
237+
fn received_fds(&mut self) -> AncillaryDataReceivedFds<'_>;
219238

220-
fn try_reserve_exact(
221-
&mut self,
222-
additional: usize
223-
) -> Result<(), TryReserveError>
224-
}
239+
// Obtain a mutable buffer usable as the `msg_control` pointer in a call
240+
// to `sendmsg()` or `recvmsg()`.
241+
fn control_messages_buf(&mut self) -> Option<&mut [u8]>;
225242

226-
impl AsRef<ControlMessages> for ControlMessagesBuf;
243+
// Update the control messages buffer length according to the result of
244+
// calling `sendmsg()` or `recvmsg()`.
245+
fn set_control_messages_len(&mut self, len: usize);
227246

228-
impl Deref for ControlMessagesBuf {
229-
type Target = ControlMessages;
247+
// Scan the control messages buffer for `SCM_RIGHTS` and take ownership of
248+
// any file descriptors found within.
249+
unsafe fn take_ownership_of_scm_rights(&mut self);
230250
}
231251

232-
impl<'a> IntoIterator for &'a ControlMessagesBuf {
233-
type Item = ControlMessage<'a>;
234-
type IntoIter = ControlMessagesIter<'a>;
235-
}
252+
struct AncillaryDataReceivedFds<'a>;
236253

237-
impl Extend<ControlMessage<'_>> for ControlMessagesBuf;
238-
impl Extend<&ControlMessage<'_>> for ControlMessagesBuf;
254+
impl<'a> Iterator for AncillaryDataReceivedFds<'a> {
255+
type Item = OwnedFd;
256+
}
239257
```
240258

241-
## Ancillary data
259+
#### `fn AncillaryData::received_fds`
260+
261+
By default an `AncillaryData` has no received FDs, and this method will
262+
ignore FDs added via `add_file_descriptors`. The iterator holds a mutable
263+
borrow on the `AncillaryData`, and will close any unclaimed FDs when
264+
it's dropped.
265+
266+
Internally, the iteration works by scanning the control message buffer
267+
for `SCM_RIGHTS`. As the iteration proceeds the buffer is mutated to set
268+
the "taken" FDs to `-1` (a sentinal value for Unix file descriptors).
269+
270+
#### `fn AncillaryData::control_messages_buf`
271+
272+
The returned slice, if not `None`, will have the same length as
273+
`control_messages_buf`. Any portion of the buffer not initialized by
274+
`add_control_message` or `add_file_descriptors` will be zeroed.
275+
276+
When this method is called the `AncillaryData` will have its control
277+
messages length cleared, so a subsequent call to `control_messages_buf()`
278+
returns `None`. If the `AncillaryData` contains received FDs, they will
279+
be closed.
280+
281+
#### `fn AncillaryData::set_control_messages_len`
282+
283+
Does not take ownership of FDs in any `SCM_RIGHTS` control messages that
284+
might exist within the new buffer length.
285+
286+
**Panics**:
287+
* if `len > control_messages_buf.len()`
288+
* if `control_messages_buf()` hasn't been called to clear the length.
289+
290+
The second panic condition means that creating an `AncillaryData` and then
291+
immediately calling `set_control_messages_len` will panic to avoid potentially
292+
reading uninitialized data.
242293

243-
The `AncillaryData` struct is responsible for combining the serialized control
244-
messages with a notion of file descriptor ownership, ensuring that (1) borrowed
245-
FDs live long enough to be sent, and (2) received FDs aren't leaked.
294+
Also, calling `set_control_messages_len()` twice without an intervening
295+
`control_messages_buf()` will panic to avoid leaking received FDs.
296+
297+
#### `fn AncillaryData::take_ownership_of_scm_rights`
298+
299+
**Panics**:
300+
* if `set_control_messages_len()` hasn't been called since the most
301+
recent call to `control_messages_buf()`.
302+
* That method is what keeps track of how much of the received buffer
303+
contains owned FDs, and trying to take ownership of `SCM_RIGHTS` without
304+
knowing how much to scan is almost certainly a programming error.
305+
306+
**Safety**: contents of control messages become `OwnedFd`, so this has all
307+
the safety requirements of `OwnedFd::from_raw_fd()`.
308+
309+
### `struct AncillaryDataBuf`
310+
311+
An `AncillaryDataBuf` is an owned variant of `AncillaryData`, using heap
312+
allocation (an internal `Vec<u8>`). It exposes a subset of the `Vec` capacity
313+
management methods.
246314

247315
```rust
248-
struct AncillaryData<'fd>;
316+
struct AncillaryDataBuf<'fd>;
317+
318+
impl AncillaryDataBuf<'fd> {
319+
fn new() -> AncillaryDataBuf<'static>;
320+
fn with_capacity(capacity: usize) -> AncillaryDataBuf<'static>;
249321

250-
impl AncillaryData<'fd> {
251-
fn new() -> AncillaryData<'fd>;
252-
fn with_capacity(capacity: usize) -> AncillaryData<'fd>;
322+
fn capacity(&self) -> usize;
253323

254324
fn control_messages(&self) -> &ControlMessages;
255-
fn control_messages_mut(&mut self) -> &mut ControlMessagesBuf;
256325

257-
// Helper for control_messages_mut().push(message.into())
258-
fn add_control_message<'b>(&mut self, message: impl Into<ControlMessage<'b>>);
326+
// copy a control message into the ancillary data; panic on alloc failure.
327+
fn add_control_message(
328+
&mut self,
329+
control_message: impl Into<ControlMessage<'_>>,
330+
);
331+
332+
// Add an `SCM_RIGHTS` control message with given borrowed FDs; panic on
333+
// alloc failure.
334+
fn add_file_descriptors(&mut self, borrowed_fds: &[BorrowedFd<'fd>]);
259335

260-
// Adds FDs to the control messages buffer so they can be sent.
261-
fn add_file_descriptors(&mut self, borrowed_fds: &impl AsRef<[BorrowedFd<'fd>]>);
336+
// Used to obtain `AncillaryData` for passing to send/recv calls.
337+
fn to_ancillary_data<'a>(&'a mut self) -> AncillaryData<'a, 'fd>;
262338

263-
// Clears the control message buffer and drops owned FDs.
339+
// Clears the control message buffer, without affecting capacity.
340+
//
341+
// This will not leak FDs because the `AncillaryData` type holds a mutable
342+
// reference to the `AncillaryDataBuf`, so if `clear()` is called then there
343+
// are no outstanding `AncillaryData`s and thus no received FDs.
264344
fn clear(&mut self);
265345

266-
// Takes ownership of FDs received from the socket. After the FDs are
267-
// taken, returns `None` until the next call to `finish_recvmsg`.
268-
fn take_owned_fds(&mut self) -> Option<Vec<OwnedFd>>;
346+
// as in Vec
347+
fn reserve(&mut self, capacity: usize);
348+
// as in Vec
349+
fn reserve_exact(&mut self, capacity: usize);
269350

270-
// API for sockets performing `recvmsg`:
271-
//
272-
// 1. Call `start_recvmsg` to obtain a mutable buffer to pass into the
273-
// `cmsghdr`. The buffer len equals `control_messages().capacity()`.
274-
//
275-
// 2. Call `finish_recvmsg` to update the control messages buffer length
276-
// according to the new `cmsghdr` length. This function will also take
277-
// ownership of FDs received from the socket.
278-
//
279-
// The caller is responsible for ensuring that the control messages buffer
280-
// content and length are provided by a successful call to `recvmsg`.
281-
fn start_recvmsg(&mut self) -> Option<&mut [u8]>;
282-
unsafe fn finish_recvmsg(&mut self, control_messages_len: usize);
283-
284-
// API for sockets performing `sendmsg` -- basically the same as the
285-
// `recvmsg` version, but doesn't scan the message buffer for `SCM_RIGHTS`
286-
// to take ownership of.
287-
//
288-
// The caller is responsible for ensuring that the control messages buffer
289-
// content and length are provided by a successful call to `sendmsg`.
290-
fn start_sendmsg(&mut self) -> Option<&mut [u8]>;
291-
unsafe fn finish_sendmsg(&mut self, control_messages_len: usize);
351+
// as in Vec
352+
fn try_reserve(
353+
&mut self,
354+
capacity: usize,
355+
) -> Result<(), TryReserveError>;
356+
357+
// as in Vec
358+
fn try_reserve_exact(
359+
&mut self,
360+
capacity: usize,
361+
) -> Result<(), TryReserveError>;
292362
}
363+
364+
impl Extend<ControlMessage<'_>> for AncillaryDataBuf;
365+
impl Extend<&ControlMessage<'_>> for AncillaryDataBuf;
293366
```
294367

368+
#### `fn AncillaryDataBuf::to_ancillary_data`
369+
370+
The returned `AncillaryData` will be initialized with the same control messages,
371+
capacity, and borrowed FDs as the `AncillaryDataBuf`. Specifically, it's as if
372+
the entire capacity of the internal `Vec` is passed to `AncillaryData::new()`.
373+
374+
When the `AncillaryData` is dropped its received FDs will be closed. The
375+
`AncillaryDataBuf` does not retain ownership of received FDs. Otherwise the
376+
API to reuse an `AncillaryDataBuf` between calls gets really complicated.
377+
378+
The only subtle part of `to_ancillary_data` is that it transfers logical
379+
ownership of the control messages, but not the control messages *buffer*. So
380+
after calling this function the `AncillaryDataBuf::control_messages()` method
381+
returns an empty `&ControlMessages`, and any calls to `add_control_message` or
382+
`add_file_descriptors`. It's basically an implicit `clear()`.
383+
295384
## Sending messages
296385

297386
### The `SendMessage` and `SendMessageTo` traits
@@ -306,7 +395,7 @@ trait SendMessage {
306395
fn send_message(
307396
&self,
308397
bufs: &[IoSlice<'_>],
309-
ancillary_data: &mut AncillaryData<'_>,
398+
ancillary_data: &mut AncillaryData<'_, '_>,
310399
options: SendOptions,
311400
) -> io::Result<usize>;
312401
}
@@ -318,7 +407,7 @@ trait SendMessageTo {
318407
&self,
319408
addr: &Self::SocketAddr,
320409
bufs: &[IoSlice<'_>],
321-
ancillary_data: &mut AncillaryData<'_>,
410+
ancillary_data: &mut AncillaryData<'_, '_>,
322411
options: SendOptions,
323412
) -> io::Result<usize>;
324413
}
@@ -389,7 +478,7 @@ impl<S, 'a> MessageSender<S, 'a> {
389478

390479
fn ancillary_data(
391480
&mut self,
392-
ancillary_data: &'a mut AncillaryData<'a>,
481+
ancillary_data: &'a mut AncillaryData<'_, '_>,
393482
) -> &mut MessageSender<S, 'a>;
394483

395484
fn options(
@@ -421,7 +510,7 @@ trait RecvMessage {
421510
fn recv_message(
422511
&self,
423512
bufs: &mut [IoSliceMut<'_>],
424-
ancillary_data: &mut AncillaryData<'_>,
513+
ancillary_data: &mut AncillaryData<'_, '_>,
425514
options: RecvOptions,
426515
) -> io::Result<(usize, RecvResult)>;
427516
}
@@ -432,7 +521,7 @@ trait RecvMessageFrom {
432521
fn recv_message_from(
433522
&self,
434523
bufs: &mut [IoSliceMut<'_>],
435-
ancillary_data: &mut AncillaryData<'_>,
524+
ancillary_data: &mut AncillaryData<'_, '_>,
436525
options: RecvOptions,
437526
) -> io::Result<(usize, RecvResult, Self::SocketAddr)>;
438527
}
@@ -534,7 +623,7 @@ impl<S, 'a> MessageReceiver<S, 'a> {
534623

535624
fn ancillary_data(
536625
&mut self,
537-
ancillary_data: &'a mut AncillaryData<'a>,
626+
ancillary_data: &'a mut AncillaryData<'_, '_>,
538627
) -> &mut MessageReceiver<S, 'a>;
539628

540629
fn options(
@@ -740,10 +829,8 @@ This RFC would significantly expand the public API surface of `os::unix::net`.
740829
The handling of file descriptor ownership is more complex than the current
741830
implementation, which uses `RawFd`. There may be soundness issues in the
742831
conversion of `SCM_RIGHTS` into a `Vec<OwnedFd>`, for example if a way is
743-
found to call `finish_recvmsg` on a user-defined buffer from safe code.
744-
745-
The API described in this RFC doesn't provide a way for a stack-allocated
746-
buffer to be used as `AncillaryData` capacity.
832+
found to call `take_ownership_of_scm_rights` on a user-defined buffer from
833+
safe code.
747834

748835
# Rationale and alternatives
749836
[rationale-and-alternatives]: #rationale-and-alternatives

0 commit comments

Comments
 (0)