@@ -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
149174fn 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