@@ -295,16 +295,22 @@ where
295295
296296#[ cfg( test) ]
297297mod tests {
298+ use std:: sync:: { Arc , Barrier } ;
299+ use std:: thread;
300+
298301 use hyperlight_common:: flatbuffer_wrappers:: function_types:: {
299302 ParameterValue , ReturnType , ReturnValue ,
300303 } ;
301- use hyperlight_testing:: simple_guest_as_string;
304+ use hyperlight_testing:: { callback_guest_as_string , simple_guest_as_string} ;
302305
303306 use crate :: func:: call_ctx:: MultiUseGuestCallContext ;
304307 use crate :: sandbox:: SandboxConfiguration ;
305308 use crate :: sandbox_state:: sandbox:: { DevolvableSandbox , EvolvableSandbox } ;
306309 use crate :: sandbox_state:: transition:: { MultiUseContextCallback , Noop } ;
307- use crate :: { GuestBinary , MultiUseSandbox , UninitializedSandbox } ;
310+ use crate :: {
311+ is_hypervisor_present, GuestBinary , HyperlightError , MultiUseSandbox , Result ,
312+ UninitializedSandbox ,
313+ } ;
308314
309315 // Tests to ensure that many (1000) function calls can be made in a call context with a small stack (1K) and heap(14K).
310316 // This test effectively ensures that the stack is being properly reset after each call and we are not leaking memory in the Guest.
@@ -384,4 +390,203 @@ mod tests {
384390 . unwrap ( ) ;
385391 assert_eq ! ( res, ReturnValue :: Int ( 0 ) ) ;
386392 }
393+
394+ #[ test]
395+ // TODO: Investigate why this test fails with an incorrect error when run alongside other tests
396+ #[ ignore]
397+ #[ cfg( target_os = "linux" ) ]
398+ fn test_violate_seccomp_filters ( ) -> Result < ( ) > {
399+ if !is_hypervisor_present ( ) {
400+ panic ! ( "Panic on create_multi_use_sandbox because no hypervisor is present" ) ;
401+ }
402+
403+ fn make_get_pid_syscall ( ) -> Result < u64 > {
404+ let pid = unsafe { libc:: syscall ( libc:: SYS_getpid ) } ;
405+ Ok ( pid as u64 )
406+ }
407+
408+ // First, run to make sure it fails.
409+ {
410+ let mut usbox = UninitializedSandbox :: new (
411+ GuestBinary :: FilePath ( simple_guest_as_string ( ) . expect ( "Guest Binary Missing" ) ) ,
412+ None ,
413+ )
414+ . unwrap ( ) ;
415+
416+ usbox. register ( "MakeGetpidSyscall" , make_get_pid_syscall) ?;
417+
418+ let mut sbox: MultiUseSandbox = usbox. evolve ( Noop :: default ( ) ) ?;
419+
420+ let res =
421+ sbox. call_guest_function_by_name ( "ViolateSeccompFilters" , ReturnType :: ULong , None ) ;
422+
423+ #[ cfg( feature = "seccomp" ) ]
424+ match res {
425+ Ok ( _) => panic ! ( "Expected to fail due to seccomp violation" ) ,
426+ Err ( e) => match e {
427+ HyperlightError :: DisallowedSyscall => { }
428+ _ => panic ! ( "Expected DisallowedSyscall error: {}" , e) ,
429+ } ,
430+ }
431+
432+ #[ cfg( not( feature = "seccomp" ) ) ]
433+ match res {
434+ Ok ( _) => ( ) ,
435+ Err ( e) => panic ! ( "Expected to succeed without seccomp: {}" , e) ,
436+ }
437+ }
438+
439+ // Second, run with allowing `SYS_getpid`
440+ #[ cfg( feature = "seccomp" ) ]
441+ {
442+ let mut usbox = UninitializedSandbox :: new (
443+ GuestBinary :: FilePath ( simple_guest_as_string ( ) . expect ( "Guest Binary Missing" ) ) ,
444+ None ,
445+ )
446+ . unwrap ( ) ;
447+
448+ usbox. register_with_extra_allowed_syscalls (
449+ "MakeGetpidSyscall" ,
450+ make_get_pid_syscall,
451+ vec ! [ libc:: SYS_getpid ] ,
452+ ) ?;
453+ // ^^^ note, we are allowing SYS_getpid
454+
455+ let mut sbox: MultiUseSandbox = usbox. evolve ( Noop :: default ( ) ) ?;
456+
457+ let res =
458+ sbox. call_guest_function_by_name ( "ViolateSeccompFilters" , ReturnType :: ULong , None ) ;
459+
460+ match res {
461+ Ok ( _) => { }
462+ Err ( e) => panic ! ( "Expected to succeed due to seccomp violation: {}" , e) ,
463+ }
464+ }
465+
466+ Ok ( ( ) )
467+ }
468+
469+ // This test is to capture the case where the guest execution is running a host function when cancelled and that host function
470+ // is never going to return.
471+ // The host function that is called will end after 5 seconds, but by this time the cancellation will have given up
472+ // (using default timeout settings) , so this tests looks for the error "Failed to cancel guest execution".
473+ #[ test]
474+ #[ ignore = "We cannot cancel host functions. TODO reenable this test when it's enabled" ]
475+ fn test_terminate_vcpu_calling_host_spinning_cpu ( ) {
476+ // This test relies upon a Hypervisor being present so for now
477+ // we will skip it if there isn't one.
478+ if !is_hypervisor_present ( ) {
479+ println ! ( "Skipping test_call_guest_function_by_name because no hypervisor is present" ) ;
480+ return ;
481+ }
482+ let mut usbox = UninitializedSandbox :: new (
483+ GuestBinary :: FilePath ( callback_guest_as_string ( ) . expect ( "Guest Binary Missing" ) ) ,
484+ None ,
485+ )
486+ . unwrap ( ) ;
487+
488+ // Make this host call run for 5 seconds
489+
490+ fn spin ( ) -> Result < ( ) > {
491+ thread:: sleep ( std:: time:: Duration :: from_secs ( 5 ) ) ;
492+ Ok ( ( ) )
493+ }
494+
495+ #[ cfg( any( target_os = "windows" , not( feature = "seccomp" ) ) ) ]
496+ usbox. register ( "Spin" , spin) . unwrap ( ) ;
497+
498+ #[ cfg( all( target_os = "linux" , feature = "seccomp" ) ) ]
499+ usbox
500+ . register_with_extra_allowed_syscalls ( "Spin" , spin, vec ! [ libc:: SYS_clock_nanosleep ] )
501+ . unwrap ( ) ;
502+
503+ let sandbox: MultiUseSandbox = usbox. evolve ( Noop :: default ( ) ) . unwrap ( ) ;
504+ let mut ctx = sandbox. new_call_context ( ) ;
505+ let result = ctx. call ( "CallHostSpin" , ReturnType :: Void , None ) ;
506+
507+ assert ! ( result. is_err( ) ) ;
508+ match result. unwrap_err ( ) {
509+ HyperlightError :: GuestExecutionHungOnHostFunctionCall ( ) => { }
510+ e => panic ! (
511+ "Expected HyperlightError::GuestExecutionHungOnHostFunctionCall but got {:?}" ,
512+ e
513+ ) ,
514+ }
515+ }
516+
517+ #[ test]
518+ fn test_trigger_exception_on_guest ( ) {
519+ let usbox = UninitializedSandbox :: new (
520+ GuestBinary :: FilePath ( simple_guest_as_string ( ) . expect ( "Guest Binary Missing" ) ) ,
521+ None ,
522+ )
523+ . unwrap ( ) ;
524+
525+ let mut multi_use_sandbox: MultiUseSandbox = usbox. evolve ( Noop :: default ( ) ) . unwrap ( ) ;
526+
527+ let res = multi_use_sandbox. call_guest_function_by_name (
528+ "TriggerException" ,
529+ ReturnType :: Void ,
530+ None ,
531+ ) ;
532+
533+ assert ! ( res. is_err( ) ) ;
534+
535+ match res. unwrap_err ( ) {
536+ HyperlightError :: GuestAborted ( _, msg) => {
537+ // msg should indicate we got an invalid opcode exception
538+ assert ! ( msg. contains( "InvalidOpcode" ) ) ;
539+ }
540+ e => panic ! (
541+ "Expected HyperlightError::GuestExecutionError but got {:?}" ,
542+ e
543+ ) ,
544+ }
545+ }
546+
547+ #[ test]
548+ #[ ignore] // this test runs by itself because it uses a lot of system resources
549+ fn create_1000_sandboxes ( ) {
550+ let barrier = Arc :: new ( Barrier :: new ( 21 ) ) ;
551+
552+ let mut handles = vec ! [ ] ;
553+
554+ for _ in 0 ..20 {
555+ let c = barrier. clone ( ) ;
556+
557+ let handle = thread:: spawn ( move || {
558+ c. wait ( ) ;
559+
560+ for _ in 0 ..50 {
561+ let usbox = UninitializedSandbox :: new (
562+ GuestBinary :: FilePath (
563+ simple_guest_as_string ( ) . expect ( "Guest Binary Missing" ) ,
564+ ) ,
565+ None ,
566+ )
567+ . unwrap ( ) ;
568+
569+ let mut multi_use_sandbox: MultiUseSandbox =
570+ usbox. evolve ( Noop :: default ( ) ) . unwrap ( ) ;
571+
572+ let res = multi_use_sandbox. call_guest_function_by_name (
573+ "GetStatic" ,
574+ ReturnType :: Int ,
575+ None ,
576+ ) ;
577+
578+ assert ! ( res. is_ok( ) ) ;
579+ assert_eq ! ( res. unwrap( ) , ReturnValue :: Int ( 0 ) ) ;
580+ }
581+ } ) ;
582+
583+ handles. push ( handle) ;
584+ }
585+
586+ barrier. wait ( ) ;
587+
588+ for handle in handles {
589+ handle. join ( ) . unwrap ( ) ;
590+ }
591+ }
387592}
0 commit comments