| OLD | NEW |
| (Empty) |
| 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 | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 use bindings::encoding::{Bits, Context, DataHeader, DataHeaderValue, DATA_HEADER
_SIZE, | |
| 6 MojomNumeric}; | |
| 7 use bindings::mojom::{MojomEncodable, MOJOM_NULL_POINTER, UNION_SIZE}; | |
| 8 use bindings::util; | |
| 9 | |
| 10 use std::mem; | |
| 11 use std::ptr; | |
| 12 use std::vec::Vec; | |
| 13 | |
| 14 use system; | |
| 15 use system::{Handle, CastHandle, UntypedHandle}; | |
| 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 | |
| 54 /// An decoding state represents the decoding logic for a single | |
| 55 /// Mojom object that is NOT inlined, such as a struct or an array. | |
| 56 pub struct DecodingState<'slice> { | |
| 57 /// The buffer the state may write to. | |
| 58 data: &'slice [u8], | |
| 59 | |
| 60 /// The offset of this serialized object into the overall buffer. | |
| 61 global_offset: usize, | |
| 62 | |
| 63 /// The current offset within 'data'. | |
| 64 offset: usize, | |
| 65 | |
| 66 /// The current bit offset within 'data'. | |
| 67 bit_offset: Bits, | |
| 68 } | |
| 69 | |
| 70 impl<'slice> DecodingState<'slice> { | |
| 71 /// Create a new decoding state. | |
| 72 pub fn new(buffer: &'slice [u8], offset: usize) -> DecodingState<'slice> { | |
| 73 DecodingState { | |
| 74 data: buffer, | |
| 75 global_offset: offset, | |
| 76 offset: 0, | |
| 77 bit_offset: Bits(0), | |
| 78 } | |
| 79 } | |
| 80 | |
| 81 /// Align the decoding state to the next byte. | |
| 82 pub fn align_to_byte(&mut self) { | |
| 83 if self.bit_offset > Bits(0) { | |
| 84 self.offset += 1; | |
| 85 self.bit_offset = Bits(0); | |
| 86 } | |
| 87 } | |
| 88 | |
| 89 /// Align the decoding state to the next 'bytes' boundary. | |
| 90 pub fn align_to_bytes(&mut self, bytes: usize) { | |
| 91 if self.offset != 0 { | |
| 92 self.offset = util::align_bytes(self.offset, bytes); | |
| 93 } | |
| 94 } | |
| 95 | |
| 96 /// Read a primitive from the buffer without incrementing the offset. | |
| 97 fn read_in_place<T: MojomNumeric>(&mut self) -> T { | |
| 98 let mut value: T = Default::default(); | |
| 99 debug_assert!(mem::size_of::<T>() + self.offset <= self.data.len()); | |
| 100 let ptr = (&self.data[self.offset..]).as_ptr(); | |
| 101 unsafe { | |
| 102 ptr::copy_nonoverlapping(mem::transmute::<*const u8, *const T>(ptr), | |
| 103 &mut value as *mut T, | |
| 104 1); | |
| 105 } | |
| 106 value | |
| 107 } | |
| 108 | |
| 109 /// Read a primitive from the buffer and increment the offset. | |
| 110 fn read<T: MojomNumeric>(&mut self) -> T { | |
| 111 let value = self.read_in_place::<T>(); | |
| 112 self.bit_offset = Bits(0); | |
| 113 self.offset += mem::size_of::<T>(); | |
| 114 value | |
| 115 } | |
| 116 | |
| 117 /// Decode a primitive from the buffer, naturally aligning before we read. | |
| 118 pub fn decode<T: MojomNumeric>(&mut self) -> T { | |
| 119 self.align_to_byte(); | |
| 120 self.align_to_bytes(mem::size_of::<T>()); | |
| 121 self.read::<T>() | |
| 122 } | |
| 123 | |
| 124 /// Decode a boolean value from the buffer as one bit. | |
| 125 pub fn decode_bool(&mut self) -> bool { | |
| 126 let offset = self.offset; | |
| 127 // Check the bit by getting the set bit and checking if its non-zero | |
| 128 let value = (self.data[offset] & self.bit_offset.as_set_bit()) > 0; | |
| 129 self.bit_offset += Bits(1); | |
| 130 let (bits, bytes) = self.bit_offset.as_bits_and_bytes(); | |
| 131 self.offset += bytes; | |
| 132 self.bit_offset = bits; | |
| 133 value | |
| 134 } | |
| 135 | |
| 136 /// If we encounter a null pointer, increment past it. | |
| 137 /// | |
| 138 /// Returns if we skipped or not. | |
| 139 pub fn skip_if_null_pointer(&mut self) -> bool { | |
| 140 self.align_to_byte(); | |
| 141 self.align_to_bytes(8); | |
| 142 let ptr = self.read_in_place::<u64>(); | |
| 143 if ptr == MOJOM_NULL_POINTER { | |
| 144 self.offset += 8; | |
| 145 } | |
| 146 (ptr == MOJOM_NULL_POINTER) | |
| 147 } | |
| 148 | |
| 149 /// If we encounter a null union, increment past it. | |
| 150 /// | |
| 151 /// Returns if we skipped or not. | |
| 152 pub fn skip_if_null_union(&mut self) -> bool { | |
| 153 self.align_to_byte(); | |
| 154 self.align_to_bytes(8); | |
| 155 let size = self.read_in_place::<u32>(); | |
| 156 if size == 0 { | |
| 157 self.offset += UNION_SIZE; | |
| 158 } | |
| 159 (size == 0) | |
| 160 } | |
| 161 | |
| 162 /// If we encounter a null handle, increment past it. | |
| 163 /// | |
| 164 /// Returns if we skipped or not. | |
| 165 pub fn skip_if_null_handle(&mut self) -> bool { | |
| 166 self.align_to_byte(); | |
| 167 self.align_to_bytes(4); | |
| 168 let index = self.read_in_place::<i32>(); | |
| 169 if index < 0 { | |
| 170 self.offset += 4; | |
| 171 } | |
| 172 (index < 0) | |
| 173 } | |
| 174 | |
| 175 /// If we encounter a null interface, increment past it. | |
| 176 /// | |
| 177 /// Returns if we skipped or not. | |
| 178 pub fn skip_if_null_interface(&mut self) -> bool { | |
| 179 self.align_to_byte(); | |
| 180 self.align_to_bytes(4); | |
| 181 let index = self.read_in_place::<i32>(); | |
| 182 if index < 0 { | |
| 183 self.offset += 8; | |
| 184 } | |
| 185 (index < 0) | |
| 186 } | |
| 187 | |
| 188 /// Decode a pointer from the buffer as a global offset into the buffer. | |
| 189 /// | |
| 190 /// The pointer in the buffer is an offset relative to the pointer to anothe
r | |
| 191 /// location in the buffer. We convert that to an absolute offset with respe
ct | |
| 192 /// to the buffer before returning. This is our defintion of a pointer. | |
| 193 pub fn decode_pointer(&mut self) -> Option<u64> { | |
| 194 self.align_to_byte(); | |
| 195 self.align_to_bytes(8); | |
| 196 let current_location = (self.global_offset + self.offset) as u64; | |
| 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))) | |
| 278 } | |
| 279 } | |
| 280 | |
| 281 /// A struct that will encode a given Mojom object and convert it into | |
| 282 /// bytes and a vector of handles. | |
| 283 pub struct Decoder<'slice> { | |
| 284 bytes: usize, | |
| 285 buffer: Option<&'slice [u8]>, | |
| 286 states: Vec<DecodingState<'slice>>, | |
| 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 | |
| 290 } | |
| 291 | |
| 292 impl<'slice> Decoder<'slice> { | |
| 293 /// Create a new Decoder. | |
| 294 pub fn new(buffer: &'slice [u8], handles: Vec<UntypedHandle>) -> Decoder<'sl
ice> { | |
| 295 let max_offset = buffer.len(); | |
| 296 Decoder { | |
| 297 bytes: 0, | |
| 298 buffer: Some(buffer), | |
| 299 states: Vec::new(), | |
| 300 handles: handles, | |
| 301 handles_claimed: 0, | |
| 302 max_offset: max_offset, | |
| 303 } | |
| 304 } | |
| 305 | |
| 306 /// Claim space in the buffer to start decoding some object. | |
| 307 /// | |
| 308 /// Creates a new decoding state for the object and returns a context. | |
| 309 pub fn claim(&mut self, offset: usize) -> Result<Context, ValidationError> { | |
| 310 // Check if the layout order is sane | |
| 311 if offset < self.bytes { | |
| 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); | |
| 321 } | |
| 322 let mut buffer = self.buffer.take().expect("No buffer?"); | |
| 323 let space = offset - self.bytes; | |
| 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 | |
| 330 let mut read_size: u32 = 0; | |
| 331 unsafe { | |
| 332 ptr::copy_nonoverlapping(mem::transmute::<*const u8, *const u32>(buf
fer.as_ptr()), | |
| 333 &mut read_size as *mut u32, | |
| 334 mem::size_of::<u32>()); | |
| 335 } | |
| 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 } | |
| 341 // TODO(mknyszek): Check size for validation | |
| 342 let (claimed, unclaimed) = buffer.split_at(size); | |
| 343 self.states.push(DecodingState::new(claimed, offset)); | |
| 344 self.buffer = Some(unclaimed); | |
| 345 self.bytes += space + size; | |
| 346 Ok(Context::new(self.states.len() - 1)) | |
| 347 } | |
| 348 | |
| 349 /// Claims a handle at some particular index in the given handles array. | |
| 350 /// | |
| 351 /// Returns the handle with all type information in-tact. | |
| 352 pub fn claim_handle<T: Handle + CastHandle>(&mut self, | |
| 353 index: i32) | |
| 354 -> Result<T, ValidationError> { | |
| 355 let real_index = if index >= 0 { | |
| 356 index as usize | |
| 357 } else { | |
| 358 return Err(ValidationError::UnexpectedInvalidHandle); | |
| 359 }; | |
| 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; | |
| 365 let raw_handle = self.handles[real_index].get_native_handle(); | |
| 366 unsafe { | |
| 367 self.handles[real_index].invalidate(); | |
| 368 Ok(T::from_untyped(system::acquire(raw_handle))) | |
| 369 } | |
| 370 } | |
| 371 | |
| 372 /// Immutably borrow a decoding state via Context. | |
| 373 pub fn get(&self, context: &Context) -> &DecodingState<'slice> { | |
| 374 &self.states[context.id()] | |
| 375 } | |
| 376 | |
| 377 /// Mutably borrow a decoding state via Context. | |
| 378 pub fn get_mut(&mut self, context: &Context) -> &mut DecodingState<'slice> { | |
| 379 &mut self.states[context.id()] | |
| 380 } | |
| 381 } | |
| OLD | NEW |