@@ -14,6 +14,35 @@ See the License for the specific language governing permissions and
1414limitations under the License.
1515*/
1616
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 using the malloc corresponding to its
32+ //! exported free.
33+ //! - **The host takes ownership of these allocations and will free them** on the next VM entry
34+ //! to prevent memory leaks.
35+ //!
36+ //! ## Host Function Parameters (Guest → Host)
37+ //! - When guest code calls host functions with String or VecBytes parameters, the guest passes
38+ //! pointers to data in its own memory space.
39+ //! - **The guest retains ownership** of these allocations and remains responsible for freeing them.
40+ //!
41+ //! ## Host Function Return Values (Host → Guest)
42+ //! - When host functions return String or VecBytes values to the guest, the host allocates memory
43+ //! in the guest's memory space and returns pointers.
44+ //! - **The guest owns these allocations and must free them** when no longer needed.
45+
1746extern crate alloc;
1847
1948use alloc:: ffi:: CString ;
@@ -27,8 +56,29 @@ use hyperlight_common::flatbuffer_wrappers::function_types::{
2756use hyperlight_common:: flatbuffer_wrappers:: guest_error:: ErrorCode ;
2857use hyperlight_common:: flatbuffer_wrappers:: util:: get_flatbuffer_result;
2958use hyperlight_guest:: error:: { HyperlightGuestError , Result } ;
59+ use spin:: Mutex ;
3060use wasmtime:: { AsContextMut , Extern , Val } ;
3161
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+
3282fn malloc < C : AsContextMut > (
3383 ctx : & mut C ,
3484 get_export : & impl Fn ( & mut C , & str ) -> Option < Extern > ,
@@ -46,6 +96,21 @@ fn malloc<C: AsContextMut>(
4696 Ok ( addr)
4797}
4898
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+
49114fn write < C : AsContextMut > (
50115 ctx : & mut C ,
51116 get_export : & impl Fn ( & mut C , & str ) -> Option < Extern > ,
@@ -126,6 +191,11 @@ fn read_cstr<C: AsContextMut>(
126191 } )
127192}
128193
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.
129199pub fn hl_param_to_val < C : AsContextMut > (
130200 mut ctx : C ,
131201 get_export : impl Fn ( & mut C , & str ) -> Option < Extern > ,
@@ -155,6 +225,11 @@ pub fn hl_param_to_val<C: AsContextMut>(
155225 }
156226}
157227
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.
158233pub fn val_to_hl_result < C : AsContextMut > (
159234 mut ctx : C ,
160235 get_export : impl Fn ( & mut C , & str ) -> Option < Extern > ,
@@ -172,15 +247,21 @@ pub fn val_to_hl_result<C: AsContextMut>(
172247 /* todo: get_flatbuffer_result_from_bool is missing */
173248 ( ReturnType :: Float , Val :: F32 ( f) ) => Ok ( get_flatbuffer_result :: < f32 > ( f32:: from_bits ( f) ) ) ,
174249 ( 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+ }
183262 ( ReturnType :: VecBytes , Val :: I32 ( ret) ) => {
263+ // Track this allocation so it can be freed on next VM entry
264+ track_return_value_allocation ( ret) ;
184265 let mut size_bytes = [ 0 ; 4 ] ;
185266 read ( & mut ctx, & get_export, ret, & mut size_bytes) ?;
186267 let size = i32:: from_le_bytes ( size_bytes) ;
@@ -198,6 +279,11 @@ pub fn val_to_hl_result<C: AsContextMut>(
198279 }
199280}
200281
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.
201287pub fn val_to_hl_param < ' a , C : AsContextMut > (
202288 ctx : & mut C ,
203289 get_export : impl Fn ( & mut C , & str ) -> Option < Extern > ,
@@ -248,6 +334,11 @@ pub fn val_to_hl_param<'a, C: AsContextMut>(
248334 }
249335}
250336
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.
251342pub fn hl_return_to_val < C : AsContextMut > (
252343 ctx : & mut C ,
253344 get_export : impl Fn ( & mut C , & str ) -> Option < Extern > ,
0 commit comments