Skip to content

Commit 12c5e9c

Browse files
authored
Merge pull request #473 from CosmWasm/add-validate_region
Add validation to get_region
2 parents 0fe8896 + 2d6cbad commit 12c5e9c

File tree

3 files changed

+201
-1
lines changed

3 files changed

+201
-1
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@
1515
value.
1616
- Increase the max. supported value for gas limit from 10_000_000_000 to
1717
0x7FFFFFFFFFFFFFFF.
18+
- Add checks to `get_region` for failing early when the contract sends a Region
19+
pointer to the VM that is not backed by a plausible Region. This helps
20+
development of standard libraries.
1821

1922
## 0.9.3 (2020-07-08)
2023

packages/vm/src/errors.rs

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,26 @@ pub enum CommunicationError {
245245
max_length: usize,
246246
backtrace: snafu::Backtrace,
247247
},
248+
#[snafu(display(
249+
"Region length exceeds capacity. Length {}, capacity {}",
250+
length,
251+
capacity
252+
))]
253+
RegionLengthExceedsCapacity {
254+
length: u32,
255+
capacity: u32,
256+
backtrace: snafu::Backtrace,
257+
},
258+
#[snafu(display(
259+
"Region exceeds address space. Offset {}, capacity {}",
260+
offset,
261+
capacity
262+
))]
263+
RegionOutOfRange {
264+
offset: u32,
265+
capacity: u32,
266+
backtrace: snafu::Backtrace,
267+
},
248268
#[snafu(display("Region too small. Got {}, required {}", size, required))]
249269
RegionTooSmall {
250270
size: usize,
@@ -281,6 +301,14 @@ impl CommunicationError {
281301
RegionLengthTooBig { length, max_length }.build()
282302
}
283303

304+
pub(crate) fn region_length_exceeds_capacity(length: u32, capacity: u32) -> Self {
305+
RegionLengthExceedsCapacity { length, capacity }.build()
306+
}
307+
308+
pub(crate) fn region_out_of_range(offset: u32, capacity: u32) -> Self {
309+
RegionOutOfRange { offset, capacity }.build()
310+
}
311+
284312
pub(crate) fn region_too_small(size: usize, required: usize) -> Self {
285313
RegionTooSmall { size, required }.build()
286314
}
@@ -547,6 +575,34 @@ mod test {
547575
}
548576
}
549577

