Skip to content

Commit fb59f4d

Browse files
committed
Add validate_region
1 parent 0fe8896 commit fb59f4d

File tree

2 files changed

+193
-0
lines changed

2 files changed

+193
-0
lines changed

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: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,27 @@ fn get_region(ctx: &Ctx, ptr: u32) -> CommunicationResult<Region> {
145145
}
146146
}
147147

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

0 commit comments

Comments
 (0)