Skip to content

Commit 5a5c4cc

Browse files
viccie30cathay4t
authored andcommitted
Fix parsing of abstract Unix socket addresses
These start with a null byte, don't end with a null byte and can contain embedded null bytes.
1 parent fb5402c commit 5a5c4cc

3 files changed

Lines changed: 139 additions & 21 deletions

File tree

src/unix/nlas.rs

Lines changed: 70 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,77 @@
11
// SPDX-License-Identifier: MIT
22

33
use netlink_packet_core::{
4-
buffer, emit_u32, fields, getter, parse_string, parse_u32, parse_u8,
5-
setter, DecodeError, DefaultNla, Emitable, ErrorContext, NlaBuffer,
6-
Parseable,
4+
buffer, emit_u32, fields, getter, parse_u32, parse_u8, setter, DecodeError,
5+
DefaultNla, Emitable, ErrorContext, NlaBuffer, Parseable,
6+
};
7+
use std::{
8+
ffi::{CStr, OsString},
9+
os::unix::ffi::{OsStrExt, OsStringExt},
710
};
811

912
use crate::constants::*;
1013

14+
#[derive(Debug, Eq, PartialEq, Clone)]
15+
pub enum UnixDiagName {
16+
/// Filesystem pathname to which the socket was bound.
17+
Pathname(OsString),
18+
/// Abstract socket address to which the socket was bound.
19+
Abstract(Vec<u8>),
20+
}
21+
22+
impl<T: AsRef<[u8]>> Parseable<T> for UnixDiagName {
23+
fn parse(buf: &T) -> Result<Self, DecodeError> {
24+
let buf = buf.as_ref();
25+
if let Some((0, address)) = buf.split_first() {
26+
Ok(UnixDiagName::Abstract(address.to_owned()))
27+
} else {
28+
Ok(UnixDiagName::Pathname(OsString::from_vec(
29+
CStr::from_bytes_with_nul(buf)
30+
.map_err(|e| {
31+
DecodeError::from(format!(
32+
"pathname is not null-terminated: {e}"
33+
))
34+
})?
35+
.to_owned()
36+
.into(),
37+
)))
38+
}
39+
}
40+
}
41+
42+
impl Emitable for UnixDiagName {
43+
fn buffer_len(&self) -> usize {
44+
match self {
45+
UnixDiagName::Pathname(pathname) => pathname.len() + 1,
46+
UnixDiagName::Abstract(address) => address.len() + 1,
47+
}
48+
}
49+
50+
fn emit(&self, buffer: &mut [u8]) {
51+
match self {
52+
UnixDiagName::Pathname(pathname) => {
53+
let (last, first) = buffer
54+
.split_last_mut()
55+
.expect("buffer should not be empty");
56+
first.copy_from_slice(pathname.as_bytes());
57+
*last = 0;
58+
}
59+
UnixDiagName::Abstract(address) => {
60+
let (first, last) = buffer
61+
.split_first_mut()
62+
.expect("buffer should not be empty");
63+
*first = 0;
64+
last.copy_from_slice(address);
65+
}
66+
}
67+
}
68+
}
69+
1170
#[derive(Debug, Eq, PartialEq, Clone)]
1271
pub enum Nla {
13-
/// Path to which the socket was bound. This attribute is known as
72+
/// Name to which the socket was bound. This attribute is known as
1473
/// `UNIX_DIAG_NAME` in the kernel.
15-
Name(String),
74+
Name(UnixDiagName),
1675
/// VFS information for this socket. This attribute is known as
1776
/// `UNIX_DIAG_VFS` in the kernel.
1877
Vfs(Vfs),
@@ -238,8 +297,7 @@ impl netlink_packet_core::Nla for Nla {
238297
fn value_len(&self) -> usize {
239298
use self::Nla::*;
240299
match *self {
241-
// +1 because we need to append a null byte
242-
Name(ref s) => s.len() + 1,
300+
Name(ref s) => s.buffer_len(),
243301
Vfs(_) => VFS_LEN,
244302
Peer(_) => 4,
245303
PendingConnections(ref v) => 4 * v.len(),
@@ -253,10 +311,7 @@ impl netlink_packet_core::Nla for Nla {
253311
fn emit_value(&self, buffer: &mut [u8]) {
254312
use self::Nla::*;
255313
match *self {
256-
Name(ref s) => {
257-
buffer[..s.len()].copy_from_slice(s.as_bytes());
258-
buffer[s.len()] = 0;
259-
}
314+
Name(ref s) => s.emit(buffer),
260315
Vfs(ref value) => value.emit(buffer),
261316
Peer(value) => emit_u32(buffer, value).unwrap(),
262317
PendingConnections(ref values) => {
@@ -293,10 +348,10 @@ impl<'a, T: AsRef<[u8]> + ?Sized> Parseable<NlaBuffer<&'a T>> for Nla {
293348
fn parse(buf: &NlaBuffer<&'a T>) -> Result<Self, DecodeError> {
294349
let payload = buf.value();
295350
Ok(match buf.kind() {
296-
UNIX_DIAG_NAME => {
297-
let err = "invalid UNIX_DIAG_NAME value";
298-
Self::Name(parse_string(payload).context(err)?)
299-
}
351+
UNIX_DIAG_NAME => Self::Name(
352+
UnixDiagName::parse(&payload)
353+
.context("invalid UNIX_DIAG_NAME value")?,
354+
),
300355
UNIX_DIAG_VFS => {
301356
let err = "invalid UNIX_DIAG_VFS value";
302357
let buf = VfsBuffer::new_checked(payload).context(err)?;

src/unix/response.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use smallvec::SmallVec;
1010

1111
use crate::{
1212
constants::*,
13-
unix::nlas::{MemInfo, Nla},
13+
unix::nlas::{MemInfo, Nla, UnixDiagName},
1414
};
1515

1616
pub const UNIX_RESPONSE_HEADER_LEN: usize = 16;
@@ -89,7 +89,7 @@ impl UnixResponse {
8989
})
9090
}
9191

92-
pub fn name(&self) -> Option<&String> {
92+
pub fn name(&self) -> Option<&UnixDiagName> {
9393
self.nlas.iter().find_map(|nla| {
9494
if let Nla::Name(name) = nla {
9595
Some(name)

src/unix/tests.rs

Lines changed: 67 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,9 @@ use netlink_packet_core::{Emitable, Parseable};
55
use crate::{
66
constants::*,
77
unix::{
8-
nlas::Nla, ShowFlags, StateFlags, UnixRequest, UnixResponse,
9-
UnixResponseBuffer, UnixResponseHeader,
8+
nlas::{Nla, UnixDiagName},
9+
ShowFlags, StateFlags, UnixRequest, UnixResponse, UnixResponseBuffer,
10+
UnixResponseHeader,
1011
},
1112
};
1213

@@ -39,7 +40,7 @@ lazy_static! {
3940
cookie: [0xa0, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
4041
},
4142
nlas: smallvec![
42-
Nla::Name("/tmp/.ICE-unix/1151".to_string()),
43+
Nla::Name(UnixDiagName::Pathname("/tmp/.ICE-unix/1151".into())),
4344
Nla::ReceiveQueueLength(0, 128),
4445
Nla::Shutdown(0),
4546
]
@@ -101,7 +102,7 @@ lazy_static! {
101102
cookie: [0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
102103
},
103104
nlas: smallvec![
104-
Nla::Name("/run/user/1000/bus".to_string()),
105+
Nla::Name(UnixDiagName::Pathname("/run/user/1000/bus".into())),
105106
Nla::Peer(31062),
106107
Nla::ReceiveQueueLength(0, 0),
107108
Nla::Shutdown(0),
@@ -165,3 +166,65 @@ fn emit_socket_info() {
165166
SOCKET_INFO.emit(&mut buf);
166167
assert_eq!(&buf[..], &SOCKET_INFO_BUF[..]);
167168
}
169+
170+
lazy_static! {
171+
static ref ABSTRACT_ADDRESS: UnixResponse = UnixResponse {
172+
header: UnixResponseHeader {
173+
kind: SOCK_STREAM,
174+
state: TCP_LISTEN,
175+
inode: 20238,
176+
cookie: [0xa0, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
177+
},
178+
nlas: smallvec![
179+
Nla::Name(UnixDiagName::Abstract(
180+
"1c1440c5f5e2a52e/bus/systemd/\0/bus-api-user".into()
181+
)),
182+
Nla::ReceiveQueueLength(0, 128),
183+
Nla::Shutdown(0),
184+
]
185+
};
186+
}
187+
188+
#[rustfmt::skip]
189+
static ABSTRACT_ADDRESS_BUF: [u8; 84] = [
190+
0x01, // family: AF_UNIX
191+
0x01, // type: SOCK_STREAM
192+
0x0a, // state: TCP_LISTEN
193+
0x00, // padding
194+
0x0e, 0x4f, 0x00, 0x00, // inode number
195+
0xa0, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // cookie
196+
197+
// NLAs
198+
0x30, 0x00, // length: 48
199+
0x00, 0x00, // type: UNIX_DIAG_NAME
200+
// value: \01c1440c5f5e2a52e/bus/systemd/\0/bus-api-user
201+
0x00, 0x31, 0x63, 0x31, 0x34, 0x34, 0x30, 0x63, 0x35, 0x66, 0x35, 0x65, 0x32, 0x61, 0x35, 0x32, 0x65, 0x2f, 0x62, 0x75, 0x73, 0x2f, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x64, 0x2f, 0x00, 0x2f, 0x62, 0x75, 0x73, 0x2d, 0x61, 0x70, 0x69, 0x2d, 0x75, 0x73, 0x65, 0x72,
202+
203+
0x0c, 0x00, // length: 12
204+
0x04, 0x00, // type: UNIX_DIAG_RQLEN
205+
// value: ReceiveQueueLength(0, 128)
206+
0x00, 0x00, 0x00, 0x00,
207+
0x80, 0x00, 0x00, 0x00,
208+
209+
0x05, 0x00, // length: 5
210+
0x06, 0x00, // type: UNIX_DIAG_SHUTDOWN
211+
0x00, // value: 0
212+
0x00, 0x00, 0x00 // padding
213+
];
214+
215+
#[test]
216+
fn parse_abstract_address() {
217+
let parsed = UnixResponse::parse(
218+
&UnixResponseBuffer::new_checked(&&ABSTRACT_ADDRESS_BUF[..]).unwrap(),
219+
)
220+
.unwrap();
221+
assert_eq!(parsed, *ABSTRACT_ADDRESS);
222+
}
223+
224+
#[test]
225+
fn emit_abstract_address() {
226+
assert_eq!(ABSTRACT_ADDRESS.buffer_len(), 84);
227+
let mut buf = vec![0xff; ABSTRACT_ADDRESS.buffer_len()];
228+
ABSTRACT_ADDRESS.emit(&mut buf);
229+
assert_eq!(&buf[..], &ABSTRACT_ADDRESS_BUF[..]);
230+
}

0 commit comments

Comments
 (0)