From 991e2271c993076ae43c62e9e139a9e261f2dca7 Mon Sep 17 00:00:00 2001 From: Grzegorz Nosek Date: Sat, 16 Nov 2024 08:04:48 +0100 Subject: [PATCH] fix(event)!: use typed-path for filesystem paths This should make them work consistently on Windows, where e.g. PT_FSPATH still describes a Unix-style path. Signed-off-by: Grzegorz Nosek --- Cargo.lock | 8 ++ falco_event/Cargo.toml | 1 + falco_event/src/fields/types.rs | 4 +- falco_event/src/types/mod.rs | 1 + falco_event/src/types/net/sockaddr.rs | 24 ++-- falco_event/src/types/net/socktuple.rs | 20 ++- falco_event/src/types/path/absolute_path.rs | 117 ++++++++++++++---- falco_event/src/types/path/mod.rs | 5 + falco_event/src/types/path/relative_path.rs | 26 ++-- falco_event_derive/src/serde_custom.rs | 7 ++ falco_plugin_tests/Cargo.toml | 1 + falco_plugin_tests/tests/scap.rs | 7 +- falco_plugin_tests/tests/scap_import_table.rs | 7 +- 13 files changed, 158 insertions(+), 70 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index cdd8cdb3..181ff434 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -174,6 +174,7 @@ dependencies = [ "serde", "serde_json", "thiserror", + "typed-path", ] [[package]] @@ -244,6 +245,7 @@ dependencies = [ "falco_plugin_runner", "log", "pkg-config", + "typed-path", ] [[package]] @@ -652,6 +654,12 @@ dependencies = [ "syn 2.0.71", ] +[[package]] +name = "typed-path" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82205ffd44a9697e34fc145491aa47310f9871540bb7909eaa9365e0a9a46607" + [[package]] name = "unicode-ident" version = "1.0.12" diff --git a/falco_event/Cargo.toml b/falco_event/Cargo.toml index 6e545f7c..c43589c6 100644 --- a/falco_event/Cargo.toml +++ b/falco_event/Cargo.toml @@ -24,6 +24,7 @@ bitflags = { version = "2.4.2" } anyhow = "1.0.81" chrono = "0.4.38" serde = { version = "1.0.210", features = ["derive"], optional = true } +typed-path = "0.9.3" [target.'cfg(target_os = "linux")'.dependencies] nix = { version = "0.29.0", features = ["signal"] } diff --git a/falco_event/src/fields/types.rs b/falco_event/src/fields/types.rs index 2e93f08d..5707ee5e 100644 --- a/falco_event/src/fields/types.rs +++ b/falco_event/src/fields/types.rs @@ -24,9 +24,9 @@ use std::ffi::CStr; pub use std::net::IpAddr as PT_IPADDR; pub use std::net::Ipv4Addr as PT_IPV4ADDR; pub use std::net::Ipv6Addr as PT_IPV6ADDR; -pub use std::path::Path as PT_FSPATH; pub use std::time::Duration as PT_RELTIME; pub use std::time::SystemTime as PT_ABSTIME; +pub use typed_path::UnixPath as PT_FSPATH; /// Signed 8-bit value ([i8]) pub type PT_INT8 = i8; @@ -97,7 +97,7 @@ pub mod owned { pub use crate::types::OwnedSockTuple as PT_SOCKTUPLE; pub use std::ffi::CString as PT_CHARBUF; use std::ffi::CString; - pub use std::path::PathBuf as PT_FSPATH; + pub use typed_path::UnixPathBuf as PT_FSPATH; /// Arbitrary (owned) byte buffer (`Vec`) pub type PT_BYTEBUF = Vec; diff --git a/falco_event/src/types/mod.rs b/falco_event/src/types/mod.rs index b3253daa..632eb4c7 100644 --- a/falco_event/src/types/mod.rs +++ b/falco_event/src/types/mod.rs @@ -12,6 +12,7 @@ mod utf_chunked; #[cfg(feature = "serde")] pub mod serde { pub use super::bytebuf::serde::*; + pub use super::path::serde::*; pub use super::string::serde::*; } diff --git a/falco_event/src/types/net/sockaddr.rs b/falco_event/src/types/net/sockaddr.rs index fca757d5..c17853a0 100644 --- a/falco_event/src/types/net/sockaddr.rs +++ b/falco_event/src/types/net/sockaddr.rs @@ -3,11 +3,9 @@ use crate::ffi::{PPM_AF_INET, PPM_AF_INET6, PPM_AF_LOCAL, PPM_AF_UNSPEC}; use crate::types::format::Format; use crate::types::{Borrow, Borrowed, EndpointV4, EndpointV6}; use byteorder::{ReadBytesExt, WriteBytesExt}; -use std::ffi::OsStr; use std::fmt::Formatter; use std::io::Write; -use std::os::unix::ffi::OsStrExt; -use std::path::{Path, PathBuf}; +use typed_path::{UnixPath, UnixPathBuf}; /// A socket address #[cfg_attr(feature = "serde", derive(serde::Serialize))] @@ -15,7 +13,9 @@ use std::path::{Path, PathBuf}; #[derive(Debug)] pub enum SockAddr<'a> { /// Unix sockets - Unix(&'a Path), + Unix( + #[cfg_attr(feature = "serde", serde(with = "crate::types::serde::unix_path"))] &'a UnixPath, + ), /// IPv4 sockets V4(EndpointV4), @@ -71,9 +71,9 @@ impl<'a> FromBytes<'a> for SockAddr<'a> { let variant = buf.read_u8()?; match variant as u32 { PPM_AF_LOCAL => { - let path = ::from_bytes(buf); - *buf = &[]; - Ok(Self::Unix(Path::new(path))) + // TODO embedded NULs + let path = std::mem::take(buf); + Ok(Self::Unix(UnixPath::new(path))) } PPM_AF_INET => { let addr = EndpointV4::from_bytes(buf)?; @@ -97,7 +97,7 @@ where fn format(&self, fmt: &mut Formatter) -> std::fmt::Result { match self { SockAddr::Unix(u) => { - let bytes = u.as_os_str().as_bytes(); + let bytes = u.as_bytes(); fmt.write_str("unix://")?; bytes.format(fmt) } @@ -114,7 +114,9 @@ where #[derive(Debug)] pub enum OwnedSockAddr { /// Unix sockets - Unix(PathBuf), + Unix( + #[cfg_attr(feature = "serde", serde(with = "crate::types::serde::unix_path"))] UnixPathBuf, + ), /// IPv4 sockets V4(EndpointV4), @@ -150,11 +152,11 @@ impl Borrow for OwnedSockAddr { mod tests { use crate::types::{OwnedSockAddr, Port, SockAddr}; use std::net::{Ipv4Addr, Ipv6Addr}; - use std::path::Path; + use typed_path::UnixPath; #[test] fn test_serde_sockaddr_unix() { - let path = Path::new("/path/to/unix"); + let path = UnixPath::new("/path/to/unix"); let sockaddr = SockAddr::Unix(path); let json = serde_json::to_string(&sockaddr).unwrap(); diff --git a/falco_event/src/types/net/socktuple.rs b/falco_event/src/types/net/socktuple.rs index 53370aac..f507182d 100644 --- a/falco_event/src/types/net/socktuple.rs +++ b/falco_event/src/types/net/socktuple.rs @@ -1,6 +1,5 @@ use std::fmt::{Debug, Display, Formatter}; use std::io::Write; -use std::path::{Path, PathBuf}; use crate::ffi::{PPM_AF_INET, PPM_AF_INET6, PPM_AF_LOCAL}; use crate::fields::{FromBytes, FromBytesResult, ToBytes}; @@ -8,6 +7,7 @@ use crate::types::format::Format; use crate::types::net::endpoint::{EndpointV4, EndpointV6}; use crate::types::{Borrow, Borrowed}; use byteorder::{ReadBytesExt, WriteBytesExt}; +use typed_path::{UnixPath, UnixPathBuf}; /// Socket tuple: describing both endpoints of a connection #[cfg_attr(feature = "serde", derive(serde::Serialize))] @@ -21,7 +21,8 @@ pub enum SockTuple<'a> { /// destination socket kernel pointer dest_ptr: u64, /// filesystem path to the socket - path: &'a Path, + #[cfg_attr(feature = "serde", serde(with = "crate::types::serde::unix_path"))] + path: &'a UnixPath, }, /// IPv4 connection @@ -146,7 +147,7 @@ impl<'a> FromBytes<'a> for SockTuple<'a> { impl Format for SockTuple<'_> where - for<'a> &'a Path: Format, + for<'a> &'a UnixPath: Format, EndpointV4: Format, EndpointV6: Format, { @@ -187,7 +188,8 @@ pub enum OwnedSockTuple { /// destination socket kernel pointer dest_ptr: u64, /// filesystem path to the socket - path: PathBuf, + #[cfg_attr(feature = "serde", serde(with = "crate::types::serde::unix_path"))] + path: UnixPathBuf, }, /// IPv4 connection @@ -249,7 +251,6 @@ mod tests { use super::*; use crate::types::Port; use std::net::{Ipv4Addr, Ipv6Addr}; - use std::os::unix::ffi::OsStrExt; use std::str::FromStr; #[test] @@ -324,10 +325,7 @@ mod tests { assert_eq!(source_addr, 0); assert_eq!(dest_addr, 0xffff98bc4ecf2000); - assert_eq!( - path.as_os_str().as_bytes(), - b"/var/run/nscd/socket".as_slice() - ); + assert_eq!(path.as_bytes(), b"/var/run/nscd/socket".as_slice()); assert_eq!(binary, binary2.as_slice(),); } @@ -337,12 +335,12 @@ mod tests { mod serde_tests { use crate::types::{OwnedSockTuple, Port, SockTuple}; use std::net::{Ipv4Addr, Ipv6Addr}; - use std::path::Path; use std::str::FromStr; + use typed_path::UnixPath; #[test] fn test_serde_socktuple_unix() { - let path = Path::new("/path/to/unix"); + let path = UnixPath::new("/path/to/unix"); let sockaddr = SockTuple::Unix { source_ptr: 1, dest_ptr: 2, diff --git a/falco_event/src/types/path/absolute_path.rs b/falco_event/src/types/path/absolute_path.rs index 1d7f7cf0..17504387 100644 --- a/falco_event/src/types/path/absolute_path.rs +++ b/falco_event/src/types/path/absolute_path.rs @@ -1,28 +1,25 @@ -use std::ffi::{CStr, OsStr}; -use std::fmt::Formatter; -use std::io::Write; -use std::os::unix::ffi::OsStrExt; -use std::path::{Path, PathBuf}; - use crate::event_derive::{FromBytes, FromBytesResult, ToBytes}; use crate::types::format::Format; use crate::types::{Borrow, Borrowed}; +use std::ffi::CStr; +use std::fmt::Formatter; +use std::io::Write; +use typed_path::{UnixPath, UnixPathBuf}; -impl<'a> FromBytes<'a> for &'a Path { +impl<'a> FromBytes<'a> for &'a UnixPath { fn from_bytes(buf: &mut &'a [u8]) -> FromBytesResult { let buf = <&CStr>::from_bytes(buf)?; - let osstr = OsStr::from_bytes(buf.to_bytes()); - Ok(Path::new(osstr)) + Ok(UnixPath::new(buf.to_bytes())) } } -impl ToBytes for &Path { +impl ToBytes for &UnixPath { fn binary_size(&self) -> usize { - self.as_os_str().len() + self.as_bytes().len() + 1 } fn write(&self, mut writer: W) -> std::io::Result<()> { - self.as_os_str().as_bytes().write(&mut writer)?; + self.as_bytes().write(&mut writer)?; 0u8.write(writer) } @@ -31,59 +28,127 @@ impl ToBytes for &Path { } } -impl Format for &Path +impl Format for &UnixPath where for<'a> &'a [u8]: Format, { fn format(&self, fmt: &mut Formatter) -> std::fmt::Result { - let bytes = self.as_os_str().as_bytes(); + let bytes = self.as_bytes(); bytes.format(fmt) } } -impl Borrowed for Path { - type Owned = PathBuf; +impl Borrowed for UnixPath { + type Owned = UnixPathBuf; } -impl Borrow for PathBuf { - type Borrowed<'a> = &'a Path; +impl Borrow for UnixPathBuf { + type Borrowed<'a> = &'a UnixPath; fn borrow(&self) -> Self::Borrowed<'_> { self.as_path() } } +#[cfg(feature = "serde")] +pub mod serde { + pub mod unix_path { + use crate::types::utf_chunked::{OwnedUtfChunked, UtfChunked}; + use serde::{Deserialize, Deserializer, Serialize, Serializer}; + use typed_path::{UnixPath, UnixPathBuf}; + + pub fn serialize(path: &UnixPath, ser: S) -> Result { + if ser.is_human_readable() { + let chunks = UtfChunked::from(path.as_bytes()); + chunks.serialize(ser) + } else { + path.as_bytes().serialize(ser) + } + } + + pub fn deserialize<'de, D: Deserializer<'de>>(de: D) -> Result { + let chunks: OwnedUtfChunked = Deserialize::deserialize(de)?; + Ok(UnixPathBuf::from(chunks.into_vec())) + } + } + + pub mod unix_path_option { + use crate::types::utf_chunked::UtfChunked; + use serde::{Serialize, Serializer}; + use typed_path::UnixPath; + + pub fn serialize( + path: &Option<&UnixPath>, + ser: S, + ) -> Result { + if ser.is_human_readable() { + let chunks = path.as_ref().map(|path| UtfChunked::from(path.as_bytes())); + chunks.serialize(ser) + } else { + path.map(|p| p.as_bytes()).serialize(ser) + } + } + } + + pub mod unix_path_option_owned { + use crate::types::utf_chunked::{OwnedUtfChunked, UtfChunked}; + use serde::{Deserialize, Deserializer, Serialize, Serializer}; + use typed_path::UnixPathBuf; + + pub fn serialize( + path: &Option, + ser: S, + ) -> Result { + let chunks = path.as_ref().map(|path| UtfChunked::from(path.as_bytes())); + chunks.serialize(ser) + } + + pub fn deserialize<'de, D: Deserializer<'de>>( + de: D, + ) -> Result, D::Error> { + let chunks: Option = Deserialize::deserialize(de)?; + Ok(chunks.map(|c| UnixPathBuf::from(c.into_vec()))) + } + } +} + #[cfg(test)] mod tests { - use std::path::{Path, PathBuf}; - use std::str::FromStr; - use crate::event_derive::{FromBytes, ToBytes}; + use std::str::FromStr; + use typed_path::{UnixPath, UnixPathBuf}; #[test] fn test_absolute_path() { - let path = PathBuf::from_str("/foo").unwrap(); + let path = UnixPathBuf::from_str("/foo").unwrap(); let mut binary = Vec::new(); + assert_eq!(path.as_path().binary_size(), 5); + path.as_path().write(&mut binary).unwrap(); hexdump::hexdump(binary.as_slice()); assert_eq!(binary.as_slice(), "/foo\0".as_bytes()); let mut buf = binary.as_slice(); - let path = <&Path>::from_bytes(&mut buf).unwrap(); + let path = <&UnixPath>::from_bytes(&mut buf).unwrap(); assert_eq!(path.to_str().unwrap(), "/foo"); } + #[cfg(feature = "serde")] #[test] fn test_serde_absolute_path() { - let path = Path::new("/foo"); + #[derive(serde::Deserialize, serde::Serialize)] + #[serde(transparent)] + struct SerPathBuf(#[serde(with = "super::serde::unix_path")] UnixPathBuf); + + let path = SerPathBuf(UnixPathBuf::from("/foo")); let json = serde_json::to_string(&path).unwrap(); assert_eq!(json, "\"/foo\""); - let path2: PathBuf = serde_json::from_str(&json).unwrap(); - assert_eq!(path2, path); + let path2: SerPathBuf = serde_json::from_str(&json).unwrap(); + assert_eq!(path2.0, path.0); let json2 = serde_json::to_string(&path2).unwrap(); assert_eq!(json, json2); diff --git a/falco_event/src/types/path/mod.rs b/falco_event/src/types/path/mod.rs index 3fbd1da8..2e516f54 100644 --- a/falco_event/src/types/path/mod.rs +++ b/falco_event/src/types/path/mod.rs @@ -2,3 +2,8 @@ mod absolute_path; mod relative_path; pub use relative_path::*; + +#[cfg(feature = "serde")] +pub mod serde { + pub use super::absolute_path::serde::*; +} diff --git a/falco_event/src/types/path/relative_path.rs b/falco_event/src/types/path/relative_path.rs index be4fb947..73105adf 100644 --- a/falco_event/src/types/path/relative_path.rs +++ b/falco_event/src/types/path/relative_path.rs @@ -3,8 +3,7 @@ use crate::types::format::Format; use crate::types::{Borrow, Borrowed}; use std::fmt::Formatter; use std::io::Write; -use std::os::unix::ffi::OsStrExt; -use std::path::{Path, PathBuf}; +use typed_path::{UnixPath, UnixPathBuf}; /// A relative path /// @@ -13,7 +12,9 @@ use std::path::{Path, PathBuf}; /// a method called `name_dirfd` that returns the corresponding `dirfd` (as an `Option`) #[cfg_attr(feature = "serde", derive(serde::Serialize))] #[derive(Debug)] -pub struct RelativePath<'a>(pub &'a Path); +pub struct RelativePath<'a>( + #[cfg_attr(feature = "serde", serde(with = "crate::types::serde::unix_path"))] pub &'a UnixPath, +); impl<'a> ToBytes for RelativePath<'a> { fn binary_size(&self) -> usize { @@ -25,13 +26,13 @@ impl<'a> ToBytes for RelativePath<'a> { } fn default_repr() -> impl ToBytes { - <&'a Path>::default_repr() + <&'a UnixPath>::default_repr() } } impl<'a> FromBytes<'a> for RelativePath<'a> { fn from_bytes(buf: &mut &'a [u8]) -> FromBytesResult { - Ok(Self(<&'a Path>::from_bytes(buf)?)) + Ok(Self(<&'a UnixPath>::from_bytes(buf)?)) } } @@ -42,7 +43,7 @@ where fn format(&self, fmt: &mut Formatter) -> std::fmt::Result { write!(fmt, "<...>")?; - let bytes = self.0.as_os_str().as_bytes(); + let bytes = self.0.as_bytes(); bytes.format(fmt) } } @@ -53,7 +54,9 @@ where /// a method called `name_dirfd` that returns the corresponding `dirfd` (as an `Option`) #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] #[derive(Debug)] -pub struct OwnedRelativePath(pub PathBuf); +pub struct OwnedRelativePath( + #[cfg_attr(feature = "serde", serde(with = "crate::types::serde::unix_path"))] pub UnixPathBuf, +); impl<'a> Borrowed for RelativePath<'a> { type Owned = OwnedRelativePath; @@ -69,7 +72,6 @@ impl Borrow for OwnedRelativePath { #[cfg(test)] mod tests { - use std::path::PathBuf; use std::str::FromStr; use crate::event_derive::{FromBytes, ToBytes}; @@ -77,12 +79,11 @@ mod tests { #[cfg(feature = "serde")] use crate::types::OwnedRelativePath; - #[cfg(feature = "serde")] - use std::path::Path; + use typed_path::UnixPathBuf; #[test] fn test_relative_path() { - let path = PathBuf::from_str("/foo").unwrap(); + let path = UnixPathBuf::from_str("/foo").unwrap(); let rel_path = RelativePath(path.as_path()); let mut binary = Vec::new(); @@ -99,7 +100,8 @@ mod tests { #[cfg(feature = "serde")] #[test] fn test_serde_relative_path() { - let path = RelativePath(Path::new("/foo")); + use typed_path::UnixPath; + let path = RelativePath(UnixPath::new("/foo")); let json = serde_json::to_string(&path).unwrap(); assert_eq!(json, "\"/foo\""); diff --git a/falco_event_derive/src/serde_custom.rs b/falco_event_derive/src/serde_custom.rs index 9d8f7ddb..6431a6fc 100644 --- a/falco_event_derive/src/serde_custom.rs +++ b/falco_event_derive/src/serde_custom.rs @@ -11,6 +11,7 @@ pub fn serde_with_tag(ty: &Ident) -> Option { "PT_CHARBUF_PAIR_ARRAY" => { Some(quote! { #[serde(with = "crate::event_derive::serde::cstr_pair_array")] }) } + "PT_FSPATH" => Some(quote! { #[serde(with = "crate::event_derive::serde::unix_path")] }), _ => None, } } @@ -27,6 +28,9 @@ pub fn serde_with_option_tag(ty: &Ident) -> Option { "PT_CHARBUF_PAIR_ARRAY" => { Some(quote! { #[serde(with = "crate::event_derive::serde::cstr_pair_array_option")] }) } + "PT_FSPATH" => { + Some(quote! { #[serde(with = "crate::event_derive::serde::unix_path_option")] }) + } _ => None, } } @@ -45,6 +49,9 @@ pub fn serde_with_option_tag_owned(ty: &Ident) -> Option { "PT_CHARBUF_PAIR_ARRAY" => Some( quote! { #[serde(with = "crate::event_derive::serde::cstr_pair_array_option_owned")] }, ), + "PT_FSPATH" => { + Some(quote! { #[serde(with = "crate::event_derive::serde::unix_path_option_owned")] }) + } _ => None, } } diff --git a/falco_plugin_tests/Cargo.toml b/falco_plugin_tests/Cargo.toml index 727577d6..11c37575 100644 --- a/falco_plugin_tests/Cargo.toml +++ b/falco_plugin_tests/Cargo.toml @@ -10,6 +10,7 @@ cxx = { version = "1.0.124", features = ["c++17"] } falco_plugin = { version = "0.4.0", path = "../falco_plugin" } falco_plugin_runner = { version = "0.4.0", path = "../falco_plugin_runner" } log = "0.4.22" +typed-path = "0.9.3" [build-dependencies] cxx-build = "1.0.124" diff --git a/falco_plugin_tests/tests/scap.rs b/falco_plugin_tests/tests/scap.rs index 9484d6c1..de96f937 100644 --- a/falco_plugin_tests/tests/scap.rs +++ b/falco_plugin_tests/tests/scap.rs @@ -64,14 +64,13 @@ mod tests { init_plugin, instantiate_sinsp_tests, CapturingTestDriver, SavefileTestDriver, ScapStatus, }; use std::ffi::CString; - use std::os::unix::ffi::OsStrExt; - use std::path::PathBuf; use std::sync::atomic::Ordering; + use typed_path::UnixPathBuf; fn open_capture_file(driver: D) -> anyhow::Result { let manifest_dir = env!("CARGO_MANIFEST_DIR"); - let scap_file = PathBuf::from(manifest_dir).join("tests/scap/kexec_x86.scap"); - let scap_file = CString::new(scap_file.as_os_str().as_bytes())?; + let scap_file = UnixPathBuf::from(manifest_dir).join("tests/scap/kexec_x86.scap"); + let scap_file = CString::new(scap_file.as_bytes())?; driver.load_capture_file(scap_file.as_c_str()) } diff --git a/falco_plugin_tests/tests/scap_import_table.rs b/falco_plugin_tests/tests/scap_import_table.rs index 1f8571bb..6774adb8 100644 --- a/falco_plugin_tests/tests/scap_import_table.rs +++ b/falco_plugin_tests/tests/scap_import_table.rs @@ -155,14 +155,13 @@ mod tests { init_plugin, instantiate_sinsp_tests, CapturingTestDriver, SavefileTestDriver, ScapStatus, }; use std::ffi::CString; - use std::os::unix::ffi::OsStrExt; - use std::path::PathBuf; use std::sync::atomic::Ordering; + use typed_path::UnixPathBuf; fn open_capture_file(driver: D) -> anyhow::Result { let manifest_dir = env!("CARGO_MANIFEST_DIR"); - let scap_file = PathBuf::from(manifest_dir).join("tests/scap/kexec_x86.scap"); - let scap_file = CString::new(scap_file.as_os_str().as_bytes())?; + let scap_file = UnixPathBuf::from(manifest_dir).join("tests/scap/kexec_x86.scap"); + let scap_file = CString::new(scap_file.as_bytes())?; driver.load_capture_file(scap_file.as_c_str()) }