Properly detect overflow

This commit is contained in:
Steven Fackler 2015-06-01 20:28:23 -07:00
parent 6635aaf172
commit 4504f9cec8
4 changed files with 52 additions and 14 deletions

View File

@ -161,23 +161,23 @@ impl<W: Write> WriteMessage for W {
try!(buf.write_cstr(portal)); try!(buf.write_cstr(portal));
try!(buf.write_cstr(statement)); try!(buf.write_cstr(statement));
try!(buf.write_u16::<BigEndian>(formats.len() as u16)); try!(buf.write_u16::<BigEndian>(try!(u16::from_usize(formats.len()))));
for &format in formats { for &format in formats {
try!(buf.write_i16::<BigEndian>(format)); try!(buf.write_i16::<BigEndian>(format));
} }
try!(buf.write_u16::<BigEndian>(values.len() as u16)); try!(buf.write_u16::<BigEndian>(try!(u16::from_usize(values.len()))));
for value in values { for value in values {
match *value { match *value {
None => try!(buf.write_i32::<BigEndian>(-1)), None => try!(buf.write_i32::<BigEndian>(-1)),
Some(ref value) => { Some(ref value) => {
try!(buf.write_i32::<BigEndian>(value.len() as i32)); try!(buf.write_i32::<BigEndian>(try!(i32::from_usize(value.len()))));
try!(buf.write_all(&**value)); try!(buf.write_all(&**value));
} }
} }
} }
try!(buf.write_u16::<BigEndian>(result_formats.len() as u16)); try!(buf.write_u16::<BigEndian>(try!(u16::from_usize(result_formats.len()))));
for &format in result_formats { for &format in result_formats {
try!(buf.write_i16::<BigEndian>(format)); try!(buf.write_i16::<BigEndian>(format));
} }
@ -215,7 +215,7 @@ impl<W: Write> WriteMessage for W {
ident = Some(b'P'); ident = Some(b'P');
try!(buf.write_cstr(name)); try!(buf.write_cstr(name));
try!(buf.write_cstr(query)); try!(buf.write_cstr(query));
try!(buf.write_u16::<BigEndian>(param_types.len() as u16)); try!(buf.write_u16::<BigEndian>(try!(u16::from_usize(param_types.len()))));
for &ty in param_types { for &ty in param_types {
try!(buf.write_u32::<BigEndian>(ty)); try!(buf.write_u32::<BigEndian>(ty));
} }
@ -246,7 +246,7 @@ impl<W: Write> WriteMessage for W {
} }
// add size of length value // add size of length value
try!(self.write_u32::<BigEndian>((buf.len() + mem::size_of::<u32>()) as u32)); try!(self.write_u32::<BigEndian>(try!(u32::from_usize(buf.len() + mem::size_of::<u32>()))));
try!(self.write_all(&*buf)); try!(self.write_all(&*buf));
Ok(()) Ok(())
@ -407,3 +407,25 @@ fn read_row_description<R: BufRead>(buf: &mut R) -> io::Result<BackendMessage> {
Ok(RowDescription { descriptions: types }) Ok(RowDescription { descriptions: types })
} }
trait FromUsize {
fn from_usize(x: usize) -> io::Result<Self>;
}
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);

View File

