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

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

Powered by Google App Engine
This is Rietveld 408576698