Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit d724b23

Browse files
committed
Christian Tismer's "trashcan" patch:
Added wrapping macros to dictobject.c, listobject.c, tupleobject.c, frameobject.c, traceback.c that safely prevends core dumps on stack overflow. Macros and functions in object.c, object.h. The method is an "elevator destructor" that turns cascading deletes into tail recursive behavior when some limit is hit.
1 parent 96a45ad commit d724b23

7 files changed

Lines changed: 104 additions & 1 deletion

File tree

Include/object.h

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -514,6 +514,53 @@ it carefully, it may save lots of calls to Py_INCREF() and Py_DECREF() at
514514
times.
515515
*/
516516

517+
/*
518+
trashcan
519+
CT 2k0130
520+
non-recursively destroy nested objects
521+
522+
CT 2k0223
523+
redefinition for better locality and less overhead.
524+
525+
Objects that want to be recursion safe need to use
526+
the macroes
527+
Py_TRASHCAN_SAFE_BEGIN(name)
528+
and
529+
Py_TRASHCAN_SAFE_END(name)
530+
surrounding their actual deallocation code.
531+
532+
It would be nice to do this using the thread state.
533+
Also, we could do an exact stack measure then.
534+
Unfortunately, deallocations also take place when
535+
the thread state is undefined.
536+
*/
537+
538+
#define PyTrash_UNWIND_LEVEL 50
539+
540+
#define Py_TRASHCAN_SAFE_BEGIN(op) \
541+
{ \
542+
++_PyTrash_delete_nesting; \
543+
if (_PyTrash_delete_nesting < PyTrash_UNWIND_LEVEL) { \
544+
545+
#define Py_TRASHCAN_SAFE_END(op) \
546+
;} \
547+
else \
548+
_PyTrash_deposit_object((PyObject*)op);\
549+
--_PyTrash_delete_nesting; \
550+
if (_PyTrash_delete_later && _PyTrash_delete_nesting <= 0) \
551+
_PyTrash_destroy_list(); \
552+
} \
553+
554+
extern DL_IMPORT(void) _PyTrash_deposit_object Py_PROTO((PyObject*));
555+
extern DL_IMPORT(void) _PyTrash_destroy_list Py_PROTO(());
556+
557+
extern DL_IMPORT(int) _PyTrash_delete_nesting;
558+
extern DL_IMPORT(PyObject *) _PyTrash_delete_later;
559+
560+
/* swap the "xx" to check the speed loss */
561+
562+
#define xxPy_TRASHCAN_SAFE_BEGIN(op)
563+
#define xxPy_TRASHCAN_SAFE_END(op) ;
517564
#ifdef __cplusplus
518565
}
519566
#endif

Objects/dictobject.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -479,6 +479,7 @@ dict_dealloc(mp)
479479
{
480480
register int i;
481481
register dictentry *ep;
482+
Py_TRASHCAN_SAFE_BEGIN(mp)
482483
for (i = 0, ep = mp->ma_table; i < mp->ma_size; i++, ep++) {
483484
if (ep->me_key != NULL) {
484485
Py_DECREF(ep->me_key);
@@ -489,6 +490,7 @@ dict_dealloc(mp)
489490
}
490491
PyMem_XDEL(mp->ma_table);
491492
PyMem_DEL(mp);
493+
Py_TRASHCAN_SAFE_END(mp)
492494
}
493495

494496
static int

Objects/frameobject.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ frame_dealloc(f)
103103
int i;
104104
PyObject **fastlocals;
105105

106+
Py_TRASHCAN_SAFE_BEGIN(f)
106107
/* Kill all local variables */
107108
fastlocals = f->f_localsplus;
108109
for (i = f->f_nlocals; --i >= 0; ++fastlocals) {
@@ -120,6 +121,7 @@ frame_dealloc(f)
120121
Py_XDECREF(f->f_exc_traceback);
121122
f->f_back = free_list;
122123
free_list = f;
124+
Py_TRASHCAN_SAFE_END(f)
123125
}
124126

125127
PyTypeObject PyFrame_Type = {

Objects/listobject.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,7 @@ list_dealloc(op)
215215
PyListObject *op;
216216
{
217217
int i;
218+
Py_TRASHCAN_SAFE_BEGIN(op)
218219
if (op->ob_item != NULL) {
219220
/* Do it backwards, for Christian Tismer.
220221
There's a simple test case where somehow this reduces
@@ -227,6 +228,7 @@ list_dealloc(op)
227228
free((ANY *)op->ob_item);
228229
}
229230
free((ANY *)op);
231+
Py_TRASHCAN_SAFE_END(op)
230232
}
231233

232234
static int

Objects/object.c

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -906,3 +906,48 @@ Py_ReprLeave(obj)
906906
}
907907
}
908908
}
909+
910+
/*
911+
trashcan
912+
CT 2k0130
913+
non-recursively destroy nested objects
914+
915+
CT 2k0223
916+
everything is now done in a macro.
917+
918+
CT 2k0305
919+
modified to use functions, after Tim Peter's suggestion.
920+
921+
CT 2k0309
922+
modified to restore a possible error.
923+
*/
924+
925+
int _PyTrash_delete_nesting = 0;
926+
PyObject * _PyTrash_delete_later = NULL;
927+
928+
void
929+
_PyTrash_deposit_object(op)
930+
PyObject *op;
931+
{
932+
PyObject *error_type, *error_value, *error_traceback;
933+
PyErr_Fetch(&error_type, &error_value, &error_traceback);
934+
935+
if (!_PyTrash_delete_later)
936+
_PyTrash_delete_later = PyList_New(0);
937+
if (_PyTrash_delete_later)
938+
PyList_Append(_PyTrash_delete_later, (PyObject *)op);
939+
940+
PyErr_Restore(error_type, error_value, error_traceback);
941+
}
942+
943+
void
944+
_PyTrash_destroy_list()
945+
{
946+
while (_PyTrash_delete_later) {
947+
PyObject *shredder = _PyTrash_delete_later;
948+
_PyTrash_delete_later = NULL;
949+
++_PyTrash_delete_nesting;
950+
Py_DECREF(shredder);
951+
--_PyTrash_delete_nesting;
952+
}
953+
}

Objects/tupleobject.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,7 @@ tupledealloc(op)
172172
{
173173
register int i;
174174

175+
Py_TRASHCAN_SAFE_BEGIN(op)
175176
if (op->ob_size > 0) {
176177
i = op->ob_size;
177178
while (--i >= 0)
@@ -180,11 +181,13 @@ tupledealloc(op)
180181
if (op->ob_size < MAXSAVESIZE) {
181182
op->ob_item[0] = (PyObject *) free_tuples[op->ob_size];
182183
free_tuples[op->ob_size] = op;
183-
return;
184+
goto done; /* return */
184185
}
185186
#endif
186187
}
187188
free((ANY *)op);
189+
done:
190+
Py_TRASHCAN_SAFE_END(op)
188191
}
189192

190193
static int

Python/traceback.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,9 +68,11 @@ static void
6868
tb_dealloc(tb)
6969
tracebackobject *tb;
7070
{
71+
Py_TRASHCAN_SAFE_BEGIN(tb)
7172
Py_XDECREF(tb->tb_next);
7273
Py_XDECREF(tb->tb_frame);
7374
PyMem_DEL(tb);
75+
Py_TRASHCAN_SAFE_END(tb)
7476
}
7577

7678
#define Tracebacktype PyTraceBack_Type

0 commit comments

Comments
 (0)