@@ -14,6 +14,34 @@ See the License for the specific language governing permissions and
14
14
limitations under the License.
15
15
*/
16
16
17
+ //! Parameter and return value marshalling for WASM guest function calls.
18
+ //!
19
+ //! # Memory Management Contract
20
+ //!
21
+ //! This module implements a clear memory ownership model for both guest function calls and host function calls:
22
+ //!
23
+ //! ## Guest Function Parameters (Host → Guest)
24
+ //! - When calling guest functions with String or VecBytes parameters, the host allocates memory
25
+ //! in the guest's memory space and passes pointers to the guest.
26
+ //! - **The guest owns these allocations and must free them** when no longer needed using the
27
+ //! `free` function exported from the guest module.
28
+ //!
29
+ //! ## Guest Function Return Values (Guest → Host)
30
+ //! - When guest functions return String or VecBytes values, the guest allocates memory in its
31
+ //! own memory space and returns pointers to the host.
32
+ //! - **The host takes ownership of these allocations and will free them** on the next VM entry
33
+ //! to prevent memory leaks.
34
+ //!
35
+ //! ## Host Function Parameters (Guest → Host)
36
+ //! - When guest code calls host functions with String or VecBytes parameters, the guest passes
37
+ //! pointers to data in its own memory space.
38
+ //! - **The guest retains ownership** of these allocations and remains responsible for freeing them.
39
+ //!
40
+ //! ## Host Function Return Values (Host → Guest)
41
+ //! - When host functions return String or VecBytes values to the guest, the host allocates memory
42
+ //! in the guest's memory space and returns pointers.
43
+ //! - **The guest owns these allocations and must free them** when no longer needed.
44
+
17
45
extern crate alloc;
18
46
19
47
use alloc:: ffi:: CString ;
@@ -29,6 +57,28 @@ use hyperlight_common::flatbuffer_wrappers::util::get_flatbuffer_result;
29
57
use hyperlight_guest:: error:: { HyperlightGuestError , Result } ;
30
58
use wasmtime:: { AsContextMut , Extern , Val } ;
31
59
60
+ use spin:: Mutex ;
61
+
62
+ // Global tracking for return value allocations that need to be freed on next VM entry
63
+ static RETURN_VALUE_ALLOCATIONS : Mutex < Vec < i32 > > = Mutex :: new ( Vec :: new ( ) ) ;
64
+
65
+ /// Track a return value allocation that should be freed on the next VM entry
66
+ fn track_return_value_allocation ( addr : i32 ) {
67
+ RETURN_VALUE_ALLOCATIONS . lock ( ) . push ( addr) ;
68
+ }
69
+
70
+ /// Free all tracked return value allocations from previous VM calls
71
+ pub fn free_return_value_allocations < C : AsContextMut > (
72
+ ctx : & mut C ,
73
+ get_export : & impl Fn ( & mut C , & str ) -> Option < Extern > ,
74
+ ) -> Result < ( ) > {
75
+ let mut allocations = RETURN_VALUE_ALLOCATIONS . lock ( ) ;
76
+ for addr in allocations. drain ( ..) {
77
+ free ( ctx, get_export, addr) ?;
78
+ }
79
+ Ok ( ( ) )
80
+ }
81
+
32
82
fn malloc < C : AsContextMut > (
33
83
ctx : & mut C ,
34
84
get_export : & impl Fn ( & mut C , & str ) -> Option < Extern > ,
@@ -46,6 +96,21 @@ fn malloc<C: AsContextMut>(
46
96
Ok ( addr)
47
97
}
48
98
99
+ fn free < C : AsContextMut > (
100
+ ctx : & mut C ,
101
+ get_export : & impl Fn ( & mut C , & str ) -> Option < Extern > ,
102
+ addr : i32 ,
103
+ ) -> Result < ( ) > {
104
+ let free = get_export ( & mut * ctx, "free" )
105
+ . and_then ( Extern :: into_func)
106
+ . ok_or ( HyperlightGuestError :: new (
107
+ ErrorCode :: GuestError ,
108
+ "free function not exported" . to_string ( ) ,
109
+ ) ) ?;
110
+ free. typed :: < i32 , ( ) > ( & mut * ctx) ?. call ( & mut * ctx, addr) ?;
111
+ Ok ( ( ) )
112
+ }
113
+
49
114
fn write < C : AsContextMut > (
50
115
ctx : & mut C ,
51
116
get_export : & impl Fn ( & mut C , & str ) -> Option < Extern > ,
@@ -126,6 +191,11 @@ fn read_cstr<C: AsContextMut>(
126
191
} )
127
192
}
128
193
194
+ /// Convert a hyperlight parameter value to a wasmtime value.
195
+ ///
196
+ /// For String and VecBytes parameter types, this allocates memory in the guest's memory space
197
+ /// and returns a pointer. The guest function is responsible for freeing this memory when it is no
198
+ /// longer needed using the `free` function exported from the guest module.
129
199
pub fn hl_param_to_val < C : AsContextMut > (
130
200
mut ctx : C ,
131
201
get_export : impl Fn ( & mut C , & str ) -> Option < Extern > ,
@@ -155,6 +225,11 @@ pub fn hl_param_to_val<C: AsContextMut>(
155
225
}
156
226
}
157
227
228
+ /// Convert guest function return values to hyperlight return value.
229
+ ///
230
+ /// For String and VecBytes return types, the guest has allocated memory in its own memory space
231
+ /// and returned pointers. The host takes ownership of these allocations and tracks them for
232
+ /// automatic cleanup on the next VM entry to prevent memory leaks.
158
233
pub fn val_to_hl_result < C : AsContextMut > (
159
234
mut ctx : C ,
160
235
get_export : impl Fn ( & mut C , & str ) -> Option < Extern > ,
@@ -172,15 +247,21 @@ pub fn val_to_hl_result<C: AsContextMut>(
172
247
/* todo: get_flatbuffer_result_from_bool is missing */
173
248
( ReturnType :: Float , Val :: F32 ( f) ) => Ok ( get_flatbuffer_result :: < f32 > ( f32:: from_bits ( f) ) ) ,
174
249
( ReturnType :: Double , Val :: F64 ( f) ) => Ok ( get_flatbuffer_result :: < f64 > ( f64:: from_bits ( f) ) ) ,
175
- ( ReturnType :: String , Val :: I32 ( p) ) => Ok ( get_flatbuffer_result :: < & str > (
176
- read_cstr ( & mut ctx, & get_export, p) ?. to_str ( ) . map_err ( |e| {
177
- HyperlightGuestError :: new (
178
- ErrorCode :: GuestError ,
179
- format ! ( "non-UTF-8 c string in guest function return: {}" , e) ,
180
- )
181
- } ) ?,
182
- ) ) ,
250
+ ( ReturnType :: String , Val :: I32 ( p) ) => {
251
+ // Track this allocation so it can be freed on next VM entry
252
+ track_return_value_allocation ( p) ;
253
+ Ok ( get_flatbuffer_result :: < & str > (
254
+ read_cstr ( & mut ctx, & get_export, p) ?. to_str ( ) . map_err ( |e| {
255
+ HyperlightGuestError :: new (
256
+ ErrorCode :: GuestError ,
257
+ format ! ( "non-UTF-8 c string in guest function return: {}" , e) ,
258
+ )
259
+ } ) ?,
260
+ ) )
261
+ }
183
262
( ReturnType :: VecBytes , Val :: I32 ( ret) ) => {
263
+ // Track this allocation so it can be freed on next VM entry
264
+ track_return_value_allocation ( ret) ;
184
265
let mut size_bytes = [ 0 ; 4 ] ;
185
266
read ( & mut ctx, & get_export, ret, & mut size_bytes) ?;
186
267
let size = i32:: from_le_bytes ( size_bytes) ;
@@ -198,6 +279,11 @@ pub fn val_to_hl_result<C: AsContextMut>(
198
279
}
199
280
}
200
281
282
+ /// Convert guest-provided WASM values to hyperlight parameters for host function calls.
283
+ ///
284
+ /// For String and VecBytes parameter types, the guest passes pointers to data in its own
285
+ /// memory space. The guest retains ownership of these allocations and remains responsible
286
+ /// for freeing them. This function only reads the data without taking ownership.
201
287
pub fn val_to_hl_param < ' a , C : AsContextMut > (
202
288
ctx : & mut C ,
203
289
get_export : impl Fn ( & mut C , & str ) -> Option < Extern > ,
@@ -248,6 +334,11 @@ pub fn val_to_hl_param<'a, C: AsContextMut>(
248
334
}
249
335
}
250
336
337
+ /// Convert a hyperlight return value to a wasmtime value for host function returns.
338
+ ///
339
+ /// For String and VecBytes return types, this allocates memory in the guest's memory space
340
+ /// and returns a pointer. The guest owns these allocations and must free them when no longer needed
341
+ /// using the `free` function exported from the guest module.
251
342
pub fn hl_return_to_val < C : AsContextMut > (
252
343
ctx : & mut C ,
253
344
get_export : impl Fn ( & mut C , & str ) -> Option < Extern > ,
0 commit comments