Skip to content

Commit 5bc5d7c

Browse files
author
Frederik Rothenberger
authored
Fix error in persistent state reading (#1021)
This PR fixes issues with regards to persistent state reading when none has been written. I.e. it adds additional checks to make sure that the code does not attempt to read memory that it should not.
1 parent 956669b commit 5bc5d7c

File tree

2 files changed

+82
-8
lines changed

2 files changed

+82
-8
lines changed

src/internet_identity/src/storage.rs

Lines changed: 38 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -366,29 +366,59 @@ impl<T: candid::CandidType + serde::de::DeserializeOwned, M: Memory> Storage<T,
366366
/// Reads the persistent state from stable memory just outside of the space allocated to the highest user number.
367367
/// This is only used to restore state in `post_upgrade`.
368368
pub fn read_persistent_state(&self) -> Result<PersistentState, PersistentStateError> {
369+
const WASM_PAGE_SIZE: u64 = 65536;
369370
let address = self.unused_memory_start();
370-
let mut reader = Reader::new(&self.memory, address);
371371

372+
if address > self.memory.size() * WASM_PAGE_SIZE {
373+
// the address where the persistent state would be is not allocated yet
374+
return Err(PersistentStateError::NotFound);
375+
}
376+
377+
let mut reader = Reader::new(&self.memory, address);
372378
let mut magic_buf: [u8; 4] = [0; 4];
373-
reader
379+
let bytes_read = reader
374380
.read(&mut magic_buf)
375-
.map_err(|err| PersistentStateError::ReadError(err))?;
381+
// if we hit out of bounds here, this means that the persistent state has not been
382+
// written at the expected location and thus cannot be found
383+
.map_err(|_| PersistentStateError::NotFound)?;
376384

377-
if magic_buf != PERSISTENT_STATE_MAGIC {
385+
if bytes_read != 4 || magic_buf != PERSISTENT_STATE_MAGIC {
386+
// less than the expected number of bytes were read or the magic does not match
387+
// --> this is not the persistent state
378388
return Err(PersistentStateError::NotFound);
379389
}
380390

381391
let mut size_buf: [u8; 8] = [0; 8];
382-
reader
392+
let bytes_read = reader
383393
.read(&mut size_buf)
384-
.map_err(|err| PersistentStateError::ReadError(err))?;
394+
.map_err(|err| PersistentStateError::ReadError(err))? as u64;
395+
396+
// check if we actually read the required amount of data
397+
// note: this will only happen if we hit the memory bounds during read
398+
if bytes_read != 8 {
399+
let max_address = address + 4 + bytes_read;
400+
return Err(PersistentStateError::ReadError(OutOfBounds {
401+
max_address,
402+
attempted_read_address: max_address + 1,
403+
}));
404+
}
385405

386406
let size = u64::from_le_bytes(size_buf);
387407
let mut data_buf = Vec::new();
388408
data_buf.resize(size as usize, 0);
389-
reader
409+
let bytes_read = reader
390410
.read(data_buf.as_mut_slice())
391-
.map_err(|err| PersistentStateError::ReadError(err))?;
411+
.map_err(|err| PersistentStateError::ReadError(err))? as u64;
412+
413+
// check if we actually read the required amount of data
414+
// note: this will only happen if we hit the memory bounds during read
415+
if bytes_read != size {
416+
let max_address = address + 4 + 8 + bytes_read;
417+
return Err(PersistentStateError::ReadError(OutOfBounds {
418+
max_address,
419+
attempted_read_address: max_address + 1,
420+
}));
421+
}
392422

393423
candid::decode_one(&data_buf).map_err(|err| PersistentStateError::CandidError(err))
394424
}

src/internet_identity/src/storage/tests.rs

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,31 @@ fn should_save_persistent_state_at_expected_memory_address() {
195195
assert_eq!(buf, PERSISTENT_STATE_MAGIC);
196196
}
197197

198+
#[test]
199+
fn should_not_find_persistent_state() {
200+
let memory = VectorMemory::default();
201+
let mut storage =
202+
Storage::<Vec<DeviceDataInternal>, VectorMemory>::new((10_000, 3_784_873), memory.clone());
203+
storage.flush();
204+
205+
let result = storage.read_persistent_state();
206+
assert!(matches!(result, Err(PersistentStateError::NotFound)))
207+
}
208+
209+
#[test]
210+
fn should_not_find_persistent_state_on_magic_bytes_mismatch() {
211+
let memory = VectorMemory::default();
212+
213+
let mut storage =
214+
Storage::<Vec<DeviceDataInternal>, VectorMemory>::new((10_000, 3_784_873), memory.clone());
215+
storage.flush();
216+
217+
memory.write(RESERVED_HEADER_BYTES, b"IIPX"); // correct magic bytes are IIPS
218+
219+
let result = storage.read_persistent_state();
220+
assert!(matches!(result, Err(PersistentStateError::NotFound)))
221+
}
222+
198223
#[test]
199224
fn should_save_persistent_state_at_expected_memory_address_with_anchors() {
200225
const EXPECTED_ADDRESS: u64 = RESERVED_HEADER_BYTES + 100 * 2048; // number of anchors is 100
@@ -233,6 +258,25 @@ fn should_save_persistent_state_at_expected_memory_address_with_many_anchors() {
233258
assert_eq!(buf, PERSISTENT_STATE_MAGIC);
234259
}
235260

261+
/// This test verifies that storage correctly reports `NotFound` if the persistent state address
262+
/// lies outside of the allocated stable memory range. This can happen on upgrade from a version
263+
/// that did not serialize a persistent state into stable memory.
264+
#[test]
265+
fn should_not_panic_on_unallocated_persistent_state_mem_address() {
266+
let memory = VectorMemory::default();
267+
let mut storage =
268+
Storage::<Vec<DeviceDataInternal>, VectorMemory>::new((10_000, 3_784_873), memory.clone());
269+
storage.flush();
270+
for _ in 0..32 {
271+
storage.allocate_user_number();
272+
}
273+
274+
assert!(matches!(
275+
storage.read_persistent_state(),
276+
Err(PersistentStateError::NotFound)
277+
));
278+
}
279+
236280
#[test]
237281
fn should_overwrite_persistent_state_with_next_anchor() {
238282
const EXPECTED_ADDRESS: u64 = RESERVED_HEADER_BYTES + 2048; // only one anchor exists

0 commit comments

Comments
 (0)