Commit 2bf44f00 authored by Julian Andres Klode's avatar Julian Andres Klode
Browse files

arfile: Regression: Collect file<->deb/ar reference cycles

The internal FileFd object now owned the PyObject* that gave us
the descriptor but we were never visiting that object during garbage
collection, so if there was a cycle, Python could not realize that.

Make the objects garbage collectable, by adding VISIT and CLEAR
calls for self->Fd, and by making the FileFd object support garbage
collection in the first place.
parent 3d9af5f1
......@@ -151,8 +151,11 @@ PyTypeObject PyFileFd_Type = {
0, // tp_getattro
0, // tp_setattro
0, // tp_as_buffer
Py_TPFLAGS_DEFAULT, // tp_flags
Py_TPFLAGS_DEFAULT | // tp_flags
Py_TPFLAGS_HAVE_GC,
filefd_doc, // tp_doc
CppTraverse<FileFd>, // tp_traverse
CppClear<FileFd>, // tp_clear
};
......@@ -447,9 +450,23 @@ static PyObject *ararchive_new(PyTypeObject *type, PyObject *args,
return self.release();
}
static int ararchive_traverse(PyObject *_self, visitproc visit, void* arg)
{
PyArArchiveObject *self = (PyArArchiveObject*)_self;
Py_VISIT(self->Fd);
return CppTraverse<ARArchive*>(self, visit, arg);
}
static int ararchive_clear(PyObject *_self)
{
PyArArchiveObject *self = (PyArArchiveObject*)_self;
Py_CLEAR(self->Fd);
return CppClear<ARArchive*>(self);
}
static void ararchive_dealloc(PyObject *self)
{
Py_CLEAR(((PyArArchiveObject *)(self))->Fd);
ararchive_clear(self);
CppDeallocPtr<ARArchive*>(self);
}
......@@ -503,8 +520,8 @@ PyTypeObject PyArArchive_Type = {
Py_TPFLAGS_DEFAULT | // tp_flags
Py_TPFLAGS_HAVE_GC,
ararchive_doc, // tp_doc
CppTraverse<ARArchive*>, // tp_traverse
CppClear<ARArchive*>, // tp_clear
ararchive_traverse, // tp_traverse
ararchive_clear, // tp_clear
0, // tp_richcompare
0, // tp_weaklistoffset
(getiterfunc)ararchive_iter, // tp_iter
......
......@@ -17,8 +17,10 @@ libdir = get_library_dir()
if libdir:
sys.path.insert(0, libdir)
import apt_inst
import gc
import subprocess
import tempfile
import warnings
@unittest.skipIf(
......@@ -41,6 +43,46 @@ class TestCVE_2020_27351(unittest.TestCase):
with open(self.GOOD_DEB) as good_deb:
apt_inst.DebFile(good_deb).control.extractdata("control")
def test_regression_bug_977000_2(self):
"""file object <-> debfile cycles should be collected by gc."""
class Cycle(object):
def __init__(self, fname):
self.file = open(fname)
self.deb = apt_inst.DebFile(self)
def fileno(self):
return self.file.fileno()
before = os.listdir("/proc/self/fd")
Cycle(self.GOOD_DEB).deb.control.extractdata("control")
warnings.filterwarnings("ignore", category=ResourceWarning)
gc.collect()
warnings.resetwarnings()
after = os.listdir("/proc/self/fd")
self.assertEqual(before, after)
def test_regression_bug_977000_2_ar(self):
"""file object <-> debfile cycles should be collected by gc."""
class Cycle(object):
def __init__(self, fname):
self.file = open(fname)
self.deb = apt_inst.ArArchive(self)
def fileno(self):
return self.file.fileno()
before = os.listdir("/proc/self/fd")
Cycle(self.GOOD_DEB).deb.gettar("control.tar.gz", "gzip").extractdata(
"control"
)
warnings.filterwarnings("ignore", category=ResourceWarning)
gc.collect()
warnings.resetwarnings()
after = os.listdir("/proc/self/fd")
self.assertEqual(before, after)
def test_success_a_member(self):
"""fd should be kept around as long as a tarfile member"""
before = os.listdir("/proc/self/fd")
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment