@@ -64,12 +64,22 @@ where
6464 . into ( )
6565 }
6666 major:: ARRAY => {
67- let len: ffi:: Py_ssize_t = types:: Array :: len ( r) ?
68- . ok_or_else ( || anyhow ! ( "Array must contain length" ) ) ?
69- . try_into ( ) ?;
67+ let len = types:: Array :: len ( r) ?. ok_or_else ( || anyhow ! ( "Array must contain length" ) ) ?;
68+ // Every element costs at least one byte; reject a claimed length
69+ // beyond the remaining input before allocating for it.
70+ if r. fill ( len) ?. as_ref ( ) . len ( ) < len {
71+ return Err ( anyhow ! ( "Array length exceeds remaining data" ) ) ;
72+ }
73+ let len: ffi:: Py_ssize_t = len. try_into ( ) ?;
7074
7175 unsafe {
7276 let ptr = ffi:: PyList_New ( len) ;
77+ if ptr. is_null ( ) {
78+ return Err ( anyhow ! ( PyErr :: fetch( py) ) ) ;
79+ }
80+ // Owned before filling so an error mid-fill releases the list;
81+ // list dealloc tolerates the remaining NULL slots.
82+ let list: Bound < ' _ , PyList > = Bound :: from_owned_ptr ( py, ptr) . cast_into_unchecked ( ) ;
7383
7484 for i in 0 ..len {
7585 ffi:: PyList_SET_ITEM (
@@ -79,12 +89,17 @@ where
7989 ) ;
8090 }
8191
82- let list: Bound < ' _ , PyList > = Bound :: from_owned_ptr ( py, ptr) . cast_into_unchecked ( ) ;
8392 list. into_pyobject ( py) ?. into ( )
8493 }
8594 }
8695 major:: MAP => {
8796 let len = types:: Map :: len ( r) ?. ok_or_else ( || anyhow ! ( "Map must contain length" ) ) ?;
97+ // Every entry costs at least two bytes (key + value); reject a
98+ // claimed length beyond the remaining input before presizing.
99+ let need = len. saturating_mul ( 2 ) ;
100+ if r. fill ( need) ?. as_ref ( ) . len ( ) < need {
101+ return Err ( anyhow ! ( "Map length exceeds remaining data" ) ) ;
102+ }
88103 // Length is known up front; presize to avoid rehashes as we fill.
89104 let dict = unsafe {
90105 let ptr = new_presized ( len) ;
@@ -189,18 +204,28 @@ where
189204 } )
190205}
191206
207+ // Wrap a decode failure; an error already set on the interpreter (e.g. the
208+ // RecursionError `restore`d above) wins, with the decode error as its cause.
209+ fn decode_error ( py : Python , e : anyhow:: Error ) -> PyErr {
210+ let err = value_error ( "Failed to decode DAG-CBOR" , e. to_string ( ) ) ;
211+ if let Some ( py_err) = PyErr :: take ( py) {
212+ py_err. set_cause ( py, Option :: from ( err) ) ;
213+ py_err
214+ } else {
215+ err
216+ }
217+ }
218+
192219#[ pyfunction]
193220pub fn decode_dag_cbor_multi < ' py > ( py : Python < ' py > , data : & [ u8 ] ) -> PyResult < Bound < ' py , PyList > > {
194221 let mut reader = SliceReader :: new ( data) ;
195222 let decoded_parts = PyList :: empty ( py) ;
196223 let max_depth = current_recursion_limit ( ) ;
197224
198- loop {
199- let py_object = to_pyobject ( py, & mut reader, 0 , max_depth) ;
200- if let Ok ( py_object) = py_object {
201- decoded_parts. append ( py_object) ?;
202- } else {
203- break ;
225+ while !reader. fill ( 1 ) ?. as_ref ( ) . is_empty ( ) {
226+ match to_pyobject ( py, & mut reader, 0 , max_depth) {
227+ Ok ( py_object) => decoded_parts. append ( py_object) ?,
228+ Err ( e) => return Err ( decode_error ( py, e) ) ,
204229 }
205230 }
206231
@@ -211,31 +236,18 @@ pub fn decode_dag_cbor_multi<'py>(py: Python<'py>, data: &[u8]) -> PyResult<Boun
211236pub fn decode_dag_cbor ( py : Python , data : & [ u8 ] ) -> PyResult < Py < PyAny > > {
212237 let mut reader = SliceReader :: new ( data) ;
213238 let max_depth = current_recursion_limit ( ) ;
214- let py_object = to_pyobject ( py, & mut reader, 0 , max_depth) ;
215- if let Ok ( py_object) = py_object {
216- // check for any remaining data in the reader
217- if reader. fill ( 1 ) ?. as_ref ( ) . is_empty ( ) {
218- Ok ( py_object)
219- } else {
220- Err ( value_error (
221- "Failed to decode DAG-CBOR" ,
222- "Invalid DAG-CBOR: contains multiple objects (CBOR sequence)" . to_string ( ) ,
223- ) )
224- }
225- } else {
226- let err = value_error (
227- "Failed to decode DAG-CBOR" ,
228- py_object. unwrap_err ( ) . to_string ( ) ,
229- ) ;
230-
231- if let Some ( py_err) = PyErr :: take ( py) {
232- py_err. set_cause ( py, Option :: from ( err) ) ;
233- // in case something set global interpreter’s error,
234- // for example C FFI function, we should return it
235- // the real case: RecursionError (set by Py_EnterRecursiveCall)
236- Err ( py_err)
237- } else {
238- Err ( err)
239+ match to_pyobject ( py, & mut reader, 0 , max_depth) {
240+ Ok ( py_object) => {
241+ // check for any remaining data in the reader
242+ if reader. fill ( 1 ) ?. as_ref ( ) . is_empty ( ) {
243+ Ok ( py_object)
244+ } else {
245+ Err ( value_error (
246+ "Failed to decode DAG-CBOR" ,
247+ "Invalid DAG-CBOR: contains multiple objects (CBOR sequence)" . to_string ( ) ,
248+ ) )
249+ }
239250 }
251+ Err ( e) => Err ( decode_error ( py, e) ) ,
240252 }
241253}
0 commit comments