Skip to content

Commit 7ff9129

Browse files
authored
Fix decoding of abstract unix sockets (#661)
* test_unix_msg: Remove thread synchronisation This refactors the test_unix_msg() test a bit. No change in behaviour is intended. Previously, two threads were started in parallel. The server thread used a mutex and a condition variable to indicate that it set up its listening socket. The client thread waited for this signal before it attempted to connect. This commit makes the code instead bind the server socket before starting the worker threads. That way, no synchronisation is necessary. Signed-off-by: Uli Schlachter <[email protected]> * Add test for abstract unix sockets This commit duplicates the existing test_unix_msg() tests. The duplicated version uses an abstract unix socket instead of a path based one. To make the code-duplication not too bad, a helper function do_test_unix_msg() is introduced that does "the actual work" and just needs as input a socket address. This new test currently fails. This reproduces #660. Signed-off-by: Uli Schlachter <[email protected]> * Fix recvmsg() on abstract unix sockets This case was just not implemented at all. Abstract unix sockets are not null-terminated and are indicated by sun_path beginning with a null byte. Fixes: #660 Signed-off-by: Uli Schlachter <[email protected]> * Strengthen recvmsg() test The fix in the previous commit added some parsing for abstract unix sockets. I wasn't sure whether I got all the indicies right, so this commit extends the existing test to check that the result from recvmsg() contains the expected socket address. Signed-off-by: Uli Schlachter <[email protected]> * Fix recvmsg() on abstract unix sockets on libc backend The previous commit for this only fixed the linux_raw backend. This commit applies the same fix to the libc backend. Fixes: #660 Signed-off-by: Uli Schlachter <[email protected]> * Restrict test_abstract_unix_msg() test to Linux Abstract unix sockets are a feature of the Linux kernel that is not available elsewhere. Signed-off-by: Uli Schlachter <[email protected]> * Skip the test extension on FreeBSD Commit "Strengthen recvmsg() test" added a new assertion to this test. For unknown reasons, this test failed in Currus CI on x86_64-unknown-freebsd-13 as follows: ---- unix::test_unix_msg stdout ---- thread 'client' panicked at 'assertion failed: `(left == right)` left: `Some("/tmp/.tmpy5Fj4e/scp_4804")`, right: `Some("(unnamed)")`', tests/net/unix.rs:243:13 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace thread 'unix::test_unix_msg' panicked at 'called `Result::unwrap()` on an `Err` value: Any { .. }', tests/net/unix.rs:276:19 The text "(unnamed)" comes from the Debug impl of SocketAddrUnix. This text is generated when SocketAddrUnix::path() returns None. I do not know why this happens. I am just trying to get CI not to complain. A random guess would be that recvmsg() does not return the endpoint for connected sockets. This is because the "recvmsg" man page for FreeBSD says: > Here msg_name and msg_namelen specify the source address if the socket > is unconnected; Signed-off-by: Uli Schlachter <[email protected]> --------- Signed-off-by: Uli Schlachter <[email protected]>
1 parent 707742b commit 7ff9129

File tree

3 files changed

+121
-95
lines changed

3 files changed

+121
-95
lines changed

src/backend/libc/net/read_sockaddr.rs

Lines changed: 43 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -186,23 +186,51 @@ unsafe fn inner_read_sockaddr_os(
186186
if len == offsetof_sun_path {
187187
SocketAddrAny::Unix(SocketAddrUnix::new(&[][..]).unwrap())
188188
} else {
189+
#[cfg(not(any(target_os = "android", target_os = "linux")))]
190+
fn try_decode_abstract_socket(
191+
_sockaddr: &c::sockaddr_un,
192+
_len: usize,
193+
) -> Option<SocketAddrUnix> {
194+
None
195+
}
196+
#[cfg(any(target_os = "android", target_os = "linux"))]
197+
fn try_decode_abstract_socket(
198+
decode: &c::sockaddr_un,
199+
len: usize,
200+
) -> Option<SocketAddrUnix> {
201+
if decode.sun_path[0] != 0 {
202+
None
203+
} else {
204+
let offsetof_sun_path = super::addr::offsetof_sun_path();
205+
let address_bytes = &decode.sun_path[1..len - offsetof_sun_path];
206+
Some(
207+
SocketAddrUnix::new_abstract_name(
208+
&address_bytes.iter().map(|c| *c as u8).collect::<Vec<u8>>(),
209+
)
210+
.unwrap(),
211+
)
212+
}
213+
}
214+
189215
let decode = *storage.cast::<c::sockaddr_un>();
190-
assert_eq!(
191-
decode.sun_path[len - 1 - offsetof_sun_path],
192-
b'\0' as c::c_char
193-
);
194-
let path_bytes = &decode.sun_path[..len - 1 - offsetof_sun_path];
195-
196-
// FreeBSD sometimes sets the length to longer than the length
197-
// of the NUL-terminated string. Find the NUL and truncate the
198-
// string accordingly.
199-
#[cfg(target_os = "freebsd")]
200-
let path_bytes = &path_bytes[..path_bytes.iter().position(|b| *b == 0).unwrap()];
201-
202-
SocketAddrAny::Unix(
216+
let result = try_decode_abstract_socket(&decode, len).unwrap_or_else(|| {
217+
assert_eq!(
218+
decode.sun_path[len - 1 - offsetof_sun_path],
219+
b'\0' as c::c_char
220+
);
221+
let path_bytes = &decode.sun_path[..len - 1 - offsetof_sun_path];
222+
223+
// FreeBSD sometimes sets the length to longer than the length
224+
// of the NUL-terminated string. Find the NUL and truncate the
225+
// string accordingly.
226+
#[cfg(target_os = "freebsd")]
227+
let path_bytes =
228+
&path_bytes[..path_bytes.iter().position(|b| *b == 0).unwrap()];
229+
203230
SocketAddrUnix::new(path_bytes.iter().map(|c| *c as u8).collect::<Vec<u8>>())
204-
.unwrap(),
205-
)
231+
.unwrap()
232+
});
233+
SocketAddrAny::Unix(result)
206234
}
207235
}
208236
other => unimplemented!("{:?}", other),

src/backend/linux_raw/net/read_sockaddr.rs

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -155,19 +155,31 @@ pub(crate) unsafe fn read_sockaddr_os(storage: *const c::sockaddr, len: usize) -
155155
SocketAddrAny::Unix(SocketAddrUnix::new(&[][..]).unwrap())
156156
} else {
157157
let decode = *storage.cast::<c::sockaddr_un>();
158-
assert_eq!(
159-
decode.sun_path[len - 1 - offsetof_sun_path],
160-
b'\0' as c::c_char
161-
);
162-
SocketAddrAny::Unix(
163-
SocketAddrUnix::new(
164-
decode.sun_path[..len - 1 - offsetof_sun_path]
165-
.iter()
166-
.map(|c| *c as u8)
167-
.collect::<Vec<u8>>(),
158+
if decode.sun_path[0] == 0 {
159+
SocketAddrAny::Unix(
160+
SocketAddrUnix::new_abstract_name(
161+
&decode.sun_path[1..len - offsetof_sun_path]
162+
.iter()
163+
.map(|c| *c as u8)
164+
.collect::<Vec<u8>>(),
165+
)
166+
.unwrap(),
167+
)
168+
} else {
169+
assert_eq!(
170+
decode.sun_path[len - 1 - offsetof_sun_path],
171+
b'\0' as c::c_char
172+
);
173+
SocketAddrAny::Unix(
174+
SocketAddrUnix::new(
175+
decode.sun_path[..len - 1 - offsetof_sun_path]
176+
.iter()
177+
.map(|c| *c as u8)
178+
.collect::<Vec<u8>>(),
179+
)
180+
.unwrap(),
168181
)
169-
.unwrap(),
170-
)
182+
}
171183
}
172184
}
173185
other => unimplemented!("{:?}", other),

