Skip to content

Commit ada077c

Browse files
authored
fix: make sure we can read CIDs at the end of memory (#497)
1 parent c9f3c6e commit ada077c

File tree

1 file changed

+88
-4
lines changed

1 file changed

+88
-4
lines changed

fvm/src/syscalls/context.rs

Lines changed: 88 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ use fvm_shared::address::Address;
66
use fvm_shared::error::ErrorNumber;
77

88
use crate::kernel::{ClassifyResult, Context as _, Result};
9-
use crate::syscalls::MAX_CID_LEN;
109

1110
pub struct Context<'a, K> {
1211
pub kernel: &'a mut K,
@@ -52,9 +51,22 @@ impl Memory {
5251
}
5352

5453
pub fn read_cid(&self, offset: u32) -> Result<Cid> {
55-
Cid::read_bytes(self.try_slice(offset, MAX_CID_LEN as u32)?)
56-
.or_error(ErrorNumber::IllegalArgument)
57-
.context("failed to parse CID")
54+
// NOTE: Be very careful when changing this code.
55+
//
56+
// We intentionally read the CID till the end of memory. We intentionally do not "slice"
57+
// with a fixed end.
58+
// - We _can't_ slice MAX_CID_LEN because there may not be MAX_CID_LEN addressable memory
59+
// after the offset.
60+
// - We can safely read from an "arbitrary" sized slice because `Cid::read_bytes` will never
61+
// read more than 4 u64 varints and 64 bytes of digest.
62+
Cid::read_bytes(
63+
self.0
64+
.get(offset as usize..)
65+
.ok_or_else(|| format!("cid at offset {} is out of bounds", offset))
66+
.or_error(ErrorNumber::IllegalArgument)?,
67+
)
68+
.or_error(ErrorNumber::IllegalArgument)
69+
.context("failed to parse cid")
5870
}
5971

6072
pub fn read_address(&self, offset: u32, len: u32) -> Result<Address> {
@@ -67,3 +79,75 @@ impl Memory {
6779
from_slice(bytes).or_error(ErrorNumber::IllegalArgument)
6880
}
6981
}
82+
83+
#[cfg(test)]
84+
mod test {
85+
use cid::multihash::MultihashDigest;
86+
87+
use super::*;
88+
89+
// TODO: move this somewhere more useful.
90+
macro_rules! expect_syscall_err {
91+
($code:ident, $res:expr) => {
92+
match $res.expect_err("expected syscall to fail") {
93+
$crate::kernel::ExecutionError::Syscall($crate::kernel::SyscallError(
94+
_,
95+
fvm_shared::error::ErrorNumber::$code,
96+
)) => {}
97+
$crate::kernel::ExecutionError::Syscall($crate::kernel::SyscallError(
98+
msg,
99+
code,
100+
)) => {
101+
panic!(
102+
"expected {}, got {}: {}",
103+
fvm_shared::error::ErrorNumber::$code,
104+
code,
105+
msg
106+
)
107+
}
108+
$crate::kernel::ExecutionError::Fatal(err) => {
109+
panic!("got unexpected fatal error: {}", err)
110+
}
111+
$crate::kernel::ExecutionError::OutOfGas => {
112+
panic!("got unexpected out of gas")
113+
}
114+
}
115+
};
116+
}
117+
118+
#[test]
119+
fn test_read_cid() {
120+
let k = Cid::new_v1(0x55, cid::multihash::Code::Sha2_256.digest(b"foo"));
121+
let mut k_bytes = k.to_bytes();
122+
let mem = Memory::new(&mut k_bytes);
123+
let k2 = mem.read_cid(0).expect("failed to read cid");
124+
assert_eq!(k, k2);
125+
}
126+
127+
#[test]
128+
fn test_read_cid_truncated() {
129+
let k = Cid::new_v1(0x55, cid::multihash::Code::Sha2_256.digest(b"foo"));
130+
let mut k_bytes = k.to_bytes();
131+
let mem = Memory::new(&mut k_bytes[..20]);
132+
expect_syscall_err!(IllegalArgument, mem.read_cid(0));
133+
}
134+
135+
#[test]
136+
fn test_read_cid_out_of_bounds() {
137+
let mem = Memory::new(&mut []);
138+
expect_syscall_err!(IllegalArgument, mem.read_cid(200));
139+
}
140+
141+
#[test]
142+
fn test_read_slice_out_of_bounds() {
143+
let mem = Memory::new(&mut []);
144+
expect_syscall_err!(IllegalArgument, mem.try_slice(10, 0));
145+
expect_syscall_err!(IllegalArgument, mem.try_slice(u32::MAX, 0));
146+
}
147+
148+
#[test]
149+
fn test_read_slice_empty() {
150+
let mem = Memory::new(&mut []);
151+
mem.try_slice(0, 0).expect("slice was in bounds");
152+
}
153+
}

0 commit comments

Comments
 (0)