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