578+
#[test]
579+
fn communication_error_region_length_exceeds_capacity_works() {
580+
let error = CommunicationError::region_length_exceeds_capacity(50, 20);
581+
match error {
582+
CommunicationError::RegionLengthExceedsCapacity {
583+
length, capacity, ..
584+
} => {
585+
assert_eq!(length, 50);
586+
assert_eq!(capacity, 20);
587+
}
588+
e => panic!("Unexpected error: {:?}", e),
589+
}
590+
}
591+
592+
#[test]
593+
fn communication_error_region_out_of_range_works() {
594+
let error = CommunicationError::region_out_of_range(u32::MAX, 1);
595+
match error {
596+
CommunicationError::RegionOutOfRange {
597+
offset, capacity, ..
598+
} => {
599+
assert_eq!(offset, u32::MAX);
600+
assert_eq!(capacity, 1);
601+
}
602+
e => panic!("Unexpected error: {:?}", e),
603+
}
604+
}
605+
550606
#[test]
551607
fn communication_error_region_too_small_works() {
552608
let error = CommunicationError::region_too_small(12, 33);

packages/vm/src/memory.rs

Lines changed: 142 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,14 +137,39 @@ fn get_region(ctx: &Ctx, ptr: u32) -> CommunicationResult<Region> {
137137
let memory = ctx.memory(0);
138138
let wptr = WasmPtr::<Region>::new(ptr);
139139
match wptr.deref(memory) {
140-
Some(cell) => Ok(cell.get()),
140+
Some(cell) => {
141+
let region = cell.get();
142+
validate_region(&region)?;
143+
Ok(region)
144+
}
141145
None => Err(CommunicationError::deref_err(
142146
ptr,
143147
"Could not dereference this pointer to a Region",
144148
)),
145149
}
146150
}
147151

152+
/// Performs plausibility checks in the given Region. Regions are always created by the
153+
/// contract and this can be used to detect problems in the standard library of the contract.
154+
fn validate_region(region: &Region) -> CommunicationResult<()> {
155+
if region.offset == 0 {
156+
return Err(CommunicationError::zero_address());
157+
}
158+
if region.length > region.capacity {
159+
return Err(CommunicationError::region_length_exceeds_capacity(
160+
region.length,
161+
region.capacity,
162+
));
163+
}
164+
if region.capacity > (u32::MAX - region.offset) {
165+
return Err(CommunicationError::region_out_of_range(
166+
region.offset,
167+
region.capacity,
168+
));
169+
}
170+
Ok(())
171+
}
172+
148173
/// Overrides a Region at ptr in wasm memory with data
149174
fn set_region(ctx: &Ctx, ptr: u32, data: Region) -> CommunicationResult<()> {
150175
let memory = ctx.memory(0);
@@ -161,3 +186,119 @@ fn set_region(ctx: &Ctx, ptr: u32, data: Region) -> CommunicationResult<()> {
161186
)),
162187
}
163188
}
189+
190+
#[cfg(test)]
191+
mod test {
192+
use super::*;
193+
194+
#[test]
195+
fn validate_region_passes_for_valid_region() {
196+
// empty
197+
let region = Region {
198+
offset: 23,
199+
capacity: 500,
200+
length: 0,
201+
};
202+
validate_region(&region).unwrap();
203+
204+
// half full
205+
let region = Region {
206+
offset: 23,
207+
capacity: 500,
208+
length: 250,
209+
};
210+
validate_region(&region).unwrap();
211+
212+
// full
213+
let region = Region {
214+
offset: 23,
215+
capacity: 500,
216+
length: 500,
217+
};
218+
validate_region(&region).unwrap();
219+
220+
// at end of linear memory (1)
221+
let region = Region {
222+
offset: u32::MAX,
223+
capacity: 0,
224+
length: 0,
225+
};
226+
validate_region(&region).unwrap();
227+
228+
// at end of linear memory (2)
229+
let region = Region {
230+
offset: 1,
231+
capacity: u32::MAX - 1,
232+
length: 0,
233+
};
234+
validate_region(&region).unwrap();
235+
}
236+
237+
#[test]
238+
fn validate_region_fails_for_zero_offset() {
239+
let region = Region {
240+
offset: 0,
241+
capacity: 500,
242+
length: 250,
243+
};
244+
let result = validate_region(&region);
245+
match result.unwrap_err() {
246+
CommunicationError::ZeroAddress { .. } => {}
247+
e => panic!("Got unexpected error: {:?}", e),
248+
}
249+
}
250+
251+
#[test]
252+
fn validate_region_fails_for_length_exceeding_capacity() {
253+
let region = Region {
254+
offset: 23,
255+
capacity: 500,
256+
length: 501,
257+
};
258+
let result = validate_region(&region);
259+
match result.unwrap_err() {
260+
CommunicationError::RegionLengthExceedsCapacity {
261+
length, capacity, ..
262+
} => {
263+
assert_eq!(length, 501);
264+
assert_eq!(capacity, 500);
265+
}
266+
e => panic!("Got unexpected error: {:?}", e),
267+
}
268+
}
269+
270+
#[test]
271+
fn validate_region_fails_when_exceeding_address_space() {
272+
let region = Region {
273+
offset: 23,
274+
capacity: u32::MAX,
275+
length: 501,
276+
};
277+
let result = validate_region(&region);
278+
match result.unwrap_err() {
279+
CommunicationError::RegionOutOfRange {
280+
offset, capacity, ..
281+
} => {
282+
assert_eq!(offset, 23);
283+
assert_eq!(capacity, u32::MAX);
284+
}
285+
e => panic!("Got unexpected error: {:?}", e),
286+
}
287+
288+
let region = Region {
289+
offset: u32::MAX,
290+
capacity: 1,
291+
length: 0,
292+
};
293+
let result = validate_region(&region);
294+
match result.unwrap_err() {
295+
CommunicationError::RegionOutOfRange {
296+
offset, capacity, ..
297+
} => {
298+
assert_eq!(offset, u32::MAX);
299+
assert_eq!(capacity, 1);
300+
}
301+
e => panic!("Got unexpected error: {:?}", e),
302+
}
303+
}
304+
}

0 commit comments

Comments
 (0)