Skip to content

Commit 0da9f9b

Browse files
authored
Fix panic on non UTF-8 string (#59)
1 parent 3a80f71 commit 0da9f9b

2 files changed

Lines changed: 16 additions & 4 deletions

File tree

pytests/test_dag_cbor.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,15 @@ def test_recursion_limit_exceed_on_nested_maps() -> None:
137137
assert 'in DAG-CBOR decoding' in str(exc_info.value)
138138

139139

140+
141+
def test_dag_cbor_decode_invalid_utf8() -> None:
142+
with pytest.raises(ValueError) as exc_info:
143+
libipld.decode_dag_cbor(bytes.fromhex('62c328'))
144+
145+
146+
assert 'Invalid UTF-8 string' in str(exc_info.value)
147+
148+
140149
def test_dab_cbor_decode_map_int_key() -> None:
141150
dag_cbor = bytes.fromhex('a10000')
142151
with pytest.raises(ValueError) as exc_info:
@@ -151,3 +160,4 @@ def test_dab_cbor_encode_map_int_key() -> None:
151160
libipld.encode_dag_cbor(obj)
152161

153162
assert 'Map keys must be strings' in str(exc_info.value)
163+

src/lib.rs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -102,11 +102,13 @@ fn get_bytes_from_py_any<'py>(obj: &'py Bound<'py, PyAny>) -> PyResult<&'py [u8]
102102
}
103103
}
104104

105-
fn string_new_bound<'py>(py: Python<'py>, s: &[u8]) -> Bound<'py, PyString> {
105+
fn string_new_bound<'py>(py: Python<'py>, s: &[u8]) -> Result<Bound<'py, PyString>> {
106+
std::str::from_utf8(s).map_err(|e| anyhow!("Invalid UTF-8 string: {}", e))?;
107+
106108
let ptr = s.as_ptr() as *const c_char;
107109
let len = s.len() as ffi::Py_ssize_t;
108110
unsafe {
109-
Bound::from_owned_ptr(py, ffi::PyUnicode_FromStringAndSize(ptr, len)).downcast_into_unchecked()
111+
Ok(Bound::from_owned_ptr(py, ffi::PyUnicode_FromStringAndSize(ptr, len)).downcast_into_unchecked())
110112
}
111113
}
112114

@@ -135,7 +137,7 @@ fn decode_dag_cbor_to_pyobject<R: Read + Seek>(
135137
}
136138
MajorKind::TextString => {
137139
let len = decode::read_uint(r, major)?;
138-
string_new_bound(py, &decode::read_bytes(r, len)?).into_pyobject(py)?.into()
140+
string_new_bound(py, &decode::read_bytes(r, len)?)?.into_pyobject(py)?.into()
139141
}
140142
MajorKind::Array => {
141143
let len: ffi::Py_ssize_t = decode_len(decode::read_uint(r, major)?)?.try_into()?;
@@ -173,7 +175,7 @@ fn decode_dag_cbor_to_pyobject<R: Read + Seek>(
173175
}
174176
}
175177

176-
let key_py = string_new_bound(py, key.as_slice()).into_pyobject(py)?;
178+
let key_py = string_new_bound(py, key.as_slice())?.into_pyobject(py)?;
177179
prev_key = Some(key);
178180

179181
let value_py = decode_dag_cbor_to_pyobject(py, r, depth + 1)?;

0 commit comments

Comments
 (0)