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

Unified Diff: mojo/public/rust/src/bindings/mojom.rs

Issue 2220183003: Rust: Add validation to decode (Closed) Base URL: git@github.com:domokit/mojo.git@coder-testing
Patch Set: Ran rustfmt, cleaned up with try! 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
Index: mojo/public/rust/src/bindings/mojom.rs
diff --git a/mojo/public/rust/src/bindings/mojom.rs b/mojo/public/rust/src/bindings/mojom.rs
index 2075dd6e4b3d9093db150e23557b8cccc4972acb..310ff7b3db07b4d9e62f0082e774b9d6a3e90cd7 100644
--- a/mojo/public/rust/src/bindings/mojom.rs
+++ b/mojo/public/rust/src/bindings/mojom.rs
@@ -2,10 +2,9 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+use bindings::decoding::{Decoder, ValidationError};
use bindings::encoding;
use bindings::encoding::{Bits, Encoder, Context, DATA_HEADER_SIZE, DataHeader, DataHeaderValue};
-use bindings::decoding::Decoder;
-
use bindings::message::MessageHeader;
use std::cmp::Eq;
@@ -24,6 +23,9 @@ use system::wait_set;
/// The size of a Mojom map plus header in bytes.
const MAP_SIZE: usize = 24;
+/// The sorted set of versions for a map.
+const MAP_VERSIONS: [(u32, u32); 1] = [(0, MAP_SIZE as u32)];
+
/// The size of a Mojom union in bytes (header included).
pub const UNION_SIZE: usize = 16;
@@ -62,7 +64,7 @@ pub trait MojomEncodable: Sized {
fn encode(self, encoder: &mut Encoder, context: Context);
/// Using a decoder, decodes itself out of a byte buffer.
- fn decode(decoder: &mut Decoder, context: Context) -> Self;
+ fn decode(decoder: &mut Decoder, context: Context) -> Result<Self, ValidationError>;
}
/// Whatever implements this trait is a Mojom pointer type which means
@@ -79,7 +81,7 @@ pub trait MojomPointer: MojomEncodable {
fn encode_value(self, encoder: &mut Encoder, context: Context);
/// Decodes the actual values of the type into the decoder.
- fn decode_value(decoder: &mut Decoder, context: Context) -> Self;
+ fn decode_value(decoder: &mut Decoder, context: Context) -> Result<Self, ValidationError>;
/// Writes a pointer inlined into the current context before calling
/// encode_value.
@@ -92,8 +94,11 @@ pub trait MojomPointer: MojomEncodable {
/// Reads a pointer inlined into the current context before calling
/// decode_value.
- fn decode_new(decoder: &mut Decoder, _context: Context, pointer: u64) -> Self {
- let new_context = decoder.claim(pointer as usize).unwrap();
+ fn decode_new(decoder: &mut Decoder,
+ _context: Context,
+ pointer: u64)
+ -> Result<Self, ValidationError> {
+ let new_context = try!(decoder.claim(pointer as usize));
Self::decode_value(decoder, new_context)
}
}
@@ -109,7 +114,7 @@ pub trait MojomUnion: MojomEncodable {
fn encode_value(self, encoder: &mut Encoder, context: Context);
/// Decode the actual value of the union.
- fn decode_value(decoder: &mut Decoder, context: Context) -> Self;
+ fn decode_value(decoder: &mut Decoder, context: Context) -> Result<Self, ValidationError>;
/// The embed_size for when the union acts as a pointer type.
fn nested_embed_size() -> Bits {
@@ -130,12 +135,18 @@ pub trait MojomUnion: MojomEncodable {
}
/// The decoding routine for when the union acts as a pointer type.
- fn nested_decode(decoder: &mut Decoder, context: Context) -> Self {
+ fn nested_decode(decoder: &mut Decoder, context: Context) -> Result<Self, ValidationError> {
let global_offset = {
let state = decoder.get_mut(&context);
- state.decode_pointer() as usize
+ match state.decode_pointer() {
+ Some(ptr) => ptr as usize,
+ None => return Err(ValidationError::IllegalPointer),
+ }
};
- let new_context = decoder.claim(global_offset).unwrap();
+ if global_offset == (MOJOM_NULL_POINTER as usize) {
+ return Err(ValidationError::UnexpectedNullPointer);
+ }
+ let new_context = try!(decoder.claim(global_offset as usize));
Self::decode_value(decoder, new_context)
}
@@ -161,19 +172,19 @@ pub trait MojomUnion: MojomEncodable {
}
/// The decoding routine for when the union is inlined into the current context.
- fn inline_decode(decoder: &mut Decoder, context: Context) -> Self {
+ fn inline_decode(decoder: &mut Decoder, context: Context) -> Result<Self, ValidationError> {
{
let mut state = decoder.get_mut(&context);
state.align_to_byte();
state.align_to_bytes(8);
}
- let value = Self::decode_value(decoder, context.clone());
+ let value = try!(Self::decode_value(decoder, context.clone()));
{
let mut state = decoder.get_mut(&context);
state.align_to_byte();
state.align_to_bytes(8);
}
- value
+ Ok(value)
}
}
@@ -243,7 +254,7 @@ pub trait MojomInterfaceRecv: MojomInterface {
type Container: MojomMessageOption;
/// Tries to read a message from a pipe and decodes it.
- fn recv_response(&self) -> Self::Container {
+ fn recv_response(&self) -> Result<Self::Container, ValidationError> {
// TODO(mknyszek): Actually handle errors with reading
let (buffer, handles) = self.pipe().read(mpflags!(Read::None)).unwrap();
Self::Container::decode_message(buffer, handles)
@@ -273,7 +284,9 @@ pub trait MojomStruct: MojomPointer {
}
/// Decode the type from a byte array and a set of handles.
- fn deserialize(buffer: &mut [u8], handles: Vec<UntypedHandle>) -> Self {
+ fn deserialize(buffer: &mut [u8],
+ handles: Vec<UntypedHandle>)
+ -> Result<Self, ValidationError> {
let mut decoder = Decoder::new(buffer, handles);
Self::decode_new(&mut decoder, Default::default(), 0)
}
@@ -294,14 +307,19 @@ pub trait MojomMessageOption: Sized {
/// Decodes the actual payload of the message.
///
/// Implemented by a code generator.
- fn decode_payload(name: u32, buffer: &mut [u8], handles: Vec<UntypedHandle>) -> Self;
+ fn decode_payload(header: MessageHeader,
+ buffer: &mut [u8],
+ handles: Vec<UntypedHandle>)
+ -> Result<Self, ValidationError>;
/// Decodes the message header and then the payload, returning a new
/// copy of itself.
- fn decode_message(mut buffer: Vec<u8>, handles: Vec<UntypedHandle>) -> Self {
- let header = MessageHeader::deserialize(&mut buffer[..], Vec::new());
+ fn decode_message(mut buffer: Vec<u8>,
+ handles: Vec<UntypedHandle>)
+ -> Result<Self, ValidationError> {
+ let header = try!(MessageHeader::deserialize(&mut buffer[..], Vec::new()));
let payload_buffer = &mut buffer[header.serialized_size(&Default::default())..];
- Self::decode_payload(header.name, payload_buffer, handles)
+ Self::decode_payload(header, payload_buffer, handles)
}
}
@@ -329,9 +347,9 @@ macro_rules! impl_encodable_for_prim {
let mut state = encoder.get_mut(&context);
state.encode(self);
}
- fn decode(decoder: &mut Decoder, context: Context) -> Self {
+ fn decode(decoder: &mut Decoder, context: Context) -> Result<Self, ValidationError> {
let mut state = decoder.get_mut(&context);
- state.decode::<Self>()
+ Ok(state.decode::<Self>())
}
}
)*
@@ -342,7 +360,7 @@ impl_encodable_for_prim!(i8, i16, i32, i64, u8, u16, u32, u64, f32, f64);
impl MojomEncodable for bool {
fn mojom_alignment() -> usize {
- panic!("Should never check mojom_alignment of bools (they're bit-aligned)!");
+ panic!("Should never check_decode mojom_alignment of bools (they're bit-aligned)!");
}
fn mojom_type() -> MojomType {
MojomType::Simple
@@ -357,9 +375,9 @@ impl MojomEncodable for bool {
let mut state = encoder.get_mut(&context);
state.encode_bool(self);
}
- fn decode(decoder: &mut Decoder, context: Context) -> Self {
+ fn decode(decoder: &mut Decoder, context: Context) -> Result<Self, ValidationError> {
let mut state = decoder.get_mut(&context);
- state.decode_bool()
+ Ok(state.decode_bool())
}
}
@@ -394,13 +412,13 @@ impl<T: MojomEncodable> MojomEncodable for Option<T> {
MojomType::Interface => {
state.encode_null_handle();
state.encode(0 as u32);
- },
+ }
MojomType::Simple => panic!("Unexpected simple type in Option!"),
}
- },
+ }
}
}
- fn decode(decoder: &mut Decoder, context: Context) -> Self {
+ fn decode(decoder: &mut Decoder, context: Context) -> Result<Self, ValidationError> {
let skipped = {
let mut state = decoder.get_mut(&context);
match T::mojom_type() {
@@ -412,9 +430,10 @@ impl<T: MojomEncodable> MojomEncodable for Option<T> {
}
};
if skipped {
- None
+ Ok(None)
} else {
- Some(T::decode(decoder, context))
+ let inner_value = try!(T::decode(decoder, context));
+ Ok(Some(inner_value))
}
}
}
@@ -454,18 +473,17 @@ impl<T: MojomEncodable> MojomPointer for Vec<T> {
elem.encode(encoder, context.clone());
}
}
- fn decode_value(decoder: &mut Decoder, context: Context) -> Vec<T> {
- // TODO(mknyszek): Verify elems, bytes, and T::embed_size match
- let (_bytes, elems) = {
+ fn decode_value(decoder: &mut Decoder, context: Context) -> Result<Vec<T>, ValidationError> {
+ let elems = {
let mut state = decoder.get_mut(&context);
- (state.decode::<u32>(), state.decode::<u32>())
+ try!(state.decode_array_header::<T>()).data()
};
let mut value = Vec::with_capacity(elems as usize);
- // TODO(mknyszek): Don't trust elems blindly
for _ in 0..elems {
- value.push(T::decode(decoder, context.clone()));
+ let elem = try!(T::decode(decoder, context.clone()));
+ value.push(elem);
}
- value
+ Ok(value)
}
}
@@ -483,24 +501,42 @@ macro_rules! impl_encodable_for_boxed_fixed_array {
elem.encode(encoder, context.clone());
}
}
- fn decode_value(decoder: &mut Decoder, context: Context) -> Box<[T; $len]> {
- // TODO(mknyszek): Verify elems, bytes, and T::embed_size match
- let (_bytes, elems) = {
+ fn decode_value(decoder: &mut Decoder, context: Context) -> Result<Box<[T; $len]>, ValidationError> {
+ let elems = {
let mut state = decoder.get_mut(&context);
- (state.decode::<u32>(), state.decode::<u32>())
+ try!(state.decode_array_header::<T>()).data()
};
- assert_eq!(elems, $len);
+ if elems != $len {
+ return Err(ValidationError::UnexpectedArrayHeader);
+ }
let mut array: [T; $len];
+ let mut error = None;
unsafe {
// Since we don't force Default to be implemented on Mojom types
// (mainly due to handles) we need to create this array as uninitialized
// and initialize it manually.
array = mem::uninitialized();
+ let mut inits = 0;
for elem in &mut array[..] {
- ptr::write(elem, T::decode(decoder, context.clone()));
+ match T::decode(decoder, context.clone()) {
+ Ok(value) => ptr::write(elem, value),
+ Err(err) => {
+ error = Some(err);
+ break;
+ },
+ }
+ inits += 1;
+ }
+ // An error occurred, so we need to do some clean-up.
+ if inits != $len {
+ for i in 0..inits {
+ ptr::drop_in_place(&mut array[i] as *mut T);
+ }
+ mem::forget(array);
+ return Err(error.take().expect("Corrupted stack?"));
}
}
- Box::new(array)
+ Ok(Box::new(array))
}
}
impl<T: MojomEncodable> MojomEncodable for Box<[T; $len]> {
@@ -527,8 +563,9 @@ impl<T: MojomEncodable> MojomPointer for Box<[T]> {
elem.encode(encoder, context.clone());
}
}
- fn decode_value(decoder: &mut Decoder, context: Context) -> Box<[T]> {
- Vec::<T>::decode_value(decoder, context).into_boxed_slice()
+ fn decode_value(decoder: &mut Decoder, context: Context) -> Result<Box<[T]>, ValidationError> {
+ let vec_value = try!(Vec::<T>::decode_value(decoder, context));
+ Ok(vec_value.into_boxed_slice())
}
}
@@ -550,19 +587,15 @@ impl MojomPointer for String {
byte.encode(encoder, context.clone());
}
}
- fn decode_value(decoder: &mut Decoder, context: Context) -> String {
+ fn decode_value(decoder: &mut Decoder, context: Context) -> Result<String, ValidationError> {
let mut state = decoder.get_mut(&context);
- // TODO(mknyszek): Verify elems, bytes, and T::embed_size match
- let _bytes = state.decode::<u32>();
- let elems = state.decode::<u32>();
+ let elems = try!(state.decode_array_header::<u8>()).data();
let mut value = Vec::with_capacity(elems as usize);
- // TODO(mknyszek): Don't trust elems blindly
for _ in 0..elems {
value.push(state.decode::<u8>());
}
match String::from_utf8(value) {
- Ok(string) => string,
- // TODO(mknyszek): Don't panic and return an error
+ Ok(string) => Ok(string),
Err(err) => panic!("Error decoding String: {}", err),
}
}
@@ -575,6 +608,19 @@ impl MojomEncodable for String {
}
}
+/// Helper function to clean up duplicate code in HashMap.
+fn array_claim_and_decode_header<T: MojomEncodable>
+ (decoder: &mut Decoder,
+ offset: usize)
+ -> Result<(Context, usize), ValidationError> {
+ let context = try!(decoder.claim(offset));
+ let elems = {
+ let state = decoder.get_mut(&context);
+ try!(state.decode_array_header::<T>()).data()
+ };
+ Ok((context, elems as usize))
+}
+
impl<K: MojomEncodable + Eq + Hash, V: MojomEncodable> MojomPointer for HashMap<K, V> {
fn header_data(&self) -> DataHeaderValue {
DataHeaderValue::Version(0)
@@ -608,42 +654,45 @@ impl<K: MojomEncodable + Eq + Hash, V: MojomEncodable> MojomPointer for HashMap<
// Encode vals
vals_vec.encode(encoder, context.clone())
}
- fn decode_value(decoder: &mut Decoder, context: Context) -> HashMap<K, V> {
- // TODO(mknyszek): Verify elems, bytes, and T::embed_size match
+ fn decode_value(decoder: &mut Decoder,
+ context: Context)
+ -> Result<HashMap<K, V>, ValidationError> {
let (keys_offset, vals_offset) = {
- let mut state = decoder.get_mut(&context);
- let _bytes = state.decode::<u32>();
- let _version = state.decode::<u32>();
- let keys_offset = state.decode_pointer() as usize;
- let vals_offset = state.decode_pointer() as usize;
- (keys_offset, vals_offset)
- };
- let keys_context = decoder.claim(keys_offset).unwrap();
- let keys_elems = {
- let state = decoder.get_mut(&keys_context);
- let _size_keys = state.decode::<u32>();
- state.decode::<u32>()
+ let state = decoder.get_mut(&context);
+ try!(state.decode_struct_header(&MAP_VERSIONS));
+ // Decode the keys pointer and check for overflow
+ let keys_offset = match state.decode_pointer() {
+ Some(ptr) => ptr,
+ None => return Err(ValidationError::IllegalPointer),
+ };
+ // Decode the keys pointer and check for overflow
+ let vals_offset = match state.decode_pointer() {
+ Some(ptr) => ptr,
+ None => return Err(ValidationError::IllegalPointer),
+ };
+ if keys_offset == MOJOM_NULL_POINTER || vals_offset == MOJOM_NULL_POINTER {
+ return Err(ValidationError::UnexpectedNullPointer);
+ }
+ (keys_offset as usize, vals_offset as usize)
};
+ let (keys_context, keys_elems) =
+ try!(array_claim_and_decode_header::<K>(decoder, keys_offset));
let mut keys_vec: Vec<K> = Vec::with_capacity(keys_elems as usize);
for _ in 0..keys_elems {
- let key = K::decode(decoder, keys_context.clone());
+ let key = try!(K::decode(decoder, keys_context.clone()));
keys_vec.push(key);
}
- let vals_context = decoder.claim(vals_offset).unwrap();
- let vals_elems = {
- let state = decoder.get_mut(&vals_context);
- let _size_vals = state.decode::<u32>();
- state.decode::<u32>()
- };
- // TODO(mknyszek): Do a validation error instead
- assert_eq!(keys_elems, vals_elems);
- // TODO(mknyszek): Don't trust elems blindly
+ let (vals_context, vals_elems) =
+ try!(array_claim_and_decode_header::<V>(decoder, vals_offset));
+ if keys_elems != vals_elems {
+ return Err(ValidationError::DifferentSizedArraysInMap);
+ }
let mut map = HashMap::with_capacity(keys_elems as usize);
for key in keys_vec.into_iter() {
- let val = V::decode(decoder, vals_context.clone());
+ let val = try!(V::decode(decoder, vals_context.clone()));
map.insert(key, val);
}
- map
+ Ok(map)
}
}
@@ -677,7 +726,7 @@ impl<K: MojomEncodable + Eq + Hash, V: MojomEncodable> MojomEncodable for HashMa
impl<T: MojomEncodable + CastHandle + Handle> MojomHandle for T {}
macro_rules! impl_encodable_for_handle {
- ($handle_type:path) => {
+ ($handle_t:path) => {
fn mojom_alignment() -> usize {
4
}
@@ -695,13 +744,12 @@ macro_rules! impl_encodable_for_handle {
let mut state = encoder.get_mut(&context);
state.encode(pos as i32);
}
- fn decode(decoder: &mut Decoder, context: Context) -> $handle_type {
- // TODO(mknyszek): Verify handle index
+ fn decode(decoder: &mut Decoder, context: Context) -> Result<$handle_t, ValidationError> {
let handle_index = {
let mut state = decoder.get_mut(&context);
state.decode::<i32>()
};
- decoder.claim_handle::<$handle_type>(handle_index)
+ decoder.claim_handle::<$handle_t>(handle_index)
}
}
}

Powered by Google App Engine
This is Rietveld 408576698