Skip to content

Commit eb7f6a4

Browse files
committed
fix a few postgres integer overflows
1 parent 7ec50d7 commit eb7f6a4

File tree

16 files changed

+74
-57
lines changed

16 files changed

+74
-57
lines changed

sqlx-core/src/error.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,9 @@ pub enum Error {
103103
#[cfg(feature = "migrate")]
104104
#[error("{0}")]
105105
Migrate(#[source] Box<crate::migrate::MigrateError>),
106+
107+
#[error("integer overflow while converting to target type")]
108+
IntegerOverflow(#[source] std::num::TryFromIntError),
106109
}
107110

108111
impl StdError for Box<dyn DatabaseError> {}

sqlx-core/src/postgres/arguments.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ impl PgArguments {
9595
}
9696

9797
for (offset, name) in type_holes {
98-
let oid = conn.fetch_type_id_by_name(&*name).await?;
98+
let oid = conn.fetch_type_id_by_name(name).await?;
9999
buffer[*offset..(*offset + 4)].copy_from_slice(&oid.0.to_be_bytes());
100100
}
101101

@@ -134,7 +134,7 @@ impl PgArgumentBuffer {
134134

135135
// encode the value into our buffer
136136
let len = if let IsNull::No = value.encode(self) {
137-
(self.len() - offset - 4) as i32
137+
i32::try_from(self.len() - offset - 4).expect("bind parameter too large")
138138
} else {
139139
// Write a -1 to indicate NULL
140140
// NOTE: It is illegal for [encode] to write any data

sqlx-core/src/postgres/migrate.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -202,11 +202,11 @@ CREATE TABLE IF NOT EXISTS _sqlx_migrations (
202202
.map_err(MigrateError::AccessMigrationMetadata)?;
203203

204204
if let Some(checksum) = checksum {
205-
return if checksum == &*migration.checksum {
205+
if checksum == *migration.checksum {
206206
Ok(())
207207
} else {
208208
Err(MigrateError::VersionMismatch(migration.version))
209-
};
209+
}
210210
} else {
211211
Err(MigrateError::VersionMissing(migration.version))
212212
}
@@ -257,7 +257,7 @@ CREATE TABLE IF NOT EXISTS _sqlx_migrations (
257257
WHERE version = $2
258258
"#,
259259
)
260-
.bind(elapsed.as_nanos() as i64)
260+
.bind(i64::try_from(elapsed.as_nanos()).map_err(crate::error::Error::IntegerOverflow)?)
261261
.bind(migration.version)
262262
.execute(self)
263263
.await?;
@@ -295,9 +295,9 @@ CREATE TABLE IF NOT EXISTS _sqlx_migrations (
295295

296296
async fn current_database(conn: &mut PgConnection) -> Result<String> {
297297
// language=SQL
298-
Ok(query_scalar("SELECT current_database()")
298+
query_scalar("SELECT current_database()")
299299
.fetch_one(conn)
300-
.await?)
300+
.await
301301
}
302302

303303
// inspired from rails: https://github.com/rails/rails/blob/6e49cc77ab3d16c06e12f93158eaf3e507d4120e/activerecord/lib/active_record/migration.rb#L1308

sqlx-core/src/postgres/types/array.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -112,10 +112,12 @@ where
112112
}
113113
}
114114

115-
buf.extend(&(self.len() as i32).to_be_bytes()); // len
115+
let len = i32::try_from(self.len()).unwrap_or(i32::MAX);
116+
117+
buf.extend(len.to_be_bytes()); // len
116118
buf.extend(&1_i32.to_be_bytes()); // lower bound
117119

118-
for element in self.iter() {
120+
for element in self.iter().take(usize::try_from(len).unwrap()) {
119121
buf.encode(element);
120122
}
121123

sqlx-core/src/postgres/types/bit_vec.rs

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,16 @@ impl PgHasArrayType for BitVec {
3131

3232
impl Encode<'_, Postgres> for BitVec {
3333
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> IsNull {
34-
buf.extend(&(self.len() as i32).to_be_bytes());
35-
buf.extend(self.to_bytes());
36-
34+
if let Ok(len) = i32::try_from(self.len()) {
35+
buf.extend(&len.to_be_bytes());
36+
buf.extend_from_slice(&self.to_bytes());
37+
} else {
38+
debug_assert!(false, "BitVec length is too large to be encoded as i32.");
39+
let len = i32::MAX;
40+
buf.extend(&len.to_be_bytes());
41+
let truncated = &self.to_bytes()[0..usize::try_from(i32::MAX).unwrap()];
42+
buf.extend_from_slice(truncated);
43+
};
3744
IsNull::No
3845
}
3946

@@ -47,17 +54,18 @@ impl Decode<'_, Postgres> for BitVec {
4754
match value.format() {
4855
PgValueFormat::Binary => {
4956
let mut bytes = value.as_bytes()?;
50-
let len = bytes.get_i32();
51-
52-
if len < 0 {
53-
Err(io::Error::new(
57+
let len = if let Ok(len) = usize::try_from(bytes.get_i32()) {
58+
len
59+
} else {
60+
return Err(io::Error::new(
5461
io::ErrorKind::InvalidData,
5562
"Negative VARBIT length.",
56-
))?
57-
}
63+
)
64+
.into());
65+
};
5866

5967
// The smallest amount of data we can read is one byte
60-
let bytes_len = (len as usize + 7) / 8;
68+
let bytes_len = (len + 7) / 8;
6169

6270
if bytes.remaining() != bytes_len {
6371
Err(io::Error::new(
@@ -66,12 +74,12 @@ impl Decode<'_, Postgres> for BitVec {
6674
))?;
6775
}
6876

69-
let mut bitvec = BitVec::from_bytes(&bytes);
77+
let mut bitvec = BitVec::from_bytes(bytes);
7078

7179
// Chop off zeroes from the back. We get bits in bytes, so if
7280
// our bitvec is not in full bytes, extra zeroes are added to
7381
// the end.
74-
while bitvec.len() > len as usize {
82+
while bitvec.len() > len {
7583
bitvec.pop();
7684
}
7785

sqlx-core/src/postgres/types/chrono/date.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,9 @@ impl PgHasArrayType for NaiveDate {
2323
impl Encode<'_, Postgres> for NaiveDate {
2424
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> IsNull {
2525
// DATE is encoded as the days since epoch
26-
let days = (*self - NaiveDate::from_ymd_opt(2000, 1, 1).unwrap()).num_days() as i32;
27-
Encode::<Postgres>::encode(&days, buf)
26+
let days = i32::try_from((*self - NaiveDate::from_ymd_opt(2000, 1, 1).unwrap()).num_days())
27+
.unwrap_or(i32::MAX);
28+
Encode::<Postgres>::encode(days, buf)
2829
}
2930

3031
fn size_hint(&self) -> usize {

sqlx-core/src/postgres/types/chrono/datetime.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ impl Encode<'_, Postgres> for NaiveDateTime {
4646
.num_microseconds()
4747
.unwrap_or_else(|| panic!("NaiveDateTime out of range for Postgres: {:?}", self));
4848

49-
Encode::<Postgres>::encode(&us, buf)
49+
Encode::<Postgres>::encode(us, buf)
5050
}
5151

5252
fn size_hint(&self) -> usize {

sqlx-core/src/postgres/types/chrono/time.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ impl Encode<'_, Postgres> for NaiveTime {
2626
// NOTE: panic! is on overflow and 1 day does not have enough micros to overflow
2727
let us = (*self - NaiveTime::default()).num_microseconds().unwrap();
2828

29-
Encode::<Postgres>::encode(&us, buf)
29+
Encode::<Postgres>::encode(us, buf)
3030
}
3131

3232
fn size_hint(&self) -> usize {

sqlx-core/src/postgres/types/decimal.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ impl TryFrom<&'_ Decimal> for PgNumeric {
9393
});
9494
}
9595

96-
let scale = decimal.scale() as u16;
96+
let scale = decimal.scale();
9797

9898
// A serialized version of the decimal number. The resulting byte array
9999
// will have the following representation:
@@ -114,7 +114,7 @@ impl TryFrom<&'_ Decimal> for PgNumeric {
114114
let remainder = 4 - groups_diff as u32;
115115
let power = 10u32.pow(remainder as u32) as u128;
116116

117-
mantissa = mantissa * power;
117+
mantissa *= power;
118118
}
119119

120120
// Array to store max mantissa of Decimal in Postgres decimal format.
@@ -130,8 +130,8 @@ impl TryFrom<&'_ Decimal> for PgNumeric {
130130
digits.reverse();
131131

132132
// Weight is number of digits on the left side of the decimal.
133-
let digits_after_decimal = (scale + 3) as u16 / 4;
134-
let weight = digits.len() as i16 - digits_after_decimal as i16 - 1;
133+
let digits_after_decimal = u16::try_from(scale + 3)? / 4;
134+
let weight = i16::try_from(digits.len())? - i16::try_from(digits_after_decimal)? - 1;
135135

136136
// Remove non-significant zeroes.
137137
while let Some(&0) = digits.last() {
@@ -143,7 +143,7 @@ impl TryFrom<&'_ Decimal> for PgNumeric {
143143
false => PgNumericSign::Positive,
144144
true => PgNumericSign::Negative,
145145
},
146-
scale: scale as i16,
146+
scale: i16::try_from(scale)?,
147147
weight,
148148
digits,
149149
})

sqlx-core/src/postgres/types/ipnetwork.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ const AF_INET: u8 = 2;
1919
const AF_INET: u8 = 0;
2020

2121
#[cfg(unix)]
22+
#[allow(clippy::cast_possible_truncation)]
2223
const AF_INET: u8 = libc::AF_INET as u8;
2324

2425
// https://github.com/postgres/postgres/blob/574925bfd0a8175f6e161936ea11d9695677ba09/src/include/utils/inet.h#L39

0 commit comments

Comments
 (0)