Skip to content

Commit 31d4664

Browse files
authored
Fix error handling in CID and ULEB128 processing functions (#96)
1 parent f7c1489 commit 31d4664

File tree

1 file changed

+31
-22
lines changed

1 file changed

+31
-22
lines changed

src/lib.rs

Lines changed: 31 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -64,29 +64,24 @@ impl<'de> dec::Read<'de> for SliceReader<'de> {
6464
}
6565
}
6666

67-
fn cid_hash_to_pydict<'py>(py: Python<'py>, cid: &Cid) -> Bound<'py, PyDict> {
67+
fn cid_hash_to_pydict<'py>(py: Python<'py>, cid: &Cid) -> PyResult<Bound<'py, PyDict>> {
6868
let hash = cid.hash();
6969
let dict_obj = PyDict::new(py);
7070

71-
dict_obj.set_item("code", hash.code()).unwrap();
72-
dict_obj.set_item("size", hash.size()).unwrap();
73-
dict_obj
74-
.set_item("digest", PyBytes::new(py, hash.digest()))
75-
.unwrap();
71+
dict_obj.set_item("code", hash.code())?;
72+
dict_obj.set_item("size", hash.size())?;
73+
dict_obj.set_item("digest", PyBytes::new(py, hash.digest()))?;
7674

77-
dict_obj
75+
Ok(dict_obj)
7876
}
7977

80-
fn cid_to_pydict<'py>(py: Python<'py>, cid: &Cid) -> Bound<'py, PyDict> {
78+
fn cid_to_pydict<'py>(py: Python<'py>, cid: &Cid) -> PyResult<Bound<'py, PyDict>> {
8179
let dict_obj = PyDict::new(py);
8280

83-
dict_obj.set_item("version", cid.version() as u64).unwrap();
84-
dict_obj.set_item("codec", cid.codec()).unwrap();
85-
dict_obj
86-
.set_item("hash", cid_hash_to_pydict(py, cid))
87-
.unwrap();
88-
89-
dict_obj
81+
dict_obj.set_item("version", cid.version() as u64)?;
82+
dict_obj.set_item("codec", cid.codec())?;
83+
dict_obj.set_item("hash", cid_hash_to_pydict(py, cid)?)?;
84+
Ok(dict_obj)
9085
}
9186

9287
fn map_key_cmp(a: &[u8], b: &[u8]) -> std::cmp::Ordering {
@@ -256,8 +251,10 @@ where
256251

257252
let cid = <types::Bytes<&[u8]>>::decode(r)?.0;
258253

259-
// Parse the CID for validation. They have a zero byte at the front, strip it off.
260-
if Cid::try_from(&cid[1..]).is_err() {
254+
if cid.len() <= 1 {
255+
return Err(anyhow!("CID is empty or too short"));
256+
} else if Cid::try_from(&cid[1..]).is_err() {
257+
// Parse the CID for validation. They have a zero byte at the front, strip it off.
261258
return Err(anyhow!("Invalid CID"));
262259
}
263260

@@ -434,11 +431,23 @@ where
434431
peek_one(r).map_err(|_| anyhow!("Unexpected EOF while reading ULEB128 number."))?;
435432
r.advance(1);
436433

437-
if (byte & 0x80) == 0 {
438-
result |= (byte as u64) << shift;
434+
if shift == 63 && byte != 0x00 && byte != 0x01 {
435+
// consume remaining continuation bytes so reader stays in sync
436+
let mut b = byte;
437+
while b & 0x80 != 0 {
438+
b = peek_one(r).map_err(|_| {
439+
anyhow!("Unexpected EOF while skipping overflowing ULEB128 number.")
440+
})?;
441+
r.advance(1);
442+
}
443+
return Err(anyhow!("ULEB128 overflow"));
444+
}
445+
446+
let low_bits = (byte & !0x80) as u64;
447+
result |= low_bits << shift;
448+
449+
if byte & 0x80 == 0 {
439450
return Ok(result);
440-
} else {
441-
result |= (byte as u64 & 0x7F) << shift;
442451
}
443452

444453
shift += 7;
@@ -603,7 +612,7 @@ fn get_cid_from_py_any(data: &Bound<PyAny>) -> PyResult<Cid> {
603612

604613
#[pyfunction]
605614
fn decode_cid<'py>(py: Python<'py>, data: &Bound<PyAny>) -> PyResult<Bound<'py, PyDict>> {
606-
Ok(cid_to_pydict(py, &get_cid_from_py_any(data)?))
615+
cid_to_pydict(py, &get_cid_from_py_any(data)?)
607616
}
608617

609618
#[pyfunction]

0 commit comments

Comments
 (0)