Skip to main content

ic_memory/runtime/
policy.rs

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