gh-125859: Fix crash when `gc.get_objects` is called during GC (#125882) · python/cpython@e545ead

GitHub

@@ -1401,10 +1401,32 @@ gc_collect_main(PyThreadState *tstate, int generation, _PyGC_Reason reason)

14011401returnn+m;

14021402}

140314031404+staticPyObject*

1405+list_from_object_stack(_PyObjectStack*stack)

1406+{

1407+PyObject*list=PyList_New(_PyObjectStack_Size(stack));

1408+if (list==NULL) {

1409+PyObject*op;

1410+while ((op=_PyObjectStack_Pop(stack)) !=NULL) {

1411+Py_DECREF(op);

1412+ }

1413+returnNULL;

1414+ }

1415+1416+PyObject*op;

1417+Py_ssize_tidx=0;

1418+while ((op=_PyObjectStack_Pop(stack)) !=NULL) {

1419+assert(idx<PyList_GET_SIZE(list));

1420+PyList_SET_ITEM(list, idx++, op);

1421+ }

1422+assert(idx==PyList_GET_SIZE(list));

1423+returnlist;

1424+}

1425+14041426structget_referrers_args {

14051427structvisitor_argsbase;

14061428PyObject*objs;

1407-structworklistresults;

1429+_PyObjectStackresults;

14081430};

1409143114101432staticint

@@ -1428,11 +1450,21 @@ visit_get_referrers(const mi_heap_t *heap, const mi_heap_area_t *area,

14281450if (op==NULL) {

14291451return true;

14301452 }

1453+if (op->ob_gc_bits& (_PyGC_BITS_UNREACHABLE | _PyGC_BITS_FROZEN)) {

1454+// Exclude unreachable objects (in-progress GC) and frozen

1455+// objects from gc.get_objects() to match the default build.

1456+return true;

1457+ }

1431145814321459structget_referrers_args*arg= (structget_referrers_args*)args;

1460+if (op==arg->objs) {

1461+// Don't include the tuple itself in the referrers list.

1462+return true;

1463+ }

14331464if (Py_TYPE(op)->tp_traverse(op, referrersvisit, arg->objs)) {

1434-op->ob_tid=0; // we will restore the refcount later

1435-worklist_push(&arg->results, op);

1465+if (_PyObjectStack_Push(&arg->results, Py_NewRef(op)) <0) {

1466+return false;

1467+ }

14361468 }

1437146914381470return true;

@@ -1441,48 +1473,25 @@ visit_get_referrers(const mi_heap_t *heap, const mi_heap_area_t *area,

14411473PyObject*

14421474_PyGC_GetReferrers(PyInterpreterState*interp, PyObject*objs)

14431475{

1444-PyObject*result=PyList_New(0);

1445-if (!result) {

1446-returnNULL;

1447- }

1448-1449-_PyEval_StopTheWorld(interp);

1450-1451-// Append all objects to a worklist. This abuses ob_tid. We will restore

1452-// it later. NOTE: We can't append to the PyListObject during

1453-// gc_visit_heaps() because PyList_Append() may reclaim an abandoned

1454-// mimalloc segments while we are traversing them.

1476+// NOTE: We can't append to the PyListObject during gc_visit_heaps()

1477+// because PyList_Append() may reclaim an abandoned mimalloc segments

1478+// while we are traversing them.

14551479structget_referrers_argsargs= { .objs=objs };

1456-gc_visit_heaps(interp, &visit_get_referrers, &args.base);

1457-1458-boolerror= false;

1459-PyObject*op;

1460-while ((op=worklist_pop(&args.results)) !=NULL) {

1461-gc_restore_tid(op);

1462-if (op!=objs&&PyList_Append(result, op) <0) {

1463-error= true;

1464-break;

1465- }

1466- }

1467-1468-// In case of error, clear the remaining worklist

1469-while ((op=worklist_pop(&args.results)) !=NULL) {

1470-gc_restore_tid(op);

1471- }

1472-1480+_PyEval_StopTheWorld(interp);

1481+interr=gc_visit_heaps(interp, &visit_get_referrers, &args.base);

14731482_PyEval_StartTheWorld(interp);

147414831475-if (error) {

1476-Py_DECREF(result);

1477-returnNULL;

1484+PyObject*list=list_from_object_stack(&args.results);

1485+if (err<0) {

1486+PyErr_NoMemory();

1487+Py_CLEAR(list);

14781488 }

1479-1480-returnresult;

1489+returnlist;

14811490}

1482149114831492structget_objects_args {

14841493structvisitor_argsbase;

1485-structworklistobjects;

1494+_PyObjectStackobjects;

14861495};

1487149614881497staticbool

@@ -1493,54 +1502,36 @@ visit_get_objects(const mi_heap_t *heap, const mi_heap_area_t *area,

14931502if (op==NULL) {

14941503return true;

14951504 }

1505+if (op->ob_gc_bits& (_PyGC_BITS_UNREACHABLE | _PyGC_BITS_FROZEN)) {

1506+// Exclude unreachable objects (in-progress GC) and frozen

1507+// objects from gc.get_objects() to match the default build.

1508+return true;

1509+ }

1496151014971511structget_objects_args*arg= (structget_objects_args*)args;

1498-op->ob_tid=0; // we will restore the refcount later

1499-worklist_push(&arg->objects, op);

1500-1512+if (_PyObjectStack_Push(&arg->objects, Py_NewRef(op)) <0) {

1513+return false;

1514+ }

15011515return true;

15021516}

1503151715041518PyObject*

15051519_PyGC_GetObjects(PyInterpreterState*interp, intgeneration)

15061520{

1507-PyObject*result=PyList_New(0);

1508-if (!result) {

1509-returnNULL;

1510- }

1511-1512-_PyEval_StopTheWorld(interp);

1513-1514-// Append all objects to a worklist. This abuses ob_tid. We will restore

1515-// it later. NOTE: We can't append to the list during gc_visit_heaps()

1516-// because PyList_Append() may reclaim an abandoned mimalloc segment

1517-// while we are traversing it.

1521+// NOTE: We can't append to the PyListObject during gc_visit_heaps()

1522+// because PyList_Append() may reclaim an abandoned mimalloc segments

1523+// while we are traversing them.

15181524structget_objects_argsargs= { 0 };

1519-gc_visit_heaps(interp, &visit_get_objects, &args.base);

1520-1521-boolerror= false;

1522-PyObject*op;

1523-while ((op=worklist_pop(&args.objects)) !=NULL) {

1524-gc_restore_tid(op);

1525-if (op!=result&&PyList_Append(result, op) <0) {

1526-error= true;

1527-break;

1528- }

1529- }

1530-1531-// In case of error, clear the remaining worklist

1532-while ((op=worklist_pop(&args.objects)) !=NULL) {

1533-gc_restore_tid(op);

1534- }

1535-1525+_PyEval_StopTheWorld(interp);

1526+interr=gc_visit_heaps(interp, &visit_get_objects, &args.base);

15361527_PyEval_StartTheWorld(interp);

153715281538-if (error) {

1539-Py_DECREF(result);

1540-returnNULL;

1529+PyObject*list=list_from_object_stack(&args.objects);

1530+if (err<0) {

1531+PyErr_NoMemory();

1532+Py_CLEAR(list);

15411533 }

1542-1543-returnresult;

1534+returnlist;

15441535}

1545153615461537staticbool