From 1f4ca74cf9114beee462e1b3aa7a239b41cfc8ec Mon Sep 17 00:00:00 2001 From: Peter Cai Date: Sun, 9 Mar 2025 16:39:46 -0400 Subject: [PATCH 1/6] [DNM] Write to sockets in order, instead of out of order --- src/io_util.rs | 22 ++++++++++++++++++++++ src/main.rs | 27 ++++++++++++--------------- 2 files changed, 34 insertions(+), 15 deletions(-) diff --git a/src/io_util.rs b/src/io_util.rs index da1aa5b..c4455b4 100644 --- a/src/io_util.rs +++ b/src/io_util.rs @@ -145,6 +145,28 @@ impl<'a> WlMsgWriter<'a> { self.write_queue.push(msg); } + pub async fn write(&mut self, msg: WlRawMsg) -> io::Result<()> { + let (buf, fds) = msg.into_parts(); + + let mut written = 0usize; + + while written < buf.len() { + self.egress.writable().await?; + + let res = self + .egress + .send_with_fd(&buf[written..], unsafe { std::mem::transmute(fds.deref()) }); + + match res { + Ok(new_written) => written += new_written, + Err(e) if e.kind() == io::ErrorKind::WouldBlock => continue, + Err(e) => return Err(e), + } + } + + Ok(()) + } + /// Try to make progress by flushing some of the queued up messages into the stream. /// When this resolves, note that we might have only partially written. In that /// case the buffer is saved internally in this structure. diff --git a/src/main.rs b/src/main.rs index 95da1e7..0cddd42 100644 --- a/src/main.rs +++ b/src/main.rs @@ -141,7 +141,7 @@ impl<'a> ConnDuplex<'a> { match verdict { WlMitmVerdict::Allowed => { - self.downstream_write.queue_write(wl_raw_msg); + self.downstream_write.write(wl_raw_msg).await?; } WlMitmVerdict::Terminate => { return Err(io::Error::new( @@ -180,18 +180,20 @@ impl<'a> ConnDuplex<'a> { match verdict { WlMitmVerdict::Allowed => { - self.upstream_write.queue_write(wl_raw_msg); + self.upstream_write.write(wl_raw_msg).await?; } WlMitmVerdict::Rejected(error_code) => { - self.downstream_write.queue_write( - WlDisplayErrorEvent::new( - WL_DISPLAY_OBJECT_ID, - wl_raw_msg.obj_id, - error_code, - "Rejected by wl-mitm", + self.downstream_write + .write( + WlDisplayErrorEvent::new( + WL_DISPLAY_OBJECT_ID, + wl_raw_msg.obj_id, + error_code, + "Rejected by wl-mitm", + ) + .build(), ) - .build(), - ); + .await?; } WlMitmVerdict::Terminate => { return Err(io::Error::new( @@ -213,11 +215,6 @@ impl<'a> ConnDuplex<'a> { pub async fn run_to_completion(mut self) -> io::Result<()> { loop { tokio::select! { - biased; - - res = self.downstream_write.dequeue_write() => res?, - res = self.upstream_write.dequeue_write() => res?, - msg = self.upstream_read.read() => { control_flow!(self.handle_s2c_event(msg?).await?); } From 5a7c4aa781d4e0cea3cffa805d04841454b6e58a Mon Sep 17 00:00:00 2001 From: Peter Cai Date: Sun, 9 Mar 2025 16:51:03 -0400 Subject: [PATCH 2/6] Enable biased again --- src/main.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main.rs b/src/main.rs index 0cddd42..8fbdd79 100644 --- a/src/main.rs +++ b/src/main.rs @@ -215,6 +215,8 @@ impl<'a> ConnDuplex<'a> { pub async fn run_to_completion(mut self) -> io::Result<()> { loop { tokio::select! { + biased; + msg = self.upstream_read.read() => { control_flow!(self.handle_s2c_event(msg?).await?); } From fd5bbe69150adea53fe2d686603f51211a8af999 Mon Sep 17 00:00:00 2001 From: Peter Cai Date: Sun, 9 Mar 2025 20:11:04 -0400 Subject: [PATCH 3/6] Add some clarification on filtering --- README.md | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/README.md b/README.md index f674dee..9431d2d 100644 --- a/README.md +++ b/README.md @@ -52,6 +52,32 @@ This repo contains an example configuration at `config.toml` that allows a few b desktop apps to function. It also demonstrates the use of `ask_cmd` and `notify_cmd` by defining filters on clipboard-related requests. Detailed explanation of the configuration format is also contained in the example. +To launch a program under `wl-mitm`, set its `WAYLAND_DISPLAY` env variable to whatever `listen` is under `[socket]` in `config.toml`. +Note that you may want to use another container and pass _only_ the `wl-mitm`'d socket through for proper isolation. + +A Word on Filtering +--- + +`wl-mitm` gives you a ton of flexibility on filtering. In particular, you are allowed to filter _any_ request on _any_ object +in `config.toml`. + +However, please keep in mind that while most requests can be filtered, Wayland (and most of its protocols) is not designed to handle +this type of filtering. While a lot of requests can be filtered without consequences, there are a significant number of them that +will result in irrecoverable de-sync between the client and the server. For example, _any_ message that creates a new object ID +(of the type `new_id`) _will_ result in undefined behavior if filtered. + +`wl-mitm` does provide the ability to _reject_ a request with an explicit error. Unfortunately, errors in Wayland are usually +fatal, and clients are not designed to recover from them. + +The most reliable (and the only protocol-compliant) way to prevent a client from using certain features is to block the +entire global object where that feature resides. These are usually "manager" objects that live in the global registry, +and can be blocked in `allowed_globals` inside `config.toml`. Each extension protocol has to expose _some_ manager object +in order for clients to access their functionalities, and thus blocking them there will prevent clients from even knowing +their existence. However, globals can't be selectively blocked using `ask_cmd`, because clients usually bind to them as soon +as they start. + +Since `notify_cmd` never blocks a request, it is safe to use on _any_ request filter. + XWayland --- From d133a2faa1732f8e3573fa81ef628fcc70a778f9 Mon Sep 17 00:00:00 2001 From: Peter Cai Date: Sat, 15 Mar 2025 10:18:36 -0400 Subject: [PATCH 4/6] Fix protocol generation for empty events / requests --- protogen/src/main.rs | 187 +++++++++++++++++++++++-------------------- 1 file changed, 101 insertions(+), 86 deletions(-) diff --git a/protogen/src/main.rs b/protogen/src/main.rs index 89fee9d..88c2d17 100644 --- a/protogen/src/main.rs +++ b/protogen/src/main.rs @@ -206,33 +206,44 @@ fn handle_interface( let mut event_opcode = 0; let mut request_opcode = 0; + let mut add_msg = |reader: &mut quick_xml::Reader<&[u8]>, + e: quick_xml::events::BytesStart<'_>, + is_empty: bool| { + let start_tag = + str::from_utf8(e.local_name().into_inner()).expect("Unable to parse start tag"); + if start_tag == "event" { + // An event! Increment our opcode tracker for it! + event_opcode += 1; + msgs.push(handle_request_or_event( + reader, + event_opcode - 1, + WlMsgType::Event, + interface_name_snake, + e, + is_empty, + )); + } else if start_tag == "request" { + // A request! Increment our opcode tracker for it! + request_opcode += 1; + msgs.push(handle_request_or_event( + reader, + request_opcode - 1, + WlMsgType::Request, + interface_name_snake, + e, + is_empty, + )); + }; + }; + loop { match reader.read_event().expect("Unable to parse XML file") { Event::Eof => panic!("Unexpected EOF"), Event::Start(e) => { - let start_tag = - str::from_utf8(e.local_name().into_inner()).expect("Unable to parse start tag"); - if start_tag == "event" { - // An event! Increment our opcode tracker for it! - event_opcode += 1; - msgs.push(handle_request_or_event( - reader, - event_opcode - 1, - WlMsgType::Event, - interface_name_snake, - e, - )); - } else if start_tag == "request" { - // A request! Increment our opcode tracker for it! - request_opcode += 1; - msgs.push(handle_request_or_event( - reader, - request_opcode - 1, - WlMsgType::Request, - interface_name_snake, - e, - )); - }; + add_msg(reader, e, false); + } + Event::Empty(e) => { + add_msg(reader, e, true); } Event::End(e) if e.local_name() == start.local_name() => break, _ => continue, @@ -251,6 +262,7 @@ fn handle_request_or_event( msg_type: WlMsgType, interface_name_snake: &str, start: quick_xml::events::BytesStart<'_>, + is_empty: bool, ) -> WlMsg { let name_attr = start .attributes() @@ -278,77 +290,80 @@ fn handle_request_or_event( // Load arguments and their types from XML let mut args: Vec<(String, WlArgType)> = Vec::new(); - loop { - match reader.read_event().expect("Unable to parse XML file") { - Event::Eof => panic!("Unexpected EOF"), - Event::Empty(e) - if str::from_utf8(e.local_name().into_inner()).expect("utf8 encoding error") - == "arg" => - { - let mut name: Option = None; - let mut tt: Option = None; - let mut interface_name: Option = None; + if !is_empty { + loop { + match reader.read_event().expect("Unable to parse XML file") { + Event::Eof => panic!("Unexpected EOF"), + Event::Empty(e) + if str::from_utf8(e.local_name().into_inner()) + .expect("utf8 encoding error") + == "arg" => + { + let mut name: Option = None; + let mut tt: Option = None; + let mut interface_name: Option = None; - for attr in e.attributes() { - let attr = attr.expect("attr parsing error"); - let attr_name = str::from_utf8(attr.key.local_name().into_inner()) - .expect("utf8 encoding error"); - if attr_name == "name" { - name = Some( - str::from_utf8(&attr.value) - .expect("utf8 encoding error") - .to_string(), - ); - } else if attr_name == "type" { - tt = Some(WlArgType::parse( - str::from_utf8(&attr.value).expect("utf8 encoding error"), - )); - } else if attr_name == "interface" { - interface_name = Some( - str::from_utf8(&attr.value) - .expect("utf8 encoding error") - .to_string(), - ); + for attr in e.attributes() { + let attr = attr.expect("attr parsing error"); + let attr_name = str::from_utf8(attr.key.local_name().into_inner()) + .expect("utf8 encoding error"); + if attr_name == "name" { + name = Some( + str::from_utf8(&attr.value) + .expect("utf8 encoding error") + .to_string(), + ); + } else if attr_name == "type" { + tt = Some(WlArgType::parse( + str::from_utf8(&attr.value).expect("utf8 encoding error"), + )); + } else if attr_name == "interface" { + interface_name = Some( + str::from_utf8(&attr.value) + .expect("utf8 encoding error") + .to_string(), + ); + } } - } - if let Some(ref mut name) = name { - if name == "type" { - *name = "_type".to_string(); - } else if name == "msg" { - *name = "_msg".to_string(); + if let Some(ref mut name) = name { + if name == "type" { + *name = "_type".to_string(); + } else if name == "msg" { + *name = "_msg".to_string(); + } } - } - if let Some(WlArgType::NewId(_)) = tt { - if let Some(interface_name) = interface_name { - tt.as_mut().unwrap().set_interface_name(interface_name); - } else { - // Unspecified interface for new_id; special serialization format! - args.push(( - format!( - "{}_interface_name", - name.as_ref().expect("needs an arg name!") - ), - WlArgType::String, - )); - args.push(( - format!( - "{}_interface_version", - name.as_ref().expect("needs an arg name!") - ), - WlArgType::Uint, - )) + if let Some(WlArgType::NewId(_)) = tt { + if let Some(interface_name) = interface_name { + tt.as_mut().unwrap().set_interface_name(interface_name); + } else { + // Unspecified interface for new_id; special serialization format! + args.push(( + format!( + "{}_interface_name", + name.as_ref().expect("needs an arg name!") + ), + WlArgType::String, + )); + args.push(( + format!( + "{}_interface_version", + name.as_ref().expect("needs an arg name!") + ), + WlArgType::Uint, + )) + } } - } - args.push(( - name.expect("args must have a name"), - tt.expect("args must have a type"), - )); + args.push(( + name.expect("args must have a name"), + tt.expect("args must have a type"), + )); + } + Event::End(e) if e.local_name() == start.local_name() => break, + _ => continue, } - Event::End(e) if e.local_name() == start.local_name() => break, - _ => continue, } } From fe9d51ccbe617f9d2bd6185b20870b2c31befe6b Mon Sep 17 00:00:00 2001 From: Peter Cai Date: Wed, 19 Mar 2025 20:58:25 -0400 Subject: [PATCH 5/6] refactor: Separate AnyWlParsedMessage from the underlying WlParsedMessage trait --- protogen/src/types.rs | 36 ++++--------- src/proto.rs | 117 +++++++++++++++++++++++++++++------------- src/state.rs | 51 +++++++++--------- 3 files changed, 118 insertions(+), 86 deletions(-) diff --git a/protogen/src/types.rs b/protogen/src/types.rs index e61657e..e79606c 100644 --- a/protogen/src/types.rs +++ b/protogen/src/types.rs @@ -192,28 +192,20 @@ impl WlMsg { #opcode } - fn self_opcode(&self) -> u16 { - #opcode - } - fn object_type() -> crate::objects::WlObjectType { crate::proto::#interface_name_snake_upper } - fn self_object_type(&self) -> crate::objects::WlObjectType { - crate::proto::#interface_name_snake_upper - } - - fn self_msg_name(&self) -> &'static str { + fn msg_name() -> &'static str { #msg_name_snake } - fn static_type_id() -> std::any::TypeId { - std::any::TypeId::of::<#struct_name<'static>>() + fn is_destructor() -> bool { + #is_destructor } - fn self_static_type_id(&self) -> std::any::TypeId { - std::any::TypeId::of::<#struct_name<'static>>() + fn num_consumed_fds() -> usize { + #num_consumed_fds } #[allow(unused, private_interfaces, non_snake_case)] @@ -231,28 +223,22 @@ impl WlMsg { }) } - fn obj_id(&self) -> u32 { + fn _obj_id(&self) -> u32 { self.obj_id } - fn is_destructor(&self) -> bool { - #is_destructor - } - - fn known_objects_created(&self) -> Option> { + fn _known_objects_created(&self) -> Option> { #known_objects_created } - fn to_json(&self) -> String { + fn _to_json(&self) -> String { serde_json::to_string(self).unwrap() } - - fn num_consumed_fds(&self) -> usize { - #num_consumed_fds - } } - unsafe impl<'a> crate::proto::AnyWlParsedMessage<'a> for #struct_name<'a> {} + unsafe impl<'a> crate::proto::DowncastableWlParsedMessage<'a> for #struct_name<'a> { + type Static = #struct_name<'static>; + } pub struct #parser_fn_name; diff --git a/src/proto.rs b/src/proto.rs index 7263b04..c3314c3 100644 --- a/src/proto.rs +++ b/src/proto.rs @@ -35,27 +35,27 @@ impl WaylandProtocolParsingOutcome { /// Internal module used to seal the [WlParsedMessage] trait mod __private { - pub(super) trait WlParsedMessagePrivate: Send {} + pub(super) trait WlParsedMessagePrivate: Sized {} pub(super) struct WlParsedMessagePrivateToken; } #[allow(private_bounds, private_interfaces)] pub trait WlParsedMessage<'a>: __private::WlParsedMessagePrivate { - fn opcode() -> u16 - where - Self: Sized; - fn object_type() -> WlObjectType - where - Self: Sized; - fn static_type_id() -> TypeId - where - Self: Sized; + fn opcode() -> u16; + fn object_type() -> WlObjectType; + fn msg_name() -> &'static str; + /// Is this request / event a destructor? That is, does it destroy [Self::obj_id()]? + fn is_destructor() -> bool; + /// How many fds have been consumed in parsing this message? + /// This is used to return any unused fds to the decoder. + fn num_consumed_fds() -> usize; + fn try_from_msg<'obj>( objects: &'obj WlObjects, msg: &'a WlRawMsg, ) -> WaylandProtocolParsingOutcome where - Self: Sized + 'a, + Self: 'a, { // Verify object type and opcode if objects.lookup_object(msg.obj_id) != Some(Self::object_type()) { @@ -74,19 +74,10 @@ pub trait WlParsedMessage<'a>: __private::WlParsedMessagePrivate { _token: __private::WlParsedMessagePrivateToken, ) -> WaylandProtocolParsingOutcome where - Self: Sized + 'a; - - // dyn-available methods - fn self_opcode(&self) -> u16; - fn self_object_type(&self) -> WlObjectType; - fn self_msg_name(&self) -> &'static str; - fn self_static_type_id(&self) -> TypeId; + Self: 'a; /// The object ID which this message acts upon - fn obj_id(&self) -> u32; - - /// Is this request / event a destructor? That is, does it destroy [Self::obj_id()]? - fn is_destructor(&self) -> bool; + fn _obj_id(&self) -> u32; /// List of (object id, object type) pairs created by this message /// Note that this only includes objects created with a fixed, known interface @@ -94,27 +85,79 @@ pub trait WlParsedMessage<'a>: __private::WlParsedMessagePrivate { /// serialized differently, and are not included here. However, the only /// widely-used message with that capability is [WlRegistryBindRequest], /// which is already handled separately on its own. - fn known_objects_created(&self) -> Option>; + fn _known_objects_created(&self) -> Option>; /// Serialize this message into a JSON string, for use with ask scripts - fn to_json(&self) -> String; - - /// How many fds have been consumed in parsing this message? - /// This is used to return any unused fds to the decoder. - fn num_consumed_fds(&self) -> usize; + fn _to_json(&self) -> String; } /// A version of [WlParsedMessage] that supports downcasting. By implementing this -/// trait, you assert that the [WlParsedMessage::static_type_id] and -/// [WlParsedMessage::self_static_type_id] implementations are sound: they MUST -/// return the [TypeId] of the implementing struct, assuming it had static lifetime. +/// trait, you assert that the [DowncastableWlParsedMessage::Static] MUST correspond +/// to a version of the implementor type with a static lifetime. /// /// Any implementor also asserts that the type implementing this trait /// does not contain any lifetime other than 'a, and that the implenetor struct is /// _covariant_ with respect to lifetime 'a. /// /// This is required for the soundness of the downcast_ref implementation. -pub unsafe trait AnyWlParsedMessage<'a>: WlParsedMessage<'a> {} +pub unsafe trait DowncastableWlParsedMessage<'a>: Send + WlParsedMessage<'a> { + type Static: 'static; +} + +/// The implementation of dyn-available methods and downcasting for +/// [DowncastableWlParsedMessage] +pub trait AnyWlParsedMessage<'a>: Send { + fn static_type_id(&self) -> TypeId; + fn opcode(&self) -> u16; + fn object_type(&self) -> WlObjectType; + fn msg_name(&self) -> &'static str; + fn is_destructor(&self) -> bool; + fn obj_id(&self) -> u32; + fn known_objects_created(&self) -> Option>; + fn to_json(&self) -> String; + fn num_consumed_fds(&self) -> usize; +} + +impl<'a, T> AnyWlParsedMessage<'a> for T +where + T: DowncastableWlParsedMessage<'a>, +{ + fn static_type_id(&self) -> TypeId { + TypeId::of::() + } + + fn opcode(&self) -> u16 { + T::opcode() + } + + fn object_type(&self) -> WlObjectType { + T::object_type() + } + + fn msg_name(&self) -> &'static str { + T::msg_name() + } + + fn is_destructor(&self) -> bool { + T::is_destructor() + } + + fn obj_id(&self) -> u32 { + T::_obj_id(self) + } + + fn known_objects_created(&self) -> Option> { + T::_known_objects_created(self) + } + + fn to_json(&self) -> String { + T::_to_json(self) + } + + fn num_consumed_fds(&self) -> usize { + T::num_consumed_fds() + } +} impl<'out, 'data: 'out> dyn AnyWlParsedMessage<'data> + 'data { /// Downcast the type-erased, borrowed Wayland message to a concrete type. Note that the @@ -123,8 +166,12 @@ impl<'out, 'data: 'out> dyn AnyWlParsedMessage<'data> + 'data { /// 1. 'data outlives 'out (guaranteed by the trait bound above) /// 2. The type implementing [AnyWlParsedMessage] does not contain any lifetime other than /// 'data (or 'a in the trait's definition). - pub fn downcast_ref + 'data>(&'out self) -> Option<&'out T> { - if self.self_static_type_id() != T::static_type_id() { + pub fn downcast_ref< + T: AnyWlParsedMessage<'data> + DowncastableWlParsedMessage<'data> + 'data, + >( + &'out self, + ) -> Option<&'out T> { + if self.static_type_id() != TypeId::of::() { return None; } @@ -154,7 +201,7 @@ pub trait WlMsgParserFn: Send + Sync { /// Messages that can be converted back to [WlRawMsg] pub trait WlConstructableMessage<'a>: Sized + WlParsedMessage<'a> { fn build(&self) -> WlRawMsg { - WlRawMsg::build(self.obj_id(), Self::opcode(), |buf, fds| { + WlRawMsg::build(self._obj_id(), Self::opcode(), |buf, fds| { self.build_inner(buf, fds) }) } diff --git a/src/state.rs b/src/state.rs index c659ac4..29c7449 100644 --- a/src/state.rs +++ b/src/state.rs @@ -8,10 +8,9 @@ use crate::{ objects::WlObjects, proto::{ AnyWlParsedMessage, WaylandProtocolParsingOutcome, WlDisplayDeleteIdEvent, - WlKeyboardEnterEvent, WlParsedMessage, WlPointerEnterEvent, WlRegistryBindRequest, - WlRegistryGlobalEvent, WlRegistryGlobalRemoveEvent, WlTouchDownEvent, - XdgSurfaceGetToplevelRequest, XdgToplevelSetAppIdRequest, XdgToplevelSetTitleRequest, - XdgWmBaseGetXdgSurfaceRequest, + WlKeyboardEnterEvent, WlPointerEnterEvent, WlRegistryBindRequest, WlRegistryGlobalEvent, + WlRegistryGlobalRemoveEvent, WlTouchDownEvent, XdgSurfaceGetToplevelRequest, + XdgToplevelSetAppIdRequest, XdgToplevelSetTitleRequest, XdgWmBaseGetXdgSurfaceRequest, }, }; @@ -133,7 +132,7 @@ impl WlMitmState { is_half_destroyed = self.objects.is_half_destroyed(id), "Trying to create object via message {}::{} but the object ID is already used!", parent_obj.interface(), - msg.self_msg_name() + msg.msg_name() ); return false; @@ -145,7 +144,7 @@ impl WlMitmState { obj_id = id, "Created object via message {}::{}", parent_obj.interface(), - msg.self_msg_name() + msg.msg_name() ); self.objects.record_object(tt, id); } @@ -164,7 +163,7 @@ impl WlMitmState { obj_id = msg.obj_id(), "Object destructed via destructor {}::{}", obj_type.interface(), - msg.self_msg_name() + msg.msg_name() ); self.objects.remove_object(msg.obj_id(), from_client); @@ -184,8 +183,8 @@ impl WlMitmState { desc: &str, ) -> tokio::process::Command { let mut cmd = tokio::process::Command::new(cmd_str); - cmd.arg(msg.self_object_type().interface()); - cmd.arg(msg.self_msg_name()); + cmd.arg(msg.object_type().interface()); + cmd.arg(msg.msg_name()); cmd.arg(desc); cmd.env("WL_MITM_MSG_JSON", msg.to_json()); @@ -250,8 +249,8 @@ impl WlMitmState { num_fds = raw_msg.fds.len(), num_consumed_fds = msg.num_consumed_fds(), "{}::{}", - msg.self_object_type().interface(), - msg.self_msg_name(), + msg.object_type().interface(), + msg.msg_name(), ) } @@ -260,7 +259,7 @@ impl WlMitmState { if self.objects.is_half_destroyed(msg.obj_id()) { error!( obj_id = msg.obj_id(), - opcode = msg.self_opcode(), + opcode = msg.opcode(), "Client request detected on object already scheduled for destruction; aborting!" ); return outcome.terminate(); @@ -334,11 +333,11 @@ impl WlMitmState { .config .filter .requests - .get(msg.self_object_type().interface()) + .get(msg.object_type().interface()) { if let Some(filtered) = filtered_requests .iter() - .find(|f| f.requests.contains(msg.self_msg_name())) + .find(|f| f.requests.contains(msg.msg_name())) { match filtered.action { WlFilterRequestAction::Ask => { @@ -346,8 +345,8 @@ impl WlMitmState { info!( ask_cmd = ask_cmd, "Running ask command for {}::{}", - msg.self_object_type().interface(), - msg.self_msg_name() + msg.object_type().interface(), + msg.msg_name() ); let mut cmd = self.prepare_command( @@ -360,8 +359,8 @@ impl WlMitmState { if !status.success() { warn!( "Blocked {}::{} because of return status {}", - msg.self_object_type().interface(), - msg.self_msg_name(), + msg.object_type().interface(), + msg.msg_name(), status ); @@ -379,8 +378,8 @@ impl WlMitmState { warn!( "Blocked {}::{} because of missing ask_cmd", - msg.self_object_type().interface(), - msg.self_msg_name() + msg.object_type().interface(), + msg.msg_name() ); return match filtered.block_type { WlFilterRequestBlockType::Ignore => outcome.filtered(), @@ -394,8 +393,8 @@ impl WlMitmState { info!( notify_cmd = notify_cmd, "Running notify command for {}::{}", - msg.self_object_type().interface(), - msg.self_msg_name() + msg.object_type().interface(), + msg.msg_name() ); let mut cmd = self.prepare_command( @@ -410,8 +409,8 @@ impl WlMitmState { WlFilterRequestAction::Block => { warn!( "Blocked {}::{}", - msg.self_object_type().interface(), - msg.self_msg_name() + msg.object_type().interface(), + msg.msg_name() ); return match filtered.block_type { WlFilterRequestBlockType::Ignore => outcome.filtered(), @@ -458,8 +457,8 @@ impl WlMitmState { num_fds = raw_msg.fds.len(), num_consumed_fds = msg.num_consumed_fds(), "{}::{}", - msg.self_object_type().interface(), - msg.self_msg_name(), + msg.object_type().interface(), + msg.msg_name(), ) } From 4dd591c4926a4d55e0098c99084dc4ce4bf91125 Mon Sep 17 00:00:00 2001 From: Peter Cai Date: Wed, 19 Mar 2025 21:21:52 -0400 Subject: [PATCH 6/6] Remove the <'a> lifetime parameter on AnyWlParsedMessage --- protogen/src/types.rs | 2 +- src/proto.rs | 22 +++++++++++----------- src/state.rs | 4 ++-- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/protogen/src/types.rs b/protogen/src/types.rs index e79606c..e301b72 100644 --- a/protogen/src/types.rs +++ b/protogen/src/types.rs @@ -247,7 +247,7 @@ impl WlMsg { &self, objects: &'obj crate::proto::WlObjects, msg: &'msg crate::codec::WlRawMsg, - ) -> crate::proto::WaylandProtocolParsingOutcome + 'msg>> { + ) -> crate::proto::WaylandProtocolParsingOutcome> { #struct_name::try_from_msg(objects, msg).map(|r| Box::new(r) as Box<_>) } } diff --git a/src/proto.rs b/src/proto.rs index c3314c3..aaff1cd 100644 --- a/src/proto.rs +++ b/src/proto.rs @@ -106,7 +106,7 @@ pub unsafe trait DowncastableWlParsedMessage<'a>: Send + WlParsedMessage<'a> { /// The implementation of dyn-available methods and downcasting for /// [DowncastableWlParsedMessage] -pub trait AnyWlParsedMessage<'a>: Send { +pub trait AnyWlParsedMessage: Send { fn static_type_id(&self) -> TypeId; fn opcode(&self) -> u16; fn object_type(&self) -> WlObjectType; @@ -118,9 +118,9 @@ pub trait AnyWlParsedMessage<'a>: Send { fn num_consumed_fds(&self) -> usize; } -impl<'a, T> AnyWlParsedMessage<'a> for T +impl<'a, T> AnyWlParsedMessage for T where - T: DowncastableWlParsedMessage<'a>, + T: DowncastableWlParsedMessage<'a> + 'a, { fn static_type_id(&self) -> TypeId { TypeId::of::() @@ -159,17 +159,17 @@ where } } -impl<'out, 'data: 'out> dyn AnyWlParsedMessage<'data> + 'data { +impl<'out, 'data: 'out> dyn AnyWlParsedMessage + 'data { /// Downcast the type-erased, borrowed Wayland message to a concrete type. Note that the /// safety of this relies on a few invariants: /// /// 1. 'data outlives 'out (guaranteed by the trait bound above) - /// 2. The type implementing [AnyWlParsedMessage] does not contain any lifetime other than + /// 2. The type implementing [DowncastableWlParsedMessage] does not contain any lifetime other than /// 'data (or 'a in the trait's definition). pub fn downcast_ref< - T: AnyWlParsedMessage<'data> + DowncastableWlParsedMessage<'data> + 'data, + T: AnyWlParsedMessage + DowncastableWlParsedMessage<'data> + 'data, >( - &'out self, + &'data self, ) -> Option<&'out T> { if self.static_type_id() != TypeId::of::() { return None; @@ -177,7 +177,7 @@ impl<'out, 'data: 'out> dyn AnyWlParsedMessage<'data> + 'data { // SAFETY: We have verified the type IDs match up. // - // In addition, because [AnyWlParsedMessage]'s contract requires that no + // In addition, because [DowncastableWlParsedMessage]'s contract requires that no // lifetime other than 'a ('data) is contained in the implemetor, the // output type T cannot contain another lifetime that may be transmuted // by this unsafe block. @@ -195,7 +195,7 @@ pub trait WlMsgParserFn: Send + Sync { &self, objects: &'obj WlObjects, msg: &'msg WlRawMsg, - ) -> WaylandProtocolParsingOutcome + 'msg>>; + ) -> WaylandProtocolParsingOutcome>; } /// Messages that can be converted back to [WlRawMsg] @@ -234,7 +234,7 @@ static WL_EVENT_REQUEST_PARSERS: LazyLock<( pub fn decode_event<'obj, 'msg>( objects: &'obj WlObjects, msg: &'msg WlRawMsg, -) -> WaylandProtocolParsingOutcome + 'msg>> { +) -> WaylandProtocolParsingOutcome> { let Some(obj_type) = objects.lookup_object(msg.obj_id) else { return WaylandProtocolParsingOutcome::Unknown; }; @@ -254,7 +254,7 @@ pub fn decode_event<'obj, 'msg>( pub fn decode_request<'obj, 'msg>( objects: &'obj WlObjects, msg: &'msg WlRawMsg, -) -> WaylandProtocolParsingOutcome + 'msg>> { +) -> WaylandProtocolParsingOutcome> { let Some(obj_type) = objects.lookup_object(msg.obj_id) else { return WaylandProtocolParsingOutcome::Unknown; }; diff --git a/src/state.rs b/src/state.rs index 29c7449..cdec137 100644 --- a/src/state.rs +++ b/src/state.rs @@ -117,7 +117,7 @@ impl WlMitmState { /// is not handled here. fn handle_created_or_destroyed_objects( &mut self, - msg: &dyn AnyWlParsedMessage<'_>, + msg: &dyn AnyWlParsedMessage, from_client: bool, ) -> bool { if let Some(created_objects) = msg.known_objects_created() { @@ -178,7 +178,7 @@ impl WlMitmState { fn prepare_command( &self, - msg: &dyn AnyWlParsedMessage<'_>, + msg: &dyn AnyWlParsedMessage, cmd_str: &str, desc: &str, ) -> tokio::process::Command {