1#!/usr/bin/env python
2# Unit tests for genseccomp.py
3
4import cStringIO
5import textwrap
6import unittest
7
8import genseccomp
9
10class TestGenseccomp(unittest.TestCase):
11  def setUp(self):
12    genseccomp.set_dir()
13
14  def get_config(self, arch):
15    for i in genseccomp.POLICY_CONFIGS:
16      if i[0] == arch:
17        return i
18    self.fail("No such architecture")
19
20  def get_headers(self, arch):
21    return self.get_config(arch)[1]
22
23  def get_switches(self, arch):
24    return self.get_config(arch)[2]
25
26  def test_get_names(self):
27    bionic = cStringIO.StringIO(textwrap.dedent("""\
28int __llseek:_llseek(int, unsigned long, unsigned long, off64_t*, int) arm,mips,x86
29int         fchown:fchown(int, uid_t, gid_t)    arm64,mips,mips64,x86_64
30    """))
31
32    whitelist = cStringIO.StringIO(textwrap.dedent("""\
33ssize_t     read(int, void*, size_t)        all
34    """))
35
36    empty = cStringIO.StringIO(textwrap.dedent("""\
37    """))
38
39    names = genseccomp.get_names([bionic, whitelist, empty], "arm")
40    bionic.seek(0)
41    whitelist.seek(0)
42    empty.seek(0)
43    names64 = genseccomp.get_names([bionic, whitelist, empty], "arm64")
44    bionic.seek(0)
45    whitelist.seek(0)
46    empty.seek(0)
47
48    self.assertIn("fchown", names64)
49    self.assertNotIn("fchown", names)
50    self.assertIn("_llseek", names)
51    self.assertNotIn("_llseek", names64)
52    self.assertIn("read", names)
53    self.assertIn("read", names64)
54
55    # Blacklist item must be in bionic
56    blacklist = cStringIO.StringIO(textwrap.dedent("""\
57int         fchown2:fchown2(int, uid_t, gid_t)    arm64,mips,mips64,x86_64
58    """))
59    with self.assertRaises(RuntimeError):
60      genseccomp.get_names([bionic, whitelist, blacklist], "arm")
61    bionic.seek(0)
62    whitelist.seek(0)
63    blacklist.seek(0)
64
65    # Test blacklist item is removed
66    blacklist = cStringIO.StringIO(textwrap.dedent("""\
67int         fchown:fchown(int, uid_t, gid_t)    arm64,mips,mips64,x86_64
68    """))
69    names = genseccomp.get_names([bionic, whitelist, blacklist], "arm64")
70    bionic.seek(0)
71    whitelist.seek(0)
72    blacklist.seek(0)
73    self.assertIn("read", names)
74    self.assertNotIn("fchown", names)
75
76    # Blacklist item must not be in whitelist
77    whitelist = cStringIO.StringIO(textwrap.dedent("""\
78int         fchown:fchown(int, uid_t, gid_t)    arm64,mips,mips64,x86_64
79    """))
80    with self.assertRaises(RuntimeError):
81      genseccomp.get_names([empty, whitelist, blacklist], "arm")
82    empty.seek(0)
83    whitelist.seek(0)
84    blacklist.seek(0)
85
86    # No dups in bionic and whitelist
87    whitelist = cStringIO.StringIO(textwrap.dedent("""\
88int __llseek:_llseek(int, unsigned long, unsigned long, off64_t*, int) arm,mips,x86
89    """))
90    with self.assertRaises(RuntimeError):
91      genseccomp.get_names([bionic, whitelist, empty], "arm")
92    bionic.seek(0)
93    whitelist.seek(0)
94    empty.seek(0)
95
96  def test_convert_names_to_NRs(self):
97    self.assertEquals(genseccomp.convert_names_to_NRs(["open"],
98                                                      self.get_headers("arm"),
99                                                      self.get_switches("arm")),
100                      [("open", 5)])
101
102    self.assertEquals(genseccomp.convert_names_to_NRs(["__ARM_NR_set_tls"],
103                                                      self.get_headers("arm"),
104                                                      self.get_switches("arm")),
105                      [('__ARM_NR_set_tls', 983045)])
106
107    self.assertEquals(genseccomp.convert_names_to_NRs(["openat"],
108                                                      self.get_headers("arm64"),
109                                                      self.get_switches("arm64")),
110                      [("openat", 56)])
111
112    self.assertEquals(genseccomp.convert_names_to_NRs(["openat"],
113                                                      self.get_headers("x86"),
114                                                      self.get_switches("x86")),
115                      [("openat", 295)])
116
117    self.assertEquals(genseccomp.convert_names_to_NRs(["openat"],
118                                                      self.get_headers("x86_64"),
119                                                      self.get_switches("x86_64")),
120                      [("openat", 257)])
121
122    self.assertEquals(genseccomp.convert_names_to_NRs(["openat"],
123                                                      self.get_headers("mips"),
124                                                      self.get_switches("mips")),
125                      [("openat", 4288)])
126
127    self.assertEquals(genseccomp.convert_names_to_NRs(["openat"],
128                                                      self.get_headers("mips64"),
129                                                      self.get_switches("mips64")),
130                      [("openat", 5247)])
131
132
133  def test_convert_NRs_to_ranges(self):
134    ranges = genseccomp.convert_NRs_to_ranges([("b", 2), ("a", 1)])
135    self.assertEquals(len(ranges), 1)
136    self.assertEquals(ranges[0].begin, 1)
137    self.assertEquals(ranges[0].end, 3)
138    self.assertItemsEqual(ranges[0].names, ["a", "b"])
139
140    ranges = genseccomp.convert_NRs_to_ranges([("b", 3), ("a", 1)])
141    self.assertEquals(len(ranges), 2)
142    self.assertEquals(ranges[0].begin, 1)
143    self.assertEquals(ranges[0].end, 2)
144    self.assertItemsEqual(ranges[0].names, ["a"])
145    self.assertEquals(ranges[1].begin, 3)
146    self.assertEquals(ranges[1].end, 4)
147    self.assertItemsEqual(ranges[1].names, ["b"])
148
149  def test_convert_to_intermediate_bpf(self):
150    ranges = genseccomp.convert_NRs_to_ranges([("b", 2), ("a", 1)])
151    bpf = genseccomp.convert_to_intermediate_bpf(ranges)
152    self.assertEquals(bpf, ['BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 3, {fail}, {allow}), //a|b'])
153
154    ranges = genseccomp.convert_NRs_to_ranges([("b", 3), ("a", 1)])
155    bpf = genseccomp.convert_to_intermediate_bpf(ranges)
156    self.assertEquals(bpf, ['BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 3, 1, 0),',
157                            'BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 2, {fail}, {allow}), //a',
158                            'BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 4, {fail}, {allow}), //b'])
159
160  def test_convert_ranges_to_bpf(self):
161    ranges = genseccomp.convert_NRs_to_ranges([("b", 2), ("a", 1)])
162    bpf = genseccomp.convert_ranges_to_bpf(ranges)
163    self.assertEquals(bpf, ['BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 1, 0, 2),',
164                            'BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 3, 1, 0), //a|b',
165                            'BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW),'])
166
167    ranges = genseccomp.convert_NRs_to_ranges([("b", 3), ("a", 1)])
168    bpf = genseccomp.convert_ranges_to_bpf(ranges)
169    self.assertEquals(bpf, ['BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 1, 0, 4),',
170                            'BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 3, 1, 0),',
171                            'BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 2, 2, 1), //a',
172                            'BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 4, 1, 0), //b',
173                            'BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW),'])
174
175  def test_convert_bpf_to_output(self):
176    output = genseccomp.convert_bpf_to_output(["line1", "line2"], "arm")
177    expected_output = textwrap.dedent("""\
178    // Autogenerated file - edit at your peril!!
179
180    #include <linux/filter.h>
181    #include <errno.h>
182
183    #include "seccomp_bpfs.h"
184    const sock_filter arm_filter[] = {
185    line1
186    line2
187    };
188
189    const size_t arm_filter_size = sizeof(arm_filter) / sizeof(struct sock_filter);
190    """)
191    self.assertEquals(output, expected_output)
192
193  def test_construct_bpf(self):
194    syscalls = cStringIO.StringIO(textwrap.dedent("""\
195    int __llseek:_llseek(int, unsigned long, unsigned long, off64_t*, int) arm,mips,x86
196    int         fchown:fchown(int, uid_t, gid_t)    arm64,mips,mips64,x86_64
197    """))
198
199    whitelist = cStringIO.StringIO(textwrap.dedent("""\
200    ssize_t     read(int, void*, size_t)        all
201    """))
202
203    blacklist = cStringIO.StringIO(textwrap.dedent("""\
204    """))
205
206    syscall_files = [syscalls, whitelist, blacklist]
207    output = genseccomp.construct_bpf(syscall_files, "arm", self.get_headers("arm"),
208                                      self.get_switches("arm"))
209
210    expected_output = textwrap.dedent("""\
211    // Autogenerated file - edit at your peril!!
212
213    #include <linux/filter.h>
214    #include <errno.h>
215
216    #include "seccomp_bpfs.h"
217    const sock_filter arm_filter[] = {
218    BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 3, 0, 4),
219    BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 140, 1, 0),
220    BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 4, 2, 1), //read
221    BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 141, 1, 0), //_llseek
222    BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW),
223    };
224
225    const size_t arm_filter_size = sizeof(arm_filter) / sizeof(struct sock_filter);
226    """)
227    self.assertEquals(output, expected_output)
228
229
230if __name__ == '__main__':
231  unittest.main()
232