1use super::super::manager::{self, RawStableMemoryState};
2use super::super::registry::{
3 MemoryRange, MemoryRangeEntry, MemoryRangeSnapshot, MemoryRegistry, MemoryRegistryEntry,
4 MemoryRegistryError, PendingRegistration, drain_pending_ranges, drain_pending_registrations,
5};
6use super::super::{ledger, policy::CanicMemoryManagerPolicy};
7use ic_memory::{
8 AllocationDeclaration, AllocationPolicy, AllocationSlotDescriptor, AllocationValidationError,
9 DeclarationSnapshot, DeclarationSnapshotError, MemoryManagerSlotError, SchemaMetadata,
10 StableKey, ValidatedAllocations, validate_allocations,
11};
12#[cfg(test)]
13use std::cell::Cell;
14#[cfg(not(test))]
15use std::sync::atomic::{AtomicBool, Ordering};
16use std::{cell::RefCell, collections::BTreeMap};
17
18#[cfg(not(test))]
19static MEMORY_REGISTRY_INITIALIZED: AtomicBool = AtomicBool::new(false);
20
21#[cfg(test)]
22thread_local! {
23 static MEMORY_REGISTRY_INITIALIZED: Cell<bool> = const { Cell::new(false) };
24}
25
26thread_local! {
27 static VALIDATED_ALLOCATIONS: RefCell<Option<ValidatedAllocations>> = const {
28 RefCell::new(None)
29 };
30}
31
32#[derive(Debug)]
41pub struct MemoryRegistryInitSummary {
42 pub ranges: Vec<(String, MemoryRange)>,
44 pub entries: Vec<(u8, MemoryRegistryEntry)>,
46}
47
48pub struct MemoryRegistryRuntime;
62
63impl MemoryRegistryRuntime {
64 pub fn init(
72 initial_range: Option<(&str, u8, u8)>,
73 ) -> Result<MemoryRegistryInitSummary, MemoryRegistryError> {
74 let raw_state = manager::classify_raw_stable_memory();
75 validate_raw_stable_memory_state(raw_state)?;
76 ledger::validate_bootstrap_state_before_cell_init(raw_state)?;
77
78 let mut ranges = drain_pending_ranges();
80 ranges.sort_by_key(|(_, start, _)| *start);
81
82 let mut regs = drain_pending_registrations();
84 regs.sort_by_key(|registration| registration.id);
85 let has_runtime_declarations = !regs.is_empty();
86 let declaration_snapshot = validate_current_registration_snapshot(®s)?;
87
88 MemoryRegistry::reserve_internal_layout_ledger()?;
89 let validated_allocations =
90 validate_pending_ledger_claims(initial_range, &ranges, ®s, declaration_snapshot)?;
91
92 if let Some((crate_name, start, end)) = initial_range {
94 MemoryRegistry::reserve_range(crate_name, start, end)?;
95 }
96
97 for (crate_name, start, end) in ranges {
98 MemoryRegistry::reserve_range(&crate_name, start, end)?;
99 }
100
101 for registration in regs {
102 MemoryRegistry::register_with_key_metadata(
103 registration.id,
104 ®istration.crate_name,
105 ®istration.label,
106 ®istration.stable_key,
107 registration.schema_version,
108 registration.schema_fingerprint.as_deref(),
109 )?;
110 }
111
112 let summary = MemoryRegistryInitSummary {
113 ranges: MemoryRegistry::export_ranges(),
114 entries: MemoryRegistry::export(),
115 };
116 if !Self::is_initialized() || has_runtime_declarations {
117 set_validated_allocations(Some(validated_allocations));
118 }
119 set_initialized(true);
120
121 Ok(summary)
122 }
123
124 #[must_use]
126 pub fn is_initialized() -> bool {
127 initialized()
128 }
129
130 #[must_use]
132 pub fn snapshot_entries() -> Vec<(u8, MemoryRegistryEntry)> {
133 MemoryRegistry::export()
134 }
135
136 #[must_use]
138 pub fn snapshot_ranges() -> Vec<(String, MemoryRange)> {
139 MemoryRegistry::export_ranges()
140 }
141
142 #[must_use]
144 pub fn snapshot_range_entries() -> Vec<MemoryRangeEntry> {
145 MemoryRegistry::export_range_entries()
146 }
147
148 #[must_use]
150 pub fn snapshot_ids_by_range() -> Vec<MemoryRangeSnapshot> {
151 MemoryRegistry::export_ids_by_range()
152 }
153
154 #[must_use]
156 pub fn get(id: u8) -> Option<MemoryRegistryEntry> {
157 MemoryRegistry::get(id)
158 }
159
160 pub fn validated_allocations() -> Result<ValidatedAllocations, MemoryRegistryError> {
162 if !Self::is_initialized() {
163 return Err(MemoryRegistryError::RegistryNotBootstrapped);
164 }
165
166 VALIDATED_ALLOCATIONS.with_borrow(|validated| {
167 validated
168 .clone()
169 .ok_or(MemoryRegistryError::RegistryNotBootstrapped)
170 })
171 }
172
173 pub fn commit_pending_if_initialized() -> Result<(), MemoryRegistryError> {
179 if !Self::is_initialized() || super::is_eager_tls_initializing() {
180 return Ok(());
181 }
182
183 let ranges = drain_pending_ranges();
184 let regs = drain_pending_registrations();
185
186 if ranges.is_empty() && regs.is_empty() {
187 return Ok(());
188 }
189
190 Err(MemoryRegistryError::RegistrationAfterBootstrap {
191 ranges: ranges.len(),
192 registrations: regs.len(),
193 })
194 }
195}
196
197const fn validate_raw_stable_memory_state(
198 raw_state: RawStableMemoryState,
199) -> Result<(), MemoryRegistryError> {
200 match raw_state {
201 RawStableMemoryState::Empty | RawStableMemoryState::MemoryManager => Ok(()),
202 RawStableMemoryState::ForeignOrCorrupt => Err(MemoryRegistryError::LedgerCorrupt {
203 reason: "foreign or corrupt raw stable memory state",
204 }),
205 }
206}
207
208fn validate_current_registration_snapshot(
209 regs: &[PendingRegistration],
210) -> Result<DeclarationSnapshot, MemoryRegistryError> {
211 let declarations = regs
212 .iter()
213 .map(allocation_declaration_from_pending)
214 .collect::<Result<Vec<_>, _>>()?;
215
216 DeclarationSnapshot::new(declarations).map_err(memory_registry_error_from_snapshot_error)
217}
218
219fn allocation_declaration_from_pending(
220 registration: &PendingRegistration,
221) -> Result<AllocationDeclaration, MemoryRegistryError> {
222 let slot = AllocationSlotDescriptor::memory_manager_checked(registration.id)
223 .map_err(memory_registry_error_from_slot_error)?;
224 let schema = SchemaMetadata::new(
225 registration.schema_version,
226 registration.schema_fingerprint.clone(),
227 )
228 .map_err(|err| MemoryRegistryError::InvalidSchemaMetadata {
229 stable_key: registration.stable_key.clone(),
230 reason: super::super::registry::schema_metadata_reason(err),
231 })?;
232
233 AllocationDeclaration::new(
234 ®istration.stable_key,
235 slot,
236 Some(registration.label.clone()),
237 schema,
238 )
239 .map_err(memory_registry_error_from_snapshot_error)
240}
241
242fn memory_registry_error_from_snapshot_error(err: DeclarationSnapshotError) -> MemoryRegistryError {
243 match err {
244 DeclarationSnapshotError::Key(err) => MemoryRegistryError::InvalidStableKey {
245 stable_key: err.stable_key,
246 reason: err.reason,
247 },
248 DeclarationSnapshotError::SchemaMetadata(err) => {
249 MemoryRegistryError::InvalidSchemaMetadata {
250 stable_key: "<unknown>".to_string(),
251 reason: super::super::registry::schema_metadata_reason(err),
252 }
253 }
254 DeclarationSnapshotError::DuplicateStableKey(key) => {
255 MemoryRegistryError::DuplicateStableKey(key.into_string())
256 }
257 DeclarationSnapshotError::DuplicateSlot(slot) => match slot.memory_manager_id() {
258 Ok(id) => MemoryRegistryError::DuplicateId(id),
259 Err(err) => memory_registry_error_from_slot_error(err),
260 },
261 }
262}
263
264fn memory_registry_error_from_slot_error(err: MemoryManagerSlotError) -> MemoryRegistryError {
265 match err {
266 MemoryManagerSlotError::InvalidMemoryManagerId { id } => {
267 MemoryRegistryError::ReservedInternalId { id }
268 }
269 MemoryManagerSlotError::UnsupportedSlot
270 | MemoryManagerSlotError::UnsupportedSubstrate { .. }
271 | MemoryManagerSlotError::UnsupportedDescriptorVersion { .. } => {
272 MemoryRegistryError::LedgerCorrupt {
273 reason: "unsupported MemoryManager allocation slot descriptor",
274 }
275 }
276 }
277}
278
279fn validate_pending_ledger_claims(
280 initial_range: Option<(&str, u8, u8)>,
281 ranges: &[(String, u8, u8)],
282 regs: &[PendingRegistration],
283 declaration_snapshot: DeclarationSnapshot,
284) -> Result<ValidatedAllocations, MemoryRegistryError> {
285 if let Some((owner, start, end)) = initial_range {
286 ledger::validate_range(owner, MemoryRange { start, end })?;
287 }
288
289 for (owner, start, end) in ranges {
290 ledger::validate_range(
291 owner,
292 MemoryRange {
293 start: *start,
294 end: *end,
295 },
296 )?;
297 }
298
299 for registration in regs {
300 ledger::validate_entry(
301 registration.id,
302 ®istration.crate_name,
303 ®istration.label,
304 ®istration.stable_key,
305 )?;
306 }
307
308 let historical_ledger = ledger::try_allocation_ledger_snapshot()?;
309 let policy = RuntimeDeclarationPolicy::from_registrations(regs);
310 validate_allocations(&historical_ledger, declaration_snapshot, &policy)
311 .map_err(|err| memory_registry_error_from_allocation_validation(err, regs))
312}
313
314struct RuntimeDeclarationPolicy {
320 declaring_crates: BTreeMap<String, String>,
321}
322
323impl RuntimeDeclarationPolicy {
324 fn from_registrations(regs: &[PendingRegistration]) -> Self {
325 let declaring_crates = regs
326 .iter()
327 .map(|registration| {
328 (
329 registration.stable_key.clone(),
330 registration.crate_name.clone(),
331 )
332 })
333 .collect();
334 Self { declaring_crates }
335 }
336}
337
338impl AllocationPolicy for RuntimeDeclarationPolicy {
339 type Error = MemoryRegistryError;
340
341 fn validate_key(&self, _key: &StableKey) -> Result<(), Self::Error> {
342 Ok(())
343 }
344
345 fn validate_slot(
346 &self,
347 key: &StableKey,
348 slot: &AllocationSlotDescriptor,
349 ) -> Result<(), Self::Error> {
350 let declaring_crate =
351 self.declaring_crates
352 .get(key.as_str())
353 .ok_or(MemoryRegistryError::LedgerCorrupt {
354 reason: "validated declaration is missing runtime crate ownership metadata",
355 })?;
356 let policy = CanicMemoryManagerPolicy::for_declaring_crate(declaring_crate);
357 AllocationPolicy::validate_slot(&policy, key, slot)
358 }
359
360 fn validate_reserved_slot(
361 &self,
362 key: &StableKey,
363 slot: &AllocationSlotDescriptor,
364 ) -> Result<(), Self::Error> {
365 let declaring_crate =
366 self.declaring_crates
367 .get(key.as_str())
368 .ok_or(MemoryRegistryError::LedgerCorrupt {
369 reason: "validated declaration is missing runtime crate ownership metadata",
370 })?;
371 let policy = CanicMemoryManagerPolicy::for_declaring_crate(declaring_crate);
372 AllocationPolicy::validate_reserved_slot(&policy, key, slot)
373 }
374}
375
376fn memory_registry_error_from_allocation_validation(
377 err: AllocationValidationError<MemoryRegistryError>,
378 regs: &[PendingRegistration],
379) -> MemoryRegistryError {
380 match err {
381 AllocationValidationError::Policy(err) => err,
382 AllocationValidationError::StableKeySlotConflict {
383 stable_key,
384 historical_slot,
385 declared_slot,
386 } => MemoryRegistryError::HistoricalStableKeyConflict {
387 stable_key: stable_key.into_string(),
388 existing_id: memory_manager_id_from_allocation_slot(&historical_slot),
389 new_id: memory_manager_id_from_allocation_slot(&declared_slot),
390 },
391 AllocationValidationError::SlotStableKeyConflict {
392 slot,
393 historical_key,
394 declared_key,
395 } => {
396 let requested = regs
397 .iter()
398 .find(|registration| registration.stable_key == declared_key.as_str());
399 MemoryRegistryError::HistoricalIdConflict {
400 id: memory_manager_id_from_allocation_slot(&slot),
401 existing_crate: historical_key.as_str().to_string(),
402 existing_label: historical_key.as_str().to_string(),
403 new_crate: requested.map_or_else(
404 || declared_key.as_str().to_string(),
405 |reg| reg.crate_name.clone(),
406 ),
407 new_label: requested.map_or_else(
408 || declared_key.as_str().to_string(),
409 |reg| reg.label.clone(),
410 ),
411 new_stable_key: declared_key.into_string(),
412 }
413 }
414 AllocationValidationError::RetiredAllocation { .. } => MemoryRegistryError::LedgerCorrupt {
415 reason: "allocation was explicitly retired and cannot be redeclared",
416 },
417 }
418}
419
420fn memory_manager_id_from_allocation_slot(slot: &AllocationSlotDescriptor) -> u8 {
421 slot.memory_manager_id()
422 .unwrap_or(ic_memory::MEMORY_MANAGER_INVALID_ID)
423}
424
425#[cfg(test)]
426pub(crate) fn reset_initialized_for_tests() {
427 set_initialized(false);
428 set_validated_allocations(None);
429}
430
431#[cfg(not(test))]
432fn initialized() -> bool {
433 MEMORY_REGISTRY_INITIALIZED.load(Ordering::SeqCst)
434}
435
436#[cfg(test)]
437fn initialized() -> bool {
438 MEMORY_REGISTRY_INITIALIZED.with(Cell::get)
439}
440
441#[cfg(not(test))]
442fn set_initialized(value: bool) {
443 MEMORY_REGISTRY_INITIALIZED.store(value, Ordering::SeqCst);
444}
445
446#[cfg(test)]
447fn set_initialized(value: bool) {
448 MEMORY_REGISTRY_INITIALIZED.with(|initialized| initialized.set(value));
449}
450
451fn set_validated_allocations(value: Option<ValidatedAllocations>) {
452 VALIDATED_ALLOCATIONS.with_borrow_mut(|validated| {
453 *validated = value;
454 });
455}
456
457#[cfg(test)]
462mod tests {
463 use super::*;
464 use crate::memory::registry::{
465 defer_register, defer_register_with_key, defer_reserve_range, reset_for_tests,
466 };
467
468 #[test]
469 fn init_applies_initial_and_pending() {
470 reset_for_tests();
471 defer_reserve_range("crate_b", 110, 111).expect("defer range");
472 defer_register(110, "crate_b", "B110").expect("defer register");
473
474 let summary =
475 MemoryRegistryRuntime::init(Some(("crate_a", 100, 102))).expect("init should succeed");
476
477 assert_eq!(summary.ranges.len(), 3);
478 assert_eq!(summary.entries.len(), 2);
479 assert!(summary.entries.iter().any(|(id, entry)| {
480 *id == 110 && entry.crate_name == "crate_b" && entry.label == "B110"
481 }));
482 }
483
484 #[test]
485 fn init_is_idempotent_for_same_initial_range() {
486 reset_for_tests();
487
488 MemoryRegistryRuntime::init(Some(("crate_a", 100, 102)))
489 .expect("first init should succeed");
490 MemoryRegistryRuntime::init(Some(("crate_a", 100, 102)))
491 .expect("second init should succeed");
492 }
493
494 #[test]
495 fn init_returns_error_on_conflict() {
496 reset_for_tests();
497 defer_reserve_range("crate_a", 100, 102).expect("defer range A");
498 defer_reserve_range("crate_b", 102, 104).expect("defer range B");
499
500 let err = MemoryRegistryRuntime::init(None).unwrap_err();
501 assert!(matches!(err, MemoryRegistryError::Overlap { .. }));
502 }
503
504 #[test]
505 fn init_rejects_duplicate_current_snapshot_id_before_user_ledger_mutation() {
506 reset_for_tests();
507 defer_reserve_range("crate_a", 100, 102).expect("defer range");
508 defer_register_with_key(100, "crate_a", "slot_a", "app.crate_a.slot_a.v1")
509 .expect("defer first register");
510 defer_register_with_key(100, "crate_a", "slot_b", "app.crate_a.slot_b.v1")
511 .expect("defer second register");
512
513 let err = MemoryRegistryRuntime::init(None)
514 .expect_err("duplicate id in one snapshot should fail");
515 assert!(matches!(err, MemoryRegistryError::DuplicateId(100)));
516 assert!(
517 !MemoryRegistry::export_historical()
518 .iter()
519 .any(|(id, _)| *id == 100)
520 );
521 }
522
523 #[test]
524 fn init_rejects_duplicate_current_snapshot_stable_key_before_user_ledger_mutation() {
525 reset_for_tests();
526 defer_reserve_range("crate_a", 100, 102).expect("defer range");
527 defer_register_with_key(100, "crate_a", "slot_a", "app.crate_a.slot.v1")
528 .expect("defer first register");
529 defer_register_with_key(101, "crate_a", "slot_b", "app.crate_a.slot.v1")
530 .expect("defer second register");
531
532 let err = MemoryRegistryRuntime::init(None)
533 .expect_err("duplicate stable key in one snapshot should fail");
534 assert!(
535 matches!(err, MemoryRegistryError::DuplicateStableKey(key) if key == "app.crate_a.slot.v1")
536 );
537 assert!(
538 !MemoryRegistry::export_historical()
539 .iter()
540 .any(|(_, entry)| entry.stable_key == "app.crate_a.slot.v1")
541 );
542 }
543
544 #[test]
545 fn init_rejects_exact_duplicate_current_snapshot_declaration() {
546 reset_for_tests();
547 defer_reserve_range("crate_a", 100, 102).expect("defer range");
548 defer_register_with_key(100, "crate_a", "slot", "app.crate_a.slot.v1")
549 .expect("defer first register");
550 defer_register_with_key(100, "crate_a", "slot", "app.crate_a.slot.v1")
551 .expect("defer second register");
552
553 let err = MemoryRegistryRuntime::init(None)
554 .expect_err("exact duplicate declaration in one snapshot should fail");
555 assert!(matches!(err, MemoryRegistryError::DuplicateId(100)));
556 }
557
558 #[test]
559 fn init_rejects_historical_conflict_before_user_ledger_mutation() {
560 reset_for_tests();
561 ledger::record_range(
562 "crate_a",
563 MemoryRange {
564 start: 100,
565 end: 102,
566 },
567 )
568 .expect("record historical range");
569 ledger::record_entry(100, "crate_a", "slot", "app.crate_a.slot.v1", None, None)
570 .expect("record historical entry");
571 defer_reserve_range("crate_a", 100, 102).expect("defer range");
572 defer_register_with_key(101, "crate_a", "new_slot", "app.crate_a.new_slot.v1")
573 .expect("defer non-conflicting register");
574 defer_register_with_key(102, "crate_a", "moved_slot", "app.crate_a.slot.v1")
575 .expect("defer conflicting register");
576
577 let err = MemoryRegistryRuntime::init(None)
578 .expect_err("historical stable key movement should fail before commit");
579 assert!(matches!(
580 err,
581 MemoryRegistryError::HistoricalStableKeyConflict { .. }
582 ));
583 assert!(
584 !MemoryRegistry::export_historical()
585 .iter()
586 .any(|(id, _)| *id == 101)
587 );
588 }
589
590 #[test]
591 fn commit_pending_after_init_rejects_late_deferred_items() {
592 reset_for_tests();
593
594 MemoryRegistryRuntime::init(Some(("core", 100, 109))).expect("init should succeed");
595 defer_reserve_range("late", 110, 120).expect("defer late range");
596 defer_register(112, "late", "late_slot").expect("defer late register");
597
598 let err = MemoryRegistryRuntime::commit_pending_if_initialized()
599 .expect_err("late pending commit should fail after bootstrap seal");
600 assert!(matches!(
601 err,
602 MemoryRegistryError::RegistrationAfterBootstrap {
603 ranges: 1,
604 registrations: 1,
605 }
606 ));
607 assert!(MemoryRegistryRuntime::get(112).is_none());
608 }
609}