Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(69)

Side by Side Diff: mojo/public/rust/src/bindings/decoding.rs

Issue 2220183003: Rust: Add validation to decode (Closed) Base URL: git@github.com:domokit/mojo.git@coder-testing
Patch Set: Update from upstream branches (fixed arrays, validation code, etc.) Created 4 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | mojo/public/rust/src/bindings/encoding.rs » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2016 The Chromium Authors. All rights reserved. 1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 use bindings::encoding::{Bits, Context, MojomNumeric}; 5 use bindings::encoding::{Bits, Context, DataHeader, DataHeaderValue, DATA_HEADER _SIZE,
6 use bindings::mojom::{MOJOM_NULL_POINTER, UNION_SIZE}; 6 MojomNumeric};
7 use bindings::mojom::{MojomEncodable, MOJOM_NULL_POINTER, UNION_SIZE};
7 use bindings::util; 8 use bindings::util;
8 9
9 use std::mem; 10 use std::mem;
10 use std::ptr; 11 use std::ptr;
11 use std::vec::Vec; 12 use std::vec::Vec;
12 13
13 use system; 14 use system;
14 use system::{Handle, CastHandle, UntypedHandle}; 15 use system::{Handle, CastHandle, UntypedHandle};
15 16
17 #[derive(Debug, Eq, PartialEq)]
18 pub enum ValidationError {
19 DifferentSizedArraysInMap,
20 IllegalHandle,
21 IllegalMemoryRange,
22 IllegalPointer,
23 MessageHeaderInvalidFlags,
24 MessageHeaderMissingRequestId,
25 MessageHeaderUnknownMethod,
26 MisalignedObject,
27 UnexpectedArrayHeader,
28 UnexpectedInvalidHandle,
29 UnexpectedNullPointer,
30 UnexpectedNullUnion,
31 UnexpectedStructHeader,
32 }
33
34 impl ValidationError {
35 pub fn as_str(self) -> &'static str {
36 match self {
37 ValidationError::DifferentSizedArraysInMap => "VALIDATION_ERROR_DIFF ERENT_SIZED_ARRAYS_IN_MAP",
38 ValidationError::IllegalHandle => "VALIDATION_ERROR_ILLEGAL_HANDLE",
39 ValidationError::IllegalMemoryRange => "VALIDATION_ERROR_ILLEGAL_MEM ORY_RANGE",
40 ValidationError::IllegalPointer => "VALIDATION_ERROR_ILLEGAL_POINTER ",
41 ValidationError::MessageHeaderInvalidFlags => "VALIDATION_ERROR_MESS AGE_HEADER_INVALID_FLAGS",
42 ValidationError::MessageHeaderMissingRequestId => "VALIDATION_ERROR_ MESSAGE_HEADER_MISSING_REQUEST_ID",
43 ValidationError::MessageHeaderUnknownMethod => "VALIDATION_ERROR_MES SAGE_HEADER_UNKNOWN_METHOD",
44 ValidationError::MisalignedObject => "VALIDATION_ERROR_MISALIGNED_OB JECT",
45 ValidationError::UnexpectedArrayHeader => "VALIDATION_ERROR_UNEXPECT ED_ARRAY_HEADER",
46 ValidationError::UnexpectedInvalidHandle => "VALIDATION_ERROR_UNEXPE CTED_INVALID_HANDLE",
47 ValidationError::UnexpectedNullPointer => "VALIDATION_ERROR_UNEXPECT ED_NULL_POINTER",
48 ValidationError::UnexpectedNullUnion => "VALIDATION_ERROR_UNEXPECTED _NULL_UNION",
49 ValidationError::UnexpectedStructHeader => "VALIDATION_ERROR_UNEXPEC TED_STRUCT_HEADER",
50 }
51 }
52 }
53
16 /// An decoding state represents the decoding logic for a single 54 /// An decoding state represents the decoding logic for a single
17 /// Mojom object that is NOT inlined, such as a struct or an array. 55 /// Mojom object that is NOT inlined, such as a struct or an array.
18 pub struct DecodingState<'slice> { 56 pub struct DecodingState<'slice> {
19 /// The buffer the state may write to. 57 /// The buffer the state may write to.
20 data: &'slice [u8], 58 data: &'slice [u8],
21 59
22 /// The offset of this serialized object into the overall buffer. 60 /// The offset of this serialized object into the overall buffer.
23 global_offset: usize, 61 global_offset: usize,
24 62
25 /// The current offset within 'data'. 63 /// The current offset within 'data'.
(...skipping 119 matching lines...) Expand 10 before | Expand all | Expand 10 after
145 self.offset += 8; 183 self.offset += 8;
146 } 184 }
147 (index < 0) 185 (index < 0)
148 } 186 }
149 187
150 /// Decode a pointer from the buffer as a global offset into the buffer. 188 /// Decode a pointer from the buffer as a global offset into the buffer.
151 /// 189 ///
152 /// The pointer in the buffer is an offset relative to the pointer to anothe r 190 /// The pointer in the buffer is an offset relative to the pointer to anothe r
153 /// location in the buffer. We convert that to an absolute offset with respe ct 191 /// location in the buffer. We convert that to an absolute offset with respe ct
154 /// to the buffer before returning. This is our defintion of a pointer. 192 /// to the buffer before returning. This is our defintion of a pointer.
155 pub fn decode_pointer(&mut self) -> u64 { 193 pub fn decode_pointer(&mut self) -> Option<u64> {
156 self.align_to_byte(); 194 self.align_to_byte();
157 self.align_to_bytes(8); 195 self.align_to_bytes(8);
158 let current_location = (self.global_offset + self.offset) as u64; 196 let current_location = (self.global_offset + self.offset) as u64;
159 self.read::<u64>() + current_location 197 let offset = self.read::<u64>();
198 if offset == MOJOM_NULL_POINTER {
199 Some(MOJOM_NULL_POINTER)
200 } else {
201 offset.checked_add(current_location)
202 }
203 }
204
205 /// A routine for decoding an array header.
206 ///
207 /// Must be called with offset zero (that is, it must be the first thing
208 /// decoded). Performs numerous validation checks.
209 pub fn decode_array_header<T>(&mut self) -> Result<DataHeader, ValidationErr or>
210 where T: MojomEncodable
211 {
212 debug_assert_eq!(self.offset, 0);
213 // Make sure we can read the size first...
214 if self.data.len() < mem::size_of::<u32>() {
215 return Err(ValidationError::UnexpectedArrayHeader);
216 }
217 let bytes = self.decode::<u32>();
218 if (bytes as usize) < DATA_HEADER_SIZE {
219 return Err(ValidationError::UnexpectedArrayHeader);
220 }
221 let elems = self.decode::<u32>();
222 match T::embed_size(&Default::default()).checked_mul(elems as usize) {
223 Some(value) => {
224 if (bytes as usize) < value.as_bytes() + DATA_HEADER_SIZE {
225 return Err(ValidationError::UnexpectedArrayHeader);
226 }
227 }
228 None => return Err(ValidationError::UnexpectedArrayHeader),
229 }
230 Ok(DataHeader::new(bytes as usize, DataHeaderValue::Elements(elems)))
231 }
232
233 /// A routine for decoding an struct header.
234 ///
235 /// Must be called with offset zero (that is, it must be the first thing
236 /// decoded). Performs numerous validation checks.
237 pub fn decode_struct_header(&mut self,
238 versions: &[(u32, u32)])
239 -> Result<DataHeader, ValidationError> {
240 debug_assert_eq!(self.offset, 0);
241 // Make sure we can read the size first...
242 if self.data.len() < mem::size_of::<u32>() {
243 return Err(ValidationError::UnexpectedStructHeader);
244 }
245 let bytes = self.decode::<u32>();
246 if (bytes as usize) < DATA_HEADER_SIZE {
247 return Err(ValidationError::UnexpectedStructHeader);
248 }
249 let version = self.decode::<u32>();
250 // Versioning validation: versions are generated as a sorted array of tu ples, so
251 // to find the version we are given by the header we use a binary search .
252 match versions.binary_search_by(|val| val.0.cmp(&version)) {
253 Ok(idx) => {
254 let (_, size) = versions[idx];
255 if bytes != size {
256 return Err(ValidationError::UnexpectedStructHeader);
257 }
258 }
259 Err(idx) => {
260 if idx == 0 {
261 panic!("Should be earliest version? \
262 Versions: {:?}, \
263 Version: {}, \
264 Size: {}", versions, version, bytes);
265 }
266 let len = versions.len();
267 let (latest_version, _) = versions[len - 1];
268 let (_, size) = versions[idx - 1];
269 // If this is higher than any version we know, its okay for the size to be bigger,
270 // but if its a version we know about, it must match the size.
271 if (version > latest_version && bytes < size) ||
272 (version <= latest_version && bytes != size) {
273 return Err(ValidationError::UnexpectedStructHeader);
274 }
275 }
276 }
277 Ok(DataHeader::new(bytes as usize, DataHeaderValue::Version(version)))
160 } 278 }
161 } 279 }
162 280
163 /// A struct that will encode a given Mojom object and convert it into 281 /// A struct that will encode a given Mojom object and convert it into
164 /// bytes and a vector of handles. 282 /// bytes and a vector of handles.
165 pub struct Decoder<'slice> { 283 pub struct Decoder<'slice> {
166 bytes: usize, 284 bytes: usize,
167 buffer: Option<&'slice [u8]>, 285 buffer: Option<&'slice [u8]>,
168 states: Vec<DecodingState<'slice>>, 286 states: Vec<DecodingState<'slice>>,
169 handles: Vec<UntypedHandle>, 287 handles: Vec<UntypedHandle>,
288 handles_claimed: usize, // A length that claims all handles were claimed up to this index
289 max_offset: usize, // Represents the maximum value an offset may have
170 } 290 }
171 291
172 impl<'slice> Decoder<'slice> { 292 impl<'slice> Decoder<'slice> {
173 /// Create a new Decoder. 293 /// Create a new Decoder.
174 pub fn new(buffer: &'slice [u8], handles: Vec<UntypedHandle>) -> Decoder<'sl ice> { 294 pub fn new(buffer: &'slice [u8], handles: Vec<UntypedHandle>) -> Decoder<'sl ice> {
295 let max_offset = buffer.len();
175 Decoder { 296 Decoder {
176 bytes: 0, 297 bytes: 0,
177 buffer: Some(buffer), 298 buffer: Some(buffer),
178 states: Vec::new(), 299 states: Vec::new(),
179 handles: handles, 300 handles: handles,
301 handles_claimed: 0,
302 max_offset: max_offset,
180 } 303 }
181 } 304 }
182 305
183 /// Claim space in the buffer to start decoding some object. 306 /// Claim space in the buffer to start decoding some object.
184 /// 307 ///
185 /// Creates a new decoding state for the object and returns a context. 308 /// Creates a new decoding state for the object and returns a context.
186 pub fn claim(&mut self, offset: usize) -> Option<Context> { 309 pub fn claim(&mut self, offset: usize) -> Result<Context, ValidationError> {
310 // Check if the layout order is sane
187 if offset < self.bytes { 311 if offset < self.bytes {
188 panic!("Tried to claim already-claimed space!"); 312 return Err(ValidationError::IllegalMemoryRange);
313 }
314 // Check for 8-byte alignment
315 if offset & 7 != 0 {
316 return Err(ValidationError::MisalignedObject);
317 }
318 // Bounds check on offset
319 if offset > self.max_offset {
320 return Err(ValidationError::IllegalPointer);
189 } 321 }
190 let mut buffer = self.buffer.take().expect("No buffer?"); 322 let mut buffer = self.buffer.take().expect("No buffer?");
191 let space = offset - self.bytes; 323 let space = offset - self.bytes;
192 buffer = &buffer[space..]; 324 buffer = &buffer[space..];
325 // Make sure we can even read the bytes in the header
326 if buffer.len() < mem::size_of::<u32>() {
327 return Err(ValidationError::IllegalMemoryRange);
328 }
329 // Read the number of bytes in the memory region according to the data h eader
193 let mut read_size: u32 = 0; 330 let mut read_size: u32 = 0;
194 unsafe { 331 unsafe {
195 ptr::copy_nonoverlapping(mem::transmute::<*const u8, *const u32>(buf fer.as_ptr()), 332 ptr::copy_nonoverlapping(mem::transmute::<*const u8, *const u32>(buf fer.as_ptr()),
196 &mut read_size as *mut u32, 333 &mut read_size as *mut u32,
197 mem::size_of::<u32>()); 334 mem::size_of::<u32>());
198 } 335 }
199 let size = u32::from_le(read_size) as usize; 336 let size = u32::from_le(read_size) as usize;
337 // Make sure the size we read is sane...
338 if size > buffer.len() {
339 return Err(ValidationError::IllegalMemoryRange);
340 }
200 // TODO(mknyszek): Check size for validation 341 // TODO(mknyszek): Check size for validation
201 let (claimed, unclaimed) = buffer.split_at(size); 342 let (claimed, unclaimed) = buffer.split_at(size);
202 self.states.push(DecodingState::new(claimed, offset)); 343 self.states.push(DecodingState::new(claimed, offset));
203 self.buffer = Some(unclaimed); 344 self.buffer = Some(unclaimed);
204 self.bytes += space + size; 345 self.bytes += space + size;
205 Some(Context::new(self.states.len() - 1)) 346 Ok(Context::new(self.states.len() - 1))
206 } 347 }
207 348
208 /// Claims a handle at some particular index in the given handles array. 349 /// Claims a handle at some particular index in the given handles array.
209 /// 350 ///
210 /// Returns the handle with all type information in-tact. 351 /// Returns the handle with all type information in-tact.
211 pub fn claim_handle<T: Handle + CastHandle>(&mut self, index: i32) -> T { 352 pub fn claim_handle<T: Handle + CastHandle>(&mut self,
353 index: i32)
354 -> Result<T, ValidationError> {
212 let real_index = if index >= 0 { 355 let real_index = if index >= 0 {
213 index as usize 356 index as usize
214 } else { 357 } else {
215 panic!("Tried to claim null handle!"); 358 return Err(ValidationError::UnexpectedInvalidHandle);
216 }; 359 };
217 // TODO(mknyszek): Check to make sure index is valid 360 // If the index exceeds our number of handles or if we have already clai med that handle
361 if real_index >= self.handles.len() || real_index < self.handles_claimed {
362 return Err(ValidationError::IllegalHandle);
363 }
364 self.handles_claimed = real_index + 1;
218 let raw_handle = self.handles[real_index].get_native_handle(); 365 let raw_handle = self.handles[real_index].get_native_handle();
219 unsafe { 366 unsafe {
220 // TODO(mknyszek): Return an error instead of panicking
221 self.handles[real_index].invalidate(); 367 self.handles[real_index].invalidate();
222 T::from_untyped(system::acquire(raw_handle)) 368 Ok(T::from_untyped(system::acquire(raw_handle)))
223 } 369 }
224 } 370 }
225 371
226 /// Immutably borrow a decoding state via Context. 372 /// Immutably borrow a decoding state via Context.
227 pub fn get(&self, context: &Context) -> &DecodingState<'slice> { 373 pub fn get(&self, context: &Context) -> &DecodingState<'slice> {
228 &self.states[context.id()] 374 &self.states[context.id()]
229 } 375 }
230 376
231 /// Mutably borrow a decoding state via Context. 377 /// Mutably borrow a decoding state via Context.
232 pub fn get_mut(&mut self, context: &Context) -> &mut DecodingState<'slice> { 378 pub fn get_mut(&mut self, context: &Context) -> &mut DecodingState<'slice> {
233 &mut self.states[context.id()] 379 &mut self.states[context.id()]
234 } 380 }
235 } 381 }
OLDNEW
« no previous file with comments | « no previous file | mojo/public/rust/src/bindings/encoding.rs » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698