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

Unified 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | mojo/public/rust/src/bindings/encoding.rs » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: mojo/public/rust/src/bindings/decoding.rs
diff --git a/mojo/public/rust/src/bindings/decoding.rs b/mojo/public/rust/src/bindings/decoding.rs
index 486b3b72a87ceeb5e99de21e8ad1112405da4a46..81c7ee769f06c88bc45039821007bdd11880ba1a 100644
--- a/mojo/public/rust/src/bindings/decoding.rs
+++ b/mojo/public/rust/src/bindings/decoding.rs
@@ -2,8 +2,9 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-use bindings::encoding::{Bits, Context, MojomNumeric};
-use bindings::mojom::{MOJOM_NULL_POINTER, UNION_SIZE};
+use bindings::encoding::{Bits, Context, DataHeader, DataHeaderValue, DATA_HEADER_SIZE,
+ MojomNumeric};
+use bindings::mojom::{MojomEncodable, MOJOM_NULL_POINTER, UNION_SIZE};
use bindings::util;
use std::mem;
@@ -13,6 +14,43 @@ use std::vec::Vec;
use system;
use system::{Handle, CastHandle, UntypedHandle};
+#[derive(Debug, Eq, PartialEq)]
+pub enum ValidationError {
+ DifferentSizedArraysInMap,
+ IllegalHandle,
+ IllegalMemoryRange,
+ IllegalPointer,
+ MessageHeaderInvalidFlags,
+ MessageHeaderMissingRequestId,
+ MessageHeaderUnknownMethod,
+ MisalignedObject,
+ UnexpectedArrayHeader,
+ UnexpectedInvalidHandle,
+ UnexpectedNullPointer,
+ UnexpectedNullUnion,
+ UnexpectedStructHeader,
+}
+
+impl ValidationError {
+ pub fn as_str(self) -> &'static str {
+ match self {
+ ValidationError::DifferentSizedArraysInMap => "VALIDATION_ERROR_DIFFERENT_SIZED_ARRAYS_IN_MAP",
+ ValidationError::IllegalHandle => "VALIDATION_ERROR_ILLEGAL_HANDLE",
+ ValidationError::IllegalMemoryRange => "VALIDATION_ERROR_ILLEGAL_MEMORY_RANGE",
+ ValidationError::IllegalPointer => "VALIDATION_ERROR_ILLEGAL_POINTER",
+ ValidationError::MessageHeaderInvalidFlags => "VALIDATION_ERROR_MESSAGE_HEADER_INVALID_FLAGS",
+ ValidationError::MessageHeaderMissingRequestId => "VALIDATION_ERROR_MESSAGE_HEADER_MISSING_REQUEST_ID",
+ ValidationError::MessageHeaderUnknownMethod => "VALIDATION_ERROR_MESSAGE_HEADER_UNKNOWN_METHOD",
+ ValidationError::MisalignedObject => "VALIDATION_ERROR_MISALIGNED_OBJECT",
+ ValidationError::UnexpectedArrayHeader => "VALIDATION_ERROR_UNEXPECTED_ARRAY_HEADER",
+ ValidationError::UnexpectedInvalidHandle => "VALIDATION_ERROR_UNEXPECTED_INVALID_HANDLE",
+ ValidationError::UnexpectedNullPointer => "VALIDATION_ERROR_UNEXPECTED_NULL_POINTER",
+ ValidationError::UnexpectedNullUnion => "VALIDATION_ERROR_UNEXPECTED_NULL_UNION",
+ ValidationError::UnexpectedStructHeader => "VALIDATION_ERROR_UNEXPECTED_STRUCT_HEADER",
+ }
+ }
+}
+
/// An decoding state represents the decoding logic for a single
/// Mojom object that is NOT inlined, such as a struct or an array.
pub struct DecodingState<'slice> {
@@ -152,11 +190,91 @@ impl<'slice> DecodingState<'slice> {
/// The pointer in the buffer is an offset relative to the pointer to another
/// location in the buffer. We convert that to an absolute offset with respect
/// to the buffer before returning. This is our defintion of a pointer.
- pub fn decode_pointer(&mut self) -> u64 {
+ pub fn decode_pointer(&mut self) -> Option<u64> {
self.align_to_byte();
self.align_to_bytes(8);
let current_location = (self.global_offset + self.offset) as u64;
- self.read::<u64>() + current_location
+ let offset = self.read::<u64>();
+ if offset == MOJOM_NULL_POINTER {
+ Some(MOJOM_NULL_POINTER)
+ } else {
+ offset.checked_add(current_location)
+ }
+ }
+
+ /// A routine for decoding an array header.
+ ///
+ /// Must be called with offset zero (that is, it must be the first thing
+ /// decoded). Performs numerous validation checks.
+ pub fn decode_array_header<T>(&mut self) -> Result<DataHeader, ValidationError>
+ where T: MojomEncodable
+ {
+ debug_assert_eq!(self.offset, 0);
+ // Make sure we can read the size first...
+ if self.data.len() < mem::size_of::<u32>() {
+ return Err(ValidationError::UnexpectedArrayHeader);
+ }
+ let bytes = self.decode::<u32>();
+ if (bytes as usize) < DATA_HEADER_SIZE {
+ return Err(ValidationError::UnexpectedArrayHeader);
+ }
+ let elems = self.decode::<u32>();
+ match T::embed_size(&Default::default()).checked_mul(elems as usize) {
+ Some(value) => {
+ if (bytes as usize) < value.as_bytes() + DATA_HEADER_SIZE {
+ return Err(ValidationError::UnexpectedArrayHeader);
+ }
+ }
+ None => return Err(ValidationError::UnexpectedArrayHeader),
+ }
+ Ok(DataHeader::new(bytes as usize, DataHeaderValue::Elements(elems)))
+ }
+
+ /// A routine for decoding an struct header.
+ ///
+ /// Must be called with offset zero (that is, it must be the first thing
+ /// decoded). Performs numerous validation checks.
+ pub fn decode_struct_header(&mut self,
+ versions: &[(u32, u32)])
+ -> Result<DataHeader, ValidationError> {
+ debug_assert_eq!(self.offset, 0);
+ // Make sure we can read the size first...
+ if self.data.len() < mem::size_of::<u32>() {
+ return Err(ValidationError::UnexpectedStructHeader);
+ }
+ let bytes = self.decode::<u32>();
+ if (bytes as usize) < DATA_HEADER_SIZE {
+ return Err(ValidationError::UnexpectedStructHeader);
+ }
+ let version = self.decode::<u32>();
+ // Versioning validation: versions are generated as a sorted array of tuples, so
+ // to find the version we are given by the header we use a binary search.
+ match versions.binary_search_by(|val| val.0.cmp(&version)) {
+ Ok(idx) => {
+ let (_, size) = versions[idx];
+ if bytes != size {
+ return Err(ValidationError::UnexpectedStructHeader);
+ }
+ }
+ Err(idx) => {
+ if idx == 0 {
+ panic!("Should be earliest version? \
+ Versions: {:?}, \
+ Version: {}, \
+ Size: {}", versions, version, bytes);
+ }
+ let len = versions.len();
+ let (latest_version, _) = versions[len - 1];
+ let (_, size) = versions[idx - 1];
+ // If this is higher than any version we know, its okay for the size to be bigger,
+ // but if its a version we know about, it must match the size.
+ if (version > latest_version && bytes < size) ||
+ (version <= latest_version && bytes != size) {
+ return Err(ValidationError::UnexpectedStructHeader);
+ }
+ }
+ }
+ Ok(DataHeader::new(bytes as usize, DataHeaderValue::Version(version)))
}
}
@@ -167,29 +285,48 @@ pub struct Decoder<'slice> {
buffer: Option<&'slice [u8]>,
states: Vec<DecodingState<'slice>>,
handles: Vec<UntypedHandle>,
+ handles_claimed: usize, // A length that claims all handles were claimed up to this index
+ max_offset: usize, // Represents the maximum value an offset may have
}
impl<'slice> Decoder<'slice> {
/// Create a new Decoder.
pub fn new(buffer: &'slice [u8], handles: Vec<UntypedHandle>) -> Decoder<'slice> {
+ let max_offset = buffer.len();
Decoder {
bytes: 0,
buffer: Some(buffer),
states: Vec::new(),
handles: handles,
+ handles_claimed: 0,
+ max_offset: max_offset,
}
}
/// Claim space in the buffer to start decoding some object.
///
/// Creates a new decoding state for the object and returns a context.
- pub fn claim(&mut self, offset: usize) -> Option<Context> {
+ pub fn claim(&mut self, offset: usize) -> Result<Context, ValidationError> {
+ // Check if the layout order is sane
if offset < self.bytes {
- panic!("Tried to claim already-claimed space!");
+ return Err(ValidationError::IllegalMemoryRange);
+ }
+ // Check for 8-byte alignment
+ if offset & 7 != 0 {
+ return Err(ValidationError::MisalignedObject);
+ }
+ // Bounds check on offset
+ if offset > self.max_offset {
+ return Err(ValidationError::IllegalPointer);
}
let mut buffer = self.buffer.take().expect("No buffer?");
let space = offset - self.bytes;
buffer = &buffer[space..];
+ // Make sure we can even read the bytes in the header
+ if buffer.len() < mem::size_of::<u32>() {
+ return Err(ValidationError::IllegalMemoryRange);
+ }
+ // Read the number of bytes in the memory region according to the data header
let mut read_size: u32 = 0;
unsafe {
ptr::copy_nonoverlapping(mem::transmute::<*const u8, *const u32>(buffer.as_ptr()),
@@ -197,29 +334,38 @@ impl<'slice> Decoder<'slice> {
mem::size_of::<u32>());
}
let size = u32::from_le(read_size) as usize;
+ // Make sure the size we read is sane...
+ if size > buffer.len() {
+ return Err(ValidationError::IllegalMemoryRange);
+ }
// TODO(mknyszek): Check size for validation
let (claimed, unclaimed) = buffer.split_at(size);
self.states.push(DecodingState::new(claimed, offset));
self.buffer = Some(unclaimed);
self.bytes += space + size;
- Some(Context::new(self.states.len() - 1))
+ Ok(Context::new(self.states.len() - 1))
}
/// Claims a handle at some particular index in the given handles array.
///
/// Returns the handle with all type information in-tact.
- pub fn claim_handle<T: Handle + CastHandle>(&mut self, index: i32) -> T {
+ pub fn claim_handle<T: Handle + CastHandle>(&mut self,
+ index: i32)
+ -> Result<T, ValidationError> {
let real_index = if index >= 0 {
index as usize
} else {
- panic!("Tried to claim null handle!");
+ return Err(ValidationError::UnexpectedInvalidHandle);
};
- // TODO(mknyszek): Check to make sure index is valid
+ // If the index exceeds our number of handles or if we have already claimed that handle
+ if real_index >= self.handles.len() || real_index < self.handles_claimed {
+ return Err(ValidationError::IllegalHandle);
+ }
+ self.handles_claimed = real_index + 1;
let raw_handle = self.handles[real_index].get_native_handle();
unsafe {
- // TODO(mknyszek): Return an error instead of panicking
self.handles[real_index].invalidate();
- T::from_untyped(system::acquire(raw_handle))
+ Ok(T::from_untyped(system::acquire(raw_handle)))
}
}
« 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