1use crate::state::{Message, State};
19use crate::types::{Action, Capability, MemAddr, PluginId, Right, Rights, SecurityLevel, Size};
20
21use super::{HostCallPrecondition, StepError};
22
23#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
38pub enum HostFunction {
39 CapCall,
43
44 CapSend,
48
49 KernelAlloc,
53
54 MemFree,
58
59 CapRevoke,
63
64 Declassify,
68
69 ThreadCreate,
73
74 ThreadConfigure,
78
79 ThreadResume,
83
84 ThreadSuspend,
88
89 ThreadDestroy,
93}
94
95#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
99pub enum ResourceType {
100 Memory {
102 size: u64,
104 },
105 Capability {
107 payload: u64,
109 },
110 Actor {
112 config: u64,
114 },
115}
116
117impl HostFunction {
120 #[deprecated(note = "Use HostFunction::CapCall instead (EROS consolidation)")]
122 pub const CAP_INVOKE: HostFunction = HostFunction::CapCall;
123
124 #[deprecated(note = "Use HostFunction::CapSend instead (EROS consolidation)")]
126 pub const IPC_SEND: HostFunction = HostFunction::CapSend;
127
128 #[deprecated(note = "Use HostFunction::KernelAlloc instead (EROS consolidation)")]
130 pub const MEM_ALLOC: HostFunction = HostFunction::KernelAlloc;
131}
132
133impl HostFunction {
134 #[inline]
140 pub fn is_effectful(&self) -> bool {
141 true
142 }
143
144 #[inline]
148 pub fn is_declassify(&self) -> bool {
149 matches!(self, HostFunction::Declassify)
150 }
151
152 #[inline]
156 pub fn is_cap_operation(&self) -> bool {
157 matches!(self, HostFunction::CapCall | HostFunction::CapRevoke)
158 }
159
160 #[inline]
164 pub fn is_mem_operation(&self) -> bool {
165 matches!(self, HostFunction::KernelAlloc | HostFunction::MemFree)
166 }
167
168 pub fn execute(
182 &self,
183 state: &State,
184 hc: &HostCall,
185 result: &HostResult,
186 ) -> Result<State, StepError> {
187 match self {
188 HostFunction::CapCall => execute_cap_call(state, hc, result),
189 HostFunction::CapSend => execute_cap_send(state, hc, result),
190 HostFunction::KernelAlloc => execute_kernel_alloc(state, hc),
191 HostFunction::MemFree => execute_mem_free(state, hc),
192 HostFunction::CapRevoke => execute_cap_revoke(state, hc),
193 HostFunction::Declassify => execute_declassify(state, hc),
194 HostFunction::ThreadCreate
196 | HostFunction::ThreadConfigure
197 | HostFunction::ThreadResume
198 | HostFunction::ThreadSuspend
199 | HostFunction::ThreadDestroy => Err(StepError::HostCallPreconditionFailed(
200 HostCallPrecondition::NotImplemented {
201 operation: "thread operations",
202 },
203 )),
204 }
205 }
206
207 pub fn execute_mut(
211 &self,
212 state: &mut State,
213 hc: &HostCall,
214 result: &HostResult,
215 ) -> Result<(), StepError> {
216 match self {
217 HostFunction::CapCall => execute_cap_call_mut(state, hc, result),
218 HostFunction::CapSend => execute_cap_send_mut(state, hc, result),
219 HostFunction::KernelAlloc => execute_kernel_alloc_mut(state, hc),
220 HostFunction::MemFree => execute_mem_free_mut(state, hc),
221 HostFunction::CapRevoke => execute_cap_revoke_mut(state, hc),
222 HostFunction::Declassify => execute_declassify_mut(state, hc),
223 HostFunction::ThreadCreate
224 | HostFunction::ThreadConfigure
225 | HostFunction::ThreadResume
226 | HostFunction::ThreadSuspend
227 | HostFunction::ThreadDestroy => Err(StepError::HostCallPreconditionFailed(
228 HostCallPrecondition::NotImplemented {
229 operation: "thread operations",
230 },
231 )),
232 }
233 }
234
235 #[inline]
239 pub fn is_thread_operation(&self) -> bool {
240 matches!(
241 self,
242 HostFunction::ThreadCreate
243 | HostFunction::ThreadConfigure
244 | HostFunction::ThreadResume
245 | HostFunction::ThreadSuspend
246 | HostFunction::ThreadDestroy
247 )
248 }
249}
250
251#[derive(Debug, Clone)]
255pub struct HostCall {
256 pub(crate) caller: PluginId,
260
261 pub(crate) function: HostFunction,
265
266 pub(crate) args: Vec<u64>,
270
271 pub(crate) reads: Vec<(MemAddr, Size)>,
275
276 pub(crate) writes: Vec<(MemAddr, Size)>,
280}
281
282impl HostCall {
283 pub fn new(
285 caller: PluginId,
286 function: HostFunction,
287 args: Vec<u64>,
288 reads: Vec<(MemAddr, Size)>,
289 writes: Vec<(MemAddr, Size)>,
290 ) -> Self {
291 HostCall {
292 caller,
293 function,
294 args,
295 reads,
296 writes,
297 }
298 }
299
300 #[inline]
302 pub fn caller(&self) -> PluginId {
303 self.caller
304 }
305
306 #[inline]
308 pub fn function(&self) -> HostFunction {
309 self.function
310 }
311
312 #[inline]
314 pub fn args(&self) -> &[u64] {
315 &self.args
316 }
317
318 #[inline]
320 pub fn reads(&self) -> &[(MemAddr, Size)] {
321 &self.reads
322 }
323
324 #[inline]
326 pub fn writes(&self) -> &[(MemAddr, Size)] {
327 &self.writes
328 }
329
330 pub fn required_action(&self) -> Result<Action, StepError> {
341 let map_policy_err =
343 |_| StepError::HostCallPreconditionFailed(HostCallPrecondition::NoCapabilityId);
344
345 match self.function {
346 HostFunction::CapCall => {
347 let target: u64 =
348 self.args
349 .first()
350 .copied()
351 .ok_or(StepError::HostCallPreconditionFailed(
352 HostCallPrecondition::NoCapabilityId,
353 ))?;
354
355 Action::new(
356 self.caller,
357 target.into(),
358 Rights::singleton(Right::Read),
359 "cap_call".to_string(),
360 )
361 .map_err(map_policy_err)
362 }
363 HostFunction::KernelAlloc => Action::new(
364 self.caller,
365 0,
366 Rights::singleton(Right::Create),
367 "kernel_alloc".to_string(),
368 )
369 .map_err(map_policy_err),
370 HostFunction::MemFree => {
371 let addr: u64 =
372 self.args
373 .first()
374 .copied()
375 .ok_or(StepError::HostCallPreconditionFailed(
376 HostCallPrecondition::NoAddress,
377 ))?;
378
379 Action::new(
380 self.caller,
381 addr.into(),
382 Rights::singleton(Right::Delete),
383 "mem_free".to_string(),
384 )
385 .map_err(map_policy_err)
386 }
387 HostFunction::CapRevoke => {
388 let cap_id: u64 =
389 self.args
390 .first()
391 .copied()
392 .ok_or(StepError::HostCallPreconditionFailed(
393 HostCallPrecondition::NoCapabilityId,
394 ))?;
395
396 Action::new(
397 self.caller,
398 cap_id.into(),
399 Rights::singleton(Right::Revoke),
400 "cap_revoke".to_string(),
401 )
402 .map_err(map_policy_err)
403 }
404 HostFunction::Declassify => Action::new(
405 self.caller,
406 0,
407 Rights::singleton(Right::Declassify),
408 "declassify".to_string(),
409 )
410 .map_err(map_policy_err),
411 HostFunction::CapSend => Action::new(
412 self.caller,
413 0,
414 Rights::singleton(Right::Send),
415 "cap_send".to_string(),
416 )
417 .map_err(map_policy_err),
418 _ => Err(StepError::HostCallPreconditionFailed(
419 HostCallPrecondition::NotImplemented {
420 operation: "thread operations",
421 },
422 )),
423 }
424 }
425
426 pub fn check_preconditions(&self, state: &State) -> Result<(), StepError> {
435 let plugin = state
437 .get_plugin(self.caller)
438 .ok_or(StepError::PluginNotFound(self.caller))?;
439
440 if let Some(err) = self.find_oob_read(plugin) {
442 return Err(err);
443 }
444
445 if let Some(err) = self.find_oob_write(plugin) {
447 return Err(err);
448 }
449
450 Ok(())
451 }
452
453 fn find_oob_read(&self, plugin: &crate::state::PluginState) -> Option<StepError> {
455 self.reads.iter().find_map(|&(addr, size)| {
456 if plugin.memory().addr_in_bounds(addr, size) {
457 None
458 } else {
459 Some(StepError::HostCallPreconditionFailed(
460 HostCallPrecondition::ReadOutOfBounds { addr, size },
461 ))
462 }
463 })
464 }
465
466 fn find_oob_write(&self, plugin: &crate::state::PluginState) -> Option<StepError> {
468 self.writes.iter().find_map(|&(addr, size)| {
469 if plugin.memory().addr_in_bounds(addr, size) {
470 None
471 } else {
472 Some(StepError::HostCallPreconditionFailed(
473 HostCallPrecondition::WriteOutOfBounds { addr, size },
474 ))
475 }
476 })
477 }
478}
479
480#[derive(Debug, Clone, Default)]
484#[must_use = "host call results must be consumed"]
485pub struct HostResult {
486 pub(crate) new_caps: Vec<Capability>,
490
491 pub(crate) new_msgs: Vec<Message>,
495}
496
497impl HostResult {
498 pub fn empty() -> Self {
500 HostResult {
501 new_caps: Vec::new(),
502 new_msgs: Vec::new(),
503 }
504 }
505
506 pub fn with_caps(caps: Vec<Capability>) -> Self {
508 HostResult {
509 new_caps: caps,
510 new_msgs: Vec::new(),
511 }
512 }
513
514 pub fn with_msgs(msgs: Vec<Message>) -> Self {
516 HostResult {
517 new_caps: Vec::new(),
518 new_msgs: msgs,
519 }
520 }
521
522 #[inline]
524 pub fn new_caps(&self) -> &[Capability] {
525 &self.new_caps
526 }
527
528 #[inline]
530 pub fn new_msgs(&self) -> &[Message] {
531 &self.new_msgs
532 }
533}
534
535fn execute_cap_call(
549 state: &State,
550 hc: &HostCall,
551 _result: &HostResult,
552) -> Result<State, StepError> {
553 let plugin = state
555 .get_plugin(hc.caller)
556 .ok_or(StepError::PluginNotFound(hc.caller))?;
557
558 let has_valid_cap = plugin
560 .held_caps_ref()
561 .iter()
562 .any(|&id| state.kernel().revocation().is_valid(id));
563
564 if !has_valid_cap {
565 return Err(StepError::HostCallPreconditionFailed(
566 HostCallPrecondition::NoValidCapability,
567 ));
568 }
569
570 Ok(state.clone())
572}
573
574fn execute_cap_send(state: &State, hc: &HostCall, result: &HostResult) -> Result<State, StepError> {
584 if result.new_msgs.is_empty() {
586 return Err(StepError::HostCallPreconditionFailed(
587 HostCallPrecondition::NoMessage,
588 ));
589 }
590
591 for msg in &result.new_msgs {
593 if msg.src() != hc.caller {
594 return Err(StepError::HostCallPreconditionFailed(
595 HostCallPrecondition::SourceMismatch,
596 ));
597 }
598
599 let dst_actor = state
601 .get_actor(msg.dst())
602 .ok_or(StepError::ActorNotFound(msg.dst()))?;
603
604 if dst_actor.mailbox_len() >= dst_actor.capacity() {
605 return Err(StepError::HostCallPreconditionFailed(
606 HostCallPrecondition::MailboxFull,
607 ));
608 }
609 }
610
611 Ok(state.clone())
613}
614
615fn execute_kernel_alloc(state: &State, hc: &HostCall) -> Result<State, StepError> {
620 let size = hc.args.first().copied().unwrap_or(0);
622
623 let plugin = state
625 .get_plugin(hc.caller)
626 .ok_or(StepError::PluginNotFound(hc.caller))?;
627
628 if !plugin.can_alloc_memory(size) {
629 return Err(StepError::QuotaExceeded(
630 0, size,
632 plugin.memory_used(),
633 plugin.memory_quota(),
634 ));
635 }
636
637 let mut new_state = state.apply_alloc(hc.caller, size);
639
640 if let Some(p) = new_state.get_plugin_mut(hc.caller) {
642 p.memory_used = p.memory_used.saturating_add(size);
643 }
644
645 Ok(new_state)
646}
647
648fn execute_mem_free(state: &State, hc: &HostCall) -> Result<State, StepError> {
652 let addr: MemAddr = hc
654 .args
655 .first()
656 .copied()
657 .ok_or(StepError::HostCallPreconditionFailed(
658 HostCallPrecondition::NoAddress,
659 ))?;
660
661 if state.ghost().is_freed(addr) {
663 return Err(StepError::AddressAlreadyFreed(addr));
664 }
665
666 if let Some(&(cap_id, _)) = state
668 .kernel()
669 .revocation()
670 .iter()
671 .find(|(_, cap)| cap.is_valid() && cap.target() == u128::from(addr))
672 {
673 return Err(StepError::CapabilityTargetsAddress(cap_id, addr));
674 }
675
676 state.apply_free(addr).map_err(|e| {
677 StepError::HostCallPreconditionFailed(HostCallPrecondition::FreeFailed { source: e })
678 })
679}
680
681fn execute_cap_revoke(state: &State, hc: &HostCall) -> Result<State, StepError> {
685 let cap_id: crate::types::CapId =
687 hc.args
688 .first()
689 .copied()
690 .map(u128::from)
691 .ok_or(StepError::HostCallPreconditionFailed(
692 HostCallPrecondition::NoCapabilityId,
693 ))?;
694
695 if state.kernel().revocation().get(cap_id).is_none() {
697 return Err(StepError::CapabilityNotFound(cap_id));
698 }
699
700 Ok(state.apply_cap_revoke(cap_id))
702}
703
704fn execute_declassify(state: &State, hc: &HostCall) -> Result<State, StepError> {
708 let new_level_val = hc.args.first().copied().unwrap_or(0);
710 let new_level = match new_level_val {
711 0 => SecurityLevel::Public,
712 1 => SecurityLevel::Internal,
713 2 => SecurityLevel::Confidential,
714 3 => SecurityLevel::Secret,
715 other => {
716 return Err(StepError::HostCallPreconditionFailed(
717 HostCallPrecondition::InvalidSecurityLevel { value: other },
718 ));
719 }
720 };
721
722 let plugin = state
724 .get_plugin(hc.caller)
725 .ok_or(StepError::PluginNotFound(hc.caller))?;
726
727 if new_level > plugin.level() {
728 return Err(StepError::HostCallPreconditionFailed(
729 HostCallPrecondition::CannotClassifyUp,
730 ));
731 }
732
733 let mut new_state = state.clone();
735 if let Some(plugin) = new_state.get_plugin_mut(hc.caller) {
736 plugin.level = new_level;
737 }
738
739 Ok(new_state)
740}
741
742fn execute_cap_call_mut(
746 state: &mut State,
747 hc: &HostCall,
748 _result: &HostResult,
749) -> Result<(), StepError> {
750 let plugin = state
751 .get_plugin(hc.caller)
752 .ok_or(StepError::PluginNotFound(hc.caller))?;
753
754 let has_valid_cap = plugin
755 .held_caps_ref()
756 .iter()
757 .any(|&id| state.kernel().revocation().is_valid(id));
758
759 if !has_valid_cap {
760 return Err(StepError::HostCallPreconditionFailed(
761 HostCallPrecondition::NoValidCapability,
762 ));
763 }
764
765 Ok(())
767}
768
769fn execute_cap_send_mut(
771 state: &mut State,
772 hc: &HostCall,
773 result: &HostResult,
774) -> Result<(), StepError> {
775 if result.new_msgs.is_empty() {
776 return Err(StepError::HostCallPreconditionFailed(
777 HostCallPrecondition::NoMessage,
778 ));
779 }
780
781 for msg in &result.new_msgs {
782 if msg.src() != hc.caller {
783 return Err(StepError::HostCallPreconditionFailed(
784 HostCallPrecondition::SourceMismatch,
785 ));
786 }
787
788 let dst_actor = state
789 .get_actor(msg.dst())
790 .ok_or(StepError::ActorNotFound(msg.dst()))?;
791
792 if dst_actor.mailbox_len() >= dst_actor.capacity() {
793 return Err(StepError::HostCallPreconditionFailed(
794 HostCallPrecondition::MailboxFull,
795 ));
796 }
797 }
798
799 Ok(())
801}
802
803fn execute_kernel_alloc_mut(state: &mut State, hc: &HostCall) -> Result<(), StepError> {
805 let size = hc.args.first().copied().unwrap_or(0);
806
807 let plugin = state
808 .get_plugin(hc.caller)
809 .ok_or(StepError::PluginNotFound(hc.caller))?;
810
811 if !plugin.can_alloc_memory(size) {
812 return Err(StepError::QuotaExceeded(
813 0, size,
815 plugin.memory_used(),
816 plugin.memory_quota(),
817 ));
818 }
819
820 let _addr = state.apply_alloc_mut(hc.caller, size);
822
823 if let Some(p) = state.get_plugin_mut(hc.caller) {
825 p.memory_used = p.memory_used.saturating_add(size);
826 }
827
828 Ok(())
829}
830
831fn execute_mem_free_mut(state: &mut State, hc: &HostCall) -> Result<(), StepError> {
833 let addr: MemAddr = hc
834 .args
835 .first()
836 .copied()
837 .ok_or(StepError::HostCallPreconditionFailed(
838 HostCallPrecondition::NoAddress,
839 ))?;
840
841 if state.ghost().is_freed(addr) {
842 return Err(StepError::AddressAlreadyFreed(addr));
843 }
844
845 if let Some(&(cap_id, _)) = state
847 .kernel()
848 .revocation()
849 .iter()
850 .find(|(_, cap)| cap.is_valid() && cap.target() == u128::from(addr))
851 {
852 return Err(StepError::CapabilityTargetsAddress(cap_id, addr));
853 }
854
855 state.apply_free_mut(addr).map_err(|e| {
856 StepError::HostCallPreconditionFailed(HostCallPrecondition::FreeFailed { source: e })
857 })
858}
859
860fn execute_cap_revoke_mut(state: &mut State, hc: &HostCall) -> Result<(), StepError> {
862 let cap_id: crate::types::CapId =
864 hc.args
865 .first()
866 .copied()
867 .map(u128::from)
868 .ok_or(StepError::HostCallPreconditionFailed(
869 HostCallPrecondition::NoCapabilityId,
870 ))?;
871
872 if state.kernel().revocation().get(cap_id).is_none() {
873 return Err(StepError::CapabilityNotFound(cap_id));
874 }
875
876 state
877 .apply_cap_revoke_mut(cap_id)
878 .map_err(|_| StepError::CapabilityNotFound(cap_id))?;
879
880 Ok(())
881}
882
883fn execute_declassify_mut(state: &mut State, hc: &HostCall) -> Result<(), StepError> {
885 let new_level_val = hc.args.first().copied().unwrap_or(0);
886 let new_level = match new_level_val {
887 0 => SecurityLevel::Public,
888 1 => SecurityLevel::Internal,
889 2 => SecurityLevel::Confidential,
890 3 => SecurityLevel::Secret,
891 other => {
892 return Err(StepError::HostCallPreconditionFailed(
893 HostCallPrecondition::InvalidSecurityLevel { value: other },
894 ));
895 }
896 };
897
898 let plugin = state
899 .get_plugin(hc.caller)
900 .ok_or(StepError::PluginNotFound(hc.caller))?;
901
902 if new_level > plugin.level() {
903 return Err(StepError::HostCallPreconditionFailed(
904 HostCallPrecondition::CannotClassifyUp,
905 ));
906 }
907
908 if let Some(plugin) = state.get_plugin_mut(hc.caller) {
909 plugin.level = new_level;
910 }
911
912 Ok(())
913}
914
915#[cfg(test)]
916mod tests {
917 use super::*;
918 use crate::state::PluginState;
919
920 #[test]
921 fn test_host_function_is_effectful() {
922 assert!(HostFunction::CapCall.is_effectful());
924 assert!(HostFunction::KernelAlloc.is_effectful());
925 assert!(HostFunction::Declassify.is_effectful());
926 }
927
928 #[test]
929 fn test_host_function_is_declassify() {
930 assert!(!HostFunction::CapCall.is_declassify());
931 assert!(!HostFunction::KernelAlloc.is_declassify());
932 assert!(HostFunction::Declassify.is_declassify());
933 }
934
935 #[test]
936 fn test_host_function_is_cap_operation() {
937 assert!(HostFunction::CapCall.is_cap_operation());
938 assert!(HostFunction::CapRevoke.is_cap_operation());
939 assert!(!HostFunction::CapSend.is_cap_operation());
940 assert!(!HostFunction::KernelAlloc.is_cap_operation());
941 }
942
943 #[test]
944 fn test_host_function_is_mem_operation() {
945 assert!(HostFunction::KernelAlloc.is_mem_operation());
946 assert!(HostFunction::MemFree.is_mem_operation());
947 assert!(!HostFunction::CapCall.is_mem_operation());
948 }
949
950 #[test]
951 fn test_host_call_check_preconditions_no_plugin() {
952 let state = State::empty();
953 let hc = HostCall::new(1, HostFunction::CapCall, vec![], vec![], vec![]);
954
955 let result = hc.check_preconditions(&state);
956 assert!(matches!(result, Err(StepError::PluginNotFound(1))));
957 }
958
959 #[test]
960 fn test_host_call_check_preconditions_out_of_bounds() {
961 let mut state = State::empty();
962 let _ = state.insert_plugin(1, PluginState::empty(SecurityLevel::Public, 100));
963
964 let hc = HostCall::new(1, HostFunction::CapCall, vec![], vec![(200, 50)], vec![]);
966
967 let result = hc.check_preconditions(&state);
968 assert!(matches!(
969 result,
970 Err(StepError::HostCallPreconditionFailed(_))
971 ));
972 }
973
974 #[test]
975 fn test_execute_kernel_alloc() {
976 let mut state = State::empty();
977 let _ = state.insert_plugin(1, PluginState::empty(SecurityLevel::Public, 1024));
978
979 let hc = HostCall::new(1, HostFunction::KernelAlloc, vec![256], vec![], vec![]);
980 let result = HostResult::empty();
981
982 let new_state = HostFunction::KernelAlloc
983 .execute(&state, &hc, &result)
984 .expect("kernel_alloc should succeed");
985
986 assert!(new_state.ghost().resource_count() > state.ghost().resource_count());
988 }
989
990 #[test]
991 fn test_execute_declassify() {
992 let mut state = State::empty();
993 let _ = state.insert_plugin(1, PluginState::empty(SecurityLevel::Secret, 0));
994
995 let hc = HostCall::new(1, HostFunction::Declassify, vec![0], vec![], vec![]);
997 let result = HostResult::empty();
998
999 let new_state = HostFunction::Declassify
1000 .execute(&state, &hc, &result)
1001 .expect("declassify should succeed");
1002
1003 assert_eq!(new_state.plugin_level(1), Some(SecurityLevel::Public));
1004 }
1005
1006 #[test]
1007 fn test_execute_declassify_up_fails() {
1008 let mut state = State::empty();
1009 let _ = state.insert_plugin(1, PluginState::empty(SecurityLevel::Public, 0));
1010
1011 let hc = HostCall::new(1, HostFunction::Declassify, vec![3], vec![], vec![]);
1013 let result = HostResult::empty();
1014
1015 let err = HostFunction::Declassify
1016 .execute(&state, &hc, &result)
1017 .expect_err("classify up should fail");
1018
1019 assert!(matches!(err, StepError::HostCallPreconditionFailed(_)));
1020 }
1021
1022 #[test]
1023 fn test_execute_declassify_invalid_level_rejected() {
1024 let mut state = State::empty();
1026 let _ = state.insert_plugin(1, PluginState::empty(SecurityLevel::Secret, 0));
1027
1028 for invalid_level in [4, 5, 100, u64::MAX] {
1029 let hc = HostCall::new(
1030 1,
1031 HostFunction::Declassify,
1032 vec![invalid_level],
1033 vec![],
1034 vec![],
1035 );
1036 let result = HostResult::empty();
1037
1038 let err = HostFunction::Declassify
1039 .execute(&state, &hc, &result)
1040 .expect_err(&format!("level {invalid_level} should be rejected"));
1041
1042 assert!(
1043 matches!(
1044 err,
1045 StepError::HostCallPreconditionFailed(
1046 HostCallPrecondition::InvalidSecurityLevel { .. }
1047 )
1048 ),
1049 "level {invalid_level}: expected InvalidSecurityLevel error, got {err:?}"
1050 );
1051 }
1052 }
1053
1054 #[test]
1055 fn test_execute_declassify_all_valid_levels() {
1056 let mut state = State::empty();
1058 let _ = state.insert_plugin(1, PluginState::empty(SecurityLevel::Secret, 0));
1059
1060 let expected = [
1061 (0, SecurityLevel::Public),
1062 (1, SecurityLevel::Internal),
1063 (2, SecurityLevel::Confidential),
1064 (3, SecurityLevel::Secret),
1065 ];
1066
1067 for (level_val, expected_level) in expected {
1068 let hc = HostCall::new(1, HostFunction::Declassify, vec![level_val], vec![], vec![]);
1069 let result = HostResult::empty();
1070
1071 let new_state = HostFunction::Declassify
1072 .execute(&state, &hc, &result)
1073 .unwrap_or_else(|e| panic!("level {level_val} should succeed: {e:?}"));
1074
1075 assert_eq!(new_state.plugin_level(1), Some(expected_level));
1076 }
1077 }
1078}