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,x86 29int fchown:fchown(int, uid_t, gid_t) arm64,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,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,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,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,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 123 def test_convert_NRs_to_ranges(self): 124 ranges = genseccomp.convert_NRs_to_ranges([("b", 2), ("a", 1)]) 125 self.assertEquals(len(ranges), 1) 126 self.assertEquals(ranges[0].begin, 1) 127 self.assertEquals(ranges[0].end, 3) 128 self.assertItemsEqual(ranges[0].names, ["a", "b"]) 129 130 ranges = genseccomp.convert_NRs_to_ranges([("b", 3), ("a", 1)]) 131 self.assertEquals(len(ranges), 2) 132 self.assertEquals(ranges[0].begin, 1) 133 self.assertEquals(ranges[0].end, 2) 134 self.assertItemsEqual(ranges[0].names, ["a"]) 135 self.assertEquals(ranges[1].begin, 3) 136 self.assertEquals(ranges[1].end, 4) 137 self.assertItemsEqual(ranges[1].names, ["b"]) 138 139 def test_convert_to_intermediate_bpf(self): 140 ranges = genseccomp.convert_NRs_to_ranges([("b", 2), ("a", 1)]) 141 bpf = genseccomp.convert_to_intermediate_bpf(ranges) 142 self.assertEquals(bpf, ['BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 3, {fail}, {allow}), //a|b']) 143 144 ranges = genseccomp.convert_NRs_to_ranges([("b", 3), ("a", 1)]) 145 bpf = genseccomp.convert_to_intermediate_bpf(ranges) 146 self.assertEquals(bpf, ['BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 3, 1, 0),', 147 'BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 2, {fail}, {allow}), //a', 148 'BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 4, {fail}, {allow}), //b']) 149 150 def test_convert_ranges_to_bpf(self): 151 ranges = genseccomp.convert_NRs_to_ranges([("b", 2), ("a", 1)]) 152 bpf = genseccomp.convert_ranges_to_bpf(ranges) 153 self.assertEquals(bpf, ['BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 1, 0, 2),', 154 'BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 3, 1, 0), //a|b', 155 'BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW),']) 156 157 ranges = genseccomp.convert_NRs_to_ranges([("b", 3), ("a", 1)]) 158 bpf = genseccomp.convert_ranges_to_bpf(ranges) 159 self.assertEquals(bpf, ['BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 1, 0, 4),', 160 'BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 3, 1, 0),', 161 'BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 2, 2, 1), //a', 162 'BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 4, 1, 0), //b', 163 'BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW),']) 164 165 def test_convert_bpf_to_output(self): 166 output = genseccomp.convert_bpf_to_output(["line1", "line2"], "arm") 167 expected_output = textwrap.dedent("""\ 168 // Autogenerated file - edit at your peril!! 169 170 #include <linux/filter.h> 171 #include <errno.h> 172 173 #include "seccomp_bpfs.h" 174 const sock_filter arm_filter[] = { 175 line1 176 line2 177 }; 178 179 const size_t arm_filter_size = sizeof(arm_filter) / sizeof(struct sock_filter); 180 """) 181 self.assertEquals(output, expected_output) 182 183 def test_construct_bpf(self): 184 syscalls = cStringIO.StringIO(textwrap.dedent("""\ 185 int __llseek:_llseek(int, unsigned long, unsigned long, off64_t*, int) arm,x86 186 int fchown:fchown(int, uid_t, gid_t) arm64,x86_64 187 """)) 188 189 whitelist = cStringIO.StringIO(textwrap.dedent("""\ 190 ssize_t read(int, void*, size_t) all 191 """)) 192 193 blacklist = cStringIO.StringIO(textwrap.dedent("""\ 194 """)) 195 196 syscall_files = [syscalls, whitelist, blacklist] 197 output = genseccomp.construct_bpf(syscall_files, "arm", self.get_headers("arm"), 198 self.get_switches("arm")) 199 200 expected_output = textwrap.dedent("""\ 201 // Autogenerated file - edit at your peril!! 202 203 #include <linux/filter.h> 204 #include <errno.h> 205 206 #include "seccomp_bpfs.h" 207 const sock_filter arm_filter[] = { 208 BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 3, 0, 4), 209 BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 140, 1, 0), 210 BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 4, 2, 1), //read 211 BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 141, 1, 0), //_llseek 212 BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW), 213 }; 214 215 const size_t arm_filter_size = sizeof(arm_filter) / sizeof(struct sock_filter); 216 """) 217 self.assertEquals(output, expected_output) 218 219 220if __name__ == '__main__': 221 unittest.main() 222