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        if crate::is_ic_memory_stable_key(key.as_str()) {
34            return Err(RuntimePolicyError::ReservedStableKeyAuthority {
35                stable_key: key.as_str().to_string(),
36                expected_authority: IC_MEMORY_AUTHORITY_OWNER,
37            });
38        }
39        self.custom_policy
40            .validate_key(key)
41            .map_err(RuntimePolicyError::Custom)
42    }
43
44    fn validate_slot(
45        &self,
46        key: &StableKey,
47        slot: &AllocationSlotDescriptor,
48    ) -> Result<(), Self::Error> {
49        self.validate_runtime_range(key, slot)?;
50        if matches!(
51            self.declaration_authority(key)?,
52            RuntimeDeclarationAuthority::Internal
53        ) {
54            return Ok(());
55        }
56        self.custom_policy
57            .validate_slot(key, slot)
58            .map_err(RuntimePolicyError::Custom)
59    }
60
61    fn validate_reserved_slot(
62        &self,
63        key: &StableKey,
64        slot: &AllocationSlotDescriptor,
65    ) -> Result<(), Self::Error> {
66        self.validate_runtime_range(key, slot)?;
67        if matches!(
68            self.declaration_authority(key)?,
69            RuntimeDeclarationAuthority::Internal
70        ) {
71            return Ok(());
72        }
73        self.custom_policy
74            .validate_reserved_slot(key, slot)
75            .map_err(RuntimePolicyError::Custom)
76    }
77}
78
79impl<P: AllocationPolicy> RuntimeMemoryManagerPolicy<'_, P> {
80    fn declaration_authority(
81        &self,
82        key: &StableKey,
83    ) -> Result<&RuntimeDeclarationAuthority, RuntimePolicyError<P::Error>> {
84        self.declarations
85            .declaration_authority()
86            .get(key.as_str())
87            .ok_or_else(|| RuntimePolicyError::MissingDeclarationMetadata(key.as_str().to_string()))
88    }
89
90    fn validate_runtime_range(
91        &self,
92        key: &StableKey,
93        slot: &AllocationSlotDescriptor,
94    ) -> Result<(), RuntimePolicyError<P::Error>> {
95        let authority = self.declaration_authority(key)?;
96        if matches!(authority, RuntimeDeclarationAuthority::Internal) {
97            self.declarations
98                .range_authority()
99                .validate_slot_authority(slot, IC_MEMORY_AUTHORITY_OWNER)?;
100            return Ok(());
101        }
102
103        let RuntimeDeclarationAuthority::External(authority) = authority else {
104            return Err(RuntimePolicyError::MissingDeclarationMetadata(
105                key.as_str().to_string(),
106            ));
107        };
108        if self.declarations.user_ranges_registered() {
109            self.declarations
110                .range_authority()
111                .validate_slot_authority(slot, authority)?;
112            return Ok(());
113        }
114
115        let id = slot
116            .memory_manager_id()
117            .map_err(MemoryManagerRangeAuthorityError::Slot)?;
118        if self
119            .declarations
120            .range_authority()
121            .authority_for_id(id)?
122            .is_some()
123        {
124            self.declarations
125                .range_authority()
126                .validate_slot_authority(slot, authority)?;
127        }
128        Ok(())
129    }
130}
131
132///
133/// GenericRangePolicy
134///
135/// Built-in bootstrap policy used by the no-argument default-runtime helpers.
136/// The runtime enforces registered range ownership and internal reservations;
137/// this policy adds no application-specific restrictions. Passing it directly
138/// to allocation validation outside the runtime does not enforce those ranges.
139///
140/// Use with configured bootstrap when the host does not require a custom
141/// policy. It retains the built-in policy identity and does not authorize
142/// replacing a different policy already bound to the runtime.
143///
144pub struct GenericRangePolicy;
145
146impl AllocationPolicy for GenericRangePolicy {
147    type Error = Infallible;
148
149    fn validate_key(&self, _key: &StableKey) -> Result<(), Self::Error> {
150        Ok(())
151    }
152
153    fn validate_slot(
154        &self,
155        _key: &StableKey,
156        _slot: &AllocationSlotDescriptor,
157    ) -> Result<(), Self::Error> {
158        Ok(())
159    }
160
161    fn validate_reserved_slot(
162        &self,
163        _key: &StableKey,
164        _slot: &AllocationSlotDescriptor,
165    ) -> Result<(), Self::Error> {
166        Ok(())
167    }
168}
169
170impl RuntimeBootstrapPolicy for GenericRangePolicy {
171    fn runtime_bootstrap_identity(&self) -> Result<PolicyIdentity, PolicyIdentityError> {
172        PolicyIdentity::new("ic-memory.noop-policy", 1)
173    }
174}