From 4504f9cec84a154e343884650a0e958c0b3a4c2d Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Mon, 1 Jun 2015 20:28:23 -0700 Subject: [PATCH] Properly detect overflow --- src/message.rs | 34 ++++++++++++++++++++++++++++------ src/types/chrono.rs | 11 +++++++++-- src/types/mod.rs | 15 ++++++++++++--- src/types/slice.rs | 6 +++--- 4 files changed, 52 insertions(+), 14 deletions(-) diff --git a/src/message.rs b/src/message.rs index 81a7be82..68aca5de 100644 --- a/src/message.rs +++ b/src/message.rs @@ -161,23 +161,23 @@ impl WriteMessage for W { try!(buf.write_cstr(portal)); try!(buf.write_cstr(statement)); - try!(buf.write_u16::(formats.len() as u16)); + try!(buf.write_u16::(try!(u16::from_usize(formats.len())))); for &format in formats { try!(buf.write_i16::(format)); } - try!(buf.write_u16::(values.len() as u16)); + try!(buf.write_u16::(try!(u16::from_usize(values.len())))); for value in values { match *value { None => try!(buf.write_i32::(-1)), Some(ref value) => { - try!(buf.write_i32::(value.len() as i32)); + try!(buf.write_i32::(try!(i32::from_usize(value.len())))); try!(buf.write_all(&**value)); } } } - try!(buf.write_u16::(result_formats.len() as u16)); + try!(buf.write_u16::(try!(u16::from_usize(result_formats.len())))); for &format in result_formats { try!(buf.write_i16::(format)); } @@ -215,7 +215,7 @@ impl WriteMessage for W { ident = Some(b'P'); try!(buf.write_cstr(name)); try!(buf.write_cstr(query)); - try!(buf.write_u16::(param_types.len() as u16)); + try!(buf.write_u16::(try!(u16::from_usize(param_types.len())))); for &ty in param_types { try!(buf.write_u32::(ty)); } @@ -246,7 +246,7 @@ impl WriteMessage for W { } // add size of length value - try!(self.write_u32::((buf.len() + mem::size_of::()) as u32)); + try!(self.write_u32::(try!(u32::from_usize(buf.len() + mem::size_of::())))); try!(self.write_all(&*buf)); Ok(()) @@ -407,3 +407,25 @@ fn read_row_description(buf: &mut R) -> io::Result { Ok(RowDescription { descriptions: types }) } + +trait FromUsize { + fn from_usize(x: usize) -> io::Result; +} + +macro_rules! from_usize { + ($t:ty) => { + impl FromUsize for $t { + fn from_usize(x: usize) -> io::Result<$t> { + if x > <$t>::max_value() as usize { + Err(io::Error::new(io::ErrorKind::InvalidInput, "value too large to transmit")) + } else { + Ok(x as $t) + } + } + } + } +} + +from_usize!(u16); +from_usize!(i32); +from_usize!(u32); diff --git a/src/types/chrono.rs b/src/types/chrono.rs index dc65b673..159bbdec 100644 --- a/src/types/chrono.rs +++ b/src/types/chrono.rs @@ -1,10 +1,12 @@ extern crate chrono; +use std::error; use std::io::prelude::*; use byteorder::{ReadBytesExt, WriteBytesExt, BigEndian}; use self::chrono::{Duration, NaiveDate, NaiveTime, NaiveDateTime, DateTime, UTC}; use Result; +use error::Error; use types::{FromSql, ToSql, IsNull, Type, SessionInfo}; fn base() -> NaiveDateTime { @@ -61,8 +63,13 @@ impl FromSql for NaiveDate { impl ToSql for NaiveDate { fn to_sql(&self, _: &Type, mut w: &mut W, _: &SessionInfo) -> Result { - let jd = *self - base().date(); - try!(w.write_i32::(jd.num_days() as i32)); + let jd = (*self - base().date()).num_days(); + if jd > i32::max_value() as i64 || jd < i32::min_value() as i64 { + let err: Box = "value too large to transmit".into(); + return Err(Error::Conversion(err)); + } + + try!(w.write_i32::(jd as i32)); Ok(IsNull::No) } diff --git a/src/types/mod.rs b/src/types/mod.rs index 5ad8eb56..49f5cc03 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -934,15 +934,15 @@ impl ToSql for HashMap> { fn to_sql(&self, _: &Type, mut w: &mut W, _: &SessionInfo) -> Result { - try!(w.write_i32::(self.len() as i32)); + try!(w.write_i32::(try!(downcast(self.len())))); for (key, val) in self { - try!(w.write_i32::(key.len() as i32)); + try!(w.write_i32::(try!(downcast(key.len())))); try!(w.write_all(key.as_bytes())); match *val { Some(ref val) => { - try!(w.write_i32::(val.len() as i32)); + try!(w.write_i32::(try!(downcast(val.len())))); try!(w.write_all(val.as_bytes())); } None => try!(w.write_i32::(-1)) @@ -959,3 +959,12 @@ impl ToSql for HashMap> { } } } + +fn downcast(len: usize) -> Result { + if len > i32::max_value() as usize { + let err: Box = "value too large to transmit".into(); + Err(Error::Conversion(err)) + } else { + Ok(len as i32) + } +} diff --git a/src/types/slice.rs b/src/types/slice.rs index cb9156b8..02cb3975 100644 --- a/src/types/slice.rs +++ b/src/types/slice.rs @@ -3,7 +3,7 @@ use byteorder::{WriteBytesExt, BigEndian}; use Result; use error::Error; -use types::{Type, ToSql, Kind, IsNull, SessionInfo}; +use types::{Type, ToSql, Kind, IsNull, SessionInfo, downcast}; /// An adapter type mapping slices to Postgres arrays. /// @@ -48,14 +48,14 @@ impl<'a, T: 'a + ToSql> ToSql for Slice<'a, T> { try!(w.write_i32::(1)); // has nulls try!(w.write_u32::(member_type.oid())); - try!(w.write_i32::(self.0.len() as i32)); + try!(w.write_i32::(try!(downcast(self.0.len())))); try!(w.write_i32::(0)); // index offset let mut inner_buf = vec![]; for e in self.0 { match try!(e.to_sql(&member_type, &mut inner_buf, ctx)) { IsNull::No => { - try!(w.write_i32::(inner_buf.len() as i32)); + try!(w.write_i32::(try!(downcast(inner_buf.len())))); try!(w.write_all(&inner_buf)); } IsNull::Yes => try!(w.write_i32::(-1)),