| @@ -161,7 +161,7 @@ pub struct PruneData { | |||||||
| impl Sanitize for PruneData { | impl Sanitize for PruneData { | ||||||
|     fn sanitize(&self) -> std::result::Result<(), SanitizeError> { |     fn sanitize(&self) -> std::result::Result<(), SanitizeError> { | ||||||
|         if self.wallclock >= MAX_WALLCLOCK { |         if self.wallclock >= MAX_WALLCLOCK { | ||||||
|             return Err(SanitizeError::ValueOutOfRange); |             return Err(SanitizeError::ValueOutOfBounds); | ||||||
|         } |         } | ||||||
|         Ok(()) |         Ok(()) | ||||||
|     } |     } | ||||||
| @@ -2822,7 +2822,7 @@ mod tests { | |||||||
|         let mut pd = PruneData::default(); |         let mut pd = PruneData::default(); | ||||||
|         pd.wallclock = MAX_WALLCLOCK; |         pd.wallclock = MAX_WALLCLOCK; | ||||||
|         let msg = Protocol::PruneMessage(Pubkey::default(), pd); |         let msg = Protocol::PruneMessage(Pubkey::default(), pd); | ||||||
|         assert_eq!(msg.sanitize(), Err(SanitizeError::ValueOutOfRange)); |         assert_eq!(msg.sanitize(), Err(SanitizeError::ValueOutOfBounds)); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     // computes the maximum size for pull request blooms |     // computes the maximum size for pull request blooms | ||||||
|   | |||||||
| @@ -42,7 +42,7 @@ pub struct ContactInfo { | |||||||
| impl Sanitize for ContactInfo { | impl Sanitize for ContactInfo { | ||||||
|     fn sanitize(&self) -> std::result::Result<(), SanitizeError> { |     fn sanitize(&self) -> std::result::Result<(), SanitizeError> { | ||||||
|         if self.wallclock >= MAX_WALLCLOCK { |         if self.wallclock >= MAX_WALLCLOCK { | ||||||
|             return Err(SanitizeError::Failed); |             return Err(SanitizeError::ValueOutOfBounds); | ||||||
|         } |         } | ||||||
|         Ok(()) |         Ok(()) | ||||||
|     } |     } | ||||||
| @@ -325,4 +325,12 @@ mod tests { | |||||||
|         ci.rpc = socketaddr!("127.0.0.1:234"); |         ci.rpc = socketaddr!("127.0.0.1:234"); | ||||||
|         assert!(ci.valid_client_facing_addr().is_some()); |         assert!(ci.valid_client_facing_addr().is_some()); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     #[test] | ||||||
|  |     fn test_sanitize() { | ||||||
|  |         let mut ci = ContactInfo::default(); | ||||||
|  |         assert_eq!(ci.sanitize(), Ok(())); | ||||||
|  |         ci.wallclock = MAX_WALLCLOCK; | ||||||
|  |         assert_eq!(ci.sanitize(), Err(SanitizeError::ValueOutOfBounds)); | ||||||
|  |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -83,13 +83,13 @@ impl Sanitize for CrdsData { | |||||||
|             CrdsData::ContactInfo(val) => val.sanitize(), |             CrdsData::ContactInfo(val) => val.sanitize(), | ||||||
|             CrdsData::Vote(ix, val) => { |             CrdsData::Vote(ix, val) => { | ||||||
|                 if *ix >= MAX_VOTES { |                 if *ix >= MAX_VOTES { | ||||||
|                     return Err(SanitizeError::Failed); |                     return Err(SanitizeError::ValueOutOfBounds); | ||||||
|                 } |                 } | ||||||
|                 val.sanitize() |                 val.sanitize() | ||||||
|             } |             } | ||||||
|             CrdsData::LowestSlot(ix, val) => { |             CrdsData::LowestSlot(ix, val) => { | ||||||
|                 if *ix as usize >= 1 { |                 if *ix as usize >= 1 { | ||||||
|                     return Err(SanitizeError::ValueOutOfRange); |                     return Err(SanitizeError::ValueOutOfBounds); | ||||||
|                 } |                 } | ||||||
|                 val.sanitize() |                 val.sanitize() | ||||||
|             } |             } | ||||||
| @@ -97,7 +97,7 @@ impl Sanitize for CrdsData { | |||||||
|             CrdsData::AccountsHashes(val) => val.sanitize(), |             CrdsData::AccountsHashes(val) => val.sanitize(), | ||||||
|             CrdsData::EpochSlots(ix, val) => { |             CrdsData::EpochSlots(ix, val) => { | ||||||
|                 if *ix as usize >= MAX_EPOCH_SLOTS as usize { |                 if *ix as usize >= MAX_EPOCH_SLOTS as usize { | ||||||
|                     return Err(SanitizeError::Failed); |                     return Err(SanitizeError::ValueOutOfBounds); | ||||||
|                 } |                 } | ||||||
|                 val.sanitize() |                 val.sanitize() | ||||||
|             } |             } | ||||||
| @@ -115,11 +115,11 @@ pub struct SnapshotHash { | |||||||
| impl Sanitize for SnapshotHash { | impl Sanitize for SnapshotHash { | ||||||
|     fn sanitize(&self) -> Result<(), SanitizeError> { |     fn sanitize(&self) -> Result<(), SanitizeError> { | ||||||
|         if self.wallclock >= MAX_WALLCLOCK { |         if self.wallclock >= MAX_WALLCLOCK { | ||||||
|             return Err(SanitizeError::Failed); |             return Err(SanitizeError::ValueOutOfBounds); | ||||||
|         } |         } | ||||||
|         for (slot, _) in &self.hashes { |         for (slot, _) in &self.hashes { | ||||||
|             if *slot >= MAX_SLOT { |             if *slot >= MAX_SLOT { | ||||||
|                 return Err(SanitizeError::Failed); |                 return Err(SanitizeError::ValueOutOfBounds); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|         self.from.sanitize() |         self.from.sanitize() | ||||||
| @@ -161,10 +161,10 @@ impl LowestSlot { | |||||||
| impl Sanitize for LowestSlot { | impl Sanitize for LowestSlot { | ||||||
|     fn sanitize(&self) -> Result<(), SanitizeError> { |     fn sanitize(&self) -> Result<(), SanitizeError> { | ||||||
|         if self.wallclock >= MAX_WALLCLOCK { |         if self.wallclock >= MAX_WALLCLOCK { | ||||||
|             return Err(SanitizeError::ValueOutOfRange); |             return Err(SanitizeError::ValueOutOfBounds); | ||||||
|         } |         } | ||||||
|         if self.lowest >= MAX_SLOT { |         if self.lowest >= MAX_SLOT { | ||||||
|             return Err(SanitizeError::ValueOutOfRange); |             return Err(SanitizeError::ValueOutOfBounds); | ||||||
|         } |         } | ||||||
|         if self.root != 0 { |         if self.root != 0 { | ||||||
|             return Err(SanitizeError::InvalidValue); |             return Err(SanitizeError::InvalidValue); | ||||||
| @@ -189,7 +189,7 @@ pub struct Vote { | |||||||
| impl Sanitize for Vote { | impl Sanitize for Vote { | ||||||
|     fn sanitize(&self) -> Result<(), SanitizeError> { |     fn sanitize(&self) -> Result<(), SanitizeError> { | ||||||
|         if self.wallclock >= MAX_WALLCLOCK { |         if self.wallclock >= MAX_WALLCLOCK { | ||||||
|             return Err(SanitizeError::Failed); |             return Err(SanitizeError::ValueOutOfBounds); | ||||||
|         } |         } | ||||||
|         self.from.sanitize()?; |         self.from.sanitize()?; | ||||||
|         self.transaction.sanitize() |         self.transaction.sanitize() | ||||||
| @@ -448,7 +448,7 @@ mod test { | |||||||
|  |  | ||||||
|         let o = ls.clone(); |         let o = ls.clone(); | ||||||
|         let v = CrdsValue::new_unsigned(CrdsData::LowestSlot(1, o.clone())); |         let v = CrdsValue::new_unsigned(CrdsData::LowestSlot(1, o.clone())); | ||||||
|         assert_eq!(v.sanitize(), Err(SanitizeError::ValueOutOfRange)); |         assert_eq!(v.sanitize(), Err(SanitizeError::ValueOutOfBounds)); | ||||||
|  |  | ||||||
|         let mut o = ls.clone(); |         let mut o = ls.clone(); | ||||||
|         o.slots.insert(1); |         o.slots.insert(1); | ||||||
| @@ -505,7 +505,7 @@ mod test { | |||||||
|             ), |             ), | ||||||
|             &keypair, |             &keypair, | ||||||
|         ); |         ); | ||||||
|         assert!(item.sanitize().is_err()); |         assert_eq!(item.sanitize(), Err(SanitizeError::ValueOutOfBounds)); | ||||||
|     } |     } | ||||||
|     #[test] |     #[test] | ||||||
|     fn test_compute_vote_index_empty() { |     fn test_compute_vote_index_empty() { | ||||||
|   | |||||||
| @@ -19,10 +19,10 @@ pub struct Uncompressed { | |||||||
| impl Sanitize for Uncompressed { | impl Sanitize for Uncompressed { | ||||||
|     fn sanitize(&self) -> std::result::Result<(), SanitizeError> { |     fn sanitize(&self) -> std::result::Result<(), SanitizeError> { | ||||||
|         if self.first_slot >= MAX_SLOT { |         if self.first_slot >= MAX_SLOT { | ||||||
|             return Err(SanitizeError::ValueOutOfRange); |             return Err(SanitizeError::ValueOutOfBounds); | ||||||
|         } |         } | ||||||
|         if self.num >= MAX_SLOTS_PER_ENTRY { |         if self.num >= MAX_SLOTS_PER_ENTRY { | ||||||
|             return Err(SanitizeError::ValueOutOfRange); |             return Err(SanitizeError::ValueOutOfBounds); | ||||||
|         } |         } | ||||||
|         Ok(()) |         Ok(()) | ||||||
|     } |     } | ||||||
| @@ -38,10 +38,10 @@ pub struct Flate2 { | |||||||
| impl Sanitize for Flate2 { | impl Sanitize for Flate2 { | ||||||
|     fn sanitize(&self) -> std::result::Result<(), SanitizeError> { |     fn sanitize(&self) -> std::result::Result<(), SanitizeError> { | ||||||
|         if self.first_slot >= MAX_SLOT { |         if self.first_slot >= MAX_SLOT { | ||||||
|             return Err(SanitizeError::ValueOutOfRange); |             return Err(SanitizeError::ValueOutOfBounds); | ||||||
|         } |         } | ||||||
|         if self.num >= MAX_SLOTS_PER_ENTRY { |         if self.num >= MAX_SLOTS_PER_ENTRY { | ||||||
|             return Err(SanitizeError::ValueOutOfRange); |             return Err(SanitizeError::ValueOutOfBounds); | ||||||
|         } |         } | ||||||
|         Ok(()) |         Ok(()) | ||||||
|     } |     } | ||||||
| @@ -221,7 +221,7 @@ pub struct EpochSlots { | |||||||
| impl Sanitize for EpochSlots { | impl Sanitize for EpochSlots { | ||||||
|     fn sanitize(&self) -> std::result::Result<(), SanitizeError> { |     fn sanitize(&self) -> std::result::Result<(), SanitizeError> { | ||||||
|         if self.wallclock >= MAX_WALLCLOCK { |         if self.wallclock >= MAX_WALLCLOCK { | ||||||
|             return Err(SanitizeError::ValueOutOfRange); |             return Err(SanitizeError::ValueOutOfBounds); | ||||||
|         } |         } | ||||||
|         self.from.sanitize()?; |         self.from.sanitize()?; | ||||||
|         self.slots.sanitize() |         self.slots.sanitize() | ||||||
| @@ -387,22 +387,22 @@ mod tests { | |||||||
|  |  | ||||||
|         let mut o = slots.clone(); |         let mut o = slots.clone(); | ||||||
|         o.first_slot = MAX_SLOT; |         o.first_slot = MAX_SLOT; | ||||||
|         assert_eq!(o.sanitize(), Err(SanitizeError::ValueOutOfRange)); |         assert_eq!(o.sanitize(), Err(SanitizeError::ValueOutOfBounds)); | ||||||
|  |  | ||||||
|         let mut o = slots.clone(); |         let mut o = slots.clone(); | ||||||
|         o.num = MAX_SLOTS_PER_ENTRY; |         o.num = MAX_SLOTS_PER_ENTRY; | ||||||
|         assert_eq!(o.sanitize(), Err(SanitizeError::ValueOutOfRange)); |         assert_eq!(o.sanitize(), Err(SanitizeError::ValueOutOfBounds)); | ||||||
|  |  | ||||||
|         let compressed = Flate2::deflate(slots).unwrap(); |         let compressed = Flate2::deflate(slots).unwrap(); | ||||||
|         assert!(compressed.sanitize().is_ok()); |         assert!(compressed.sanitize().is_ok()); | ||||||
|  |  | ||||||
|         let mut o = compressed.clone(); |         let mut o = compressed.clone(); | ||||||
|         o.first_slot = MAX_SLOT; |         o.first_slot = MAX_SLOT; | ||||||
|         assert_eq!(o.sanitize(), Err(SanitizeError::ValueOutOfRange)); |         assert_eq!(o.sanitize(), Err(SanitizeError::ValueOutOfBounds)); | ||||||
|  |  | ||||||
|         let mut o = compressed.clone(); |         let mut o = compressed.clone(); | ||||||
|         o.num = MAX_SLOTS_PER_ENTRY; |         o.num = MAX_SLOTS_PER_ENTRY; | ||||||
|         assert_eq!(o.sanitize(), Err(SanitizeError::ValueOutOfRange)); |         assert_eq!(o.sanitize(), Err(SanitizeError::ValueOutOfBounds)); | ||||||
|  |  | ||||||
|         let mut slots = EpochSlots::default(); |         let mut slots = EpochSlots::default(); | ||||||
|         let range: Vec<Slot> = (0..5000).into_iter().collect(); |         let range: Vec<Slot> = (0..5000).into_iter().collect(); | ||||||
| @@ -412,7 +412,7 @@ mod tests { | |||||||
|  |  | ||||||
|         let mut o = slots.clone(); |         let mut o = slots.clone(); | ||||||
|         o.wallclock = MAX_WALLCLOCK; |         o.wallclock = MAX_WALLCLOCK; | ||||||
|         assert_eq!(o.sanitize(), Err(SanitizeError::ValueOutOfRange)); |         assert_eq!(o.sanitize(), Err(SanitizeError::ValueOutOfBounds)); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     #[test] |     #[test] | ||||||
|   | |||||||
| @@ -1,5 +1,6 @@ | |||||||
| //! The `hash` module provides functions for creating SHA-256 hashes. | //! The `hash` module provides functions for creating SHA-256 hashes. | ||||||
|  |  | ||||||
|  | use crate::sanitize::Sanitize; | ||||||
| use sha2::{Digest, Sha256}; | use sha2::{Digest, Sha256}; | ||||||
| use std::{convert::TryFrom, fmt, mem, str::FromStr}; | use std::{convert::TryFrom, fmt, mem, str::FromStr}; | ||||||
| use thiserror::Error; | use thiserror::Error; | ||||||
| @@ -30,6 +31,8 @@ impl Hasher { | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | impl Sanitize for Hash {} | ||||||
|  |  | ||||||
| impl AsRef<[u8]> for Hash { | impl AsRef<[u8]> for Hash { | ||||||
|     fn as_ref(&self) -> &[u8] { |     fn as_ref(&self) -> &[u8] { | ||||||
|         &self.0[..] |         &self.0[..] | ||||||
|   | |||||||
| @@ -1,5 +1,6 @@ | |||||||
| //! Defines a composable Instruction type and a memory-efficient CompiledInstruction. | //! Defines a composable Instruction type and a memory-efficient CompiledInstruction. | ||||||
|  |  | ||||||
|  | use crate::sanitize::Sanitize; | ||||||
| use crate::{pubkey::Pubkey, short_vec, system_instruction::SystemError}; | use crate::{pubkey::Pubkey, short_vec, system_instruction::SystemError}; | ||||||
| use bincode::serialize; | use bincode::serialize; | ||||||
| use serde::Serialize; | use serde::Serialize; | ||||||
| @@ -228,6 +229,8 @@ pub struct CompiledInstruction { | |||||||
|     pub data: Vec<u8>, |     pub data: Vec<u8>, | ||||||
| } | } | ||||||
|  |  | ||||||
|  | impl Sanitize for CompiledInstruction {} | ||||||
|  |  | ||||||
| impl CompiledInstruction { | impl CompiledInstruction { | ||||||
|     pub fn new<T: Serialize>(program_ids_index: u8, data: &T, accounts: Vec<u8>) -> Self { |     pub fn new<T: Serialize>(program_ids_index: u8, data: &T, accounts: Vec<u8>) -> Self { | ||||||
|         let data = serialize(data).unwrap(); |         let data = serialize(data).unwrap(); | ||||||
|   | |||||||
| @@ -184,6 +184,9 @@ impl Sanitize for Message { | |||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |         self.account_keys.sanitize()?; | ||||||
|  |         self.recent_blockhash.sanitize()?; | ||||||
|  |         self.instructions.sanitize()?; | ||||||
|         Ok(()) |         Ok(()) | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,11 +1,22 @@ | |||||||
| #[derive(PartialEq, Debug)] | use thiserror::Error; | ||||||
|  |  | ||||||
|  | #[derive(PartialEq, Debug, Error, Eq, Clone)] | ||||||
| pub enum SanitizeError { | pub enum SanitizeError { | ||||||
|     Failed, |     #[error("index out of bounds")] | ||||||
|     IndexOutOfBounds, |     IndexOutOfBounds, | ||||||
|     ValueOutOfRange, |     #[error("value out of bounds")] | ||||||
|  |     ValueOutOfBounds, | ||||||
|  |     #[error("invalid value")] | ||||||
|     InvalidValue, |     InvalidValue, | ||||||
| } | } | ||||||
|  |  | ||||||
|  | /// Trait for sanitizing values and members of over the wire messages. | ||||||
|  | /// Implementation should recursively decent through the data structure | ||||||
|  | /// and sanitize all struct members and enum clauses.  Sanitize excludes | ||||||
|  | /// signature verification checks, those are handled by another pass. | ||||||
|  | /// Sanitize checks should include but are not limited too: | ||||||
|  | ///   * All index values are in range | ||||||
|  | ///   * All values are within their static max/min bounds | ||||||
| pub trait Sanitize { | pub trait Sanitize { | ||||||
|     fn sanitize(&self) -> Result<(), SanitizeError> { |     fn sanitize(&self) -> Result<(), SanitizeError> { | ||||||
|         Ok(()) |         Ok(()) | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user