summaryrefslogtreecommitdiffstats
path: root/tests/test_cve_2020_27351.py
blob: 1ec1c76f45b7bf3085a4e66649093e83bb157906 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
#!/usr/bin/python
#
# Copyright (C) 2020 Canonical Ltd
#
# Copying and distribution of this file, with or without modification,
# are permitted in any medium without royalty provided the copyright
# notice and this notice are preserved.
"""Unit tests for verifying the correctness of DebFile descriptor handling."""
import os
import sys
import unittest

from test_all import get_library_dir

libdir = get_library_dir()
if libdir:
    sys.path.insert(0, libdir)
import gc
import subprocess
import tempfile
import warnings

import apt_inst


@unittest.skipIf(not os.path.exists("/proc/self/fd"), "no /proc/self/fd available")
class TestCVE_2020_27351(unittest.TestCase):
    """test the debfile"""

    GOOD_DEB = "data/test_debs/utf8-package_1.0-1_all.deb"

    def test_success(self):
        """opening package successfully should not leak fd"""
        before = os.listdir("/proc/self/fd")
        apt_inst.DebFile(self.GOOD_DEB)
        after = os.listdir("/proc/self/fd")
        self.assertEqual(before, after)

    def test_regression_bug_977000(self):
        """opening with a file handle should work correctly"""
        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:
            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:
            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")
        data = apt_inst.DebFile(self.GOOD_DEB).data
        after = os.listdir("/proc/self/fd")
        self.assertEqual(len(before), len(after) - 1)
        del data
        after = os.listdir("/proc/self/fd")
        self.assertEqual(before, after)

    def _create_deb_without(self, member):
        temp = tempfile.NamedTemporaryFile(mode="wb")
        try:
            with open(self.GOOD_DEB, "rb") as deb:
                temp.write(deb.read())
            temp.flush()
            subprocess.check_call(["ar", "d", temp.name, member])
            return temp
        except Exception as e:
            temp.close()
            raise e

    def test_nocontrol(self):
        """opening package without control.tar.gz should not leak fd"""
        before = os.listdir("/proc/self/fd")
        with self._create_deb_without("control.tar.gz") as temp:
            try:
                apt_inst.DebFile(temp.name)
            except SystemError as e:
                self.assertIn("control.tar", str(e))
            else:
                self.fail("Did not raise an exception")

        after = os.listdir("/proc/self/fd")
        self.assertEqual(before, after)

    def test_nodata(self):
        """opening package without data.tar.gz should not leak fd"""
        before = os.listdir("/proc/self/fd")
        with self._create_deb_without("data.tar.gz") as temp:
            try:
                apt_inst.DebFile(temp.name)
            except SystemError as e:
                self.assertIn("data.tar", str(e))
            else:
                self.fail("Did not raise an exception")

        after = os.listdir("/proc/self/fd")
        self.assertEqual(before, after)

    def test_no_debian_binary(self):
        """opening package without debian-binary should not leak fd"""
        before = os.listdir("/proc/self/fd")
        with self._create_deb_without("debian-binary") as temp:
            try:
                apt_inst.DebFile(temp.name)
            except SystemError as e:
                self.assertIn("missing debian-binary", str(e))
            else:
                self.fail("Did not raise an exception")

        after = os.listdir("/proc/self/fd")
        self.assertEqual(before, after)


if __name__ == "__main__":
    # logging.basicConfig(level=logging.DEBUG)
    unittest.main()