@ -1,10 +1,12 @@
extern crate chrono; extern crate chrono;
use std::error;
use std::io::prelude::*; use std::io::prelude::*;
use byteorder::{ReadBytesExt, WriteBytesExt, BigEndian}; use byteorder::{ReadBytesExt, WriteBytesExt, BigEndian};
use self::chrono::{Duration, NaiveDate, NaiveTime, NaiveDateTime, DateTime, UTC}; use self::chrono::{Duration, NaiveDate, NaiveTime, NaiveDateTime, DateTime, UTC};
use Result; use Result;
use error::Error;
use types::{FromSql, ToSql, IsNull, Type, SessionInfo}; use types::{FromSql, ToSql, IsNull, Type, SessionInfo};
fn base() -> NaiveDateTime { fn base() -> NaiveDateTime {
@ -61,8 +63,13 @@ impl FromSql for NaiveDate {
impl ToSql for NaiveDate { impl ToSql for NaiveDate {
fn to_sql<W: Write+?Sized>(&self, _: &Type, mut w: &mut W, _: &SessionInfo) -> Result<IsNull> { fn to_sql<W: Write+?Sized>(&self, _: &Type, mut w: &mut W, _: &SessionInfo) -> Result<IsNull> {
let jd = *self - base().date(); let jd = (*self - base().date()).num_days();
try!(w.write_i32::<BigEndian>(jd.num_days() as i32)); if jd > i32::max_value() as i64 || jd < i32::min_value() as i64 {
let err: Box<error::Error+Sync+Send> = "value too large to transmit".into();
return Err(Error::Conversion(err));
}
try!(w.write_i32::<BigEndian>(jd as i32));
Ok(IsNull::No) Ok(IsNull::No)
} }

View File

@ -934,15 +934,15 @@ impl ToSql for HashMap<String, Option<String>> {
fn to_sql<W: Write+?Sized>(&self, _: &Type, mut w: &mut W, _: &SessionInfo) fn to_sql<W: Write+?Sized>(&self, _: &Type, mut w: &mut W, _: &SessionInfo)
-> Result<IsNull> { -> Result<IsNull> {
try!(w.write_i32::<BigEndian>(self.len() as i32)); try!(w.write_i32::<BigEndian>(try!(downcast(self.len()))));
for (key, val) in self { for (key, val) in self {
try!(w.write_i32::<BigEndian>(key.len() as i32)); try!(w.write_i32::<BigEndian>(try!(downcast(key.len()))));
try!(w.write_all(key.as_bytes())); try!(w.write_all(key.as_bytes()));
match *val { match *val {
Some(ref val) => { Some(ref val) => {
try!(w.write_i32::<BigEndian>(val.len() as i32)); try!(w.write_i32::<BigEndian>(try!(downcast(val.len()))));
try!(w.write_all(val.as_bytes())); try!(w.write_all(val.as_bytes()));
} }
None => try!(w.write_i32::<BigEndian>(-1)) None => try!(w.write_i32::<BigEndian>(-1))
@ -959,3 +959,12 @@ impl ToSql for HashMap<String, Option<String>> {
} }
} }
} }
fn downcast(len: usize) -> Result<i32> {
if len > i32::max_value() as usize {
let err: Box<error::Error+Sync+Send> = "value too large to transmit".into();
Err(Error::Conversion(err))
} else {
Ok(len as i32)
}
}

View File

@ -3,7 +3,7 @@ use byteorder::{WriteBytesExt, BigEndian};
use Result; use Result;
use error::Error; 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. /// 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::<BigEndian>(1)); // has nulls try!(w.write_i32::<BigEndian>(1)); // has nulls
try!(w.write_u32::<BigEndian>(member_type.oid())); try!(w.write_u32::<BigEndian>(member_type.oid()));
try!(w.write_i32::<BigEndian>(self.0.len() as i32)); try!(w.write_i32::<BigEndian>(try!(downcast(self.0.len()))));
try!(w.write_i32::<BigEndian>(0)); // index offset try!(w.write_i32::<BigEndian>(0)); // index offset
let mut inner_buf = vec![]; let mut inner_buf = vec![];
for e in self.0 { for e in self.0 {
match try!(e.to_sql(&member_type, &mut inner_buf, ctx)) { match try!(e.to_sql(&member_type, &mut inner_buf, ctx)) {
IsNull::No => { IsNull::No => {
try!(w.write_i32::<BigEndian>(inner_buf.len() as i32)); try!(w.write_i32::<BigEndian>(try!(downcast(inner_buf.len()))));
try!(w.write_all(&inner_buf)); try!(w.write_all(&inner_buf));
} }
IsNull::Yes => try!(w.write_i32::<BigEndian>(-1)), IsNull::Yes => try!(w.write_i32::<BigEndian>(-1)),