Skip to content

Commit 96869b2

Browse files
authored
Merge pull request #418 from CosmWasm/create-deref-error
Create CommunicationError::DerefErr to avoid panics
2 parents e223918 + 9ec07b6 commit 96869b2

File tree

3 files changed

+66
-17
lines changed

3 files changed

+66
-17
lines changed

CHANGELOG.md

+3
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,9 @@
5757
directly to get back the external dependencies.
5858
- Rename `MockQuerier::with_staking` to `MockQuerier::update_staking` to match
5959
`::update_balance`.
60+
- Instead of panicking, `read_region`/`write_region`/`get_region`/`set_region`
61+
now return a new `CommunicationError::DerefErr` when dereferencing a pointer
62+
provided by the contract fails.
6063

6164
## 0.8.1 (2020-06-08)
6265

packages/vm/src/errors.rs

+33-1
Original file line numberDiff line numberDiff line change
@@ -239,14 +239,35 @@ pub type VmResult<T> = core::result::Result<T, VmError>;
239239
pub enum CommunicationError {
240240
#[snafu(display("Got a zero Wasm address"))]
241241
ZeroAddress { backtrace: snafu::Backtrace },
242+
#[snafu(display(
243+
"The Wasm memory address {} provided by the contract could not be dereferenced: {}",
244+
offset,
245+
msg
246+
))]
247+
DerefErr {
248+
/// the position in a Wasm linear memory
249+
offset: u32,
250+
msg: String,
251+
backtrace: snafu::Backtrace,
252+
},
242253
}
243254

244255
impl CommunicationError {
245256
pub fn zero_address() -> Self {
246257
ZeroAddress {}.build()
247258
}
259+
260+
pub fn deref_err<S: Into<String>>(offset: u32, msg: S) -> Self {
261+
DerefErr {
262+
offset,
263+
msg: msg.into(),
264+
}
265+
.build()
266+
}
248267
}
249268

269+
pub type CommunicationResult<T> = core::result::Result<T, CommunicationError>;
270+
250271
impl From<CommunicationError> for VmError {
251272
fn from(communication_error: CommunicationError) -> Self {
252273
VmError::CommunicationErr {
@@ -494,7 +515,6 @@ mod test {
494515

495516
// CommunicationError constructors
496517

497-
#[allow(unreachable_patterns)] // since CommunicationError is non_exhaustive, this should not create a warning. But it does :(
498518
#[test]
499519
fn communication_error_zero_address() {
500520
let error = CommunicationError::zero_address();
@@ -504,6 +524,18 @@ mod test {
504524
}
505525
}
506526

527+
#[test]
528+
fn communication_error_deref_err() {
529+
let error = CommunicationError::deref_err(345, "broken stuff");
530+
match error {
531+
CommunicationError::DerefErr { offset, msg, .. } => {
532+
assert_eq!(offset, 345);
533+
assert_eq!(msg, "broken stuff");
534+
}
535+
e => panic!("Unexpected error: {:?}", e),
536+
}
537+
}
538+
507539
// FfiError constructors
508540

509541
#[test]

packages/vm/src/memory.rs

+30-16
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use wasmer_runtime_core::{
55
};
66

77
use crate::conversion::to_u32;
8-
use crate::errors::{VmError, VmResult};
8+
use crate::errors::{CommunicationError, CommunicationResult, VmError, VmResult};
99

1010
/****** read/write to wasm memory buffer ****/
1111

@@ -62,7 +62,7 @@ pub fn get_memory_info(ctx: &Ctx) -> MemoryInfo {
6262
/// memory region, which is copied in the second step.
6363
/// Errors if the length of the region exceeds `max_length`.
6464
pub fn read_region(ctx: &Ctx, ptr: u32, max_length: usize) -> VmResult<Vec<u8>> {
65-
let region = get_region(ctx, ptr);
65+
let region = get_region(ctx, ptr)?;
6666

6767
if region.length > to_u32(max_length)? {
6868
return Err(VmError::region_length_too_big(
@@ -83,11 +83,11 @@ pub fn read_region(ctx: &Ctx, ptr: u32, max_length: usize) -> VmResult<Vec<u8>>
8383
}
8484
Ok(result)
8585
}
86-
None => panic!(
87-
"Error dereferencing region {:?} in wasm memory of size {}. This typically happens when the given pointer does not point to a Region struct.",
86+
None => Err(CommunicationError::deref_err(region.offset, format!(
87+
"Tried to access memory of region {:?} in wasm memory of size {} bytes. This typically happens when the given Region pointer does not point to a proper Region struct.",
8888
region,
8989
memory.size().bytes().0
90-
),
90+
)).into()),
9191
}
9292
}
9393

@@ -106,7 +106,7 @@ pub fn maybe_read_region(ctx: &Ctx, ptr: u32, max_length: usize) -> VmResult<Opt
106106
///
107107
/// Returns number of bytes written on success.
108108
pub fn write_region(ctx: &Ctx, ptr: u32, data: &[u8]) -> VmResult<()> {
109-
let mut region = get_region(ctx, ptr);
109+
let mut region = get_region(ctx, ptr)?;
110110

111111
let region_capacity = region.capacity as usize;
112112
if data.len() > region_capacity {
@@ -122,29 +122,43 @@ pub fn write_region(ctx: &Ctx, ptr: u32, data: &[u8]) -> VmResult<()> {
122122
cells[i].set(data[i])
123123
}
124124
region.length = data.len() as u32;
125-
set_region(ctx, ptr, region);
125+
set_region(ctx, ptr, region)?;
126126
Ok(())
127127
},
128-
None => panic!(
129-
"Error dereferencing region {:?} in wasm memory of size {}. This typically happens when the given pointer does not point to a Region struct.",
128+
None => Err(CommunicationError::deref_err(region.offset, format!(
129+
"Tried to access memory of region {:?} in wasm memory of size {} bytes. This typically happens when the given Region pointer does not point to a proper Region struct.",
130130
region,
131131
memory.size().bytes().0
132-
),
132+
)).into()),
133133
}
134134
}
135135

136136
/// Reads in a Region at ptr in wasm memory and returns a copy of it
137-
fn get_region(ctx: &Ctx, ptr: u32) -> Region {
137+
fn get_region(ctx: &Ctx, ptr: u32) -> CommunicationResult<Region> {
138138
let memory = ctx.memory(0);
139139
let wptr = WasmPtr::<Region>::new(ptr);
140-
let cell = wptr.deref(memory).unwrap();
141-
cell.get()
140+
match wptr.deref(memory) {
141+
Some(cell) => Ok(cell.get()),
142+
None => Err(CommunicationError::deref_err(
143+
ptr,
144+
"Could not dereference this pointer to a Region",
145+
)),
146+
}
142147
}
143148

144149
/// Overrides a Region at ptr in wasm memory with data
145-
fn set_region(ctx: &Ctx, ptr: u32, data: Region) {
150+
fn set_region(ctx: &Ctx, ptr: u32, data: Region) -> CommunicationResult<()> {
146151
let memory = ctx.memory(0);
147152
let wptr = WasmPtr::<Region>::new(ptr);
148-
let cell = wptr.deref(memory).unwrap();
149-
cell.set(data);
153+
154+
match wptr.deref(memory) {
155+
Some(cell) => {
156+
cell.set(data);
157+
Ok(())
158+
}
159+
None => Err(CommunicationError::deref_err(
160+
ptr,
161+
"Could not dereference this pointer to a Region",
162+
)),
163+
}
150164
}

0 commit comments

Comments
 (0)