@@ -810,6 +810,8 @@ _elementtree_Element___deepcopy___impl(ElementObject *self, PyObject *memo)
810
810
811
811
PyTypeObject * tp = Py_TYPE (self );
812
812
elementtreestate * st = get_elementtree_state_by_type (tp );
813
+ // The deepcopy() helper takes care of incrementing the refcount
814
+ // of the object to copy so to avoid use-after-frees.
813
815
tag = deepcopy (st , self -> tag , memo );
814
816
if (!tag )
815
817
return NULL ;
@@ -844,11 +846,13 @@ _elementtree_Element___deepcopy___impl(ElementObject *self, PyObject *memo)
844
846
845
847
assert (!element -> extra || !element -> extra -> length );
846
848
if (self -> extra ) {
847
- if (element_resize (element , self -> extra -> length ) < 0 )
849
+ Py_ssize_t expected_count = self -> extra -> length ;
850
+ if (element_resize (element , expected_count ) < 0 ) {
851
+ assert (!element -> extra -> length );
848
852
goto error ;
853
+ }
849
854
850
- // TODO(picnixz): check for an evil child's __deepcopy__ on 'self'
851
- for (i = 0 ; i < self -> extra -> length ; i ++ ) {
855
+ for (i = 0 ; self -> extra && i < self -> extra -> length ; i ++ ) {
852
856
PyObject * child = deepcopy (st , self -> extra -> children [i ], memo );
853
857
if (!child || !Element_Check (st , child )) {
854
858
if (child ) {
@@ -858,11 +862,24 @@ _elementtree_Element___deepcopy___impl(ElementObject *self, PyObject *memo)
858
862
element -> extra -> length = i ;
859
863
goto error ;
860
864
}
865
+ if (self -> extra && expected_count != self -> extra -> length ) {
866
+ // 'self->extra' got mutated and 'element' may not have
867
+ // sufficient space to hold the next iteration's item.
868
+ expected_count = self -> extra -> length ;
869
+ if (element_resize (element , expected_count ) < 0 ) {
870
+ Py_DECREF (child );
871
+ element -> extra -> length = i ;
872
+ goto error ;
873
+ }
874
+ }
861
875
element -> extra -> children [i ] = child ;
862
876
}
863
877
864
878
assert (!element -> extra -> length );
865
- element -> extra -> length = self -> extra -> length ;
879
+ // The original 'self->extra' may be gone at this point if deepcopy()
880
+ // has side-effects. However, 'i' is the number of copied items that
881
+ // we were able to successfully copy.
882
+ element -> extra -> length = i ;
866
883
}
867
884
868
885
/* add object to memo dictionary (so deepcopy won't visit it again) */
@@ -905,13 +922,20 @@ deepcopy(elementtreestate *st, PyObject *object, PyObject *memo)
905
922
break ;
906
923
}
907
924
}
908
- if (simple )
925
+ if (simple ) {
909
926
return PyDict_Copy (object );
927
+ }
910
928
/* Fall through to general case */
911
929
}
912
930
else if (Element_CheckExact (st , object )) {
913
- return _elementtree_Element___deepcopy___impl (
931
+ // The __deepcopy__() call may call arbitrary code even if the
932
+ // object to copy is a built-in XML element (one of its children
933
+ // any of its parents in its own __deepcopy__() implementation).
934
+ Py_INCREF (object );
935
+ PyObject * res = _elementtree_Element___deepcopy___impl (
914
936
(ElementObject * )object , memo );
937
+ Py_DECREF (object );
938
+ return res ;
915
939
}
916
940
}
917
941
@@ -922,8 +946,11 @@ deepcopy(elementtreestate *st, PyObject *object, PyObject *memo)
922
946
return NULL ;
923
947
}
924
948
949
+ Py_INCREF (object );
925
950
PyObject * args [2 ] = {object , memo };
926
- return PyObject_Vectorcall (st -> deepcopy_obj , args , 2 , NULL );
951
+ PyObject * res = PyObject_Vectorcall (st -> deepcopy_obj , args , 2 , NULL );
952
+ Py_DECREF (object );
953
+ return res ;
927
954
}
928
955
929
956
0 commit comments