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 |