tests/net/unix.rs

Lines changed: 54 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -142,39 +142,21 @@ fn test_unix() {
142142
}
143143

144144
#[cfg(not(any(target_os = "redox", target_os = "wasi")))]
145-
#[test]
146-
fn test_unix_msg() {
145+
fn do_test_unix_msg(addr: SocketAddrUnix) {
147146
use rustix::io::{IoSlice, IoSliceMut};
148147
use rustix::net::{recvmsg, sendmsg_noaddr, RecvFlags, SendFlags};
149-
use std::string::ToString;
150-
151-
let tmpdir = tempfile::tempdir().unwrap();
152-
let path = tmpdir.path().join("scp_4804");
153-
let ready = Arc::new((Mutex::new(false), Condvar::new()));
154148

155149
let server = {
156-
let ready = ready.clone();
157-
let path = path.clone();
150+
let connection_socket = socket(
151+
AddressFamily::UNIX,
152+
SocketType::SEQPACKET,
153+
Protocol::default(),
154+
)
155+
.unwrap();
156+
bind_unix(&connection_socket, &addr).unwrap();
157+
listen(&connection_socket, 1).unwrap();
158158

159159
move || {
160-
let connection_socket = socket(
161-
AddressFamily::UNIX,
162-
SocketType::SEQPACKET,
163-
Protocol::default(),
164-
)
165-
.unwrap();
166-
167-
let name = SocketAddrUnix::new(&path).unwrap();
168-
bind_unix(&connection_socket, &name).unwrap();
169-
listen(&connection_socket, 1).unwrap();
170-
171-
{
172-
let (lock, cvar) = &*ready;
173-
let mut started = lock.lock().unwrap();
174-
*started = true;
175-
cvar.notify_all();
176-
}
177-
178160
let mut buffer = vec![0; BUFFER_SIZE];
179161
'exit: loop {
180162
let data_socket = accept(&connection_socket).unwrap();
@@ -208,21 +190,10 @@ fn test_unix_msg() {
208190
)
209191
.unwrap();
210192
}
211-
212-
unlinkat(cwd(), path, AtFlags::empty()).unwrap();
213193
}
214194
};
215195

216196
let client = move || {
217-
{
218-
let (lock, cvar) = &*ready;
219-
let mut started = lock.lock().unwrap();
220-
while !*started {
221-
started = cvar.wait(started).unwrap();
222-
}
223-
}
224-
225-
let addr = SocketAddrUnix::new(path).unwrap();
226197
let mut buffer = vec![0; BUFFER_SIZE];
227198
let runs: &[(&[&str], i32)] = &[
228199
(&["1", "2"], 3),
@@ -257,18 +228,25 @@ fn test_unix_msg() {
257228
)
258229
.unwrap();
259230

260-
let nread = recvmsg(
231+
let result = recvmsg(
261232
&data_socket,
262233
&mut [IoSliceMut::new(&mut buffer)],
263234
&mut Default::default(),
264235
RecvFlags::empty(),
265236
)
266-
.unwrap()
267-
.bytes;
237+
.unwrap();
238+
let nread = result.bytes;
268239
assert_eq!(
269240
i32::from_str(&String::from_utf8_lossy(&buffer[..nread])).unwrap(),
270241
*sum
271242
);
243+
// Don't ask me why, but this was seen to fail on FreeBSD. SocketAddrUnix::path()
244+
// returned None for some reason.
245+
#[cfg(not(target_os = "freebsd"))]
246+
assert_eq!(
247+
Some(rustix::net::SocketAddrAny::Unix(addr.clone())),
248+
result.address
249+
);
272250
}
273251

274252
let data_socket = socket(
@@ -305,6 +283,30 @@ fn test_unix_msg() {
305283
server.join().unwrap();
306284
}
307285

286+
#[cfg(not(any(target_os = "redox", target_os = "wasi")))]
287+
#[test]
288+
fn test_unix_msg() {
289+
let tmpdir = tempfile::tempdir().unwrap();
290+
let path = tmpdir.path().join("scp_4804");
291+
292+
let name = SocketAddrUnix::new(&path).unwrap();
293+
do_test_unix_msg(name);
294+
295+
unlinkat(cwd(), path, AtFlags::empty()).unwrap();
296+
}
297+
298+
#[cfg(any(target_os = "android", target_os = "linux"))]
299+
#[test]
300+
fn test_abstract_unix_msg() {
301+
use std::os::unix::ffi::OsStrExt;
302+
303+
let tmpdir = tempfile::tempdir().unwrap();
304+
let path = tmpdir.path().join("scp_4804");
305+
306+
let name = SocketAddrUnix::new_abstract_name(path.as_os_str().as_bytes()).unwrap();
307+
do_test_unix_msg(name);
308+
}
309+
308310
#[cfg(not(any(target_os = "redox", target_os = "wasi")))]
309311
#[test]
310312
fn test_unix_msg_with_scm_rights() {
@@ -318,31 +320,23 @@ fn test_unix_msg_with_scm_rights() {
318320

319321
let tmpdir = tempfile::tempdir().unwrap();
320322
let path = tmpdir.path().join("scp_4804");
321-
let ready = Arc::new((Mutex::new(false), Condvar::new()));
322323

323324
let server = {
324-
let ready = ready.clone();
325325
let path = path.clone();
326326

327-
move || {
328-
let connection_socket = socket(
329-
AddressFamily::UNIX,
330-
SocketType::SEQPACKET,
331-
Protocol::default(),
332-
)
333-
.unwrap();
334-
let mut pipe_end = None;
327+
let connection_socket = socket(
328+
AddressFamily::UNIX,
329+
SocketType::SEQPACKET,
330+
Protocol::default(),
331+
)
332+
.unwrap();
335333

336-
let name = SocketAddrUnix::new(&path).unwrap();
337-
bind_unix(&connection_socket, &name).unwrap();
338-
listen(&connection_socket, 1).unwrap();
334+
let name = SocketAddrUnix::new(&path).unwrap();
335+
bind_unix(&connection_socket, &name).unwrap();
336+
listen(&connection_socket, 1).unwrap();
339337

340-
{
341-
let (lock, cvar) = &*ready;
342-
let mut started = lock.lock().unwrap();
343-
*started = true;
344-
cvar.notify_all();
345-
}
338+
move || {
339+
let mut pipe_end = None;
346340

347341
let mut buffer = vec![0; BUFFER_SIZE];
348342
let mut cmsg_space = vec![0; rustix::cmsg_space!(ScmRights(1))];
@@ -403,14 +397,6 @@ fn test_unix_msg_with_scm_rights() {
403397
};
404398

405399
let client = move || {
406-
{
407-
let (lock, cvar) = &*ready;
408-
let mut started = lock.lock().unwrap();
409-
while !*started {
410-
started = cvar.wait(started).unwrap();
411-
}
412-
}
413-
414400
let addr = SocketAddrUnix::new(path).unwrap();
415401
let (read_end, write_end) = pipe().unwrap();
416402
let mut buffer = vec![0; BUFFER_SIZE];

0 commit comments

Comments
 (0)