Skip to content

Commit 8a17e46

Browse files
committed
fix: audit sqlx_postgres::types::hstore for bad casts
1 parent 627c289 commit 8a17e46

File tree

1 file changed

+61
-32
lines changed

1 file changed

+61
-32
lines changed

sqlx-postgres/src/types/hstore.rs

Lines changed: 61 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,18 @@ use std::{
22
collections::{btree_map, BTreeMap},
33
mem::size_of,
44
ops::{Deref, DerefMut},
5-
str::from_utf8,
5+
str,
66
};
77

8-
use serde::{Deserialize, Serialize};
9-
108
use crate::{
119
decode::Decode,
1210
encode::{Encode, IsNull},
1311
error::BoxDynError,
1412
types::Type,
1513
PgArgumentBuffer, PgTypeInfo, PgValueRef, Postgres,
1614
};
15+
use serde::{Deserialize, Serialize};
16+
use sqlx_core::bytes::Buf;
1717

1818
/// Key-value support (`hstore`) for Postgres.
1919
///
@@ -143,41 +143,64 @@ impl<'r> Decode<'r, Postgres> for PgHstore {
143143
let mut buf = <&[u8] as Decode<Postgres>>::decode(value)?;
144144
let len = read_length(&mut buf)?;
145145

146-
if len < 0 {
147-
Err(format!("hstore, invalid entry count: {len}"))?;
148-
}
146+
let len =
147+
usize::try_from(len).map_err(|_| format!("PgHstore: length out of range: {len}"))?;
149148

150149
let mut result = Self::default();
151150

152-
while !buf.is_empty() {
153-
let key_len = read_length(&mut buf)?;
154-
let key = read_value(&mut buf, key_len)?.ok_or("hstore, key not found")?;
151+
for i in 0..len {
152+
let key = read_string(&mut buf)
153+
.map_err(|e| format!("PgHstore: error reading {i}th key: {e}"))?
154+
.ok_or_else(|| format!("PgHstore: expected {i}th key, got nothing"))?;
155155

156-
let value_len = read_length(&mut buf)?;
157-
let value = read_value(&mut buf, value_len)?;
156+
let value = read_string(&mut buf)
157+
.map_err(|e| format!("PgHstore: error reading value for key {key:?}: {e}"))?;
158158

159159
result.insert(key, value);
160160
}
161161

162+
if !buf.is_empty() {
163+
tracing::warn!("{} unread bytes at the end of HSTORE value", buf.len());
164+
}
165+
162166
Ok(result)
163167
}
164168
}
165169

166170
impl Encode<'_, Postgres> for PgHstore {
167171
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> Result<IsNull, BoxDynError> {
168-
buf.extend_from_slice(&i32::to_be_bytes(self.0.len() as i32));
169-
170-
for (key, val) in &self.0 {
172+
buf.extend_from_slice(&i32::to_be_bytes(
173+
self.0
174+
.len()
175+
.try_into()
176+
.map_err(|_| format!("PgHstore length out of range: {}", self.0.len()))?,
177+
));
178+
179+
for (i, (key, val)) in self.0.iter().enumerate() {
171180
let key_bytes = key.as_bytes();
172181

173-
buf.extend_from_slice(&i32::to_be_bytes(key_bytes.len() as i32));
182+
let key_len = i32::try_from(key_bytes.len()).map_err(|_| {
183+
// Doesn't make sense to print the key itself: it's more than 2 GiB long!
184+
format!(
185+
"PgHstore: length of {i}th key out of range: {} bytes",
186+
key_bytes.len()
187+
)
188+
})?;
189+
190+
buf.extend_from_slice(&i32::to_be_bytes(key_len));
174191
buf.extend_from_slice(key_bytes);
175192

176193
match val {
177194
Some(val) => {
178195
let val_bytes = val.as_bytes();
179196

180-
buf.extend_from_slice(&i32::to_be_bytes(val_bytes.len() as i32));
197+
let val_len = i32::try_from(val_bytes.len()).map_err(|_| {
198+
format!(
199+
"PgHstore: value length for key {key:?} out of range: {} bytes",
200+
val_bytes.len()
201+
)
202+
})?;
203+
buf.extend_from_slice(&i32::to_be_bytes(val_len));
181204
buf.extend_from_slice(val_bytes);
182205
}
183206
None => {
@@ -190,30 +213,36 @@ impl Encode<'_, Postgres> for PgHstore {
190213
}
191214
}
192215

193-
fn read_length(buf: &mut &[u8]) -> Result<i32, BoxDynError> {
194-
let (bytes, rest) = buf.split_at(size_of::<i32>());
195-
196-
*buf = rest;
216+
fn read_length(buf: &mut &[u8]) -> Result<i32, String> {
217+
if buf.len() < size_of::<i32>() {
218+
return Err(format!(
219+
"expected {} bytes, got {}",
220+
size_of::<i32>(),
221+
buf.len()
222+
));
223+
}
197224

198-
Ok(i32::from_be_bytes(
199-
bytes
200-
.try_into()
201-
.map_err(|err| format!("hstore, reading length: {err}"))?,
202-
))
225+
Ok(buf.get_i32())
203226
}
204227

205-
fn read_value(buf: &mut &[u8], len: i32) -> Result<Option<String>, BoxDynError> {
228+
fn read_string(buf: &mut &[u8]) -> Result<Option<String>, String> {
229+
let len = read_length(buf)?;
230+
206231
match len {
207-
len if len <= 0 => Ok(None),
232+
-1 => Ok(None),
208233
len => {
209-
let (val, rest) = buf.split_at(len as usize);
234+
let len =
235+
usize::try_from(len).map_err(|_| format!("string length out of range: {len}"))?;
236+
237+
if buf.len() < len {
238+
return Err(format!("expected {len} bytes, got {}", buf.len()));
239+
}
210240

241+
let (val, rest) = buf.split_at(len);
211242
*buf = rest;
212243

213244
Ok(Some(
214-
from_utf8(val)
215-
.map_err(|err| format!("hstore, reading value: {err}"))?
216-
.to_string(),
245+
str::from_utf8(val).map_err(|e| e.to_string())?.to_string(),
217246
))
218247
}
219248
}
@@ -258,7 +287,7 @@ mod test {
258287
}
259288

260289
#[test]
261-
#[should_panic(expected = "hstore, invalid entry count: -5")]
290+
#[should_panic(expected = "PgHstore: length out of range: -5")]
262291
fn hstore_deserialize_buffer_length_error() {
263292
let buf = PgValueRef {
264293
value: Some(&[255, 255, 255, 251]),

0 commit comments

Comments
 (0)