@@ -24,7 +24,7 @@ use ipc_queue::{self, QueueEvent};
24
24
use sgxs:: loader:: Tcs as SgxsTcs ;
25
25
use std:: alloc:: { GlobalAlloc , Layout , System } ;
26
26
use std:: cell:: RefCell ;
27
- use std:: collections:: VecDeque ;
27
+ use std:: collections:: { HashMap , VecDeque } ;
28
28
use std:: io:: { self , ErrorKind as IoErrorKind , Read , Result as IoResult } ;
29
29
use std:: pin:: Pin ;
30
30
use std:: result:: Result as StdResult ;
@@ -46,16 +46,17 @@ lazy_static! {
46
46
pub ( crate ) mod abi;
47
47
mod interface;
48
48
49
- use self :: abi:: dispatch;
49
+ use self :: abi:: { dispatch, UsercallList } ;
50
50
use self :: interface:: { Handler , OutputBuffer } ;
51
51
52
- const EV_ABORT : u64 = 0b0000_0000_0000_1000 ;
52
+ const EV_ABORT : u64 = 0b0000_0000_0001_0000 ;
53
53
54
54
// Experiments show that tha actual size of these queues is less important than
55
55
// the ratio between them. It appears that a much larger return queue performs
56
56
// much better when multiple enclave threads send usercalls.
57
57
const USERCALL_QUEUE_SIZE : usize = 16 ;
58
58
const RETURN_QUEUE_SIZE : usize = 1024 ;
59
+ const CANCEL_QUEUE_SIZE : usize = USERCALL_QUEUE_SIZE * 2 ;
59
60
60
61
enum UsercallSendData {
61
62
Sync ( ThreadResult < ErasedTcs > , RunningTcs , RefCell < [ u8 ; 1024 ] > ) ,
@@ -64,7 +65,7 @@ enum UsercallSendData {
64
65
65
66
enum UsercallHandleData {
66
67
Sync ( tcs:: Usercall < ErasedTcs > , RunningTcs , RefCell < [ u8 ; 1024 ] > ) ,
67
- Async ( Usercall ) ,
68
+ Async ( Usercall , Option < async_mpsc :: UnboundedSender < UsercallEvent > > ) ,
68
69
}
69
70
70
71
type EnclaveResult = StdResult < ( u64 , u64 ) , EnclaveAbort < Option < EnclavePanic > > > ;
@@ -513,11 +514,11 @@ struct PendingEvents {
513
514
}
514
515
515
516
impl PendingEvents {
516
- const MAX_EVENT : usize = 8 ;
517
+ const MAX_EVENT : usize = 16 ;
517
518
518
519
fn new ( ) -> Self {
519
520
// sanity check to ensure we update MAX_EVENT if new events are added in the future
520
- const EV_ALL : u64 = EV_USERCALLQ_NOT_FULL | EV_RETURNQ_NOT_EMPTY | EV_UNPARK ;
521
+ const EV_ALL : u64 = EV_USERCALLQ_NOT_FULL | EV_RETURNQ_NOT_EMPTY | EV_CANCELQ_NOT_FULL | EV_UNPARK ;
521
522
assert ! ( EV_ALL < Self :: MAX_EVENT as u64 ) ;
522
523
assert ! ( Self :: MAX_EVENT <= 1usize + u8 :: max_value( ) as usize ) ;
523
524
@@ -587,6 +588,7 @@ impl EnclaveKind {
587
588
struct FifoDescriptors {
588
589
usercall_queue : FifoDescriptor < Usercall > ,
589
590
return_queue : FifoDescriptor < Return > ,
591
+ cancel_queue : FifoDescriptor < Cancel > ,
590
592
}
591
593
592
594
pub ( crate ) struct EnclaveState {
@@ -630,6 +632,31 @@ impl Work {
630
632
}
631
633
}
632
634
635
+ enum UsercallEvent {
636
+ Started ( u64 , tokio:: sync:: oneshot:: Sender < ( ) > ) ,
637
+ Finished ( u64 ) ,
638
+ Cancelled ( u64 , Instant ) ,
639
+ }
640
+
641
+ fn ignore_cancel_impl ( usercall_nr : u64 ) -> bool {
642
+ usercall_nr != UsercallList :: read as u64 &&
643
+ usercall_nr != UsercallList :: read_alloc as u64 &&
644
+ usercall_nr != UsercallList :: write as u64 &&
645
+ usercall_nr != UsercallList :: accept_stream as u64 &&
646
+ usercall_nr != UsercallList :: connect_stream as u64 &&
647
+ usercall_nr != UsercallList :: wait as u64
648
+ }
649
+
650
+ trait IgnoreCancel {
651
+ fn ignore_cancel ( & self ) -> bool ;
652
+ }
653
+ impl IgnoreCancel for Usercall {
654
+ fn ignore_cancel ( & self ) -> bool { ignore_cancel_impl ( self . args . 0 ) }
655
+ }
656
+ impl IgnoreCancel for Cancel {
657
+ fn ignore_cancel ( & self ) -> bool { ignore_cancel_impl ( self . usercall_nr ) }
658
+ }
659
+
633
660
impl EnclaveState {
634
661
fn event_queue_add_tcs (
635
662
event_queues : & mut FnvHashMap < TcsAddress , futures:: channel:: mpsc:: UnboundedSender < u8 > > ,
@@ -702,15 +729,41 @@ impl EnclaveState {
702
729
tx_return_channel : tokio:: sync:: mpsc:: UnboundedSender < ( EnclaveResult , EnclaveEntry ) > ,
703
730
mut handle_data : UsercallHandleData ,
704
731
) {
732
+ let notifier_rx = match handle_data {
733
+ UsercallHandleData :: Async ( ref usercall, Some ( ref usercall_event_tx) ) => {
734
+ let ( notifier_tx, notifier_rx) = tokio:: sync:: oneshot:: channel ( ) ;
735
+ usercall_event_tx. send ( UsercallEvent :: Started ( usercall. id , notifier_tx) ) . ok ( )
736
+ . expect ( "failed to send usercall event" ) ;
737
+ Some ( notifier_rx)
738
+ } ,
739
+ _ => None ,
740
+ } ;
705
741
let ( parameters, mode, tcs) = match handle_data {
706
742
UsercallHandleData :: Sync ( ref usercall, ref mut tcs, _) => ( usercall. parameters ( ) , tcs. mode , Some ( tcs) ) ,
707
- UsercallHandleData :: Async ( ref usercall) => ( usercall. args , EnclaveEntry :: ExecutableNonMain , None ) ,
743
+ UsercallHandleData :: Async ( ref usercall, _ ) => ( usercall. args , EnclaveEntry :: ExecutableNonMain , None ) ,
708
744
} ;
709
745
let mut input = IOHandlerInput { enclave : enclave. clone ( ) , tcs, work_sender : & work_sender } ;
710
746
let handler = Handler ( & mut input) ;
711
- let ( _handler, result) = {
747
+ let result = {
748
+ use self :: interface:: ToSgxResult ;
749
+ use self :: abi:: ReturnValue ;
750
+
712
751
let ( p1, p2, p3, p4, p5) = parameters;
713
- dispatch ( handler, p1, p2, p3, p4, p5) . await
752
+ match notifier_rx {
753
+ None => dispatch ( handler, p1, p2, p3, p4, p5) . await . 1 ,
754
+ Some ( notifier_rx) => {
755
+ let a = dispatch ( handler, p1, p2, p3, p4, p5) . boxed_local ( ) ;
756
+ let b = notifier_rx;
757
+ match futures:: future:: select ( a, b) . await {
758
+ Either :: Left ( ( ret, _) ) => ret. 1 ,
759
+ Either :: Right ( ( Ok ( ( ) ) , _) ) => {
760
+ let result: IoResult < usize > = Err ( IoErrorKind :: Interrupted . into ( ) ) ;
761
+ ReturnValue :: into_registers ( Ok ( result. to_sgx_result ( ) ) )
762
+ } ,
763
+ Either :: Right ( ( Err ( _) , _) ) => panic ! ( "notifier channel closed unexpectedly" ) ,
764
+ }
765
+ } ,
766
+ }
714
767
} ;
715
768
let ret = match result {
716
769
Ok ( ret) => {
@@ -721,7 +774,11 @@ impl EnclaveState {
721
774
entry : CoEntry :: Resume ( usercall, ret) ,
722
775
} ) . expect ( "Work sender couldn't send data to receiver" ) ;
723
776
}
724
- UsercallHandleData :: Async ( usercall) => {
777
+ UsercallHandleData :: Async ( usercall, usercall_event_tx) => {
778
+ if let Some ( usercall_event_tx) = usercall_event_tx {
779
+ usercall_event_tx. send ( UsercallEvent :: Finished ( usercall. id ) ) . ok ( )
780
+ . expect ( "failed to send usercall event" ) ;
781
+ }
725
782
let return_queue_tx = enclave. return_queue_tx . lock ( ) . await . clone ( ) . expect ( "return_queue_tx not initialized" ) ;
726
783
let ret = Return {
727
784
id : usercall. id ,
@@ -740,7 +797,7 @@ impl EnclaveState {
740
797
trap_attached_debugger ( usercall. tcs_address ( ) as _ ) . await ;
741
798
EnclavePanic :: from ( debug_buf. into_inner ( ) )
742
799
}
743
- UsercallHandleData :: Async ( _) => {
800
+ UsercallHandleData :: Async ( _, _ ) => {
744
801
// FIXME: find a better panic message
745
802
EnclavePanic :: DebugStr ( "async exit with a panic" . to_owned ( ) )
746
803
}
@@ -815,14 +872,16 @@ impl EnclaveState {
815
872
} ;
816
873
let enclave_clone = enclave. clone ( ) ;
817
874
let io_future = async move {
818
- let ( usercall_queue_synchronizer , return_queue_synchronizer , sync_usercall_tx) = QueueSynchronizer :: new ( enclave_clone. clone ( ) ) ;
875
+ let ( uqs , rqs , cqs , sync_usercall_tx) = QueueSynchronizer :: new ( enclave_clone. clone ( ) ) ;
819
876
820
- let ( usercall_queue_tx, usercall_queue_rx) = ipc_queue:: bounded_async ( USERCALL_QUEUE_SIZE , usercall_queue_synchronizer) ;
821
- let ( return_queue_tx, return_queue_rx) = ipc_queue:: bounded_async ( RETURN_QUEUE_SIZE , return_queue_synchronizer) ;
877
+ let ( usercall_queue_tx, usercall_queue_rx) = ipc_queue:: bounded_async ( USERCALL_QUEUE_SIZE , uqs) ;
878
+ let ( return_queue_tx, return_queue_rx) = ipc_queue:: bounded_async ( RETURN_QUEUE_SIZE , rqs) ;
879
+ let ( cancel_queue_tx, cancel_queue_rx) = ipc_queue:: bounded_async ( CANCEL_QUEUE_SIZE , cqs) ;
822
880
823
881
let fifo_descriptors = FifoDescriptors {
824
882
usercall_queue : usercall_queue_tx. into_descriptor ( ) ,
825
883
return_queue : return_queue_rx. into_descriptor ( ) ,
884
+ cancel_queue : cancel_queue_tx. into_descriptor ( ) ,
826
885
} ;
827
886
828
887
* enclave_clone. fifo_descriptors . lock ( ) . await = Some ( fifo_descriptors) ;
@@ -834,14 +893,51 @@ impl EnclaveState {
834
893
}
835
894
} ) ;
836
895
896
+ let ( usercall_event_tx, mut usercall_event_rx) = async_mpsc:: unbounded_channel ( ) ;
897
+ let usercall_event_tx_clone = usercall_event_tx. clone ( ) ;
898
+ tokio:: task:: spawn_local ( async move {
899
+ while let Ok ( c) = cancel_queue_rx. recv ( ) . await {
900
+ if !c. ignore_cancel ( ) {
901
+ let _ = usercall_event_tx_clone. send ( UsercallEvent :: Cancelled ( c. id , Instant :: now ( ) ) ) ;
902
+ }
903
+ }
904
+ } ) ;
905
+
906
+ tokio:: task:: spawn_local ( async move {
907
+ let mut notifiers = HashMap :: new ( ) ;
908
+ let mut cancels: HashMap < u64 , Instant > = HashMap :: new ( ) ;
909
+ // This should be greater than the amount of time it takes for the enclave runner
910
+ // to start executing a usercall after the enclave sends it on the usercall_queue.
911
+ const CANCEL_EXPIRY : Duration = Duration :: from_millis ( 100 ) ;
912
+ loop {
913
+ match usercall_event_rx. recv ( ) . await . expect ( "usercall_event channel closed unexpectedly" ) {
914
+ UsercallEvent :: Started ( id, notifier) => match cancels. remove ( & id) {
915
+ Some ( t) if t. elapsed ( ) < CANCEL_EXPIRY => { let _ = notifier. send ( ( ) ) ; } ,
916
+ _ => { notifiers. insert ( id, notifier) ; } ,
917
+ } ,
918
+ UsercallEvent :: Finished ( id) => { notifiers. remove ( & id) ; } ,
919
+ UsercallEvent :: Cancelled ( id, t) => if t. elapsed ( ) < CANCEL_EXPIRY {
920
+ match notifiers. remove ( & id) {
921
+ Some ( notifier) => { let _ = notifier. send ( ( ) ) ; } ,
922
+ None => { cancels. insert ( id, t) ; } ,
923
+ }
924
+ } ,
925
+ }
926
+ // cleanup expired cancels
927
+ let now = Instant :: now ( ) ;
928
+ cancels. retain ( |_id, & mut t| now - t < CANCEL_EXPIRY ) ;
929
+ }
930
+ } ) ;
931
+
837
932
let mut recv_queue = io_queue_receive. into_future ( ) ;
838
933
while let ( Some ( work) , stream) = recv_queue. await {
839
934
recv_queue = stream. into_future ( ) ;
840
935
let enclave_clone = enclave_clone. clone ( ) ;
841
936
let tx_return_channel = tx_return_channel. clone ( ) ;
842
937
match work {
843
938
UsercallSendData :: Async ( usercall) => {
844
- let uchd = UsercallHandleData :: Async ( usercall) ;
939
+ let usercall_event_tx = if usercall. ignore_cancel ( ) { None } else { Some ( usercall_event_tx. clone ( ) ) } ;
940
+ let uchd = UsercallHandleData :: Async ( usercall, usercall_event_tx) ;
845
941
let fut = Self :: handle_usercall ( enclave_clone, work_sender. clone ( ) , tx_return_channel, uchd) ;
846
942
tokio:: task:: spawn_local ( fut) ;
847
943
}
@@ -1416,7 +1512,7 @@ impl<'tcs> IOHandlerInput<'tcs> {
1416
1512
}
1417
1513
1418
1514
fn check_event_set ( set : u64 ) -> IoResult < u8 > {
1419
- const EV_ALL : u64 = EV_USERCALLQ_NOT_FULL | EV_RETURNQ_NOT_EMPTY | EV_UNPARK ;
1515
+ const EV_ALL : u64 = EV_USERCALLQ_NOT_FULL | EV_RETURNQ_NOT_EMPTY | EV_CANCELQ_NOT_FULL | EV_UNPARK ;
1420
1516
if ( set & !EV_ALL ) != 0 {
1421
1517
return Err ( IoErrorKind :: InvalidInput . into ( ) ) ;
1422
1518
}
@@ -1561,12 +1657,16 @@ impl<'tcs> IOHandlerInput<'tcs> {
1561
1657
& mut self ,
1562
1658
usercall_queue : & mut FifoDescriptor < Usercall > ,
1563
1659
return_queue : & mut FifoDescriptor < Return > ,
1660
+ cancel_queue : Option < & mut FifoDescriptor < Cancel > > ,
1564
1661
) -> StdResult < ( ) , EnclaveAbort < bool > > {
1565
1662
let fifo_descriptors = self . enclave . fifo_descriptors . lock ( ) . await . take ( ) ;
1566
1663
match fifo_descriptors {
1567
1664
Some ( fifo_descriptors) => {
1568
1665
* usercall_queue = fifo_descriptors. usercall_queue ;
1569
1666
* return_queue = fifo_descriptors. return_queue ;
1667
+ if let Some ( cancel_queue) = cancel_queue {
1668
+ * cancel_queue = fifo_descriptors. cancel_queue ;
1669
+ }
1570
1670
Ok ( ( ) )
1571
1671
}
1572
1672
None => Err ( self . exit ( true ) ) ,
@@ -1578,6 +1678,7 @@ impl<'tcs> IOHandlerInput<'tcs> {
1578
1678
enum Queue {
1579
1679
Usercall ,
1580
1680
Return ,
1681
+ Cancel ,
1581
1682
}
1582
1683
1583
1684
struct QueueSynchronizer {
@@ -1590,7 +1691,7 @@ struct QueueSynchronizer {
1590
1691
}
1591
1692
1592
1693
impl QueueSynchronizer {
1593
- fn new ( enclave : Arc < EnclaveState > ) -> ( Self , Self , async_pubsub:: Sender < ( ) > ) {
1694
+ fn new ( enclave : Arc < EnclaveState > ) -> ( Self , Self , Self , async_pubsub:: Sender < ( ) > ) {
1594
1695
// This broadcast channel is used to notify enclave-runner of any
1595
1696
// synchronous usercalls made by the enclave for the purpose of
1596
1697
// synchronizing access to usercall and return queues.
@@ -1605,11 +1706,17 @@ impl QueueSynchronizer {
1605
1706
} ;
1606
1707
let return_queue_synchronizer = QueueSynchronizer {
1607
1708
queue : Queue :: Return ,
1709
+ enclave : enclave. clone ( ) ,
1710
+ subscription : Mutex :: new ( tx. subscribe ( ) ) ,
1711
+ subscription_maker : tx. clone ( ) ,
1712
+ } ;
1713
+ let cancel_queue_synchronizer = QueueSynchronizer {
1714
+ queue : Queue :: Cancel ,
1608
1715
enclave,
1609
1716
subscription : Mutex :: new ( tx. subscribe ( ) ) ,
1610
1717
subscription_maker : tx. clone ( ) ,
1611
1718
} ;
1612
- ( usercall_queue_synchronizer, return_queue_synchronizer, tx)
1719
+ ( usercall_queue_synchronizer, return_queue_synchronizer, cancel_queue_synchronizer , tx)
1613
1720
}
1614
1721
}
1615
1722
@@ -1628,6 +1735,7 @@ impl ipc_queue::AsyncSynchronizer for QueueSynchronizer {
1628
1735
fn wait ( & self , event : QueueEvent ) -> Pin < Box < dyn Future < Output = StdResult < ( ) , ipc_queue:: SynchronizationError > > + ' _ > > {
1629
1736
match ( self . queue , event) {
1630
1737
( Queue :: Usercall , QueueEvent :: NotFull ) => panic ! ( "enclave runner should not send on the usercall queue" ) ,
1738
+ ( Queue :: Cancel , QueueEvent :: NotFull ) => panic ! ( "enclave runner should not send on the cancel queue" ) ,
1631
1739
( Queue :: Return , QueueEvent :: NotEmpty ) => panic ! ( "enclave runner should not receive on the return queue" ) ,
1632
1740
_ => { }
1633
1741
}
@@ -1646,12 +1754,14 @@ impl ipc_queue::AsyncSynchronizer for QueueSynchronizer {
1646
1754
fn notify ( & self , event : QueueEvent ) {
1647
1755
let ev = match ( self . queue , event) {
1648
1756
( Queue :: Usercall , QueueEvent :: NotEmpty ) => panic ! ( "enclave runner should not send on the usercall queue" ) ,
1757
+ ( Queue :: Cancel , QueueEvent :: NotEmpty ) => panic ! ( "enclave runner should not send on the cancel queue" ) ,
1649
1758
( Queue :: Return , QueueEvent :: NotFull ) => panic ! ( "enclave runner should not receive on the return queue" ) ,
1650
1759
( Queue :: Usercall , QueueEvent :: NotFull ) => EV_USERCALLQ_NOT_FULL ,
1760
+ ( Queue :: Cancel , QueueEvent :: NotFull ) => EV_CANCELQ_NOT_FULL ,
1651
1761
( Queue :: Return , QueueEvent :: NotEmpty ) => EV_RETURNQ_NOT_EMPTY ,
1652
1762
} ;
1653
1763
// When the enclave needs to wait on a queue, it executes the wait() usercall synchronously,
1654
- // specifying EV_USERCALLQ_NOT_FULL, EV_RETURNQ_NOT_EMPTY, or both in the event_mask.
1764
+ // specifying EV_USERCALLQ_NOT_FULL, EV_RETURNQ_NOT_EMPTY or EV_CANCELQ_NOT_FULL in the event_mask.
1655
1765
// Userspace will wake any or all threads waiting on the appropriate event when it is triggered.
1656
1766
for queue in self . enclave . event_queues . values ( ) {
1657
1767
let _ = queue. unbounded_send ( ev as _ ) ;
0 commit comments