1515 * 02110-1301, USA.
1616 */
1717
18+ use std:: ffi:: CString ;
1819use std:: marker:: PhantomData ;
1920use std:: os:: raw:: c_void;
2021
21- use suricata_sys:: sys:: { self , SCThreadRegisterInitCallback } ;
22+ use suricata_sys:: sys:: {
23+ self , SCThreadGetStorageById , SCThreadRegisterInitCallback , SCThreadSetStorageById ,
24+ SCThreadStorageId , SCThreadStorageRegister ,
25+ } ;
2226
2327/// A safe wrapper around a Suricata `sys::ThreadVars` pointer.
2428///
@@ -45,6 +49,129 @@ impl<'a> ThreadVars<'a> {
4549 pub fn as_ptr ( & self ) -> * const sys:: ThreadVars {
4650 self . tv
4751 }
52+
53+ /// Return the underlying raw `ThreadVars` pointer for mutable access.
54+ ///
55+ /// Requires `&mut self` so that mutable use of the underlying
56+ /// `ThreadVars` (such as setting thread storage) is gated by an exclusive
57+ /// borrow of the wrapper.
58+ fn as_mut_ptr ( & mut self ) -> * mut sys:: ThreadVars {
59+ self . tv
60+ }
61+ }
62+
63+ /// A typed handle to a per-thread storage slot.
64+ ///
65+ /// `ThreadStorage<T>` wraps the `SCThreadStorageId` returned when registering
66+ /// thread storage with Suricata. Values are stored as a `Box<T>` owned by
67+ /// Suricata's thread storage and are dropped automatically when the thread's
68+ /// storage is freed.
69+ ///
70+ /// The handle only holds the storage id, so it is `Copy` and `Send`/`Sync`
71+ /// regardless of `T`, and can be passed by value into the callbacks that need
72+ /// it.
73+ pub struct ThreadStorage < T > {
74+ id : SCThreadStorageId ,
75+ _marker : PhantomData < fn ( ) -> T > ,
76+ }
77+
78+ // Manual `Copy`/`Clone` impls so the handle is copyable regardless of whether
79+ // `T` is; it only holds the storage id.
80+ impl < T > Clone for ThreadStorage < T > {
81+ fn clone ( & self ) -> Self {
82+ * self
83+ }
84+ }
85+
86+ impl < T > Copy for ThreadStorage < T > { }
87+
88+ impl < T : Send + ' static > ThreadStorage < T > {
89+ /// Register a new thread storage slot for values of type `T`.
90+ ///
91+ /// `name` must be unique among registered thread storage. Registration has
92+ /// to happen during initialization, before Suricata finalizes storage
93+ /// registration (`SCStorageFinalize`).
94+ ///
95+ /// Returns an error if `name` contains an interior nul byte or if Suricata
96+ /// rejects the registration.
97+ pub fn register ( name : & str ) -> Result < Self , & ' static str > {
98+ let name = CString :: new ( name) . map_err ( |_| "thread storage name contains a nul byte" ) ?;
99+ let id = unsafe { SCThreadStorageRegister ( name. as_ptr ( ) , Some ( Self :: free) ) } ;
100+ if id. id < 0 {
101+ return Err ( "Failed to register thread storage" ) ;
102+ }
103+
104+ // Suricata keeps the storage name pointer in its storage mapping for
105+ // the lifetime of the process, so the CString is intentionally leaked.
106+ std:: mem:: forget ( name) ;
107+
108+ Ok ( Self {
109+ id,
110+ _marker : PhantomData ,
111+ } )
112+ }
113+
114+ /// Return a reference to the value stored for `tv`, if any.
115+ pub fn get < ' t > ( & self , tv : & ' t ThreadVars < ' _ > ) -> Option < & ' t T > {
116+ let ptr = unsafe { SCThreadGetStorageById ( tv. as_ptr ( ) , self . id ) } ;
117+ if ptr. is_null ( ) {
118+ None
119+ } else {
120+ Some ( unsafe { & * ( ptr as * const T ) } )
121+ }
122+ }
123+
124+ /// Return a mutable reference to the value stored for `tv`, if any.
125+ ///
126+ /// Takes `&mut ThreadVars` so the returned `&mut T` is the only live
127+ /// reference to the stored value for the duration of the borrow.
128+ pub fn get_mut < ' t > ( & self , tv : & ' t mut ThreadVars < ' _ > ) -> Option < & ' t mut T > {
129+ let ptr = unsafe { SCThreadGetStorageById ( tv. as_ptr ( ) , self . id ) } ;
130+ if ptr. is_null ( ) {
131+ None
132+ } else {
133+ Some ( unsafe { & mut * ( ptr as * mut T ) } )
134+ }
135+ }
136+
137+ /// Return a mutable reference to the value stored for `tv`, inserting the
138+ /// value produced by `init` if none is present yet.
139+ ///
140+ /// Takes `&mut ThreadVars` so the returned `&mut T` is the only live
141+ /// reference to the stored value for the duration of the borrow.
142+ pub fn get_or_insert_with < ' t > (
143+ & self , tv : & ' t mut ThreadVars < ' _ > , init : impl FnOnce ( ) -> T ,
144+ ) -> Result < & ' t mut T , & ' static str > {
145+ let ptr = unsafe { SCThreadGetStorageById ( tv. as_ptr ( ) , self . id ) } ;
146+ if !ptr. is_null ( ) {
147+ return Ok ( unsafe { & mut * ( ptr as * mut T ) } ) ;
148+ }
149+
150+ // `SCThreadSetStorageById` overwrites the slot without freeing any
151+ // previous value; we only reach here when the slot is empty.
152+ let ptr = Box :: into_raw ( Box :: new ( init ( ) ) ) ;
153+ let rc = unsafe { SCThreadSetStorageById ( tv. as_mut_ptr ( ) , self . id , ptr. cast ( ) ) } ;
154+ if rc != 0 {
155+ unsafe {
156+ drop ( Box :: from_raw ( ptr) ) ;
157+ }
158+ return Err ( "Failed to set thread storage" ) ;
159+ }
160+
161+ Ok ( unsafe { & mut * ptr } )
162+ }
163+
164+ /// Free callback registered with Suricata thread storage that drops the
165+ /// `Box<T>` backing a stored value.
166+ unsafe extern "C" fn free ( ptr : * mut c_void ) {
167+ if !ptr. is_null ( ) {
168+ // The drop runs across an FFI boundary, so guard against unwinding
169+ // into C if `T`'s `Drop` panics.
170+ let _ = std:: panic:: catch_unwind ( std:: panic:: AssertUnwindSafe ( || {
171+ drop ( Box :: from_raw ( ptr as * mut T ) ) ;
172+ } ) ) ;
173+ }
174+ }
48175}
49176
50177/// Register a thread initialization callback.
0 commit comments