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