Skip to main content

ic_memory/runtime/
policy.rs

1use super::{RuntimeBootstrapError, RuntimePolicyError};
2use crate::{
3    AllocationPolicy, AllocationSlotDescriptor, PolicyIdentity, PolicyIdentityError,
4    RuntimeBootstrapPolicy, StableKey,
5    registry::{RuntimeDeclarationAuthority, SealedDeclarationSnapshot},
6    slot::{IC_MEMORY_AUTHORITY_OWNER, MemoryManagerRangeAuthorityError},
7};
8use std::convert::Infallible;
9
10pub(super) fn runtime_bootstrap_error_from_bootstrap<P>(
11    err: crate::BootstrapError<RuntimePolicyError<P>>,
12) -> RuntimeBootstrapError<P> {
13    match err {
14        crate::BootstrapError::Ledger(err) => RuntimeBootstrapError::LedgerCommit(err),
15        crate::BootstrapError::Validation(err) => RuntimeBootstrapError::Validation(err),
16        crate::BootstrapError::Staging(err) => RuntimeBootstrapError::Staging(err),
17    }
18}
19
20pub(super) struct RuntimeMemoryManagerPolicy<'a, P> {
21    pub(super) declarations: &'a SealedDeclarationSnapshot,
22    pub(super) custom_policy: &'a P,
23}
24
25impl<P: AllocationPolicy> AllocationPolicy for RuntimeMemoryManagerPolicy<'_, P> {
26    type Error = RuntimePolicyError<P::Error>;
27
28    fn validate_key(&self, key: &StableKey) -> Result<(), Self::Error> {
29        let authority = self.declaration_authority(key)?;
30        if matches!(authority, RuntimeDeclarationAuthority::Internal) {
31            return Ok(());
32        }
33        self.custom_policy
34            .validate_key(key)
35            .map_err(RuntimePolicyError::Custom)
36    }
37
38    fn validate_slot(
39        &self,
40        key: &StableKey,
41        slot: &AllocationSlotDescriptor,
42    ) -> Result<(), Self::Error> {
43        let authority = self.declaration_authority(key)?;
44        self.validate_runtime_range(authority, slot)?;
45        if matches!(authority, RuntimeDeclarationAuthority::Internal) {
46            return Ok(());
47        }
48        self.custom_policy
49            .validate_slot(key, slot)
50            .map_err(RuntimePolicyError::Custom)
51    }
52
53    fn validate_reserved_slot(
54        &self,
55        key: &StableKey,
56        slot: &AllocationSlotDescriptor,
57    ) -> Result<(), Self::Error> {
58        let authority = self.declaration_authority(key)?;
59        self.validate_runtime_range(authority, slot)?;
60        if matches!(authority, RuntimeDeclarationAuthority::Internal) {
61            return Ok(());
62        }
63        self.custom_policy
64            .validate_reserved_slot(key, slot)
65            .map_err(RuntimePolicyError::Custom)
66    }
67}
68
69impl<P: AllocationPolicy> RuntimeMemoryManagerPolicy<'_, P> {
70    fn declaration_authority(
71        &self,
72        key: &StableKey,
73    ) -> Result<&RuntimeDeclarationAuthority, RuntimePolicyError<P::Error>> {
74        self.declarations
75            .declaration_authority()
76            .get(key.as_str())
77            .ok_or_else(|| RuntimePolicyError::MissingDeclarationMetadata(key.as_str().to_string()))
78    }
79
80    fn validate_runtime_range(
81        &self,
82        authority: &RuntimeDeclarationAuthority,
83        slot: &AllocationSlotDescriptor,
84    ) -> Result<(), RuntimePolicyError<P::Error>> {
85        let authority = match authority {
86            RuntimeDeclarationAuthority::Internal => {
87                return self
88                    .declarations
89                    .range_authority()
90                    .validate_slot_authority(slot, IC_MEMORY_AUTHORITY_OWNER)
91                    .map(|_| ())
92                    .map_err(RuntimePolicyError::Range);
93            }
94            RuntimeDeclarationAuthority::External(authority) => authority,
95        };
96        if self.declarations.user_ranges_registered() {
97            self.declarations
98                .range_authority()
99                .validate_slot_authority(slot, authority)?;
100            return Ok(());
101        }
102
103        let id = slot
104            .memory_manager_id()
105            .map_err(MemoryManagerRangeAuthorityError::Slot)?;
106        if self
107            .declarations
108            .range_authority()
109            .authority_for_id(id)?
110            .is_some()
111        {
112            self.declarations
113                .range_authority()
114                .validate_slot_authority(slot, authority)?;
115        }
116        Ok(())
117    }
118}
119
120///
121/// GenericRangePolicy
122///
123/// Built-in bootstrap policy used by the no-argument default-runtime helpers.
124/// The runtime enforces registered range ownership and internal reservations;
125/// this policy adds no application-specific restrictions. Passing it directly
126/// to allocation validation outside the runtime does not enforce those ranges.
127///
128/// Use with configured bootstrap when the host does not require a custom
129/// policy. It retains the built-in policy identity and does not authorize
130/// replacing a different policy already bound to the runtime.
131///
132pub struct GenericRangePolicy;
133
134impl AllocationPolicy for GenericRangePolicy {
135    type Error = Infallible;
136
137    fn validate_key(&self, _key: &StableKey) -> Result<(), Self::Error> {
138        Ok(())
139    }
140
141    fn validate_slot(
142        &self,
143        _key: &StableKey,
144        _slot: &AllocationSlotDescriptor,
145    ) -> Result<(), Self::Error> {
146        Ok(())
147    }
148
149    fn validate_reserved_slot(
150        &self,
151        _key: &StableKey,
152        _slot: &AllocationSlotDescriptor,
153    ) -> Result<(), Self::Error> {
154        Ok(())
155    }
156}
157
158impl RuntimeBootstrapPolicy for GenericRangePolicy {
159    fn runtime_bootstrap_identity(&self) -> Result<PolicyIdentity, PolicyIdentityError> {
160        PolicyIdentity::new("ic-memory.noop-policy", 1)
161    }
162}