1 //! Types and routines used to manipulate arguments from the wire format
2
3 use std::ffi::CStr;
4 use std::io::Result as IoResult;
5 use std::os::unix::io::RawFd;
6 use std::ptr;
7
8 use nix::Error as NixError;
9
10 use crate::protocol::{Argument, ArgumentType, Message};
11
12 use smallvec::SmallVec;
13
14 /// Error generated when trying to serialize a message into buffers
15 #[derive(Debug)]
16 pub enum MessageWriteError {
17 /// The buffer is too small to hold the message contents
18 BufferTooSmall,
19 /// The message contains a FD that could not be dup-ed
20 DupFdFailed(std::io::Error),
21 }
22
23 impl std::error::Error for MessageWriteError {}
24
25 #[cfg(not(tarpaulin_include))]
26 impl std::fmt::Display for MessageWriteError {
fmt(&self, f: &mut ::std::fmt::Formatter) -> Result<(), ::std::fmt::Error>27 fn fmt(&self, f: &mut ::std::fmt::Formatter) -> Result<(), ::std::fmt::Error> {
28 match self {
29 MessageWriteError::BufferTooSmall => {
30 f.write_str("The provided buffer is too small to hold message content.")
31 }
32 MessageWriteError::DupFdFailed(e) => {
33 write!(
34 f,
35 "The message contains a file descriptor that could not be dup()-ed ({}).",
36 e
37 )
38 }
39 }
40 }
41 }
42
43 /// Error generated when trying to deserialize a message from buffers
44 #[derive(Debug, Clone)]
45 pub enum MessageParseError {
46 /// The message references a FD but the buffer FD is empty
47 MissingFD,
48 /// More data is needed to deserialize the message
49 MissingData,
50 /// The message is malformed and cannot be parsed
51 Malformed,
52 }
53
54 impl std::error::Error for MessageParseError {}
55
56 #[cfg(not(tarpaulin_include))]
57 impl std::fmt::Display for MessageParseError {
fmt(&self, f: &mut ::std::fmt::Formatter) -> Result<(), ::std::fmt::Error>58 fn fmt(&self, f: &mut ::std::fmt::Formatter) -> Result<(), ::std::fmt::Error> {
59 match *self {
60 MessageParseError::MissingFD => {
61 f.write_str("The message references a FD but the buffer FD is empty.")
62 }
63 MessageParseError::MissingData => {
64 f.write_str("More data is needed to deserialize the message")
65 }
66 MessageParseError::Malformed => {
67 f.write_str("The message is malformed and cannot be parsed")
68 }
69 }
70 }
71 }
72
73 /// Serialize the contents of this message into provided buffers
74 ///
75 /// Returns the number of elements written in each buffer
76 ///
77 /// Any serialized Fd will be `dup()`-ed in the process
write_to_buffers( msg: &Message<u32>, payload: &mut [u32], mut fds: &mut [RawFd], ) -> Result<(usize, usize), MessageWriteError>78 pub fn write_to_buffers(
79 msg: &Message<u32>,
80 payload: &mut [u32],
81 mut fds: &mut [RawFd],
82 ) -> Result<(usize, usize), MessageWriteError> {
83 let orig_payload_len = payload.len();
84 let orig_fds_len = fds.len();
85 // Helper function to write a u32 or a RawFd to its buffer
86 fn write_buf<T>(u: T, payload: &mut [T]) -> Result<&mut [T], MessageWriteError> {
87 if let Some((head, tail)) = payload.split_first_mut() {
88 *head = u;
89 Ok(tail)
90 } else {
91 Err(MessageWriteError::BufferTooSmall)
92 }
93 }
94
95 // Helper function to write byte arrays in payload
96 fn write_array_to_payload<'a>(
97 array: &[u8],
98 payload: &'a mut [u32],
99 ) -> Result<&'a mut [u32], MessageWriteError> {
100 let array_len = array.len();
101 let word_len = array_len / 4 + if array_len % 4 != 0 { 1 } else { 0 };
102 // need enough space to store the whole array with padding and a size header
103 if payload.len() < 1 + word_len {
104 return Err(MessageWriteError::BufferTooSmall);
105 }
106 // size header
107 payload[0] = array_len as u32;
108 let (buffer_slice, rest) = payload[1..].split_at_mut(word_len);
109 unsafe {
110 ptr::copy(array.as_ptr(), buffer_slice.as_mut_ptr() as *mut u8, array_len);
111 }
112 Ok(rest)
113 }
114
115 let free_size = payload.len();
116 if free_size < 2 {
117 return Err(MessageWriteError::BufferTooSmall);
118 }
119
120 let (header, mut payload) = payload.split_at_mut(2);
121
122 // we store all fds we dup-ed in this, which will auto-close
123 // them on drop, if any of the `?` early-returns
124 let mut pending_fds = FdStore::new();
125
126 // write the contents in the buffer
127 for arg in &msg.args {
128 // Just to make the borrow checker happy
129 let old_payload = payload;
130 match *arg {
131 Argument::Int(i) => payload = write_buf(i as u32, old_payload)?,
132 Argument::Uint(u) => payload = write_buf(u, old_payload)?,
133 Argument::Fixed(f) => payload = write_buf(f as u32, old_payload)?,
134 Argument::Str(ref s) => {
135 payload = write_array_to_payload(s.as_bytes_with_nul(), old_payload)?;
136 }
137 Argument::Object(o) => payload = write_buf(o, old_payload)?,
138 Argument::NewId(n) => payload = write_buf(n, old_payload)?,
139 Argument::Array(ref a) => {
140 payload = write_array_to_payload(a, old_payload)?;
141 }
142 Argument::Fd(fd) => {
143 let old_fds = fds;
144 let dup_fd = dup_fd_cloexec(fd).map_err(MessageWriteError::DupFdFailed)?;
145 pending_fds.push(dup_fd);
146 fds = write_buf(dup_fd, old_fds)?;
147 payload = old_payload;
148 }
149 }
150 }
151
152 // we reached here, all writing was successful
153 // no FD needs to be closed
154 pending_fds.clear();
155
156 let wrote_size = (free_size - payload.len()) * 4;
157 header[0] = msg.sender_id;
158 header[1] = ((wrote_size as u32) << 16) | u32::from(msg.opcode);
159 Ok((orig_payload_len - payload.len(), orig_fds_len - fds.len()))
160 }
161
162 /// Attempts to parse a single wayland message with the given signature.
163 ///
164 /// If the buffers contains several messages, only the first one will be parsed,
165 /// and the unused tail of the buffers is returned. If a single message was present,
166 /// the returned slices should thus be empty.
167 ///
168 /// Errors if the message is malformed.
169 #[allow(clippy::type_complexity)]
parse_message<'a, 'b>( raw: &'a [u32], signature: &[ArgumentType], fds: &'b [RawFd], ) -> Result<(Message<u32>, &'a [u32], &'b [RawFd]), MessageParseError>170 pub fn parse_message<'a, 'b>(
171 raw: &'a [u32],
172 signature: &[ArgumentType],
173 fds: &'b [RawFd],
174 ) -> Result<(Message<u32>, &'a [u32], &'b [RawFd]), MessageParseError> {
175 // helper function to read arrays
176 fn read_array_from_payload(
177 array_len: usize,
178 payload: &[u32],
179 ) -> Result<(&[u8], &[u32]), MessageParseError> {
180 let word_len = array_len / 4 + if array_len % 4 != 0 { 1 } else { 0 };
181 if word_len > payload.len() {
182 return Err(MessageParseError::MissingData);
183 }
184 let (array_contents, rest) = payload.split_at(word_len);
185 let array = unsafe {
186 ::std::slice::from_raw_parts(array_contents.as_ptr() as *const u8, array_len)
187 };
188 Ok((array, rest))
189 }
190
191 if raw.len() < 2 {
192 return Err(MessageParseError::MissingData);
193 }
194
195 let sender_id = raw[0];
196 let word_2 = raw[1];
197 let opcode = (word_2 & 0x0000_FFFF) as u16;
198 let len = (word_2 >> 16) as usize / 4;
199
200 if len < 2 || len > raw.len() {
201 return Err(MessageParseError::Malformed);
202 }
203
204 let (mut payload, rest) = raw.split_at(len);
205 payload = &payload[2..];
206 let mut fds = fds;
207
208 let arguments = signature
209 .iter()
210 .map(|argtype| {
211 if let ArgumentType::Fd = *argtype {
212 // don't consume input but fd
213 if let Some((&front, tail)) = fds.split_first() {
214 fds = tail;
215 Ok(Argument::Fd(front))
216 } else {
217 Err(MessageParseError::MissingFD)
218 }
219 } else if let Some((&front, mut tail)) = payload.split_first() {
220 let arg =
221 match *argtype {
222 ArgumentType::Int => Ok(Argument::Int(front as i32)),
223 ArgumentType::Uint => Ok(Argument::Uint(front)),
224 ArgumentType::Fixed => Ok(Argument::Fixed(front as i32)),
225 ArgumentType::Str(_) => read_array_from_payload(front as usize, tail)
226 .and_then(|(v, rest)| {
227 tail = rest;
228 match CStr::from_bytes_with_nul(v) {
229 Ok(s) => Ok(Argument::Str(Box::new(s.into()))),
230 Err(_) => Err(MessageParseError::Malformed),
231 }
232 }),
233 ArgumentType::Object(_) => Ok(Argument::Object(front)),
234 ArgumentType::NewId(_) => Ok(Argument::NewId(front)),
235 ArgumentType::Array(_) => read_array_from_payload(front as usize, tail)
236 .map(|(v, rest)| {
237 tail = rest;
238 Argument::Array(Box::new(v.into()))
239 }),
240 ArgumentType::Fd => unreachable!(),
241 };
242 payload = tail;
243 arg
244 } else {
245 Err(MessageParseError::MissingData)
246 }
247 })
248 .collect::<Result<SmallVec<_>, MessageParseError>>()?;
249
250 let msg = Message { sender_id, opcode, args: arguments };
251 Ok((msg, rest, fds))
252 }
253
254 /// Duplicate a `RawFd` and set the CLOEXEC flag on the copy
dup_fd_cloexec(fd: RawFd) -> IoResult<RawFd>255 pub fn dup_fd_cloexec(fd: RawFd) -> IoResult<RawFd> {
256 use nix::fcntl;
257 match fcntl::fcntl(fd, fcntl::FcntlArg::F_DUPFD_CLOEXEC(0)) {
258 Ok(newfd) => Ok(newfd),
259 Err(NixError::EINVAL) => {
260 // F_DUPFD_CLOEXEC is not recognized, kernel too old, fallback
261 // to setting CLOEXEC manually
262 let newfd = fcntl::fcntl(fd, fcntl::FcntlArg::F_DUPFD(0))?;
263
264 let flags = fcntl::fcntl(newfd, fcntl::FcntlArg::F_GETFD);
265 let result = flags
266 .map(|f| fcntl::FdFlag::from_bits(f).unwrap() | fcntl::FdFlag::FD_CLOEXEC)
267 .and_then(|f| fcntl::fcntl(newfd, fcntl::FcntlArg::F_SETFD(f)));
268 match result {
269 Ok(_) => {
270 // setting the O_CLOEXEC worked
271 Ok(newfd)
272 }
273 Err(e) => {
274 // something went wrong in F_GETFD or F_SETFD
275 let _ = ::nix::unistd::close(newfd);
276 Err(e.into())
277 }
278 }
279 }
280 Err(e) => Err(e.into()),
281 }
282 }
283
284 /*
285 * utility struct that closes every FD it contains on drop
286 */
287
288 struct FdStore {
289 fds: Vec<RawFd>,
290 }
291
292 impl FdStore {
new() -> FdStore293 fn new() -> FdStore {
294 FdStore { fds: Vec::new() }
295 }
push(&mut self, fd: RawFd)296 fn push(&mut self, fd: RawFd) {
297 self.fds.push(fd);
298 }
clear(&mut self)299 fn clear(&mut self) {
300 self.fds.clear();
301 }
302 }
303
304 impl Drop for FdStore {
drop(&mut self)305 fn drop(&mut self) {
306 use nix::unistd::close;
307 for fd in self.fds.drain(..) {
308 // not much can be done if we can't close that anyway...
309 let _ = close(fd);
310 }
311 }
312 }
313
314 #[cfg(test)]
315 mod tests {
316 use super::*;
317 use crate::protocol::AllowNull;
318 use smallvec::smallvec;
319 use std::ffi::CString;
320
321 #[test]
into_from_raw_cycle()322 fn into_from_raw_cycle() {
323 let mut bytes_buffer = vec![0; 1024];
324 let mut fd_buffer = vec![0; 10];
325
326 let msg = Message {
327 sender_id: 42,
328 opcode: 7,
329 args: smallvec![
330 Argument::Uint(3),
331 Argument::Fixed(-89),
332 Argument::Str(Box::new(CString::new(&b"I like trains!"[..]).unwrap())),
333 Argument::Array(vec![1, 2, 3, 4, 5, 6, 7, 8, 9].into()),
334 Argument::Object(88),
335 Argument::NewId(56),
336 Argument::Int(-25),
337 ],
338 };
339 // write the message to the buffers
340 write_to_buffers(&msg, &mut bytes_buffer[..], &mut fd_buffer[..]).unwrap();
341 // read them back
342 let (rebuilt, _, _) = parse_message(
343 &bytes_buffer[..],
344 &[
345 ArgumentType::Uint,
346 ArgumentType::Fixed,
347 ArgumentType::Str(AllowNull::No),
348 ArgumentType::Array(AllowNull::No),
349 ArgumentType::Object(AllowNull::No),
350 ArgumentType::NewId(AllowNull::No),
351 ArgumentType::Int,
352 ],
353 &fd_buffer[..],
354 )
355 .unwrap();
356 assert_eq!(rebuilt, msg);
357 }
358 }
359