From 44f8d7c6a39f7f605b9466023f088e2034a98904 Mon Sep 17 00:00:00 2001 From: Grzegorz Nosek Date: Mon, 15 Sep 2025 21:34:54 +0200 Subject: [PATCH 01/11] fix(plugin): back to "C" ABI Using "C-unwind" was the result of confusion about panic handling, it's not safe to unwind into C++ code under (sinsp) under any circumstances. Signed-off-by: Grzegorz Nosek --- falco_plugin/src/async_event/async_handler.rs | 2 +- falco_plugin/src/async_event/wrappers.rs | 9 +- falco_plugin/src/base/logger.rs | 2 +- falco_plugin/src/base/wrappers.rs | 30 ++-- falco_plugin/src/error/last_error.rs | 4 +- falco_plugin/src/extract/wrappers.rs | 8 +- falco_plugin/src/listen/routine.rs | 8 +- falco_plugin/src/listen/wrappers.rs | 4 +- falco_plugin/src/parse/wrappers.rs | 7 +- falco_plugin/src/source/wrappers.rs | 16 +- falco_plugin/src/tables/export/wrappers.rs | 28 +-- falco_plugin/src/tables/import/entry/raw.rs | 5 +- falco_plugin/src/tables/import/table/raw.rs | 4 +- falco_plugin/src/tables/vtable/mod.rs | 6 +- falco_plugin/src/tables/vtable/reader.rs | 30 ++-- falco_plugin/src/tables/vtable/writer.rs | 26 ++- falco_plugin_api/src/ffi.rs | 167 ++++++++---------- falco_plugin_runner/src/event.rs | 2 +- falco_plugin_runner/src/plugin/async_event.rs | 2 +- .../src/plugin/event_source_filter.rs | 4 +- falco_plugin_runner/src/plugin/listen.rs | 6 +- falco_plugin_runner/src/plugin/mod.rs | 10 +- falco_plugin_runner/src/tables.rs | 30 ++-- justfile | 1 - 24 files changed, 192 insertions(+), 219 deletions(-) diff --git a/falco_plugin/src/async_event/async_handler.rs b/falco_plugin/src/async_event/async_handler.rs index 3649fc17..0b34e07e 100644 --- a/falco_plugin/src/async_event/async_handler.rs +++ b/falco_plugin/src/async_event/async_handler.rs @@ -12,7 +12,7 @@ use std::ffi::c_char; #[derive(Debug, Clone)] pub struct AsyncHandler { pub(crate) owner: *mut ss_plugin_owner_t, - pub(crate) raw_handler: unsafe extern "C-unwind" fn( + pub(crate) raw_handler: unsafe extern "C" fn( o: *mut ss_plugin_owner_t, evt: *const ss_plugin_event, err: *mut c_char, diff --git a/falco_plugin/src/async_event/wrappers.rs b/falco_plugin/src/async_event/wrappers.rs index 5c999ad0..6b9f9ffc 100644 --- a/falco_plugin/src/async_event/wrappers.rs +++ b/falco_plugin/src/async_event/wrappers.rs @@ -50,8 +50,7 @@ impl AsyncPluginApi { pub const IMPLEMENTS_ASYNC: bool = true; } -pub extern "C-unwind" fn plugin_get_async_event_sources( -) -> *const c_char { +pub extern "C" fn plugin_get_async_event_sources() -> *const c_char { static SOURCES: Mutex> = Mutex::new(BTreeMap::new()); let ty = TypeId::of::(); @@ -68,7 +67,7 @@ pub extern "C-unwind" fn plugin_get_async_event_sources() -> *const c_char { +pub extern "C" fn plugin_get_async_events() -> *const c_char { static EVENTS: Mutex> = Mutex::new(BTreeMap::new()); let ty = TypeId::of::(); @@ -88,7 +87,7 @@ pub extern "C-unwind" fn plugin_get_async_events( /// # Safety /// /// All pointers must be valid -pub unsafe extern "C-unwind" fn plugin_set_async_event_handler( +pub unsafe extern "C" fn plugin_set_async_event_handler( plugin: *mut ss_plugin_t, owner: *mut ss_plugin_owner_t, handler: ss_plugin_async_event_handler_t, @@ -127,7 +126,7 @@ pub unsafe extern "C-unwind" fn plugin_set_async_event_handler( +pub unsafe extern "C" fn plugin_dump_state( plugin: *mut ss_plugin_t, owner: *mut ss_plugin_owner_t, handler: ss_plugin_async_event_handler_t, diff --git a/falco_plugin/src/base/logger.rs b/falco_plugin/src/base/logger.rs index df7a9ace..a96471c5 100644 --- a/falco_plugin/src/base/logger.rs +++ b/falco_plugin/src/base/logger.rs @@ -13,7 +13,7 @@ use std::sync::RwLock; pub(super) struct FalcoPluginLoggerImpl { pub(super) owner: *mut ss_plugin_owner_t, - pub(super) logger_fn: unsafe extern "C-unwind" fn( + pub(super) logger_fn: unsafe extern "C" fn( o: *mut ss_plugin_owner_t, component: *const c_char, msg: *const c_char, diff --git a/falco_plugin/src/base/wrappers.rs b/falco_plugin/src/base/wrappers.rs index d53e83f0..d10db0c8 100644 --- a/falco_plugin/src/base/wrappers.rs +++ b/falco_plugin/src/base/wrappers.rs @@ -30,7 +30,7 @@ use std::sync::Mutex; )] pub unsafe trait BasePluginExported {} -pub extern "C-unwind" fn plugin_get_required_api_version< +pub extern "C" fn plugin_get_required_api_version< const MAJOR: usize, const MINOR: usize, const PATCH: usize, @@ -49,26 +49,26 @@ pub extern "C-unwind" fn plugin_get_required_api_version< .as_ptr() } -pub extern "C-unwind" fn plugin_get_version() -> *const c_char { +pub extern "C" fn plugin_get_version() -> *const c_char { T::PLUGIN_VERSION.as_ptr() } -pub extern "C-unwind" fn plugin_get_name() -> *const c_char { +pub extern "C" fn plugin_get_name() -> *const c_char { T::NAME.as_ptr() } -pub extern "C-unwind" fn plugin_get_description() -> *const c_char { +pub extern "C" fn plugin_get_description() -> *const c_char { T::DESCRIPTION.as_ptr() } -pub extern "C-unwind" fn plugin_get_contact() -> *const c_char { +pub extern "C" fn plugin_get_contact() -> *const c_char { T::CONTACT.as_ptr() } /// # Safety /// /// init_input must be null or a valid pointer -pub unsafe extern "C-unwind" fn plugin_init( +pub unsafe extern "C" fn plugin_init( init_input: *const ss_plugin_init_input, rc: *mut ss_plugin_rc, ) -> *mut falco_plugin_api::ss_plugin_t { @@ -127,7 +127,7 @@ pub unsafe extern "C-unwind" fn plugin_init( /// # Safety /// /// schema_type must be null or a valid pointer -pub unsafe extern "C-unwind" fn plugin_get_init_schema( +pub unsafe extern "C" fn plugin_get_init_schema( schema_type: *mut falco_plugin_api::ss_plugin_schema_type, ) -> *const c_char { let schema_type = unsafe { @@ -151,9 +151,7 @@ pub unsafe extern "C-unwind" fn plugin_get_init_schema( /// # Safety /// /// `plugin` must have been created by `init()` and not destroyed since -pub unsafe extern "C-unwind" fn plugin_destroy( - plugin: *mut falco_plugin_api::ss_plugin_t, -) { +pub unsafe extern "C" fn plugin_destroy(plugin: *mut falco_plugin_api::ss_plugin_t) { unsafe { let plugin = plugin as *mut PluginWrapper

