Skip to content

Commit c571e5b

Browse files
committed
rust/ffi: provide safe flow callback type
1 parent fe6d2e1 commit c571e5b

3 files changed

Lines changed: 94 additions & 55 deletions

File tree

doc/userguide/devguide/extending/flow-lifecycle-callbacks.rst

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -100,23 +100,26 @@ In Rust, use the ``suricata_ffi::flow`` module:
100100
- ``flow::register_finish_callback``
101101

102102
The Rust wrappers register closures or function items and return
103-
``Result<(), &'static str>``.
103+
``Result<(), &'static str>``. The flow argument is wrapped in
104+
``flow::Flow`` so safe APIs can be added for common flow operations without
105+
exposing the entire raw C ``Flow`` layout.
104106

105107
.. code-block:: rust
106108
107-
use suricata_ffi::flow::{self, Flow, Packet, ThreadVars};
109+
use suricata_ffi::flow::{self, Flow};
108110
use suricata_ffi::SCLogNotice;
111+
use suricata_sys::sys::{Packet, ThreadVars};
109112
110-
fn flow_init(_tv: *mut ThreadVars, f: *mut Flow, _p: *const Packet) {
111-
SCLogNotice!("flow initialized: {:p}", f);
113+
fn flow_init(_tv: *mut ThreadVars, f: Flow<'_>, _p: *const Packet) {
114+
SCLogNotice!("flow initialized: {:p}", f.as_ptr());
112115
}
113116
114-
fn flow_update(_tv: *mut ThreadVars, f: *mut Flow, p: *mut Packet) {
115-
SCLogNotice!("flow updated: {:p} packet: {:p}", f, p);
117+
fn flow_update(_tv: *mut ThreadVars, f: Flow<'_>, p: *mut Packet) {
118+
SCLogNotice!("flow updated: {:p} packet: {:p}", f.as_ptr(), p);
116119
}
117120
118-
fn flow_finish(_tv: *mut ThreadVars, f: *mut Flow) {
119-
SCLogNotice!("flow finished: {:p}", f);
121+
fn flow_finish(_tv: *mut ThreadVars, f: Flow<'_>) {
122+
SCLogNotice!("flow finished: {:p}", f.as_ptr());
120123
}
121124
122125
fn register_flow_callbacks() -> Result<(), &'static str> {
@@ -126,6 +129,6 @@ The Rust wrappers register closures or function items and return
126129
Ok(())
127130
}
128131
129-
The raw pointers passed into callbacks are only valid for the duration
130-
of the callback invocation and must not be stored. Rust callbacks must
131-
not panic.
132+
The flow wrapper and raw packet/thread pointers passed into callbacks are only
133+
valid for the duration of the callback invocation and must not be stored. Rust
134+
callbacks must not panic.

examples/plugins/rust/src/mod.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -64,20 +64,20 @@ fn log_eve_wrapped(
6464
Ok(())
6565
}
6666

67-
fn log_flow_init(_tv: *mut ThreadVars, _f: *mut Flow, _p: *const Packet) {
68-
SCLogNotice!("rust example flow init callback: flow={:p}", _f);
67+
fn log_flow_init(_tv: *mut ThreadVars, f: flow::Flow<'_>, _p: *const Packet) {
68+
SCLogNotice!("rust example flow init callback: flow={:p}", f.as_ptr());
6969
}
7070

71-
fn log_flow_update(_tv: *mut ThreadVars, _f: *mut Flow, _p: *mut Packet) {
71+
fn log_flow_update(_tv: *mut ThreadVars, f: flow::Flow<'_>, p: *mut Packet) {
7272
SCLogNotice!(
7373
"rust example flow update callback: flow={:p}, packet={:p}",
74-
_f,
75-
_p
74+
f.as_ptr(),
75+
p
7676
);
7777
}
7878

79-
fn log_flow_finish(_tv: *mut ThreadVars, _f: *mut Flow) {
80-
SCLogNotice!("rust example flow finish callback: flow={:p}", _f);
79+
fn log_flow_finish(_tv: *mut ThreadVars, f: flow::Flow<'_>) {
80+
SCLogNotice!("rust example flow finish callback: flow={:p}", f.as_ptr());
8181
}
8282

8383
#[no_mangle]

rust/ffi/src/flow.rs

Lines changed: 73 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -15,29 +15,64 @@
1515
* 02110-1301, USA.
1616
*/
1717

18+
use std::marker::PhantomData;
1819
use std::os::raw::c_void;
20+
use std::ptr::NonNull;
1921

20-
use suricata_sys::sys::{Flow, Packet, ThreadVars};
22+
use suricata_sys::sys::{Flow as CFlow, Packet, ThreadVars};
2123
use suricata_sys::sys::{
2224
SCFlowRegisterFinishCallback, SCFlowRegisterInitCallback, SCFlowRegisterUpdateCallback,
2325
};
2426

27+
/// Access to a Suricata flow for the lifetime of a callback.
28+
///
29+
/// This type is intended to be the place where safe Rust accessors for C
30+
/// `Flow` fields are added. It deliberately does not implement `Deref` or
31+
/// `DerefMut` to the bindgen type, so field access can be exposed as reviewed
32+
/// methods instead of exposing the whole raw C layout as safe Rust API.
33+
pub struct Flow<'a> {
34+
ptr: NonNull<CFlow>,
35+
_marker: PhantomData<&'a mut CFlow>,
36+
}
37+
38+
impl<'a> Flow<'a> {
39+
/// Create a flow wrapper from a raw pointer.
40+
///
41+
/// # Safety
42+
///
43+
/// `ptr` must point to a live `Flow` that the caller may access for the
44+
/// duration of `'a`. The returned wrapper must not be stored beyond that
45+
/// validity period.
46+
pub unsafe fn from_ptr(ptr: *mut CFlow) -> Option<Self> {
47+
NonNull::new(ptr).map(|ptr| Self {
48+
ptr,
49+
_marker: PhantomData,
50+
})
51+
}
52+
53+
/// Return the wrapped raw C pointer.
54+
pub fn as_ptr(&self) -> *const CFlow {
55+
self.ptr.as_ptr()
56+
}
57+
58+
/// Return the wrapped raw C pointer as mutable.
59+
pub fn as_mut_ptr(&mut self) -> *mut CFlow {
60+
self.ptr.as_ptr()
61+
}
62+
}
63+
2564
/// Register a flow initialization callback.
2665
///
2766
/// The callback is invoked whenever Suricata initializes a flow. It receives:
2867
/// - `tv`: the `ThreadVars` for the thread creating the flow
29-
/// - `f`: the newly initialized `Flow`
68+
/// - `flow`: access to the newly initialized `Flow`
3069
/// - `p`: the packet related to creating the flow
3170
///
32-
/// # Safety
33-
///
34-
/// The callback receives raw pointers from Suricata. These pointers are only
35-
/// valid for the duration of the callback invocation and must not be stored.
36-
///
37-
/// The callback must not panic.
71+
/// The flow wrapper is only valid for the duration of the callback invocation
72+
/// and must not be stored. The callback must not panic.
3873
pub fn register_init_callback<F>(callback: F) -> Result<(), &'static str>
3974
where
40-
F: Fn(*mut ThreadVars, *mut Flow, *const Packet) + Send + Sync + 'static,
75+
F: for<'a> Fn(*mut ThreadVars, Flow<'a>, *const Packet) + Send + Sync + 'static,
4176
{
4277
let user = Box::into_raw(Box::new(callback)) as *mut c_void;
4378
if unsafe { SCFlowRegisterInitCallback(Some(init_callback_wrapper::<F>), user) } {
@@ -55,18 +90,14 @@ where
5590
/// The callback is invoked whenever Suricata updates a flow with a packet. It
5691
/// receives:
5792
/// - `tv`: the `ThreadVars` for the thread updating the flow
58-
/// - `f`: the flow being updated
93+
/// - `flow`: access to the flow being updated
5994
/// - `p`: the packet responsible for the flow update
6095
///
61-
/// # Safety
62-
///
63-
/// The callback receives raw pointers from Suricata. These pointers are only
64-
/// valid for the duration of the callback invocation and must not be stored.
65-
///
66-
/// The callback must not panic.
96+
/// The flow wrapper is only valid for the duration of the callback invocation
97+
/// and must not be stored. The callback must not panic.
6798
pub fn register_update_callback<F>(callback: F) -> Result<(), &'static str>
6899
where
69-
F: Fn(*mut ThreadVars, *mut Flow, *mut Packet) + Send + Sync + 'static,
100+
F: for<'a> Fn(*mut ThreadVars, Flow<'a>, *mut Packet) + Send + Sync + 'static,
70101
{
71102
let user = Box::into_raw(Box::new(callback)) as *mut c_void;
72103
if unsafe { SCFlowRegisterUpdateCallback(Some(update_callback_wrapper::<F>), user) } {
@@ -83,17 +114,13 @@ where
83114
///
84115
/// The callback is invoked when Suricata is finished with a flow. It receives:
85116
/// - `tv`: the `ThreadVars` for the thread finishing the flow
86-
/// - `f`: the flow being finished
87-
///
88-
/// # Safety
89-
///
90-
/// The callback receives raw pointers from Suricata. These pointers are only
91-
/// valid for the duration of the callback invocation and must not be stored.
117+
/// - `flow`: access to the flow being finished
92118
///
93-
/// The callback must not panic.
119+
/// The flow wrapper is only valid for the duration of the callback invocation
120+
/// and must not be stored. The callback must not panic.
94121
pub fn register_finish_callback<F>(callback: F) -> Result<(), &'static str>
95122
where
96-
F: Fn(*mut ThreadVars, *mut Flow) + Send + Sync + 'static,
123+
F: for<'a> Fn(*mut ThreadVars, Flow<'a>) + Send + Sync + 'static,
97124
{
98125
let user = Box::into_raw(Box::new(callback)) as *mut c_void;
99126
if unsafe { SCFlowRegisterFinishCallback(Some(finish_callback_wrapper::<F>), user) } {
@@ -107,28 +134,37 @@ where
107134
}
108135

109136
unsafe extern "C" fn init_callback_wrapper<F>(
110-
tv: *mut ThreadVars, f: *mut Flow, p: *const Packet, user: *mut c_void,
137+
tv: *mut ThreadVars, f: *mut CFlow, p: *const Packet, user: *mut c_void,
111138
) where
112-
F: Fn(*mut ThreadVars, *mut Flow, *const Packet) + Send + Sync + 'static,
139+
F: for<'a> Fn(*mut ThreadVars, Flow<'a>, *const Packet) + Send + Sync + 'static,
113140
{
114-
let callback = &*(user as *const F);
115-
callback(tv, f, p);
141+
let Some(flow) = (unsafe { Flow::from_ptr(f) }) else {
142+
return;
143+
};
144+
let callback = unsafe { &*(user as *const F) };
145+
callback(tv, flow, p);
116146
}
117147

118148
unsafe extern "C" fn update_callback_wrapper<F>(
119-
tv: *mut ThreadVars, f: *mut Flow, p: *mut Packet, user: *mut c_void,
149+
tv: *mut ThreadVars, f: *mut CFlow, p: *mut Packet, user: *mut c_void,
120150
) where
121-
F: Fn(*mut ThreadVars, *mut Flow, *mut Packet) + Send + Sync + 'static,
151+
F: for<'a> Fn(*mut ThreadVars, Flow<'a>, *mut Packet) + Send + Sync + 'static,
122152
{
123-
let callback = &*(user as *const F);
124-
callback(tv, f, p);
153+
let Some(flow) = (unsafe { Flow::from_ptr(f) }) else {
154+
return;
155+
};
156+
let callback = unsafe { &*(user as *const F) };
157+
callback(tv, flow, p);
125158
}
126159

127160
unsafe extern "C" fn finish_callback_wrapper<F>(
128-
tv: *mut ThreadVars, f: *mut Flow, user: *mut c_void,
161+
tv: *mut ThreadVars, f: *mut CFlow, user: *mut c_void,
129162
) where
130-
F: Fn(*mut ThreadVars, *mut Flow) + Send + Sync + 'static,
163+
F: for<'a> Fn(*mut ThreadVars, Flow<'a>) + Send + Sync + 'static,
131164
{
132-
let callback = &*(user as *const F);
133-
callback(tv, f);
165+
let Some(flow) = (unsafe { Flow::from_ptr(f) }) else {
166+
return;
167+
};
168+
let callback = unsafe { &*(user as *const F) };
169+
callback(tv, flow);
134170
}

0 commit comments

Comments
 (0)