From a30f0b6c0586e467cd48d759b903eb8d4ec7c3e7 Mon Sep 17 00:00:00 2001 From: Nikhil Benesch Date: Thu, 30 Jul 2020 22:51:18 -0400 Subject: [PATCH] Use checked arithmetic when decoding into chrono types This avoids an overflow panic if the timestamp is the special "infinity" or "-infinity" value and produces an error instead. Fix #640. --- postgres-types/src/chrono_04.rs | 9 +++-- tokio-postgres/tests/test/types/chrono_04.rs | 35 +++++++++++++++++++- 2 files changed, 41 insertions(+), 3 deletions(-) diff --git a/postgres-types/src/chrono_04.rs b/postgres-types/src/chrono_04.rs index 9bfbb786..fcd25e6d 100644 --- a/postgres-types/src/chrono_04.rs +++ b/postgres-types/src/chrono_04.rs @@ -12,7 +12,9 @@ fn base() -> NaiveDateTime { impl<'a> FromSql<'a> for NaiveDateTime { fn from_sql(_: &Type, raw: &[u8]) -> Result> { let t = types::timestamp_from_sql(raw)?; - Ok(base() + Duration::microseconds(t)) + base() + .checked_add_signed(Duration::microseconds(t)) + .ok_or_else(|| "value too large to decode".into()) } accepts!(TIMESTAMP); @@ -104,7 +106,10 @@ impl ToSql for DateTime { impl<'a> FromSql<'a> for NaiveDate { fn from_sql(_: &Type, raw: &[u8]) -> Result> { let jd = types::date_from_sql(raw)?; - Ok(base().date() + Duration::days(i64::from(jd))) + base() + .date() + .checked_add_signed(Duration::days(i64::from(jd))) + .ok_or_else(|| "value too large to decode".into()) } accepts!(DATE); diff --git a/tokio-postgres/tests/test/types/chrono_04.rs b/tokio-postgres/tests/test/types/chrono_04.rs index 13c8dc14..a8e9e5af 100644 --- a/tokio-postgres/tests/test/types/chrono_04.rs +++ b/tokio-postgres/tests/test/types/chrono_04.rs @@ -1,6 +1,9 @@ use chrono_04::{DateTime, NaiveDate, NaiveDateTime, NaiveTime, TimeZone, Utc}; -use tokio_postgres::types::{Date, Timestamp}; +use std::fmt; +use tokio_postgres::types::{Date, FromSqlOwned, Timestamp}; +use tokio_postgres::Client; +use crate::connect; use crate::types::test_type; #[tokio::test] @@ -153,3 +156,33 @@ async fn test_time_params() { ) .await; } + +#[tokio::test] +async fn test_special_params_without_wrapper() { + async fn assert_overflows(client: &mut Client, val: &str, sql_type: &str) + where + T: FromSqlOwned + fmt::Debug, + { + let err = client + .query_one(&*format!("SELECT {}::{}", val, sql_type), &[]) + .await + .unwrap() + .try_get::<_, T>(0) + .unwrap_err(); + assert_eq!( + err.to_string(), + "error deserializing column 0: value too large to decode" + ); + } + + let mut client = connect("user=postgres").await; + + assert_overflows::>(&mut client, "'-infinity'", "timestamptz").await; + assert_overflows::>(&mut client, "'infinity'", "timestamptz").await; + + assert_overflows::(&mut client, "'-infinity'", "timestamp").await; + assert_overflows::(&mut client, "'infinity'", "timestamp").await; + + assert_overflows::(&mut client, "'-infinity'", "date").await; + assert_overflows::(&mut client, "'infinity'", "date").await; +}