; let _ = Box::from_raw(plugin); @@ -163,7 +161,7 @@ pub unsafe extern "C-unwind" fn plugin_destroy( /// # Safety /// /// `plugin` must be a valid pointer to `PluginWrapper

` -pub unsafe extern "C-unwind" fn plugin_get_last_error( +pub unsafe extern "C" fn plugin_get_last_error( plugin: *mut falco_plugin_api::ss_plugin_t, ) -> *const c_char { let plugin = plugin as *mut PluginWrapper

; @@ -173,7 +171,7 @@ pub unsafe extern "C-unwind" fn plugin_get_last_error( } } -pub unsafe extern "C-unwind" fn plugin_set_config( +pub unsafe extern "C" fn plugin_set_config( plugin: *mut falco_plugin_api::ss_plugin_t, config_input: *const falco_plugin_api::ss_plugin_set_config_input, ) -> falco_plugin_api::ss_plugin_rc { @@ -202,7 +200,7 @@ pub unsafe extern "C-unwind" fn plugin_set_config( res.rc(&mut plugin.error_buf) } -pub unsafe extern "C-unwind" fn plugin_get_metrics( +pub unsafe extern "C" fn plugin_get_metrics( plugin: *mut ss_plugin_t, num_metrics: *mut u32, ) -> *mut ss_plugin_metric { @@ -236,7 +234,7 @@ pub unsafe extern "C-unwind" fn plugin_get_metrics( plugin.metric_storage.as_ptr().cast_mut() } -pub extern "C-unwind" fn plugin_get_required_event_schema_version( +pub extern "C" fn plugin_get_required_event_schema_version( _plugin: *mut ss_plugin_t, ) -> *const c_char { T::SCHEMA_VERSION.as_ptr() @@ -253,7 +251,7 @@ macro_rules! wrap_ffi { ) => { $( #[$attr] - pub unsafe extern "C-unwind" fn $name ( $($param: $param_ty),*) -> $ret { + pub unsafe extern "C" fn $name ( $($param: $param_ty),*) -> $ret { use $mod as wrappers; wrappers::$name::<$ty>($($param),*) @@ -525,7 +523,7 @@ macro_rules! ensure_plugin_capabilities { macro_rules! base_plugin_ffi_wrappers { ($maj:expr; $min:expr; $patch:expr => #[$attr:meta] $ty:ty) => { #[$attr] - pub extern "C-unwind" fn plugin_get_required_api_version() -> *const std::ffi::c_char { + pub extern "C" fn plugin_get_required_api_version() -> *const std::ffi::c_char { $crate::base::wrappers::plugin_get_required_api_version::< { $maj }, { $min }, diff --git a/falco_plugin/src/error/last_error.rs b/falco_plugin/src/error/last_error.rs index 232ffc25..7c37232b 100644 --- a/falco_plugin/src/error/last_error.rs +++ b/falco_plugin/src/error/last_error.rs @@ -5,13 +5,13 @@ use std::ffi::c_char; #[derive(Clone, Debug)] pub struct LastError { owner: *mut ss_plugin_owner_t, - get_owner_last_error: unsafe extern "C-unwind" fn(o: *mut ss_plugin_owner_t) -> *const c_char, + get_owner_last_error: unsafe extern "C" fn(o: *mut ss_plugin_owner_t) -> *const c_char, } impl LastError { pub unsafe fn new( owner: *mut ss_plugin_owner_t, - get_owner_last_error: unsafe extern "C-unwind" fn(*mut ss_plugin_owner_t) -> *const c_char, + get_owner_last_error: unsafe extern "C" fn(*mut ss_plugin_owner_t) -> *const c_char, ) -> Self { Self { owner, diff --git a/falco_plugin/src/extract/wrappers.rs b/falco_plugin/src/extract/wrappers.rs index 44398853..b19560de 100644 --- a/falco_plugin/src/extract/wrappers.rs +++ b/falco_plugin/src/extract/wrappers.rs @@ -53,14 +53,14 @@ impl ExtractPluginApi { pub const IMPLEMENTS_EXTRACT: bool = true; } -pub extern "C-unwind" fn plugin_get_fields() -> *const c_char { +pub extern "C" fn plugin_get_fields() -> *const c_char { T::get_fields().as_ptr() } /// # Safety /// /// All pointers must be valid -pub unsafe extern "C-unwind" fn plugin_get_extract_event_types( +pub unsafe extern "C" fn plugin_get_extract_event_types( numtypes: *mut u32, _plugin: *mut ss_plugin_t, ) -> *mut u16 { @@ -70,7 +70,7 @@ pub unsafe extern "C-unwind" fn plugin_get_extract_event_types } //noinspection DuplicatedCode -pub extern "C-unwind" fn plugin_get_extract_event_sources() -> *const c_char { +pub extern "C" fn plugin_get_extract_event_sources() -> *const c_char { static SOURCES: Mutex> = Mutex::new(BTreeMap::new()); let ty = TypeId::of::(); let mut sources_map = SOURCES.lock().unwrap(); @@ -89,7 +89,7 @@ pub extern "C-unwind" fn plugin_get_extract_event_sources() -> /// # Safety /// /// All pointers must be valid -pub unsafe extern "C-unwind" fn plugin_extract_fields( +pub unsafe extern "C" fn plugin_extract_fields( plugin: *mut ss_plugin_t, event_input: *const ss_plugin_event_input, extract_input: *const ss_plugin_field_extract_input, diff --git a/falco_plugin/src/listen/routine.rs b/falco_plugin/src/listen/routine.rs index c1c35b7e..70c765c8 100644 --- a/falco_plugin/src/listen/routine.rs +++ b/falco_plugin/src/listen/routine.rs @@ -46,12 +46,12 @@ impl Drop for Routine { #[derive(Debug)] pub struct ThreadPool { owner: *mut ss_plugin_owner_t, - subscribe: unsafe extern "C-unwind" fn( + subscribe: unsafe extern "C" fn( o: *mut ss_plugin_owner_t, f: ss_plugin_routine_fn_t, i: *mut ss_plugin_routine_state_t, ) -> *mut ss_plugin_routine_t, - unsubscribe: unsafe extern "C-unwind" fn( + unsubscribe: unsafe extern "C" fn( o: *mut ss_plugin_owner_t, r: *mut ss_plugin_routine_t, ) -> ss_plugin_rc, @@ -87,7 +87,7 @@ impl ThreadPool { where F: FnMut() -> ControlFlow<()> + Send + 'static, { - unsafe extern "C-unwind" fn cb_wrapper( + unsafe extern "C" fn cb_wrapper( _plugin: *mut ss_plugin_t, data: *mut ss_plugin_routine_state_t, ) -> ss_plugin_bool @@ -110,7 +110,7 @@ impl ThreadPool { let callback = Some( cb_wrapper:: - as unsafe extern "C-unwind" fn( + as unsafe extern "C" fn( _plugin: *mut ss_plugin_t, data: *mut ss_plugin_routine_state_t, ) -> ss_plugin_bool, diff --git a/falco_plugin/src/listen/wrappers.rs b/falco_plugin/src/listen/wrappers.rs index 87bff51f..60bb652d 100644 --- a/falco_plugin/src/listen/wrappers.rs +++ b/falco_plugin/src/listen/wrappers.rs @@ -42,7 +42,7 @@ impl CaptureListenApi { pub const IMPLEMENTS_LISTEN: bool = true; } -pub unsafe extern "C-unwind" fn plugin_capture_open( +pub unsafe extern "C" fn plugin_capture_open( plugin: *mut ss_plugin_t, listen_input: *const ss_plugin_capture_listen_input, ) -> ss_plugin_rc { @@ -74,7 +74,7 @@ pub unsafe extern "C-unwind" fn plugin_capture_open( ss_plugin_rc_SS_PLUGIN_SUCCESS } -pub unsafe extern "C-unwind" fn plugin_capture_close( +pub unsafe extern "C" fn plugin_capture_close( plugin: *mut ss_plugin_t, listen_input: *const ss_plugin_capture_listen_input, ) -> ss_plugin_rc { diff --git a/falco_plugin/src/parse/wrappers.rs b/falco_plugin/src/parse/wrappers.rs index 89d42a91..10753a9b 100644 --- a/falco_plugin/src/parse/wrappers.rs +++ b/falco_plugin/src/parse/wrappers.rs @@ -54,7 +54,7 @@ impl ParsePluginApi { /// # Safety /// /// All pointers must be valid -pub unsafe extern "C-unwind" fn plugin_get_parse_event_types( +pub unsafe extern "C" fn plugin_get_parse_event_types( numtypes: *mut u32, _plugin: *mut ss_plugin_t, ) -> *mut u16 { @@ -68,8 +68,7 @@ pub unsafe extern "C-unwind" fn plugin_get_parse_event_types( } //noinspection DuplicatedCode -pub extern "C-unwind" fn plugin_get_parse_event_sources() -> *const c_char -{ +pub extern "C" fn plugin_get_parse_event_sources() -> *const c_char { static SOURCES: Mutex> = Mutex::new(BTreeMap::new()); let ty = TypeId::of::(); @@ -89,7 +88,7 @@ pub extern "C-unwind" fn plugin_get_parse_event_sources( +pub unsafe extern "C" fn plugin_parse_event( plugin: *mut ss_plugin_t, event: *const ss_plugin_event_input, parse_input: *const ss_plugin_event_parse_input, diff --git a/falco_plugin/src/source/wrappers.rs b/falco_plugin/src/source/wrappers.rs index 21097e7a..2025a78b 100644 --- a/falco_plugin/src/source/wrappers.rs +++ b/falco_plugin/src/source/wrappers.rs @@ -68,18 +68,18 @@ impl SourcePluginApi { pub const IMPLEMENTS_SOURCE: bool = true; } -pub extern "C-unwind" fn plugin_get_event_source() -> *const c_char { +pub extern "C" fn plugin_get_event_source() -> *const c_char { T::EVENT_SOURCE.as_ptr() } -pub extern "C-unwind" fn plugin_get_id() -> u32 { +pub extern "C" fn plugin_get_id() -> u32 { T::PLUGIN_ID } /// # Safety /// /// All pointers must be valid -pub unsafe extern "C-unwind" fn plugin_list_open_params( +pub unsafe extern "C" fn plugin_list_open_params( plugin: *mut ss_plugin_t, rc: *mut i32, ) -> *const c_char { @@ -114,7 +114,7 @@ pub unsafe extern "C-unwind" fn plugin_list_open_params( /// # Safety /// /// All pointers must be valid -pub unsafe extern "C-unwind" fn plugin_open( +pub unsafe extern "C" fn plugin_open( plugin: *mut ss_plugin_t, params: *const c_char, rc: *mut ss_plugin_rc, @@ -170,7 +170,7 @@ pub unsafe extern "C-unwind" fn plugin_open( /// # Safety /// /// All pointers must be valid -pub unsafe extern "C-unwind" fn plugin_close( +pub unsafe extern "C" fn plugin_close( plugin: *mut ss_plugin_t, instance: *mut ss_instance_t, ) { @@ -198,7 +198,7 @@ pub unsafe extern "C-unwind" fn plugin_close( /// # Safety /// /// All pointers must be valid -pub unsafe extern "C-unwind" fn plugin_next_batch( +pub unsafe extern "C" fn plugin_next_batch( plugin: *mut ss_plugin_t, instance: *mut ss_instance_t, nevts: *mut u32, @@ -243,7 +243,7 @@ pub unsafe extern "C-unwind" fn plugin_next_batch( /// # Safety /// /// All pointers must be valid -pub unsafe extern "C-unwind" fn plugin_get_progress( +pub unsafe extern "C" fn plugin_get_progress( _plugin: *mut ss_plugin_t, instance: *mut ss_instance_t, progress_pct: *mut u32, @@ -272,7 +272,7 @@ pub unsafe extern "C-unwind" fn plugin_get_progress( /// # Safety /// /// All pointers must be valid -pub unsafe extern "C-unwind" fn plugin_event_to_string( +pub unsafe extern "C" fn plugin_event_to_string( plugin: *mut ss_plugin_t, event: *const ss_plugin_event_input, ) -> *const c_char { diff --git a/falco_plugin/src/tables/export/wrappers.rs b/falco_plugin/src/tables/export/wrappers.rs index 34aee349..1570070e 100644 --- a/falco_plugin/src/tables/export/wrappers.rs +++ b/falco_plugin/src/tables/export/wrappers.rs @@ -16,7 +16,7 @@ use std::borrow::Borrow; use std::ffi::{c_char, CStr}; // SAFETY: `table` must be a valid pointer to Table -unsafe extern "C-unwind" fn get_table_name(table: *mut ss_plugin_table_t) -> *const c_char +unsafe extern "C" fn get_table_name(table: *mut ss_plugin_table_t) -> *const c_char where K: Key + Ord, K: Borrow<::Borrowed>, @@ -33,7 +33,7 @@ where } // SAFETY: `table` must be a valid pointer to Table -unsafe extern "C-unwind" fn get_table_size(table: *mut ss_plugin_table_t) -> u64 +unsafe extern "C" fn get_table_size(table: *mut ss_plugin_table_t) -> u64 where K: Key + Ord, K: Borrow<::Borrowed>, @@ -51,7 +51,7 @@ where // SAFETY: `table` must be a valid pointer to Table // SAFETY: `key` must be a valid pointer to ss_plugin_state_data -unsafe extern "C-unwind" fn get_table_entry( +unsafe extern "C" fn get_table_entry( table: *mut ss_plugin_table_t, key: *const ss_plugin_state_data, ) -> *mut ss_plugin_table_entry_t @@ -79,7 +79,7 @@ where } // SAFETY: all pointers must be valid -unsafe extern "C-unwind" fn read_entry_field( +unsafe extern "C" fn read_entry_field( table: *mut ss_plugin_table_t, entry: *mut ss_plugin_table_entry_t, field: *const ss_plugin_table_field_t, @@ -111,7 +111,7 @@ where } // SAFETY: all pointers must be valid -unsafe extern "C-unwind" fn release_table_entry( +unsafe extern "C" fn release_table_entry( _table: *mut ss_plugin_table_t, entry: *mut ss_plugin_table_entry_t, ) where @@ -126,7 +126,7 @@ unsafe extern "C-unwind" fn release_table_entry( } // SAFETY: all pointers must be valid -unsafe extern "C-unwind" fn iterate_entries( +unsafe extern "C" fn iterate_entries( table: *mut ss_plugin_table_t, func: ss_plugin_table_iterator_func_t, state: *mut ss_plugin_table_iterator_state_t, @@ -156,7 +156,7 @@ where } // SAFETY: `table` must be a valid pointer to Table -unsafe extern "C-unwind" fn clear_table(table: *mut ss_plugin_table_t) -> ss_plugin_rc +unsafe extern "C" fn clear_table(table: *mut ss_plugin_table_t) -> ss_plugin_rc where K: Key + Ord, K: Borrow<::Borrowed>, @@ -174,7 +174,7 @@ where } // SAFETY: all pointers must be valid -unsafe extern "C-unwind" fn erase_table_entry( +unsafe extern "C" fn erase_table_entry( table: *mut ss_plugin_table_t, key: *const ss_plugin_state_data, ) -> ss_plugin_rc @@ -201,7 +201,7 @@ where } // SAFETY: `table` must be a valid pointer to Table -unsafe extern "C-unwind" fn create_table_entry( +unsafe extern "C" fn create_table_entry( table: *mut ss_plugin_table_t, ) -> *mut ss_plugin_table_entry_t where @@ -224,7 +224,7 @@ where } // SAFETY: all pointers must be valid -unsafe extern "C-unwind" fn add_table_entry( +unsafe extern "C" fn add_table_entry( table: *mut ss_plugin_table_t, key: *const ss_plugin_state_data, entry: *mut ss_plugin_table_entry_t, @@ -258,7 +258,7 @@ where } // SAFETY: all pointers must be valid -unsafe extern "C-unwind" fn write_entry_field( +unsafe extern "C" fn write_entry_field( table: *mut ss_plugin_table_t, entry: *mut ss_plugin_table_entry_t, field: *const ss_plugin_table_field_t, @@ -289,7 +289,7 @@ where } // SAFETY: all pointers must be valid -unsafe extern "C-unwind" fn list_table_fields( +unsafe extern "C" fn list_table_fields( table: *mut ss_plugin_table_t, nfields: *mut u32, ) -> *const ss_plugin_table_fieldinfo @@ -311,7 +311,7 @@ where } // SAFETY: all pointers must be valid -unsafe extern "C-unwind" fn get_table_field( +unsafe extern "C" fn get_table_field( table: *mut ss_plugin_table_t, name: *const c_char, data_type: ss_plugin_state_type, @@ -343,7 +343,7 @@ where } // SAFETY: all pointers must be valid -unsafe extern "C-unwind" fn add_table_field( +unsafe extern "C" fn add_table_field( table: *mut ss_plugin_table_t, name: *const c_char, data_type: ss_plugin_state_type, diff --git a/falco_plugin/src/tables/import/entry/raw.rs b/falco_plugin/src/tables/import/entry/raw.rs index 9b1da48d..47e72934 100644 --- a/falco_plugin/src/tables/import/entry/raw.rs +++ b/falco_plugin/src/tables/import/entry/raw.rs @@ -10,9 +10,8 @@ use falco_plugin_api::{ pub struct RawEntry { pub(crate) table: *mut ss_plugin_table_t, pub(crate) entry: *mut ss_plugin_table_entry_t, - pub(crate) destructor: Option< - unsafe extern "C-unwind" fn(t: *mut ss_plugin_table_t, e: *mut ss_plugin_table_entry_t), - >, + pub(crate) destructor: + Option, } impl RawEntry { diff --git a/falco_plugin/src/tables/import/table/raw.rs b/falco_plugin/src/tables/import/table/raw.rs index ff4714b7..b96d6e7b 100644 --- a/falco_plugin/src/tables/import/table/raw.rs +++ b/falco_plugin/src/tables/import/table/raw.rs @@ -370,7 +370,7 @@ pub enum IterationResult { fn iter_inner( table: *mut ss_plugin_table_t, - iterate_entries: unsafe extern "C-unwind" fn( + iterate_entries: unsafe extern "C" fn( *mut ss_plugin_table_t, it: ss_plugin_table_iterator_func_t, s: *mut ss_plugin_table_iterator_state_t, @@ -380,7 +380,7 @@ fn iter_inner( where F: FnMut(*mut ss_plugin_table_entry_t) -> bool, { - extern "C-unwind" fn iter_wrapper( + extern "C" fn iter_wrapper( s: *mut ss_plugin_table_iterator_state_t, entry: *mut ss_plugin_table_entry_t, ) -> ss_plugin_bool diff --git a/falco_plugin/src/tables/vtable/mod.rs b/falco_plugin/src/tables/vtable/mod.rs index ca521338..a6e55d72 100644 --- a/falco_plugin/src/tables/vtable/mod.rs +++ b/falco_plugin/src/tables/vtable/mod.rs @@ -27,16 +27,16 @@ pub enum TableError { pub struct TablesInput<'t> { pub(crate) owner: *mut ss_plugin_owner_t, pub(crate) last_error: LastError, - pub(crate) list_tables: unsafe extern "C-unwind" fn( + pub(crate) list_tables: unsafe extern "C" fn( o: *mut ss_plugin_owner_t, ntables: *mut u32, ) -> *mut ss_plugin_table_info, - pub(crate) get_table: unsafe extern "C-unwind" fn( + pub(crate) get_table: unsafe extern "C" fn( o: *mut ss_plugin_owner_t, name: *const ::std::os::raw::c_char, key_type: ss_plugin_state_type, ) -> *mut ss_plugin_table_t, - pub(crate) add_table: unsafe extern "C-unwind" fn( + pub(crate) add_table: unsafe extern "C" fn( o: *mut ss_plugin_owner_t, in_: *const ss_plugin_table_input, ) -> ss_plugin_rc, diff --git a/falco_plugin/src/tables/vtable/reader.rs b/falco_plugin/src/tables/vtable/reader.rs index e90ff37c..60a3ac86 100644 --- a/falco_plugin/src/tables/vtable/reader.rs +++ b/falco_plugin/src/tables/vtable/reader.rs @@ -44,14 +44,12 @@ pub(crate) mod private { fn release_table_entry_fn( &self, - ) -> Option< - unsafe extern "C-unwind" fn(t: *mut ss_plugin_table_t, e: *mut ss_plugin_table_entry_t), - >; + ) -> Option; fn iterate_entries_fn( &self, ) -> Result< - unsafe extern "C-unwind" fn( + unsafe extern "C" fn( t: *mut ss_plugin_table_t, it: ss_plugin_table_iterator_func_t, s: *mut ss_plugin_table_iterator_state_t, @@ -172,16 +170,15 @@ impl private::TableReaderImpl for LazyTableReader<'_> { fn release_table_entry_fn( &self, - ) -> Option< - unsafe extern "C-unwind" fn(t: *mut ss_plugin_table_t, e: *mut ss_plugin_table_entry_t), - > { + ) -> Option + { self.reader_ext.release_table_entry } fn iterate_entries_fn( &self, ) -> Result< - unsafe extern "C-unwind" fn( + unsafe extern "C" fn( t: *mut ss_plugin_table_t, it: ss_plugin_table_iterator_func_t, s: *mut ss_plugin_table_iterator_state_t, @@ -205,21 +202,21 @@ impl private::TableReaderImpl for LazyTableReader<'_> { #[derive(Debug)] pub struct ValidatedTableReader<'t> { pub(crate) get_table_name: - unsafe extern "C-unwind" fn(t: *mut ss_plugin_table_t) -> *const ::std::os::raw::c_char, - pub(crate) get_table_size: unsafe extern "C-unwind" fn(t: *mut ss_plugin_table_t) -> u64, - pub(crate) get_table_entry: unsafe extern "C-unwind" fn( + unsafe extern "C" fn(t: *mut ss_plugin_table_t) -> *const ::std::os::raw::c_char, + pub(crate) get_table_size: unsafe extern "C" fn(t: *mut ss_plugin_table_t) -> u64, + pub(crate) get_table_entry: unsafe extern "C" fn( t: *mut ss_plugin_table_t, key: *const ss_plugin_state_data, ) -> *mut ss_plugin_table_entry_t, - pub(crate) read_entry_field: unsafe extern "C-unwind" fn( + pub(crate) read_entry_field: unsafe extern "C" fn( t: *mut ss_plugin_table_t, e: *mut ss_plugin_table_entry_t, f: *const ss_plugin_table_field_t, out: *mut ss_plugin_state_data, ) -> ss_plugin_rc, pub(crate) release_table_entry: - unsafe extern "C-unwind" fn(t: *mut ss_plugin_table_t, e: *mut ss_plugin_table_entry_t), - pub(crate) iterate_entries: unsafe extern "C-unwind" fn( + unsafe extern "C" fn(t: *mut ss_plugin_table_t, e: *mut ss_plugin_table_entry_t), + pub(crate) iterate_entries: unsafe extern "C" fn( t: *mut ss_plugin_table_t, it: ss_plugin_table_iterator_func_t, s: *mut ss_plugin_table_iterator_state_t, @@ -263,15 +260,14 @@ impl private::TableReaderImpl for ValidatedTableReader<'_> { fn release_table_entry_fn( &self, - ) -> Option - { + ) -> Option { Some(self.release_table_entry) } fn iterate_entries_fn( &self, ) -> Result< - unsafe extern "C-unwind" fn( + unsafe extern "C" fn( *mut ss_plugin_table_t, ss_plugin_table_iterator_func_t, *mut ss_plugin_table_iterator_state_t, diff --git a/falco_plugin/src/tables/vtable/writer.rs b/falco_plugin/src/tables/vtable/writer.rs index 59e89f0a..1fb3d02c 100644 --- a/falco_plugin/src/tables/vtable/writer.rs +++ b/falco_plugin/src/tables/vtable/writer.rs @@ -45,9 +45,7 @@ pub(crate) mod private { fn destroy_table_entry_fn( &self, - ) -> Option< - unsafe extern "C-unwind" fn(t: *mut ss_plugin_table_t, e: *mut ss_plugin_table_entry_t), - >; + ) -> Option; unsafe fn add_table_entry( &self, @@ -180,9 +178,8 @@ impl private::TableWriterImpl for LazyTableWriter<'_> { fn destroy_table_entry_fn( &self, - ) -> Option< - unsafe extern "C-unwind" fn(t: *mut ss_plugin_table_t, e: *mut ss_plugin_table_entry_t), - > { + ) -> Option + { self.writer_ext.destroy_table_entry } @@ -229,21 +226,21 @@ impl private::TableWriterImpl for LazyTableWriter<'_> { /// It's used as a token to prove you're allowed to write tables in a particular context #[derive(Debug)] pub struct ValidatedTableWriter<'t> { - clear_table: unsafe extern "C-unwind" fn(t: *mut ss_plugin_table_t) -> ss_plugin_rc, - erase_table_entry: unsafe extern "C-unwind" fn( + clear_table: unsafe extern "C" fn(t: *mut ss_plugin_table_t) -> ss_plugin_rc, + erase_table_entry: unsafe extern "C" fn( t: *mut ss_plugin_table_t, key: *const ss_plugin_state_data, ) -> ss_plugin_rc, create_table_entry: - unsafe extern "C-unwind" fn(t: *mut ss_plugin_table_t) -> *mut ss_plugin_table_entry_t, + unsafe extern "C" fn(t: *mut ss_plugin_table_t) -> *mut ss_plugin_table_entry_t, destroy_table_entry: - unsafe extern "C-unwind" fn(t: *mut ss_plugin_table_t, e: *mut ss_plugin_table_entry_t), - add_table_entry: unsafe extern "C-unwind" fn( + unsafe extern "C" fn(t: *mut ss_plugin_table_t, e: *mut ss_plugin_table_entry_t), + add_table_entry: unsafe extern "C" fn( t: *mut ss_plugin_table_t, key: *const ss_plugin_state_data, entry: *mut ss_plugin_table_entry_t, ) -> *mut ss_plugin_table_entry_t, - write_entry_field: unsafe extern "C-unwind" fn( + write_entry_field: unsafe extern "C" fn( t: *mut ss_plugin_table_t, e: *mut ss_plugin_table_entry_t, f: *const ss_plugin_table_field_t, @@ -286,9 +283,8 @@ impl private::TableWriterImpl for ValidatedTableWriter<'_> { fn destroy_table_entry_fn( &self, - ) -> Option< - unsafe extern "C-unwind" fn(t: *mut ss_plugin_table_t, e: *mut ss_plugin_table_entry_t), - > { + ) -> Option + { Some(self.destroy_table_entry) } diff --git a/falco_plugin_api/src/ffi.rs b/falco_plugin_api/src/ffi.rs index 89268288..55cd2d8d 100644 --- a/falco_plugin_api/src/ffi.rs +++ b/falco_plugin_api/src/ffi.rs @@ -351,20 +351,20 @@ const _: () = { #[derive(Debug, Copy, Clone)] pub struct ss_plugin_table_fields_vtable { pub list_table_fields: ::std::option::Option< - unsafe extern "C-unwind" fn( + unsafe extern "C" fn( t: *mut ss_plugin_table_t, nfields: *mut u32, ) -> *const ss_plugin_table_fieldinfo, >, pub get_table_field: ::std::option::Option< - unsafe extern "C-unwind" fn( + unsafe extern "C" fn( t: *mut ss_plugin_table_t, name: *const ::std::os::raw::c_char, data_type: ss_plugin_state_type, ) -> *mut ss_plugin_table_field_t, >, pub add_table_field: ::std::option::Option< - unsafe extern "C-unwind" fn( + unsafe extern "C" fn( t: *mut ss_plugin_table_t, name: *const ::std::os::raw::c_char, data_type: ss_plugin_state_type, @@ -388,20 +388,20 @@ const _: () = { #[derive(Debug, Copy, Clone)] pub struct ss_plugin_table_fields_vtable_ext { pub list_table_fields: ::std::option::Option< - unsafe extern "C-unwind" fn( + unsafe extern "C" fn( t: *mut ss_plugin_table_t, nfields: *mut u32, ) -> *const ss_plugin_table_fieldinfo, >, pub get_table_field: ::std::option::Option< - unsafe extern "C-unwind" fn( + unsafe extern "C" fn( t: *mut ss_plugin_table_t, name: *const ::std::os::raw::c_char, data_type: ss_plugin_state_type, ) -> *mut ss_plugin_table_field_t, >, pub add_table_field: ::std::option::Option< - unsafe extern "C-unwind" fn( + unsafe extern "C" fn( t: *mut ss_plugin_table_t, name: *const ::std::os::raw::c_char, data_type: ss_plugin_state_type, @@ -425,18 +425,18 @@ const _: () = { #[derive(Debug, Copy, Clone)] pub struct ss_plugin_table_reader_vtable { pub get_table_name: ::std::option::Option< - unsafe extern "C-unwind" fn(t: *mut ss_plugin_table_t) -> *const ::std::os::raw::c_char, + unsafe extern "C" fn(t: *mut ss_plugin_table_t) -> *const ::std::os::raw::c_char, >, pub get_table_size: - ::std::option::Option u64>, + ::std::option::Option u64>, pub get_table_entry: ::std::option::Option< - unsafe extern "C-unwind" fn( + unsafe extern "C" fn( t: *mut ss_plugin_table_t, key: *const ss_plugin_state_data, ) -> *mut ss_plugin_table_entry_t, >, pub read_entry_field: ::std::option::Option< - unsafe extern "C-unwind" fn( + unsafe extern "C" fn( t: *mut ss_plugin_table_t, e: *mut ss_plugin_table_entry_t, f: *const ss_plugin_table_field_t, @@ -463,7 +463,7 @@ const _: () = { #[derive(Debug)] pub struct ss_plugin_table_iterator_state_t(pub ::std::os::raw::c_void); pub type ss_plugin_table_iterator_func_t = ::std::option::Option< - unsafe extern "C-unwind" fn( + unsafe extern "C" fn( s: *mut ss_plugin_table_iterator_state_t, e: *mut ss_plugin_table_entry_t, ) -> ss_plugin_bool, @@ -472,18 +472,18 @@ pub type ss_plugin_table_iterator_func_t = ::std::option::Option< #[derive(Debug, Copy, Clone)] pub struct ss_plugin_table_reader_vtable_ext { pub get_table_name: ::std::option::Option< - unsafe extern "C-unwind" fn(t: *mut ss_plugin_table_t) -> *const ::std::os::raw::c_char, + unsafe extern "C" fn(t: *mut ss_plugin_table_t) -> *const ::std::os::raw::c_char, >, pub get_table_size: - ::std::option::Option u64>, + ::std::option::Option u64>, pub get_table_entry: ::std::option::Option< - unsafe extern "C-unwind" fn( + unsafe extern "C" fn( t: *mut ss_plugin_table_t, key: *const ss_plugin_state_data, ) -> *mut ss_plugin_table_entry_t, >, pub read_entry_field: ::std::option::Option< - unsafe extern "C-unwind" fn( + unsafe extern "C" fn( t: *mut ss_plugin_table_t, e: *mut ss_plugin_table_entry_t, f: *const ss_plugin_table_field_t, @@ -491,10 +491,10 @@ pub struct ss_plugin_table_reader_vtable_ext { ) -> ss_plugin_rc, >, pub release_table_entry: ::std::option::Option< - unsafe extern "C-unwind" fn(t: *mut ss_plugin_table_t, e: *mut ss_plugin_table_entry_t), + unsafe extern "C" fn(t: *mut ss_plugin_table_t, e: *mut ss_plugin_table_entry_t), >, pub iterate_entries: ::std::option::Option< - unsafe extern "C-unwind" fn( + unsafe extern "C" fn( t: *mut ss_plugin_table_t, it: ss_plugin_table_iterator_func_t, s: *mut ss_plugin_table_iterator_state_t, @@ -523,30 +523,29 @@ const _: () = { #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct ss_plugin_table_writer_vtable { - pub clear_table: ::std::option::Option< - unsafe extern "C-unwind" fn(t: *mut ss_plugin_table_t) -> ss_plugin_rc, - >, + pub clear_table: + ::std::option::Option ss_plugin_rc>, pub erase_table_entry: ::std::option::Option< - unsafe extern "C-unwind" fn( + unsafe extern "C" fn( t: *mut ss_plugin_table_t, key: *const ss_plugin_state_data, ) -> ss_plugin_rc, >, pub create_table_entry: ::std::option::Option< - unsafe extern "C-unwind" fn(t: *mut ss_plugin_table_t) -> *mut ss_plugin_table_entry_t, + unsafe extern "C" fn(t: *mut ss_plugin_table_t) -> *mut ss_plugin_table_entry_t, >, pub destroy_table_entry: ::std::option::Option< - unsafe extern "C-unwind" fn(t: *mut ss_plugin_table_t, e: *mut ss_plugin_table_entry_t), + unsafe extern "C" fn(t: *mut ss_plugin_table_t, e: *mut ss_plugin_table_entry_t), >, pub add_table_entry: ::std::option::Option< - unsafe extern "C-unwind" fn( + unsafe extern "C" fn( t: *mut ss_plugin_table_t, key: *const ss_plugin_state_data, entry: *mut ss_plugin_table_entry_t, ) -> *mut ss_plugin_table_entry_t, >, pub write_entry_field: ::std::option::Option< - unsafe extern "C-unwind" fn( + unsafe extern "C" fn( t: *mut ss_plugin_table_t, e: *mut ss_plugin_table_entry_t, f: *const ss_plugin_table_field_t, @@ -576,30 +575,29 @@ const _: () = { #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct ss_plugin_table_writer_vtable_ext { - pub clear_table: ::std::option::Option< - unsafe extern "C-unwind" fn(t: *mut ss_plugin_table_t) -> ss_plugin_rc, - >, + pub clear_table: + ::std::option::Option ss_plugin_rc>, pub erase_table_entry: ::std::option::Option< - unsafe extern "C-unwind" fn( + unsafe extern "C" fn( t: *mut ss_plugin_table_t, key: *const ss_plugin_state_data, ) -> ss_plugin_rc, >, pub create_table_entry: ::std::option::Option< - unsafe extern "C-unwind" fn(t: *mut ss_plugin_table_t) -> *mut ss_plugin_table_entry_t, + unsafe extern "C" fn(t: *mut ss_plugin_table_t) -> *mut ss_plugin_table_entry_t, >, pub destroy_table_entry: ::std::option::Option< - unsafe extern "C-unwind" fn(t: *mut ss_plugin_table_t, e: *mut ss_plugin_table_entry_t), + unsafe extern "C" fn(t: *mut ss_plugin_table_t, e: *mut ss_plugin_table_entry_t), >, pub add_table_entry: ::std::option::Option< - unsafe extern "C-unwind" fn( + unsafe extern "C" fn( t: *mut ss_plugin_table_t, key: *const ss_plugin_state_data, entry: *mut ss_plugin_table_entry_t, ) -> *mut ss_plugin_table_entry_t, >, pub write_entry_field: ::std::option::Option< - unsafe extern "C-unwind" fn( + unsafe extern "C" fn( t: *mut ss_plugin_table_t, e: *mut ss_plugin_table_entry_t, f: *const ss_plugin_table_field_t, @@ -667,20 +665,20 @@ const _: () = { #[derive(Debug, Copy, Clone)] pub struct ss_plugin_init_tables_input { pub list_tables: ::std::option::Option< - unsafe extern "C-unwind" fn( + unsafe extern "C" fn( o: *mut ss_plugin_owner_t, ntables: *mut u32, ) -> *mut ss_plugin_table_info, >, pub get_table: ::std::option::Option< - unsafe extern "C-unwind" fn( + unsafe extern "C" fn( o: *mut ss_plugin_owner_t, name: *const ::std::os::raw::c_char, key_type: ss_plugin_state_type, ) -> *mut ss_plugin_table_t, >, pub add_table: ::std::option::Option< - unsafe extern "C-unwind" fn( + unsafe extern "C" fn( o: *mut ss_plugin_owner_t, in_: *const ss_plugin_table_input, ) -> ss_plugin_rc, @@ -712,7 +710,7 @@ const _: () = { [::std::mem::offset_of!(ss_plugin_init_tables_input, writer_ext) - 64usize]; }; pub type ss_plugin_log_fn_t = ::std::option::Option< - unsafe extern "C-unwind" fn( + unsafe extern "C" fn( o: *mut ss_plugin_owner_t, component: *const ::std::os::raw::c_char, msg: *const ::std::os::raw::c_char, @@ -725,7 +723,7 @@ pub struct ss_plugin_init_input { pub config: *const ::std::os::raw::c_char, pub owner: *mut ss_plugin_owner_t, pub get_owner_last_error: ::std::option::Option< - unsafe extern "C-unwind" fn(o: *mut ss_plugin_owner_t) -> *const ::std::os::raw::c_char, + unsafe extern "C" fn(o: *mut ss_plugin_owner_t) -> *const ::std::os::raw::c_char, >, pub tables: *const ss_plugin_init_tables_input, pub log_fn: ss_plugin_log_fn_t, @@ -750,7 +748,7 @@ const _: () = { pub struct ss_plugin_field_extract_input { pub owner: *mut ss_plugin_owner_t, pub get_owner_last_error: ::std::option::Option< - unsafe extern "C-unwind" fn(o: *mut ss_plugin_owner_t) -> *const ::std::os::raw::c_char, + unsafe extern "C" fn(o: *mut ss_plugin_owner_t) -> *const ::std::os::raw::c_char, >, pub num_fields: u32, pub fields: *mut ss_plugin_extract_field, @@ -784,7 +782,7 @@ const _: () = { pub struct ss_plugin_event_parse_input { pub owner: *mut ss_plugin_owner_t, pub get_owner_last_error: ::std::option::Option< - unsafe extern "C-unwind" fn(o: *mut ss_plugin_owner_t) -> *const ::std::os::raw::c_char, + unsafe extern "C" fn(o: *mut ss_plugin_owner_t) -> *const ::std::os::raw::c_char, >, pub table_reader: ss_plugin_table_reader_vtable, pub table_writer: ss_plugin_table_writer_vtable, @@ -831,23 +829,20 @@ pub struct ss_plugin_routine_t(pub ::std::os::raw::c_void); #[derive(Debug)] pub struct ss_plugin_routine_state_t(pub ::std::os::raw::c_void); pub type ss_plugin_routine_fn_t = ::std::option::Option< - unsafe extern "C-unwind" fn( - s: *mut ss_plugin_t, - i: *mut ss_plugin_routine_state_t, - ) -> ss_plugin_bool, + unsafe extern "C" fn(s: *mut ss_plugin_t, i: *mut ss_plugin_routine_state_t) -> ss_plugin_bool, >; #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct ss_plugin_routine_vtable { pub subscribe: ::std::option::Option< - unsafe extern "C-unwind" fn( + unsafe extern "C" fn( o: *mut ss_plugin_owner_t, f: ss_plugin_routine_fn_t, i: *mut ss_plugin_routine_state_t, ) -> *mut ss_plugin_routine_t, >, pub unsubscribe: ::std::option::Option< - unsafe extern "C-unwind" fn( + unsafe extern "C" fn( o: *mut ss_plugin_owner_t, r: *mut ss_plugin_routine_t, ) -> ss_plugin_rc, @@ -872,7 +867,7 @@ pub struct ss_plugin_capture_listen_input { pub table_reader_ext: *mut ss_plugin_table_reader_vtable_ext, pub table_writer_ext: *mut ss_plugin_table_writer_vtable_ext, pub get_owner_last_error: ::std::option::Option< - unsafe extern "C-unwind" fn(o: *mut ss_plugin_owner_t) -> *const ::std::os::raw::c_char, + unsafe extern "C" fn(o: *mut ss_plugin_owner_t) -> *const ::std::os::raw::c_char, >, } #[allow(clippy::unnecessary_operation, clippy::identity_op)] @@ -893,7 +888,7 @@ const _: () = { [::std::mem::offset_of!(ss_plugin_capture_listen_input, get_owner_last_error) - 32usize]; }; pub type ss_plugin_async_event_handler_t = ::std::option::Option< - unsafe extern "C-unwind" fn( + unsafe extern "C" fn( o: *mut ss_plugin_owner_t, evt: *const ss_plugin_event, err: *mut ::std::os::raw::c_char, @@ -903,88 +898,81 @@ pub type ss_plugin_async_event_handler_t = ::std::option::Option< #[derive(Debug, Copy, Clone)] pub struct plugin_api { pub get_required_api_version: - ::std::option::Option *const ::std::os::raw::c_char>, + ::std::option::Option *const ::std::os::raw::c_char>, pub get_init_schema: ::std::option::Option< - unsafe extern "C-unwind" fn( + unsafe extern "C" fn( schema_type: *mut ss_plugin_schema_type, ) -> *const ::std::os::raw::c_char, >, pub init: ::std::option::Option< - unsafe extern "C-unwind" fn( + unsafe extern "C" fn( input: *const ss_plugin_init_input, rc: *mut ss_plugin_rc, ) -> *mut ss_plugin_t, >, - pub destroy: ::std::option::Option, + pub destroy: ::std::option::Option, pub get_last_error: ::std::option::Option< - unsafe extern "C-unwind" fn(s: *mut ss_plugin_t) -> *const ::std::os::raw::c_char, + unsafe extern "C" fn(s: *mut ss_plugin_t) -> *const ::std::os::raw::c_char, >, - pub get_name: - ::std::option::Option *const ::std::os::raw::c_char>, + pub get_name: ::std::option::Option *const ::std::os::raw::c_char>, pub get_description: - ::std::option::Option *const ::std::os::raw::c_char>, - pub get_contact: - ::std::option::Option *const ::std::os::raw::c_char>, - pub get_version: - ::std::option::Option *const ::std::os::raw::c_char>, + ::std::option::Option *const ::std::os::raw::c_char>, + pub get_contact: ::std::option::Option *const ::std::os::raw::c_char>, + pub get_version: ::std::option::Option *const ::std::os::raw::c_char>, pub __bindgen_anon_1: plugin_api__bindgen_ty_1, pub __bindgen_anon_2: plugin_api__bindgen_ty_2, pub __bindgen_anon_3: plugin_api__bindgen_ty_3, pub __bindgen_anon_4: plugin_api__bindgen_ty_4, pub set_config: ::std::option::Option< - unsafe extern "C-unwind" fn( + unsafe extern "C" fn( s: *mut ss_plugin_t, i: *const ss_plugin_set_config_input, ) -> ss_plugin_rc, >, pub get_metrics: ::std::option::Option< - unsafe extern "C-unwind" fn( - s: *mut ss_plugin_t, - num_metrics: *mut u32, - ) -> *mut ss_plugin_metric, + unsafe extern "C" fn(s: *mut ss_plugin_t, num_metrics: *mut u32) -> *mut ss_plugin_metric, >, pub __bindgen_anon_5: plugin_api__bindgen_ty_5, pub get_required_event_schema_version: ::std::option::Option< - unsafe extern "C-unwind" fn(s: *mut ss_plugin_t) -> *const ::std::os::raw::c_char, + unsafe extern "C" fn(s: *mut ss_plugin_t) -> *const ::std::os::raw::c_char, >, } #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct plugin_api__bindgen_ty_1 { - pub get_id: ::std::option::Option u32>, + pub get_id: ::std::option::Option u32>, pub get_event_source: - ::std::option::Option *const ::std::os::raw::c_char>, + ::std::option::Option *const ::std::os::raw::c_char>, pub open: ::std::option::Option< - unsafe extern "C-unwind" fn( + unsafe extern "C" fn( s: *mut ss_plugin_t, params: *const ::std::os::raw::c_char, rc: *mut ss_plugin_rc, ) -> *mut ss_instance_t, >, - pub close: ::std::option::Option< - unsafe extern "C-unwind" fn(s: *mut ss_plugin_t, h: *mut ss_instance_t), - >, + pub close: + ::std::option::Option, pub list_open_params: ::std::option::Option< - unsafe extern "C-unwind" fn( + unsafe extern "C" fn( s: *mut ss_plugin_t, rc: *mut ss_plugin_rc, ) -> *const ::std::os::raw::c_char, >, pub get_progress: ::std::option::Option< - unsafe extern "C-unwind" fn( + unsafe extern "C" fn( s: *mut ss_plugin_t, h: *mut ss_instance_t, progress_pct: *mut u32, ) -> *const ::std::os::raw::c_char, >, pub event_to_string: ::std::option::Option< - unsafe extern "C-unwind" fn( + unsafe extern "C" fn( s: *mut ss_plugin_t, evt: *const ss_plugin_event_input, ) -> *const ::std::os::raw::c_char, >, pub next_batch: ::std::option::Option< - unsafe extern "C-unwind" fn( + unsafe extern "C" fn( s: *mut ss_plugin_t, h: *mut ss_instance_t, nevts: *mut u32, @@ -1019,14 +1007,13 @@ const _: () = { #[derive(Debug, Copy, Clone)] pub struct plugin_api__bindgen_ty_2 { pub get_extract_event_types: ::std::option::Option< - unsafe extern "C-unwind" fn(numtypes: *mut u32, s: *mut ss_plugin_t) -> *mut u16, + unsafe extern "C" fn(numtypes: *mut u32, s: *mut ss_plugin_t) -> *mut u16, >, pub get_extract_event_sources: - ::std::option::Option *const ::std::os::raw::c_char>, - pub get_fields: - ::std::option::Option *const ::std::os::raw::c_char>, + ::std::option::Option *const ::std::os::raw::c_char>, + pub get_fields: ::std::option::Option *const ::std::os::raw::c_char>, pub extract_fields: ::std::option::Option< - unsafe extern "C-unwind" fn( + unsafe extern "C" fn( s: *mut ss_plugin_t, evt: *const ss_plugin_event_input, in_: *const ss_plugin_field_extract_input, @@ -1052,12 +1039,12 @@ const _: () = { #[derive(Debug, Copy, Clone)] pub struct plugin_api__bindgen_ty_3 { pub get_parse_event_types: ::std::option::Option< - unsafe extern "C-unwind" fn(numtypes: *mut u32, s: *mut ss_plugin_t) -> *mut u16, + unsafe extern "C" fn(numtypes: *mut u32, s: *mut ss_plugin_t) -> *mut u16, >, pub get_parse_event_sources: - ::std::option::Option *const ::std::os::raw::c_char>, + ::std::option::Option *const ::std::os::raw::c_char>, pub parse_event: ::std::option::Option< - unsafe extern "C-unwind" fn( + unsafe extern "C" fn( s: *mut ss_plugin_t, evt: *const ss_plugin_event_input, in_: *const ss_plugin_event_parse_input, @@ -1081,18 +1068,18 @@ const _: () = { #[derive(Debug, Copy, Clone)] pub struct plugin_api__bindgen_ty_4 { pub get_async_event_sources: - ::std::option::Option *const ::std::os::raw::c_char>, + ::std::option::Option *const ::std::os::raw::c_char>, pub get_async_events: - ::std::option::Option *const ::std::os::raw::c_char>, + ::std::option::Option *const ::std::os::raw::c_char>, pub set_async_event_handler: ::std::option::Option< - unsafe extern "C-unwind" fn( + unsafe extern "C" fn( s: *mut ss_plugin_t, owner: *mut ss_plugin_owner_t, handler: ss_plugin_async_event_handler_t, ) -> ss_plugin_rc, >, pub dump_state: ::std::option::Option< - unsafe extern "C-unwind" fn( + unsafe extern "C" fn( s: *mut ss_plugin_t, owner: *mut ss_plugin_owner_t, handler: ss_plugin_async_event_handler_t, @@ -1118,13 +1105,13 @@ const _: () = { #[derive(Debug, Copy, Clone)] pub struct plugin_api__bindgen_ty_5 { pub capture_open: ::std::option::Option< - unsafe extern "C-unwind" fn( + unsafe extern "C" fn( s: *mut ss_plugin_t, i: *const ss_plugin_capture_listen_input, ) -> ss_plugin_rc, >, pub capture_close: ::std::option::Option< - unsafe extern "C-unwind" fn( + unsafe extern "C" fn( s: *mut ss_plugin_t, i: *const ss_plugin_capture_listen_input, ) -> ss_plugin_rc, diff --git a/falco_plugin_runner/src/event.rs b/falco_plugin_runner/src/event.rs index 12b271e9..843cceae 100644 --- a/falco_plugin_runner/src/event.rs +++ b/falco_plugin_runner/src/event.rs @@ -6,7 +6,7 @@ pub struct Event { pub source: *const c_char, pub source_plugin: *mut ss_plugin_t, pub to_string: Option< - unsafe extern "C-unwind" fn( + unsafe extern "C" fn( *mut falco_plugin_api::ss_plugin_t, *const falco_plugin_api::ss_plugin_event_input, ) -> *const c_char, diff --git a/falco_plugin_runner/src/plugin/async_event.rs b/falco_plugin_runner/src/plugin/async_event.rs index 9f48c282..a51fefaa 100644 --- a/falco_plugin_runner/src/plugin/async_event.rs +++ b/falco_plugin_runner/src/plugin/async_event.rs @@ -106,7 +106,7 @@ pub struct AsyncEvent<'a> { pub data: &'a [u8], } -unsafe extern "C-unwind" fn async_handler( +unsafe extern "C" fn async_handler( owner: *mut ss_plugin_owner_t, event: *const ss_plugin_event, err: *mut c_char, diff --git a/falco_plugin_runner/src/plugin/event_source_filter.rs b/falco_plugin_runner/src/plugin/event_source_filter.rs index 0ba11a9d..395c5e97 100644 --- a/falco_plugin_runner/src/plugin/event_source_filter.rs +++ b/falco_plugin_runner/src/plugin/event_source_filter.rs @@ -11,8 +11,8 @@ impl EventSourceFilter { pub fn new( plugin: *mut ss_plugin_t, api: &plugin_api, - event_types_fn: Option *mut u16>, - event_sources_fn: Option *const c_char>, + event_types_fn: Option *mut u16>, + event_sources_fn: Option *const c_char>, ) -> Result { let mut event_sources = match event_sources_fn { Some(event_sources_fn) => { diff --git a/falco_plugin_runner/src/plugin/listen.rs b/falco_plugin_runner/src/plugin/listen.rs index bc65d623..9cfb7de7 100644 --- a/falco_plugin_runner/src/plugin/listen.rs +++ b/falco_plugin_runner/src/plugin/listen.rs @@ -83,7 +83,7 @@ impl CaptureListenPlugin { })) } - unsafe extern "C-unwind" fn subscribe( + unsafe extern "C" fn subscribe( owner: *mut ss_plugin_owner_t, func: ss_plugin_routine_fn_t, state: *mut ss_plugin_routine_state_t, @@ -103,7 +103,7 @@ impl CaptureListenPlugin { ret } - unsafe extern "C-unwind" fn unsubscribe( + unsafe extern "C" fn unsubscribe( owner: *mut ss_plugin_owner_t, routine: *mut ss_plugin_routine_t, ) -> ss_plugin_rc { @@ -116,7 +116,7 @@ impl CaptureListenPlugin { ss_plugin_rc_SS_PLUGIN_SUCCESS } - extern "C-unwind" fn get_owner_last_error(_owner: *mut ss_plugin_owner_t) -> *const c_char { + extern "C" fn get_owner_last_error(_owner: *mut ss_plugin_owner_t) -> *const c_char { std::ptr::null() } diff --git a/falco_plugin_runner/src/plugin/mod.rs b/falco_plugin_runner/src/plugin/mod.rs index 7ff3a747..628b56a7 100644 --- a/falco_plugin_runner/src/plugin/mod.rs +++ b/falco_plugin_runner/src/plugin/mod.rs @@ -489,11 +489,11 @@ impl Plugin { } } -extern "C-unwind" fn get_last_owner_error(_owner: *mut ss_plugin_owner_t) -> *const c_char { +extern "C" fn get_last_owner_error(_owner: *mut ss_plugin_owner_t) -> *const c_char { std::ptr::null() } -unsafe extern "C-unwind" fn log( +unsafe extern "C" fn log( _owner: *mut ss_plugin_owner_t, component: *const c_char, msg: *const c_char, @@ -509,7 +509,7 @@ unsafe extern "C-unwind" fn log( eprintln!(); } -unsafe extern "C-unwind" fn list_tables( +unsafe extern "C" fn list_tables( owner: *mut ss_plugin_owner_t, ntables: *mut u32, ) -> *mut ss_plugin_table_info { @@ -523,7 +523,7 @@ unsafe extern "C-unwind" fn list_tables( tables.as_mut_ptr() } -pub unsafe extern "C-unwind" fn get_table( +pub unsafe extern "C" fn get_table( owner: *mut ss_plugin_owner_t, name: *const ::std::os::raw::c_char, key_type: ss_plugin_state_type, @@ -544,7 +544,7 @@ pub unsafe extern "C-unwind" fn get_table( } } -pub unsafe extern "C-unwind" fn add_table( +pub unsafe extern "C" fn add_table( owner: *mut ss_plugin_owner_t, table_input: *const ss_plugin_table_input, ) -> ss_plugin_rc { diff --git a/falco_plugin_runner/src/tables.rs b/falco_plugin_runner/src/tables.rs index 7b876508..1e294a67 100644 --- a/falco_plugin_runner/src/tables.rs +++ b/falco_plugin_runner/src/tables.rs @@ -43,18 +43,18 @@ macro_rules! delegate_table_method { }}; } -unsafe extern "C-unwind" fn get_table_name(table: *mut ss_plugin_table_t) -> *const c_char { +unsafe extern "C" fn get_table_name(table: *mut ss_plugin_table_t) -> *const c_char { let (get_table_name, table) = delegate_table_method!(table => reader_ext.get_table_name or std::ptr::null()); unsafe { get_table_name(table) } } -unsafe extern "C-unwind" fn get_table_size(table: *mut ss_plugin_table_t) -> u64 { +unsafe extern "C" fn get_table_size(table: *mut ss_plugin_table_t) -> u64 { let (get_table_size, table) = delegate_table_method!(table => reader_ext.get_table_size or 0); unsafe { get_table_size(table) } } -unsafe extern "C-unwind" fn get_table_entry( +unsafe extern "C" fn get_table_entry( table: *mut ss_plugin_table_t, key: *const ss_plugin_state_data, ) -> *mut ss_plugin_table_entry_t { @@ -63,7 +63,7 @@ unsafe extern "C-unwind" fn get_table_entry( unsafe { get_table_entry(table, key) } } -unsafe extern "C-unwind" fn read_entry_field( +unsafe extern "C" fn read_entry_field( table: *mut ss_plugin_table_t, entry: *mut ss_plugin_table_entry_t, field: *const ss_plugin_table_field_t, @@ -73,7 +73,7 @@ unsafe extern "C-unwind" fn read_entry_field( unsafe { read_entry_field(table, entry, field, out) } } -unsafe extern "C-unwind" fn release_table_entry( +unsafe extern "C" fn release_table_entry( table: *mut ss_plugin_table_t, entry: *mut ss_plugin_table_entry_t, ) { @@ -82,7 +82,7 @@ unsafe extern "C-unwind" fn release_table_entry( unsafe { release_table_entry(table, entry) } } -unsafe extern "C-unwind" fn iterate_entries( +unsafe extern "C" fn iterate_entries( table: *mut ss_plugin_table_t, iter: ss_plugin_table_iterator_func_t, state: *mut ss_plugin_table_iterator_state_t, @@ -91,13 +91,13 @@ unsafe extern "C-unwind" fn iterate_entries( unsafe { iterate_entries(table, iter, state) } } -unsafe extern "C-unwind" fn clear_table(table: *mut ss_plugin_table_t) -> ss_plugin_rc { +unsafe extern "C" fn clear_table(table: *mut ss_plugin_table_t) -> ss_plugin_rc { let (clear_table, table) = delegate_table_method!(table => writer_ext.clear_table or ss_plugin_rc_SS_PLUGIN_FAILURE); unsafe { clear_table(table) } } -unsafe extern "C-unwind" fn erase_table_entry( +unsafe extern "C" fn erase_table_entry( table: *mut ss_plugin_table_t, key: *const ss_plugin_state_data, ) -> ss_plugin_rc { @@ -105,7 +105,7 @@ unsafe extern "C-unwind" fn erase_table_entry( unsafe { erase_table_entry(table, key) } } -unsafe extern "C-unwind" fn create_table_entry( +unsafe extern "C" fn create_table_entry( table: *mut ss_plugin_table_t, ) -> *mut ss_plugin_table_entry_t { let (create_table_entry, table) = @@ -113,7 +113,7 @@ unsafe extern "C-unwind" fn create_table_entry( unsafe { create_table_entry(table) } } -unsafe extern "C-unwind" fn destroy_table_entry( +unsafe extern "C" fn destroy_table_entry( table: *mut ss_plugin_table_t, entry: *mut ss_plugin_table_entry_t, ) { @@ -122,7 +122,7 @@ unsafe extern "C-unwind" fn destroy_table_entry( unsafe { destroy_table_entry(table, entry) } } -unsafe extern "C-unwind" fn add_table_entry( +unsafe extern "C" fn add_table_entry( table: *mut ss_plugin_table_t, key: *const ss_plugin_state_data, entry: *mut ss_plugin_table_entry_t, @@ -132,7 +132,7 @@ unsafe extern "C-unwind" fn add_table_entry( unsafe { add_table_entry(table, key, entry) } } -unsafe extern "C-unwind" fn write_entry_field( +unsafe extern "C" fn write_entry_field( table: *mut ss_plugin_table_t, entry: *mut ss_plugin_table_entry_t, field: *const ss_plugin_table_field_t, @@ -142,7 +142,7 @@ unsafe extern "C-unwind" fn write_entry_field( unsafe { write_entry_field(table, entry, field, value) } } -unsafe extern "C-unwind" fn list_table_fields( +unsafe extern "C" fn list_table_fields( table: *mut ss_plugin_table_t, nfields: *mut u32, ) -> *const ss_plugin_table_fieldinfo { @@ -151,7 +151,7 @@ unsafe extern "C-unwind" fn list_table_fields( unsafe { list_table_fields(table, nfields) } } -unsafe extern "C-unwind" fn get_table_field( +unsafe extern "C" fn get_table_field( table: *mut ss_plugin_table_t, name: *const c_char, data_type: ss_plugin_state_type, @@ -161,7 +161,7 @@ unsafe extern "C-unwind" fn get_table_field( unsafe { get_table_field(table, name, data_type) } } -unsafe extern "C-unwind" fn add_table_field( +unsafe extern "C" fn add_table_field( table: *mut ss_plugin_table_t, name: *const c_char, data_type: ss_plugin_state_type, diff --git a/justfile b/justfile index 11101efb..d8c9eeda 100644 --- a/justfile +++ b/justfile @@ -26,7 +26,6 @@ regen_api: --new-type-alias ss_plugin_routine_t --no-copy ss_plugin_routine_t \ --new-type-alias ss_plugin_routine_state_t --no-copy ss_plugin_routine_state_t \ --no-debug 'ss_plugin_table_info|ss_plugin_table_fieldinfo' \ - --override-abi '.*=C-unwind' \ --allowlist-item plugin_api \ --allowlist-item 'ss_plugin_.*' \ --allowlist-item 'PLUGIN_.*' \ From d8db5b72ea20d6a59dabf8144b35d355644b1f59 Mon Sep 17 00:00:00 2001 From: Grzegorz Nosek Date: Wed, 27 May 2026 07:11:40 +0200 Subject: [PATCH 02/11] fix(runner): catch panics around async event handling Signed-off-by: Grzegorz Nosek --- falco_plugin_runner/src/plugin/async_event.rs | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/falco_plugin_runner/src/plugin/async_event.rs b/falco_plugin_runner/src/plugin/async_event.rs index a51fefaa..8b10a378 100644 --- a/falco_plugin_runner/src/plugin/async_event.rs +++ b/falco_plugin_runner/src/plugin/async_event.rs @@ -110,6 +110,34 @@ unsafe extern "C" fn async_handler( owner: *mut ss_plugin_owner_t, event: *const ss_plugin_event, err: *mut c_char, +) -> i32 { + let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| unsafe { + async_handler_inner(owner, event, err) + })); + + match result { + Ok(rc) => rc, + Err(payload) => { + let msg = if let Some(s) = payload.downcast_ref::<&'static str>() { + format!("async_handler panicked: {s}") + } else if let Some(s) = payload.downcast_ref::() { + format!("async_handler panicked: {s}") + } else { + "async_handler panicked".to_string() + }; + let err = unsafe { + std::slice::from_raw_parts_mut(err as *mut _, PLUGIN_MAX_ERRLEN as usize) + }; + write_err_msg(err, &msg); + ss_plugin_rc_SS_PLUGIN_FAILURE + } + } +} + +unsafe fn async_handler_inner( + owner: *mut ss_plugin_owner_t, + event: *const ss_plugin_event, + err: *mut c_char, ) -> i32 { let err = unsafe { std::slice::from_raw_parts_mut(err as *mut _, PLUGIN_MAX_ERRLEN as usize) }; let owner = unsafe { &mut *(owner as *mut AsyncPlugin) }; From 8f649c07054455859be3e389b5bc0e55ef12eb84 Mon Sep 17 00:00:00 2001 From: Grzegorz Nosek Date: Thu, 21 May 2026 07:28:59 +0200 Subject: [PATCH 03/11] new(plugin): add a panic-catching wrapper Signed-off-by: Grzegorz Nosek --- falco_plugin/src/error/mod.rs | 1 + falco_plugin/src/error/panic.rs | 20 ++++++++++++++++++++ 2 files changed, 21 insertions(+) create mode 100644 falco_plugin/src/error/panic.rs diff --git a/falco_plugin/src/error/mod.rs b/falco_plugin/src/error/mod.rs index dbccc93b..a41f7011 100644 --- a/falco_plugin/src/error/mod.rs +++ b/falco_plugin/src/error/mod.rs @@ -1,6 +1,7 @@ pub mod as_result; pub mod ffi_result; pub mod last_error; +pub mod panic; use thiserror::Error; diff --git a/falco_plugin/src/error/panic.rs b/falco_plugin/src/error/panic.rs new file mode 100644 index 00000000..a4c9e60a --- /dev/null +++ b/falco_plugin/src/error/panic.rs @@ -0,0 +1,20 @@ +use std::panic::UnwindSafe; + +#[expect(dead_code)] +pub(crate) fn catch_panic(f: F) -> Result +where + F: UnwindSafe + FnOnce() -> Result, +{ + // Call `f` explicitly so debuggers can step into the closure body + // before entering std's catch_unwind machinery. + #[allow(clippy::redundant_closure)] + std::panic::catch_unwind(move || f()).unwrap_or_else(|e| { + if let Some(e) = e.downcast_ref::<&'static str>() { + Err(anyhow::anyhow!("{}", e)) + } else if let Some(e) = e.downcast_ref::() { + Err(anyhow::anyhow!("{}", e)) + } else { + Err(anyhow::anyhow!("panic")) + } + }) +} From b792cc60c4e60a752e0409b7175bcf2658dd47a4 Mon Sep 17 00:00:00 2001 From: Grzegorz Nosek Date: Sun, 28 Sep 2025 10:28:21 +0200 Subject: [PATCH 04/11] fix(plugin): panic safety for base plugins Signed-off-by: Grzegorz Nosek --- falco_plugin/src/base/wrappers.rs | 77 +++++++++++++++++++------------ falco_plugin/src/error/panic.rs | 1 - 2 files changed, 48 insertions(+), 30 deletions(-) diff --git a/falco_plugin/src/base/wrappers.rs b/falco_plugin/src/base/wrappers.rs index d10db0c8..dc4aa001 100644 --- a/falco_plugin/src/base/wrappers.rs +++ b/falco_plugin/src/base/wrappers.rs @@ -3,6 +3,7 @@ use crate::base::schema::{ConfigSchema, ConfigSchemaType}; use crate::base::Plugin; use crate::error::ffi_result::FfiResult; use crate::error::last_error::LastError; +use crate::error::panic::catch_panic; use crate::strings::from_ptr::try_str_from_ptr; use crate::strings::WriteIntoCString; use crate::tables::TablesInput; @@ -15,6 +16,7 @@ use std::collections::BTreeMap; use std::ffi::{c_char, CString}; use std::fmt::Display; use std::io::Write; +use std::panic::AssertUnwindSafe; use std::sync::Mutex; /// Marker trait to mark a plugin as exported to the API @@ -72,38 +74,40 @@ pub unsafe extern "C" fn plugin_init( init_input: *const ss_plugin_init_input, rc: *mut ss_plugin_rc, ) -> *mut falco_plugin_api::ss_plugin_t { - let res = (|| -> Result<*mut PluginWrapper

, anyhow::Error> { - let init_input = unsafe { init_input.as_ref() } - .ok_or_else(|| anyhow::anyhow!("Got empty init_input"))?; + let res = catch_panic(AssertUnwindSafe( + || -> Result<*mut PluginWrapper

, anyhow::Error> { + let init_input = unsafe { init_input.as_ref() } + .ok_or_else(|| anyhow::anyhow!("Got empty init_input"))?; - let init_config = - try_str_from_ptr(&init_input.config).context("Failed to get config string")?; + let init_config = + try_str_from_ptr(&init_input.config).context("Failed to get config string")?; - let config = P::ConfigType::from_str(init_config).context("Failed to parse config")?; - if let Some(log_fn) = init_input.log_fn { - let logger_impl = FalcoPluginLoggerImpl { - owner: init_input.owner, - logger_fn: log_fn, - }; + let config = P::ConfigType::from_str(init_config).context("Failed to parse config")?; + if let Some(log_fn) = init_input.log_fn { + let logger_impl = FalcoPluginLoggerImpl { + owner: init_input.owner, + logger_fn: log_fn, + }; - *FALCO_LOGGER.inner.write().unwrap() = Some(logger_impl); - log::set_logger(&FALCO_LOGGER).ok(); + *FALCO_LOGGER.inner.write().unwrap() = Some(logger_impl); + log::set_logger(&FALCO_LOGGER).ok(); - #[cfg(debug_assertions)] - log::set_max_level(log::LevelFilter::Trace); + #[cfg(debug_assertions)] + log::set_max_level(log::LevelFilter::Trace); - #[cfg(not(debug_assertions))] - log::set_max_level(log::LevelFilter::Info); - } + #[cfg(not(debug_assertions))] + log::set_max_level(log::LevelFilter::Info); + } - let tables_input = - TablesInput::try_from(init_input).context("Failed to build tables input")?; + let tables_input = + TablesInput::try_from(init_input).context("Failed to build tables input")?; - let last_error = unsafe { LastError::from(init_input)? }; + let last_error = unsafe { LastError::from(init_input)? }; - P::new(tables_input.as_ref(), config) - .map(|plugin| Box::into_raw(Box::new(PluginWrapper::new(plugin, last_error)))) - })(); + P::new(tables_input.as_ref(), config) + .map(|plugin| Box::into_raw(Box::new(PluginWrapper::new(plugin, last_error)))) + }, + )); match res { Ok(plugin) => { @@ -154,7 +158,15 @@ pub unsafe extern "C" fn plugin_get_init_schema( pub unsafe extern "C" fn plugin_destroy(plugin: *mut falco_plugin_api::ss_plugin_t) { unsafe { let plugin = plugin as *mut PluginWrapper

; - let _ = Box::from_raw(plugin); + match catch_panic(AssertUnwindSafe(|| { + let _ = Box::from_raw(plugin); + Ok(()) + })) { + Ok(()) => {} + Err(e) => { + log::error!("Failed to destroy plugin: {e}"); + } + } } } @@ -187,7 +199,7 @@ pub unsafe extern "C" fn plugin_set_config( return ss_plugin_rc_SS_PLUGIN_FAILURE; }; - let res = (|| -> Result<(), anyhow::Error> { + let res = catch_panic(AssertUnwindSafe(|| -> Result<(), anyhow::Error> { let config_input = unsafe { config_input.as_ref() }.context("Got NULL config")?; let updated_config = @@ -195,7 +207,7 @@ pub unsafe extern "C" fn plugin_set_config( let config = P::ConfigType::from_str(updated_config).context("Failed to parse config")?; actual_plugin.plugin.set_config(config) - })(); + })); res.rc(&mut plugin.error_buf) } @@ -226,8 +238,15 @@ pub unsafe extern "C" fn plugin_get_metrics( }; plugin.metric_storage.clear(); - for metric in actual_plugin.plugin.get_metrics() { - plugin.metric_storage.push(metric.as_raw()); + if let Err(e) = catch_panic(AssertUnwindSafe(|| { + for metric in actual_plugin.plugin.get_metrics() { + plugin.metric_storage.push(metric.as_raw()); + } + Ok(()) + })) { + e.set_last_error(&mut plugin.error_buf); + *num_metrics = 0; + return std::ptr::null_mut(); } *num_metrics = plugin.metric_storage.len() as u32; diff --git a/falco_plugin/src/error/panic.rs b/falco_plugin/src/error/panic.rs index a4c9e60a..b172235e 100644 --- a/falco_plugin/src/error/panic.rs +++ b/falco_plugin/src/error/panic.rs @@ -1,6 +1,5 @@ use std::panic::UnwindSafe; -#[expect(dead_code)] pub(crate) fn catch_panic(f: F) -> Result where F: UnwindSafe + FnOnce() -> Result, From c71509cbdfec303520c244b212de0e9d2bd127d7 Mon Sep 17 00:00:00 2001 From: Grzegorz Nosek Date: Sun, 28 Sep 2025 10:15:08 +0200 Subject: [PATCH 05/11] fix(plugin): panic safety for source plugins Signed-off-by: Grzegorz Nosek --- falco_plugin/src/source/wrappers.rs | 70 +++++++++++++++++++++-------- 1 file changed, 52 insertions(+), 18 deletions(-) diff --git a/falco_plugin/src/source/wrappers.rs b/falco_plugin/src/source/wrappers.rs index 2025a78b..95d20872 100644 --- a/falco_plugin/src/source/wrappers.rs +++ b/falco_plugin/src/source/wrappers.rs @@ -1,5 +1,6 @@ use crate::base::wrappers::PluginWrapper; use crate::error::ffi_result::FfiResult; +use crate::error::panic::catch_panic; use crate::source::SourcePluginInstanceWrapper; use crate::source::{EventBatch, EventInput, SourcePlugin, SourcePluginInstance}; use crate::strings::cstring_writer::WriteIntoCString; @@ -12,6 +13,7 @@ use falco_plugin_api::{ use std::ffi::c_char; use std::io::Write; use std::marker::PhantomData; +use std::panic::AssertUnwindSafe; /// Marker trait to mark a source plugin as exported to the API /// @@ -94,12 +96,17 @@ pub unsafe extern "C" fn plugin_list_open_params( return std::ptr::null(); }; - match actual_plugin.plugin.list_open_params() { + // .as_ptr() breaks the lifetime requirement, which implies that the caller + // cannot hold on to the result before the next API call. As this whole method + // is apparently unused, this holds (for now). + match catch_panic(AssertUnwindSafe(|| { + actual_plugin.plugin.list_open_params().map(|s| s.as_ptr()) + })) { Ok(s) => { unsafe { *rc = ss_plugin_rc_SS_PLUGIN_SUCCESS; } - s.as_ptr() + s } Err(e) => { unsafe { @@ -149,7 +156,7 @@ pub unsafe extern "C" fn plugin_open( } }; - match actual_plugin.plugin.open(params) { + match catch_panic(AssertUnwindSafe(|| actual_plugin.plugin.open(params))) { Ok(instance) => { *rc = ss_plugin_rc_SS_PLUGIN_SUCCESS; Box::into_raw(Box::new(SourcePluginInstanceWrapper { @@ -191,7 +198,14 @@ pub unsafe extern "C" fn plugin_close( } unsafe { let mut inst = Box::from_raw(instance); - actual_plugin.plugin.close(&mut inst.instance); + let res = catch_panic(AssertUnwindSafe(|| { + actual_plugin.plugin.close(&mut inst.instance); + Ok(()) + })); + + if let Err(e) = res { + log::error!("Error closing source plugin instance: {}", e) + } } } @@ -220,9 +234,11 @@ pub unsafe extern "C" fn plugin_next_batch( instance.batch.reset(); let mut batch = EventBatch::new(&instance.batch); - let batch_result = instance - .instance - .next_batch(&mut actual_plugin.plugin, &mut batch); + let batch_result = catch_panic(AssertUnwindSafe(|| { + instance + .instance + .next_batch(&mut actual_plugin.plugin, &mut batch) + })); match batch_result { Ok(()) => { let events = batch.get_events(); @@ -249,23 +265,39 @@ pub unsafe extern "C" fn plugin_get_progress( progress_pct: *mut u32, ) -> *const c_char { let instance = instance as *mut SourcePluginInstanceWrapper; - let progress = unsafe { instance.as_mut() }.map(|instance| instance.instance.get_progress()); - - if let Some(progress) = progress { + let Some(instance) = (unsafe { instance.as_mut() }) else { unsafe { - *progress_pct = (progress.value * 100.0) as u32; + *progress_pct = 0; } + return std::ptr::null(); + }; + // .as_ptr() implies that the caller cannot hold on to the result before the next API call + let progress = catch_panic(AssertUnwindSafe(|| { + let progress = instance.instance.get_progress(); match progress.detail { - Some(s) => s.as_ptr(), - None => std::ptr::null(), + Some(s) => Ok((progress.value, s.as_ptr())), + None => Ok((progress.value, std::ptr::null())), } - } else { - unsafe { - *progress_pct = 0; + })); + + match progress { + Ok((pct, detail)) => { + unsafe { + *progress_pct = (pct * 100.0) as u32; + } + + detail } + Err(e) => { + log::error!("Error getting progress for source plugin instance: {}", e); - std::ptr::null() + unsafe { + *progress_pct = 0; + } + + std::ptr::null() + } } } @@ -290,7 +322,9 @@ pub unsafe extern "C" fn plugin_event_to_string( }; let event = EventInput(*event, PhantomData); - match actual_plugin.plugin.event_to_string(&event) { + match catch_panic(AssertUnwindSafe(|| { + actual_plugin.plugin.event_to_string(&event) + })) { Ok(s) => { plugin.string_storage = s; plugin.string_storage.as_ptr() From cade215c5de7fe1eb2131de012c026ab5ab7ee74 Mon Sep 17 00:00:00 2001 From: Grzegorz Nosek Date: Sun, 28 Sep 2025 10:29:37 +0200 Subject: [PATCH 06/11] fix(plugin): panic safety for async plugins Signed-off-by: Grzegorz Nosek --- falco_plugin/src/async_event/wrappers.rs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/falco_plugin/src/async_event/wrappers.rs b/falco_plugin/src/async_event/wrappers.rs index 6b9f9ffc..e7606a63 100644 --- a/falco_plugin/src/async_event/wrappers.rs +++ b/falco_plugin/src/async_event/wrappers.rs @@ -2,6 +2,7 @@ use crate::async_event::async_handler::AsyncHandler; use crate::async_event::AsyncEventPlugin; use crate::base::wrappers::PluginWrapper; use crate::error::ffi_result::FfiResult; +use crate::error::panic::catch_panic; use falco_plugin_api::plugin_api__bindgen_ty_4 as async_plugin_api; use falco_plugin_api::{ ss_plugin_async_event_handler_t, ss_plugin_owner_t, ss_plugin_rc, @@ -10,6 +11,7 @@ use falco_plugin_api::{ use std::any::TypeId; use std::collections::BTreeMap; use std::ffi::{c_char, CString}; +use std::panic::AssertUnwindSafe; use std::sync::Mutex; /// Marker trait to mark an async plugin as exported to the API @@ -101,7 +103,7 @@ pub unsafe extern "C" fn plugin_set_async_event_handler( return ss_plugin_rc_SS_PLUGIN_FAILURE; }; - if let Err(e) = actual_plugin.plugin.stop_async() { + if let Err(e) = catch_panic(AssertUnwindSafe(|| actual_plugin.plugin.stop_async())) { e.set_last_error(&mut plugin.error_buf); return e.status_code(); } @@ -114,7 +116,9 @@ pub unsafe extern "C" fn plugin_set_async_event_handler( owner, raw_handler: *raw_handler, }; - if let Err(e) = actual_plugin.plugin.start_async(handler) { + if let Err(e) = catch_panic(AssertUnwindSafe(|| { + actual_plugin.plugin.start_async(handler) + })) { e.set_last_error(&mut plugin.error_buf); return e.status_code(); } @@ -148,7 +152,9 @@ pub unsafe extern "C" fn plugin_dump_state( owner, raw_handler: *raw_handler, }; - if let Err(e) = actual_plugin.plugin.dump_state(handler) { + if let Err(e) = catch_panic(AssertUnwindSafe(|| { + actual_plugin.plugin.dump_state(handler) + })) { e.set_last_error(&mut plugin.error_buf); return e.status_code(); } From 26a04a2c8166fe9a906acacf577cdee73f5afbe1 Mon Sep 17 00:00:00 2001 From: Grzegorz Nosek Date: Sun, 28 Sep 2025 10:30:48 +0200 Subject: [PATCH 07/11] fix(plugin): panic safety for extract plugins Signed-off-by: Grzegorz Nosek --- falco_plugin/src/extract/wrappers.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/falco_plugin/src/extract/wrappers.rs b/falco_plugin/src/extract/wrappers.rs index b19560de..5a809bc2 100644 --- a/falco_plugin/src/extract/wrappers.rs +++ b/falco_plugin/src/extract/wrappers.rs @@ -1,5 +1,6 @@ use crate::base::wrappers::PluginWrapper; use crate::error::ffi_result::FfiResult; +use crate::error::panic::catch_panic; use crate::event::EventInput; use crate::extract::ExtractPlugin; use crate::tables::LazyTableReader; @@ -12,6 +13,7 @@ use std::any::TypeId; use std::collections::BTreeMap; use std::ffi::{c_char, CString}; use std::marker::PhantomData; +use std::panic::AssertUnwindSafe; use std::sync::Mutex; /// Marker trait to mark an extract plugin as exported to the API @@ -124,16 +126,16 @@ pub unsafe extern "C" fn plugin_extract_fields( let table_reader = LazyTableReader::new(reader_ext, actual_plugin.last_error.clone()); plugin.field_storage.reset(); - actual_plugin - .plugin - .extract_fields( + catch_panic(AssertUnwindSafe(|| { + actual_plugin.plugin.extract_fields( &event_input, &table_reader, fields, offsets, &plugin.field_storage, ) - .rc(&mut plugin.error_buf) + })) + .rc(&mut plugin.error_buf) } } From f6e6ae525b2e7c9d35389b63f017aecb60017c8b Mon Sep 17 00:00:00 2001 From: Grzegorz Nosek Date: Sun, 28 Sep 2025 10:31:44 +0200 Subject: [PATCH 08/11] fix(plugin): panic safety for capture listen plugins Signed-off-by: Grzegorz Nosek --- falco_plugin/src/listen/wrappers.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/falco_plugin/src/listen/wrappers.rs b/falco_plugin/src/listen/wrappers.rs index 60bb652d..1e99068b 100644 --- a/falco_plugin/src/listen/wrappers.rs +++ b/falco_plugin/src/listen/wrappers.rs @@ -1,11 +1,13 @@ use crate::base::wrappers::PluginWrapper; use crate::error::ffi_result::FfiResult; +use crate::error::panic::catch_panic; use crate::listen::CaptureListenInput; use crate::listen::CaptureListenPlugin; use falco_plugin_api::{ plugin_api__bindgen_ty_5 as listen_plugin_api, ss_plugin_capture_listen_input, ss_plugin_rc, ss_plugin_rc_SS_PLUGIN_FAILURE, ss_plugin_rc_SS_PLUGIN_SUCCESS, ss_plugin_t, }; +use std::panic::AssertUnwindSafe; /// Marker trait to mark a capture listen plugin as exported to the API /// @@ -66,7 +68,9 @@ pub unsafe extern "C" fn plugin_capture_open( listen_input }; - if let Err(e) = actual_plugin.plugin.capture_open(&listen_input) { + if let Err(e) = catch_panic(AssertUnwindSafe(|| { + actual_plugin.plugin.capture_open(&listen_input) + })) { e.set_last_error(&mut plugin.error_buf); return e.status_code(); } @@ -98,7 +102,9 @@ pub unsafe extern "C" fn plugin_capture_close( listen_input }; - if let Err(e) = actual_plugin.plugin.capture_close(&listen_input) { + if let Err(e) = catch_panic(AssertUnwindSafe(|| { + actual_plugin.plugin.capture_close(&listen_input) + })) { e.set_last_error(&mut plugin.error_buf); return e.status_code(); } From 69ebce63d023092b14f0a51d3ac1c72cba7e0b4f Mon Sep 17 00:00:00 2001 From: Grzegorz Nosek Date: Sun, 28 Sep 2025 10:32:35 +0200 Subject: [PATCH 09/11] fix(plugin): panic safety for parse plugins Signed-off-by: Grzegorz Nosek --- falco_plugin/src/parse/wrappers.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/falco_plugin/src/parse/wrappers.rs b/falco_plugin/src/parse/wrappers.rs index 10753a9b..dcb57301 100644 --- a/falco_plugin/src/parse/wrappers.rs +++ b/falco_plugin/src/parse/wrappers.rs @@ -1,5 +1,6 @@ use crate::base::wrappers::PluginWrapper; use crate::error::ffi_result::FfiResult; +use crate::error::panic::catch_panic; use crate::parse::EventInput; use crate::parse::{ParseInput, ParsePlugin}; use falco_event::events::AnyEventPayload; @@ -12,6 +13,7 @@ use std::any::TypeId; use std::collections::BTreeMap; use std::ffi::{c_char, CString}; use std::marker::PhantomData; +use std::panic::AssertUnwindSafe; use std::sync::Mutex; /// Marker trait to mark a parse plugin as exported to the API @@ -112,10 +114,10 @@ pub unsafe extern "C" fn plugin_parse_event( return ss_plugin_rc_SS_PLUGIN_FAILURE; }; - actual_plugin - .plugin - .parse_event(&event, &parse_input) - .rc(&mut plugin.error_buf) + catch_panic(AssertUnwindSafe(|| { + actual_plugin.plugin.parse_event(&event, &parse_input) + })) + .rc(&mut plugin.error_buf) } } From 3c359d97a1330274bc94eedf2c361843149f9510 Mon Sep 17 00:00:00 2001 From: Grzegorz Nosek Date: Wed, 24 Sep 2025 07:00:28 +0200 Subject: [PATCH 10/11] new(tests): add tests for panic handling Signed-off-by: Grzegorz Nosek --- falco_plugin_tests/tests/panic/extract.rs | 68 ++++++++++++++++++++ falco_plugin_tests/tests/panic/init.rs | 45 +++++++++++++ falco_plugin_tests/tests/panic/listen.rs | 77 +++++++++++++++++++++++ falco_plugin_tests/tests/panic/parse.rs | 62 ++++++++++++++++++ falco_plugin_tests/tests/panic/source.rs | 77 +++++++++++++++++++++++ falco_plugin_tests/tests/panic_tests.rs | 7 +++ 6 files changed, 336 insertions(+) create mode 100644 falco_plugin_tests/tests/panic/extract.rs create mode 100644 falco_plugin_tests/tests/panic/init.rs create mode 100644 falco_plugin_tests/tests/panic/listen.rs create mode 100644 falco_plugin_tests/tests/panic/parse.rs create mode 100644 falco_plugin_tests/tests/panic/source.rs create mode 100644 falco_plugin_tests/tests/panic_tests.rs diff --git a/falco_plugin_tests/tests/panic/extract.rs b/falco_plugin_tests/tests/panic/extract.rs new file mode 100644 index 00000000..45e2b121 --- /dev/null +++ b/falco_plugin_tests/tests/panic/extract.rs @@ -0,0 +1,68 @@ +use falco_plugin::anyhow::Error; +use falco_plugin::base::Plugin; +use falco_plugin::event::events::RawEvent; +use falco_plugin::extract::{field, ExtractFieldInfo, ExtractPlugin, ExtractRequest}; +use falco_plugin::static_plugin; +use falco_plugin::tables::TablesInput; +use falco_plugin_tests::plugin_collection::source::countdown::{ + CountdownPlugin, COUNTDOWN_PLUGIN_API, +}; +use falco_plugin_tests::{ + init_plugin, instantiate_tests, CapturingTestDriver, PlatformData, TestDriver, +}; +use std::ffi::{CStr, CString}; + +struct ExtractablePlugin; + +impl Plugin for ExtractablePlugin { + const NAME: &'static CStr = c"extractable"; + const PLUGIN_VERSION: &'static CStr = c"0.0.0"; + const DESCRIPTION: &'static CStr = c"panics in extract_fields"; + const CONTACT: &'static CStr = c"rust@localdomain.pl"; + type ConfigType = (); + + fn new(_input: Option<&TablesInput>, _config: Self::ConfigType) -> Result { + Ok(Self) + } +} + +impl ExtractablePlugin { + fn extract_panic(&mut self, _req: ExtractRequest) -> Result { + panic!("extract panic") + } +} + +impl ExtractPlugin for ExtractablePlugin { + type Event<'a> = RawEvent<'a>; + type ExtractContext = (); + + const EXTRACT_FIELDS: &'static [ExtractFieldInfo] = + &[field("extractable.panicking", &Self::extract_panic)]; +} + +static_plugin!(EXTRACTABLE_PLUGIN_API = ExtractablePlugin); + +fn test_extract_panic() { + let (mut driver, _source_plugin) = init_plugin::( + &COUNTDOWN_PLUGIN_API, + cr#"{"remaining": 1, "batch_size": 1}"#, + ) + .unwrap(); + let extract_plugin = driver + .register_plugin(&EXTRACTABLE_PLUGIN_API, c"") + .unwrap(); + driver + .add_filterchecks(&extract_plugin, c"countdown") + .unwrap(); + + let mut driver = driver + .start_capture(CountdownPlugin::NAME, c"", PlatformData::Disabled) + .unwrap(); + + let event = driver.next_event().unwrap(); + assert!(driver + .event_field_as_string(c"extractable.panicking", &event) + .is_err()); +} + +instantiate_tests!(test_extract_panic); diff --git a/falco_plugin_tests/tests/panic/init.rs b/falco_plugin_tests/tests/panic/init.rs new file mode 100644 index 00000000..d3584a10 --- /dev/null +++ b/falco_plugin_tests/tests/panic/init.rs @@ -0,0 +1,45 @@ +use falco_plugin::anyhow::Error; +use falco_plugin::base::Plugin; +use falco_plugin::event::events::RawEvent; +use falco_plugin::parse::{EventInput, ParseInput, ParsePlugin}; +use falco_plugin::static_plugin; +use falco_plugin::tables::TablesInput; +use falco_plugin_tests::{init_plugin, instantiate_tests, TestDriver}; +use std::ffi::CStr; + +struct InitPlugin; + +impl Plugin for InitPlugin { + const NAME: &'static CStr = c"init_fails"; + const PLUGIN_VERSION: &'static CStr = c"0.0.0"; + const DESCRIPTION: &'static CStr = c"panics in Plugin::new"; + const CONTACT: &'static CStr = c"rust@localdomain.pl"; + type ConfigType = (); + + fn new(_input: Option<&TablesInput>, _config: Self::ConfigType) -> Result { + panic!("new panic") + } +} + +impl ParsePlugin for InitPlugin { + type Event<'a> = RawEvent<'a>; + + fn parse_event( + &mut self, + _event: &EventInput>, + _parse_input: &ParseInput, + ) -> Result<(), Error> { + Ok(()) + } +} + +static_plugin!(INIT_PLUGIN_API = InitPlugin); + +fn test_init_panic() { + let res = init_plugin::(&INIT_PLUGIN_API, c""); + assert!(res.is_err()); + let err = res.unwrap_err().to_string(); + assert!(err.contains("new panic")); +} + +instantiate_tests!(test_init_panic); diff --git a/falco_plugin_tests/tests/panic/listen.rs b/falco_plugin_tests/tests/panic/listen.rs new file mode 100644 index 00000000..a6409f1b --- /dev/null +++ b/falco_plugin_tests/tests/panic/listen.rs @@ -0,0 +1,77 @@ +use falco_plugin::anyhow::{self, Error}; +use falco_plugin::base::Plugin; +use falco_plugin::event::events::RawEvent; +use falco_plugin::listen::{CaptureListenInput, CaptureListenPlugin}; +use falco_plugin::source::{EventBatch, EventInput, SourcePlugin, SourcePluginInstance}; +use falco_plugin::tables::TablesInput; +use falco_plugin::{static_plugin, FailureReason}; +use falco_plugin_tests::{init_plugin, instantiate_tests, PlatformData, TestDriver}; +use std::ffi::{CStr, CString}; + +struct ListenablePlugin; + +impl Plugin for ListenablePlugin { + const NAME: &'static CStr = c"listenable"; + const PLUGIN_VERSION: &'static CStr = c"0.0.0"; + const DESCRIPTION: &'static CStr = c"panics in capture_open"; + const CONTACT: &'static CStr = c"rust@localdomain.pl"; + type ConfigType = (); + + fn new(_input: Option<&TablesInput>, _config: Self::ConfigType) -> Result { + Ok(Self) + } +} + +struct ListenableInstance; + +impl SourcePluginInstance for ListenableInstance { + type Plugin = ListenablePlugin; + + fn next_batch( + &mut self, + _plugin: &mut Self::Plugin, + _batch: &mut EventBatch, + ) -> Result<(), Error> { + Err(anyhow::anyhow!("done").context(FailureReason::Eof)) + } +} + +impl SourcePlugin for ListenablePlugin { + type Instance = ListenableInstance; + const EVENT_SOURCE: &'static CStr = c"listenable"; + const PLUGIN_ID: u32 = 1112; + type Event<'a> = RawEvent<'a>; + + fn open(&mut self, _params: Option<&str>) -> Result { + Ok(ListenableInstance) + } + + fn event_to_string(&mut self, _event: &EventInput) -> Result { + Ok(CString::from(c"noop")) + } +} + +impl CaptureListenPlugin for ListenablePlugin { + fn capture_open(&mut self, _listen_input: &CaptureListenInput) -> Result<(), Error> { + panic!("listen panic") + } + + fn capture_close(&mut self, _listen_input: &CaptureListenInput) -> Result<(), Error> { + Ok(()) + } +} + +static_plugin!(LISTENABLE_PLUGIN_API = ListenablePlugin); + +fn test_listen_panic() { + let (driver, _plugin) = init_plugin::(&LISTENABLE_PLUGIN_API, c"").unwrap(); + let res = driver.start_capture(ListenablePlugin::NAME, c"", PlatformData::Disabled); + match res { + Ok(_) => panic!("expected start_capture to fail"), + Err(e) => { + assert!(e.to_string().contains("listen panic")); + } + } +} + +instantiate_tests!(test_listen_panic); diff --git a/falco_plugin_tests/tests/panic/parse.rs b/falco_plugin_tests/tests/panic/parse.rs new file mode 100644 index 00000000..a72b6690 --- /dev/null +++ b/falco_plugin_tests/tests/panic/parse.rs @@ -0,0 +1,62 @@ +use falco_plugin::anyhow::Error; +use falco_plugin::base::Plugin; +use falco_plugin::event::events::RawEvent; +use falco_plugin::extract::EventInput; +use falco_plugin::parse::{ParseInput, ParsePlugin}; +use falco_plugin::static_plugin; +use falco_plugin::tables::TablesInput; +use falco_plugin_tests::plugin_collection::source::countdown::{ + CountdownPlugin, COUNTDOWN_PLUGIN_API, +}; +use falco_plugin_tests::{ + init_plugin, instantiate_tests, CapturingTestDriver, PlatformData, TestDriver, +}; +use std::ffi::CStr; + +struct ParseablePlugin; + +impl Plugin for ParseablePlugin { + const NAME: &'static CStr = c"parseable"; + const PLUGIN_VERSION: &'static CStr = c"0.0.0"; + const DESCRIPTION: &'static CStr = c"panics in parse_event"; + const CONTACT: &'static CStr = c"rust@localdomain.pl"; + type ConfigType = (); + + fn new(_input: Option<&TablesInput>, _config: Self::ConfigType) -> Result { + Ok(Self) + } +} + +impl ParsePlugin for ParseablePlugin { + type Event<'a> = RawEvent<'a>; + + fn parse_event( + &mut self, + _event: &EventInput>, + _parse_input: &ParseInput, + ) -> Result<(), Error> { + panic!("parse panic") + } +} + +static_plugin!(PARSEABLE_PLUGIN_API = ParseablePlugin); + +fn test_parse_panic() { + let (mut driver, _source_plugin) = init_plugin::( + &COUNTDOWN_PLUGIN_API, + cr#"{"remaining": 1, "batch_size": 1}"#, + ) + .unwrap(); + driver.register_plugin(&PARSEABLE_PLUGIN_API, c"").unwrap(); + + let mut driver = driver + .start_capture(CountdownPlugin::NAME, c"", PlatformData::Disabled) + .unwrap(); + + // the Rust driver will propagate the error while sinsp will silently + // ignore it and return the event, so we're just checking here that + // we catch the panic. + driver.next_event().ok(); +} + +instantiate_tests!(test_parse_panic); diff --git a/falco_plugin_tests/tests/panic/source.rs b/falco_plugin_tests/tests/panic/source.rs new file mode 100644 index 00000000..b6d3ac4d --- /dev/null +++ b/falco_plugin_tests/tests/panic/source.rs @@ -0,0 +1,77 @@ +use falco_plugin::anyhow::Error; +use falco_plugin::base::Plugin; +use falco_plugin::event::events::RawEvent; +use falco_plugin::source::{EventBatch, EventInput, SourcePlugin, SourcePluginInstance}; +use falco_plugin::tables::TablesInput; +use falco_plugin::{anyhow, static_plugin, FailureReason}; +use std::ffi::{CStr, CString}; + +struct DummyPlugin; + +impl Plugin for DummyPlugin { + const NAME: &'static CStr = c"dummy"; + const PLUGIN_VERSION: &'static CStr = c"0.0.0"; + const DESCRIPTION: &'static CStr = c"dummy no-op plugin"; + const CONTACT: &'static CStr = c"rust@localdomain.pl"; + type ConfigType = (); + + fn new(_input: Option<&TablesInput>, _config: Self::ConfigType) -> Result { + log::set_max_level(log::LevelFilter::Trace); + Ok(Self) + } + + fn set_config(&mut self, _config: Self::ConfigType) -> Result<(), Error> { + Ok(()) + } +} + +struct DummyPluginInstance; + +impl SourcePluginInstance for DummyPluginInstance { + type Plugin = DummyPlugin; + + fn next_batch( + &mut self, + _plugin: &mut Self::Plugin, + _batch: &mut EventBatch, + ) -> Result<(), Error> { + Err(anyhow::anyhow!("this plugin does nothing").context(FailureReason::Eof)) + } +} + +impl SourcePlugin for DummyPlugin { + type Instance = DummyPluginInstance; + const EVENT_SOURCE: &'static CStr = c"dummy"; + const PLUGIN_ID: u32 = 1111; + type Event<'a> = RawEvent<'a>; + + fn open(&mut self, _params: Option<&str>) -> Result { + panic!("failed!") + } + + fn event_to_string(&mut self, _event: &EventInput) -> Result { + Ok(CString::from(c"what event?")) + } +} + +static_plugin!(DUMMY_PLUGIN_API = DummyPlugin); + +#[cfg(test)] +mod tests { + use falco_plugin::base::Plugin; + use falco_plugin_tests::{init_plugin, instantiate_tests, PlatformData, TestDriver}; + + fn test_source_panic() { + let (driver, _plugin) = init_plugin::(&super::DUMMY_PLUGIN_API, c"").unwrap(); + let driver = driver.start_capture(super::DummyPlugin::NAME, c"", PlatformData::Disabled); + match driver { + Ok(_) => panic!("expected error"), + Err(e) => { + dbg!(&e); + assert!(e.to_string().contains("failed!")) + } + } + } + + instantiate_tests!(test_source_panic); +} diff --git a/falco_plugin_tests/tests/panic_tests.rs b/falco_plugin_tests/tests/panic_tests.rs new file mode 100644 index 00000000..5d1f753f --- /dev/null +++ b/falco_plugin_tests/tests/panic_tests.rs @@ -0,0 +1,7 @@ +mod panic { + mod extract; + mod init; + mod listen; + mod parse; + mod source; +} From 21dc4731b8adb1dabf050ba021aa5a6daa8ff773 Mon Sep 17 00:00:00 2001 From: Grzegorz Nosek Date: Wed, 27 May 2026 21:32:52 +0200 Subject: [PATCH 11/11] fix(plugin)!: make Routine drop-safe MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previously, Routine was an opaque handle with no built-in unsubscribe — callers had to manually call ThreadPool::unsubscribe and were warned to use ManuallyDrop to avoid unsafety. The closure was a bare Box passed as the thread pool's data pointer, with no coordination between drop and the callback. Redesign Routine::drop to automatically unsubscribe and free the closure, using an atomic phase protocol to coordinate with the callback: The closure (Box) and the coordination state (Arc) are separate allocations. SharedState holds an AtomicUsize phase flag, a type-erased pointer to the closure, and a typed destructor. The thread pool receives Arc::into_raw(SharedState) as its data pointer; Routine holds its own Arc clone. Three phase states — IDLE, RUNNING, DROP_REQUESTED — determine who frees the closure: - cb_wrapper: borrows the raw Arc via from_raw/clone/forget (leaving the raw refcount undisturbed), then swaps RUNNING into phase. If it sees DROP_REQUESTED, the closure is already freed — return 0. After executing the closure, CAS RUNNING→IDLE; if that fails (drop set DROP_REQUESTED), free the closure and reclaim the raw Arc refcount. On Break (return 0), also reclaim the raw refcount since the framework won't call again. - Routine::drop: calls unsubscribe, then swaps DROP_REQUESTED into phase. If previous value was IDLE, free the closure. If RUNNING, the callback will handle cleanup on return. Known leak: Routine::drop does not reclaim the thread pool's Arc clone of SharedState, because a callback may have been dispatched but not yet entered cb_wrapper (the framework provides no "drain" hook). This is bounded — just the SharedState (AtomicUsize + two pointers + Arc overhead). The closure itself (which may capture arbitrarily large state) is always freed. Also remove ThreadPool::unsubscribe — unsubscription is now automatic. Routine is #[must_use] to prevent accidental immediate drops. Signed-off-by: Grzegorz Nosek --- falco_plugin/src/listen/mod.rs | 15 +- falco_plugin/src/listen/routine.rs | 196 ++++++++++++++---- .../tests/capture_listen_resched.rs | 7 +- .../tests/capture_listen_run_forever.rs | 6 +- 4 files changed, 172 insertions(+), 52 deletions(-) diff --git a/falco_plugin/src/listen/mod.rs b/falco_plugin/src/listen/mod.rs index 89119da7..dd9a040d 100644 --- a/falco_plugin/src/listen/mod.rs +++ b/falco_plugin/src/listen/mod.rs @@ -9,6 +9,12 @@ //! Capture listening plugins receive a reference to a thread pool, which can be used to submit //! "routines" (tasks running in a separate thread, effectively). //! +//! *Note* due to API limitations, this functionality cannot be made fully leak-free. +//! When a routine gets dropped at precisely the wrong point in time (as it starts executing), +//! it will leak a small amount of memory. Consider never dropping routines at all, +//! and instead use some mechanism to signal to the routine that it should stop rescheduling itself +//! (e.g., an atomic boolean in a shared struct). +//! //! *Note* there is no built-in mechanism to stop a running routine, so you should avoid doing this //! in the routine: //! ```ignore @@ -21,7 +27,7 @@ //! Instead, have your routine just do a single iteration and request a rerun from the scheduler: //! ```ignore //! do_something(); -//! std::thread::sleep(some_time) +//! std::thread::sleep(some_time); //! std::ops::ControlFlow::Continue(()) //! ``` //! @@ -73,12 +79,9 @@ //! Ok(()) //! } //! -//! fn capture_close(&mut self, listen_input: &CaptureListenInput) -> Result<(), Error> { +//! fn capture_close(&mut self, _listen_input: &CaptureListenInput) -> Result<(), Error> { //! log::info!("Capture stopped"); -//! for routine in self.tasks.drain(..) { -//! listen_input.thread_pool.unsubscribe(&routine)?; -//! } -//! +//! self.tasks.clear(); // dropping the handles auto-unsubscribes //! Ok(()) //! } //! } diff --git a/falco_plugin/src/listen/routine.rs b/falco_plugin/src/listen/routine.rs index 70c765c8..ec4308ec 100644 --- a/falco_plugin/src/listen/routine.rs +++ b/falco_plugin/src/listen/routine.rs @@ -1,38 +1,107 @@ -use crate::error::as_result::{AsResult, WithLastError}; +use crate::error::as_result::WithLastError; use crate::error::last_error::LastError; use falco_plugin_api::{ ss_plugin_bool, ss_plugin_owner_t, ss_plugin_rc, ss_plugin_routine_fn_t, ss_plugin_routine_state_t, ss_plugin_routine_t, ss_plugin_routine_vtable, ss_plugin_t, }; use std::ops::ControlFlow; +use std::sync::atomic::{AtomicUsize, Ordering}; +use std::sync::Arc; use thiserror::Error; +const IDLE: usize = 0; +const RUNNING: usize = 1; +const DROP_REQUESTED: usize = 2; + #[derive(Error, Debug)] pub(super) enum ThreadPoolError { #[error("Missing entry {0} in thread pool operations vtable")] BadVtable(&'static str), } -/// # A handle for a routine running in the background +/// Shared coordination state between [`Routine`] and `cb_wrapper`. /// -/// This is an opaque object, coming from [`ThreadPool::subscribe`], that will drop -/// the wrapped closure when dropped itself. +/// This is what gets passed to the thread pool (via `Arc::into_raw`) as the +/// `ss_plugin_routine_state_t` pointer. Both [`Routine`] and the callback +/// hold `Arc` clones, so the `phase` flag is guaranteed to outlive both sides. /// -/// *Note*: it's your responsibility to hold on to the handle as long as the closure -/// may be called. Sadly, our capabilities are limited here, so one approach might be -/// to skip the destructor call with e.g. [`std::mem::ManuallyDrop`] and dropping the wrapper. -/// This will leak memory but will be guaranteed safe. -#[derive(Debug)] +/// The `func` pointer and `dtor` are set once at creation and never modified. +/// `func` points to a heap-allocated closure (`Box::into_raw`); exactly one +/// side frees it, determined by the `phase` protocol: +/// +/// - **`cb_wrapper`**: swap `RUNNING` into `phase`. If the previous value was +/// `DROP_REQUESTED`, free the closure and return 0. Otherwise, execute the +/// closure, then CAS `RUNNING → IDLE`; if that fails (`DROP_REQUESTED`), +/// free the closure. +/// - **`Routine::drop`**: swap `DROP_REQUESTED` into `phase`. If the previous +/// value was `IDLE`, free the closure. Otherwise (`RUNNING`), the callback +/// will free it on return. +struct SharedState { + phase: AtomicUsize, + /// Pointer to the heap-allocated closure, created via `Box::into_raw`. + func: *mut (), + /// Typed destructor that calls `Box::from_raw` on `func`. + dtor: unsafe fn(*mut ()), +} + +// SAFETY: `func` (a raw pointer) is the only non-Send/Sync field. +// The `phase` protocol ensures it is only accessed by one side at a time. +unsafe impl Send for SharedState {} +unsafe impl Sync for SharedState {} + +/// # A handle for a routine running in the background +/// +/// Returned by [`ThreadPool::subscribe`]. Dropping the handle: +/// 1. Calls `unsubscribe` (prevents future scheduling) +/// 2. Frees the closure and all captured state (immediately if the callback +/// is not running, or deferred to the callback otherwise) #[must_use] pub struct Routine { routine: *mut ss_plugin_routine_t, - state: *mut ss_plugin_routine_state_t, - dtor: unsafe fn(*mut ss_plugin_routine_state_t) -> (), + owner: *mut ss_plugin_owner_t, + unsubscribe_fn: unsafe extern "C" fn( + o: *mut ss_plugin_owner_t, + r: *mut ss_plugin_routine_t, + ) -> ss_plugin_rc, + state: Arc, +} + +impl std::fmt::Debug for Routine { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("Routine") + .field("routine", &self.routine) + .finish_non_exhaustive() + } } +// SAFETY: `routine` and `owner` are opaque framework handles passed only to +// the C API; they are never dereferenced on the Rust side. +unsafe impl Send for Routine {} +unsafe impl Sync for Routine {} + impl Drop for Routine { fn drop(&mut self) { - unsafe { (self.dtor)(self.state) } + if !self.routine.is_null() { + // Prevents future scheduling. + unsafe { (self.unsubscribe_fn)(self.owner, self.routine) }; + } + + // Atomically set DROP_REQUESTED and check the previous state. + // - Was IDLE: callback is not running — free the closure now. + // - Was RUNNING: callback will see DROP_REQUESTED when it finishes + // and free the closure itself. + let prev = self.state.phase.swap(DROP_REQUESTED, Ordering::AcqRel); + + if prev == IDLE { + unsafe { + (self.state.dtor)(self.state.func); + } + // The thread pool's Arc clone (from Arc::into_raw) is not + // reclaimed here: a callback may have already been dispatched + // but not yet started executing. It will see DROP_REQUESTED + // and return without touching the closure. The Arc clone is + // a small leak (AtomicUsize + two pointers) in this case. + } } } @@ -42,7 +111,7 @@ impl Drop for Routine { /// by the thread pool until they return [`ControlFlow::Break`]. /// /// To submit a task, pass it to [`ThreadPool::subscribe`] and store the received handle. -/// To cancel a task, pass its handle to [`ThreadPool::unsubscribe`]. +/// Dropping the handle automatically unsubscribes and frees the routine. #[derive(Debug)] pub struct ThreadPool { owner: *mut ss_plugin_owner_t, @@ -83,6 +152,10 @@ impl ThreadPool { } /// Run a task in a background thread + /// + /// Returns a [`Routine`]. Dropping the handle automatically unsubscribes + /// the routine and frees the closure (immediately if idle, or after the + /// current callback invocation finishes). pub fn subscribe(&self, func: F) -> Result where F: FnMut() -> ControlFlow<()> + Send + 'static, @@ -94,18 +167,54 @@ impl ThreadPool { where F: FnMut() -> ControlFlow<()> + Send + 'static, { - let f = data as *mut F; - unsafe { - match (*f)() { - ControlFlow::Continue(()) => 1, - ControlFlow::Break(()) => 0, + // Reconstruct the Arc from the raw pointer. Only cb_wrapper + // touches this refcount — Routine::drop never reclaims it. + // If we return Continue (1), we forget it to preserve the + // refcount for the next call. Otherwise we let it drop. + let arc = unsafe { Arc::from_raw(data as *const SharedState) }; + + // Swap RUNNING into phase. If previous value was DROP_REQUESTED, + // the handle has been dropped and the closure already freed. + let prev = arc.phase.swap(RUNNING, Ordering::AcqRel); + if prev == DROP_REQUESTED { + // arc drops here, reclaiming the refcount. + return 0; + } + + // We hold RUNNING — safe to access the closure. + let f = unsafe { &mut *(arc.func as *mut F) }; + let result = match f() { + ControlFlow::Continue(()) => 1, + ControlFlow::Break(()) => 0, + }; + + // Try to go back to IDLE. If drop set DROP_REQUESTED, we free. + if arc + .phase + .compare_exchange(RUNNING, IDLE, Ordering::AcqRel, Ordering::Acquire) + .is_err() + { + // DROP_REQUESTED: free the closure. + unsafe { + (arc.dtor)(arc.func); } + // arc drops here, reclaiming the refcount. + return 0; + } + + if result == 1 { + // Continue — preserve the refcount for the next call. + std::mem::forget(arc); } + // else: Break — arc drops here, reclaiming the refcount. + + result } - unsafe fn cb_drop(data: *mut ss_plugin_routine_state_t) { - let cb = data as *mut F; - let _ = unsafe { Box::from_raw(cb) }; + unsafe fn cb_drop(ptr: *mut ()) { + unsafe { + drop(Box::from_raw(ptr as *mut F)); + } } let callback = Some( @@ -116,30 +225,43 @@ impl ThreadPool { ) -> ss_plugin_bool, ); - let boxed_func = Box::new(func); - let boxed_func = Box::into_raw(boxed_func) as *mut ss_plugin_routine_state_t; + let func_ptr = Box::into_raw(Box::new(func)); + + let state = Arc::new(SharedState { + phase: AtomicUsize::new(IDLE), + func: func_ptr as *mut (), + dtor: cb_drop::, + }); + + // Give the thread pool its own Arc clone via into_raw. + let tp_clone = Arc::clone(&state); + let raw_ptr = Arc::into_raw(tp_clone); - let ptr = unsafe { (self.subscribe)(self.owner, callback, boxed_func) }; + let ptr = unsafe { + (self.subscribe)( + self.owner, + callback, + raw_ptr as *mut ss_plugin_routine_state_t, + ) + }; if ptr.is_null() { + // Reclaim the thread pool's Arc clone. + unsafe { + Arc::from_raw(raw_ptr); + } + // Free the closure. + unsafe { + drop(Box::from_raw(func_ptr)); + } Err(anyhow::anyhow!("Failed to subscribe function")).with_last_error(&self.last_error) } else { Ok(Routine { routine: ptr, - state: boxed_func, - dtor: cb_drop::, + owner: self.owner, + unsubscribe_fn: self.unsubscribe, + state, }) } } - - /// Cancel a task running in a background thread - /// - /// *Note*: this does not kill a running task, only prevent it from being scheduled again - pub fn unsubscribe(&self, routine: &Routine) -> Result<(), anyhow::Error> { - unsafe { - (self.unsubscribe)(self.owner, routine.routine) - .as_result() - .with_last_error(&self.last_error) - } - } } diff --git a/falco_plugin_tests/tests/capture_listen_resched.rs b/falco_plugin_tests/tests/capture_listen_resched.rs index 482a40b9..ceac3e55 100644 --- a/falco_plugin_tests/tests/capture_listen_resched.rs +++ b/falco_plugin_tests/tests/capture_listen_resched.rs @@ -84,11 +84,8 @@ impl CaptureListenPlugin for DummyPlugin { Ok(()) } - fn capture_close(&mut self, listen_input: &CaptureListenInput) -> Result<(), Error> { - if let Some(task) = self.task.take() { - listen_input.thread_pool.unsubscribe(&task)?; - } - + fn capture_close(&mut self, _listen_input: &CaptureListenInput) -> Result<(), Error> { + self.task.take(); Ok(()) } } diff --git a/falco_plugin_tests/tests/capture_listen_run_forever.rs b/falco_plugin_tests/tests/capture_listen_run_forever.rs index 71654795..54eeb2c9 100644 --- a/falco_plugin_tests/tests/capture_listen_run_forever.rs +++ b/falco_plugin_tests/tests/capture_listen_run_forever.rs @@ -95,10 +95,8 @@ impl CaptureListenPlugin for DummyPlugin { Ok(()) } - fn capture_close(&mut self, listen_input: &CaptureListenInput) -> Result<(), Error> { - if let Some(task) = self.task.take() { - listen_input.thread_pool.unsubscribe(&task)?; - } + fn capture_close(&mut self, _listen_input: &CaptureListenInput) -> Result<(), Error> { + self.task.take(); self.task_state.request_stop_and_notify()?; Ok(())