@@ -14,232 +14,153 @@ See the License for the specific language governing permissions and
1414limitations under the License.
1515*/
1616
17- #![ allow( non_snake_case) ]
1817use std:: sync:: { Arc , Mutex } ;
1918
20- use hyperlight_common:: flatbuffer_wrappers:: function_types:: ParameterValue ;
21- use tracing:: { instrument, Span } ;
19+ use hyperlight_common:: flatbuffer_wrappers:: function_types:: { ParameterValue , ReturnValue } ;
2220
23- use super :: { HyperlightFunction , SupportedParameterType , SupportedReturnType } ;
21+ use super :: utils:: for_each_tuple;
22+ use super :: { ParameterTuple , ResultType , SupportedReturnType } ;
2423use crate :: sandbox:: { ExtraAllowedSyscall , UninitializedSandbox } ;
25- use crate :: HyperlightError :: UnexpectedNoOfArguments ;
2624use crate :: { log_then_return, new_error, Result } ;
2725
28- /// Trait for registering a host function
29- pub trait HostFunction < R , Args > {
30- /// Register the host function with the given name in the sandbox.
31- fn register ( & self , sandbox : & mut UninitializedSandbox , name : & str ) -> Result < ( ) > ;
32-
33- /// Register the host function with the given name in the sandbox, allowing extra syscalls.
34- #[ cfg( all( feature = "seccomp" , target_os = "linux" ) ) ]
35- fn register_with_extra_allowed_syscalls (
36- & self ,
37- sandbox : & mut UninitializedSandbox ,
38- name : & str ,
39- extra_allowed_syscalls : Vec < ExtraAllowedSyscall > ,
40- ) -> Result < ( ) > ;
26+ /// A representation of a host function.
27+ /// This is a thin wrapper around a `Fn(Args) -> Result<Output>`.
28+ #[ derive( Clone ) ]
29+ pub struct HostFunction < Output , Args >
30+ where
31+ Args : ParameterTuple ,
32+ Output : SupportedReturnType ,
33+ {
34+ // This is a thin wrapper around a `Fn(Args) -> Result<Output>`.
35+ // But unlike `Fn` which is a trait, this is a concrete type.
36+ // This allows us to:
37+ // 1. Impose contraints on the function arguments and return type.
38+ // 2. Impose a single function signature.
39+ //
40+ // This second point is important because the `Fn` trait is generic
41+ // over the function arguments (with an associated return type).
42+ // This means that a given type could implement `Fn` for multiple
43+ // function signatures.
44+ // This means we can't do something like:
45+ // ```rust,ignore
46+ // impl<Args, Output, F> SomeTrait for F
47+ // where
48+ // F: Fn(Args) -> Result<Output>,
49+ // { ... }
50+ // ```
51+ // because the concrete type F might implement `Fn` for multiple times,
52+ // and that would means implementing `SomeTrait` multiple times for the
53+ // same type.
54+
55+ // Use Arc in here instead of Box because it's useful in tests and
56+ // presumably in other places to be able to clone a HostFunction and
57+ // use it across different sandboxes.
58+ func : Arc < dyn Fn ( Args ) -> Result < Output > + Send + Sync + ' static > ,
4159}
4260
43- /// Tait for types that can be converted into types implementing `HostFunction`.
44- pub trait IntoHostFunction < R , Args > {
45- /// Concrete type of the returned host function
46- type Output : HostFunction < R , Args > ;
47-
48- /// Convert the type into a host function
49- fn into_host_function ( self ) -> Self :: Output ;
61+ pub ( crate ) struct TypeErasedHostFunction {
62+ func : Box < dyn Fn ( Vec < ParameterValue > ) -> Result < ReturnValue > + Send + Sync + ' static > ,
5063}
5164
52- macro_rules! impl_host_function {
53- ( @count) => { 0 } ;
54- ( @count $P: ident $( , $R: ident) * ) => {
55- impl_host_function!( @count $( $R) ,* ) + 1
56- } ;
57- ( @impl $( $P: ident) ,* ) => {
58- const _: ( ) = {
59- impl <R $( , $P) * , F > HostFunction <R , ( $( $P, ) * ) > for Arc <Mutex <F >>
60- where
61- F : FnMut ( $( $P) ,* ) -> Result <R > + Send + ' static ,
62- $( $P: SupportedParameterType + Clone , ) *
63- R : SupportedReturnType ,
64- {
65- /// Register the host function with the given name in the sandbox.
66- #[ instrument(
67- err( Debug ) , skip( self , sandbox) , parent = Span :: current( ) , level = "Trace"
68- ) ]
69- fn register(
70- & self ,
71- sandbox: & mut UninitializedSandbox ,
72- name: & str ,
73- ) -> Result <( ) > {
74- register_host_function( self . clone( ) , sandbox, name, None )
75- }
76-
77- /// Register the host function with the given name in the sandbox, allowing extra syscalls.
78- #[ cfg( all( feature = "seccomp" , target_os = "linux" ) ) ]
79- #[ instrument(
80- err( Debug ) , skip( self , sandbox, extra_allowed_syscalls) ,
81- parent = Span :: current( ) , level = "Trace"
82- ) ]
83- fn register_with_extra_allowed_syscalls(
84- & self ,
85- sandbox: & mut UninitializedSandbox ,
86- name: & str ,
87- extra_allowed_syscalls: Vec <ExtraAllowedSyscall >,
88- ) -> Result <( ) > {
89- register_host_function( self . clone( ) , sandbox, name, Some ( extra_allowed_syscalls) )
90- }
91- }
92-
93- impl <R $( , $P) * > HostFunction <R , ( $( $P, ) * ) > for & dyn HostFunction <R , ( $( $P, ) * ) >
94- where
95- $( $P: SupportedParameterType + Clone , ) *
96- R : SupportedReturnType ,
97- {
98- /// Register the host function with the given name in the sandbox.
99- #[ instrument(
100- err( Debug ) , skip( self , sandbox) , parent = Span :: current( ) , level = "Trace"
101- ) ]
102- fn register(
103- & self ,
104- sandbox: & mut UninitializedSandbox ,
105- name: & str ,
106- ) -> Result <( ) > {
107- ( * * self ) . register( sandbox, name)
108- }
109-
110- /// Register the host function with the given name in the sandbox, allowing extra syscalls.
111- #[ cfg( all( feature = "seccomp" , target_os = "linux" ) ) ]
112- #[ instrument(
113- err( Debug ) , skip( self , sandbox, extra_allowed_syscalls) ,
114- parent = Span :: current( ) , level = "Trace"
115- ) ]
116- fn register_with_extra_allowed_syscalls(
117- & self ,
118- sandbox: & mut UninitializedSandbox ,
119- name: & str ,
120- extra_allowed_syscalls: Vec <ExtraAllowedSyscall >,
121- ) -> Result <( ) > {
122- ( * * self ) . register_with_extra_allowed_syscalls( sandbox, name, extra_allowed_syscalls)
123- }
124- }
125-
126- impl <R $( , $P) * , F > IntoHostFunction <R , ( $( $P, ) * ) > for F
127- where
128- F : FnMut ( $( $P) ,* ) -> Result <R > + Send + ' static ,
129- Arc <Mutex <F >>: HostFunction <R , ( $( $P, ) * ) >,
130- {
131- type Output = Arc <Mutex <F >>;
132-
133- fn into_host_function( self ) -> Self :: Output {
134- Arc :: new( Mutex :: new( self ) )
135- }
136- }
137-
138- impl <R $( , $P) * , F > IntoHostFunction <R , ( $( $P, ) * ) > for Arc <Mutex <F >>
139- where
140- F : FnMut ( $( $P) ,* ) -> Result <R > + Send + ' static ,
141- Arc <Mutex <F >>: HostFunction <R , ( $( $P, ) * ) >,
142- {
143- type Output = Arc <Mutex <F >>;
65+ impl < Args , Output > HostFunction < Output , Args >
66+ where
67+ Args : ParameterTuple ,
68+ Output : SupportedReturnType ,
69+ {
70+ /// Call the host function with the given arguments.
71+ pub fn call ( & self , args : Args ) -> Result < Output > {
72+ ( self . func ) ( args)
73+ }
74+ }
14475
145- fn into_host_function( self ) -> Self :: Output {
146- self
147- }
148- }
76+ impl TypeErasedHostFunction {
77+ pub ( crate ) fn call ( & self , args : Vec < ParameterValue > ) -> Result < ReturnValue > {
78+ ( self . func ) ( args)
79+ }
80+ }
14981
150- impl <R $( , $P) * , F > IntoHostFunction <R , ( $( $P, ) * ) > for & Arc <Mutex <F >>
151- where
152- F : FnMut ( $( $P) ,* ) -> Result <R > + Send + ' static ,
153- Arc <Mutex <F >>: HostFunction <R , ( $( $P, ) * ) >,
154- {
155- type Output = Arc <Mutex <F >>;
82+ impl < Args , Output > From < HostFunction < Output , Args > > for TypeErasedHostFunction
83+ where
84+ Args : ParameterTuple ,
85+ Output : SupportedReturnType ,
86+ {
87+ fn from ( func : HostFunction < Output , Args > ) -> TypeErasedHostFunction {
88+ TypeErasedHostFunction {
89+ func : Box :: new ( move |args : Vec < ParameterValue > | {
90+ let args = Args :: from_value ( args) ?;
91+ Ok ( func. call ( args) ?. into_value ( ) )
92+ } ) ,
93+ }
94+ }
95+ }
15696
157- fn into_host_function( self ) -> Self :: Output {
158- self . clone( )
97+ macro_rules! impl_host_function {
98+ ( [ $N: expr] ( $( $p: ident: $P: ident) ,* ) ) => {
99+ /*
100+ // Normally for a `Fn + Send + Sync` we don't need to use a Mutex
101+ // like we do in the case of a `FnMut`.
102+ // However, we can't implement `IntoHostFunction` for `Fn` and `FnMut`
103+ // because `FnMut` is a supertrait of `Fn`.
104+ */
105+
106+ impl <F , R , $( $P) ,* > From <F > for HostFunction <R :: ReturnType , ( $( $P, ) * ) >
107+ where
108+ F : FnMut ( $( $P) ,* ) -> R + Send + ' static ,
109+ ( $( $P, ) * ) : ParameterTuple ,
110+ R : ResultType ,
111+ {
112+ fn from( mut func: F ) -> HostFunction <R :: ReturnType , ( $( $P, ) * ) > {
113+ let func = move |( $( $p, ) * ) : ( $( $P, ) * ) | -> Result <R :: ReturnType > {
114+ func( $( $p) ,* ) . into_result( )
115+ } ;
116+ let func = Mutex :: new( func) ;
117+ HostFunction {
118+ func: Arc :: new( move |args: ( $( $P, ) * ) | {
119+ func. try_lock( )
120+ . map_err( |e| new_error!( "Error locking at {}:{}: {}" , file!( ) , line!( ) , e) ) ?
121+ ( args)
122+ } )
159123 }
160124 }
125+ }
126+ } ;
127+ }
161128
162- impl <R $( , $P) * > IntoHostFunction <R , ( $( $P, ) * ) > for & dyn HostFunction <R , ( $( $P, ) * ) >
163- where
164- R : SupportedReturnType ,
165- $( $P: SupportedParameterType + Clone , ) *
166- {
167- type Output = Self ;
129+ for_each_tuple ! ( impl_host_function) ;
168130
169- fn into_host_function( self ) -> Self :: Output {
170- self
171- }
172- }
131+ pub ( crate ) fn register_host_function < Args : ParameterTuple , Output : SupportedReturnType > (
132+ func : impl Into < HostFunction < Output , Args > > ,
133+ sandbox : & mut UninitializedSandbox ,
134+ name : & str ,
135+ extra_allowed_syscalls : Option < Vec < ExtraAllowedSyscall > > ,
136+ ) -> Result < ( ) > {
137+ let func = func. into ( ) . into ( ) ;
173138
174- fn register_host_function<T , $( $P, ) * R >(
175- self_: Arc <Mutex <T >>,
176- sandbox: & mut UninitializedSandbox ,
177- name: & str ,
178- extra_allowed_syscalls: Option <Vec <ExtraAllowedSyscall >>,
179- ) -> Result <( ) >
180- where
181- T : FnMut ( $( $P) ,* ) -> Result <R > + Send + ' static ,
182- $( $P: SupportedParameterType + Clone , ) *
183- R : SupportedReturnType ,
139+ if let Some ( _eas) = extra_allowed_syscalls {
140+ if cfg ! ( all( feature = "seccomp" , target_os = "linux" ) ) {
141+ // Register with extra allowed syscalls
142+ #[ cfg( all( feature = "seccomp" , target_os = "linux" ) ) ]
184143 {
185- const N : usize = impl_host_function!( @count $( $P) ,* ) ;
186- let cloned = self_. clone( ) ;
187- let func = Box :: new( move |args: Vec <ParameterValue >| {
188- let ( $( $P, ) * ) = match <[ ParameterValue ; N ] >:: try_from( args) {
189- Ok ( [ $( $P, ) * ] ) => ( $( $P:: from_value( $P) ?, ) * ) ,
190- Err ( args) => { log_then_return!( UnexpectedNoOfArguments ( args. len( ) , N ) ) ; }
191- } ;
192-
193- let result = cloned
194- . try_lock( )
195- . map_err( |e| new_error!( "Error locking at {}:{}: {}" , file!( ) , line!( ) , e) ) ?(
196- $( $P) ,*
197- ) ?;
198- Ok ( result. into_value( ) )
199- } ) ;
200-
201- if let Some ( _eas) = extra_allowed_syscalls {
202- if cfg!( all( feature = "seccomp" , target_os = "linux" ) ) {
203- // Register with extra allowed syscalls
204- #[ cfg( all( feature = "seccomp" , target_os = "linux" ) ) ]
205- {
206- sandbox
207- . host_funcs
208- . try_lock( )
209- . map_err( |e| new_error!( "Error locking at {}:{}: {}" , file!( ) , line!( ) , e) ) ?
210- . register_host_function_with_syscalls(
211- name. to_string( ) ,
212- HyperlightFunction :: new( func) ,
213- _eas,
214- ) ?;
215- }
216- } else {
217- // Log and return an error
218- log_then_return!( "Extra allowed syscalls are only supported on Linux with seccomp enabled" ) ;
219- }
220- } else {
221- // Register without extra allowed syscalls
222- sandbox
223- . host_funcs
224- . try_lock( )
225- . map_err( |e| new_error!( "Error locking at {}:{}: {}" , file!( ) , line!( ) , e) ) ?
226- . register_host_function(
227- name. to_string( ) ,
228- HyperlightFunction :: new( func) ,
229- ) ?;
230- }
231-
232- Ok ( ( ) )
144+ sandbox
145+ . host_funcs
146+ . try_lock ( )
147+ . map_err ( |e| new_error ! ( "Error locking at {}:{}: {}" , file!( ) , line!( ) , e) ) ?
148+ . register_host_function_with_syscalls ( name. to_string ( ) , func, _eas) ?;
233149 }
234- } ;
235- } ;
236- ( ) => {
237- impl_host_function!( @impl ) ;
238- } ;
239- ( $P: ident $( , $R: ident) * ) => {
240- impl_host_function!( $( $R) ,* ) ;
241- impl_host_function!( @impl $P $( , $R) * ) ;
242- } ;
150+ } else {
151+ // Log and return an error
152+ log_then_return ! (
153+ "Extra allowed syscalls are only supported on Linux with seccomp enabled"
154+ ) ;
155+ }
156+ } else {
157+ // Register without extra allowed syscalls
158+ sandbox
159+ . host_funcs
160+ . try_lock ( )
161+ . map_err ( |e| new_error ! ( "Error locking at {}:{}: {}" , file!( ) , line!( ) , e) ) ?
162+ . register_host_function ( name. to_string ( ) , func) ?;
163+ }
164+
165+ Ok ( ( ) )
243166}
244-
245- impl_host_function ! ( P1 , P2 , P3 , P4 , P5 , P6 , P7 , P8 , P9 , P10 ) ;
0 commit comments