| Paul Lawrence | 7ea4090 | 2017-02-14 13:32:23 -0800 | [diff] [blame] | 1 | #!/usr/bin/env python | 
 | 2 | # Unit tests for genseccomp.py | 
 | 3 |  | 
 | 4 | import cStringIO | 
 | 5 | import textwrap | 
 | 6 | import unittest | 
 | 7 |  | 
 | 8 | import genseccomp | 
 | 9 |  | 
 | 10 | class 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 |  | 
| Paul Lawrence | 89fa81f | 2017-02-17 10:22:03 -0800 | [diff] [blame] | 23 |   def get_switches(self, arch): | 
 | 24 |     return self.get_config(arch)[2] | 
 | 25 |  | 
| Paul Lawrence | 7ea4090 | 2017-02-14 13:32:23 -0800 | [diff] [blame] | 26 |   def test_get_names(self): | 
| Paul Lawrence | 3dd3d55 | 2017-04-12 10:02:54 -0700 | [diff] [blame] | 27 |     bionic = cStringIO.StringIO(textwrap.dedent("""\ | 
| Paul Lawrence | 7ea4090 | 2017-02-14 13:32:23 -0800 | [diff] [blame] | 28 | int __llseek:_llseek(int, unsigned long, unsigned long, off64_t*, int) arm,mips,x86 | 
 | 29 | int         fchown:fchown(int, uid_t, gid_t)    arm64,mips,mips64,x86_64 | 
 | 30 |     """)) | 
 | 31 |  | 
 | 32 |     whitelist = cStringIO.StringIO(textwrap.dedent("""\ | 
 | 33 | ssize_t     read(int, void*, size_t)        all | 
 | 34 |     """)) | 
 | 35 |  | 
| Paul Lawrence | 3dd3d55 | 2017-04-12 10:02:54 -0700 | [diff] [blame] | 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) | 
| Paul Lawrence | 7ea4090 | 2017-02-14 13:32:23 -0800 | [diff] [blame] | 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 |  | 
| Paul Lawrence | 3dd3d55 | 2017-04-12 10:02:54 -0700 | [diff] [blame] | 55 |     # Blacklist item must be in bionic | 
 | 56 |     blacklist = cStringIO.StringIO(textwrap.dedent("""\ | 
 | 57 | int         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("""\ | 
 | 67 | int         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("""\ | 
 | 78 | int         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("""\ | 
 | 88 | int __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 |  | 
| Paul Lawrence | 7ea4090 | 2017-02-14 13:32:23 -0800 | [diff] [blame] | 96 |   def test_convert_names_to_NRs(self): | 
 | 97 |     self.assertEquals(genseccomp.convert_names_to_NRs(["open"], | 
| Paul Lawrence | 89fa81f | 2017-02-17 10:22:03 -0800 | [diff] [blame] | 98 |                                                       self.get_headers("arm"), | 
 | 99 |                                                       self.get_switches("arm")), | 
| Paul Lawrence | 7ea4090 | 2017-02-14 13:32:23 -0800 | [diff] [blame] | 100 |                       [("open", 5)]) | 
 | 101 |  | 
 | 102 |     self.assertEquals(genseccomp.convert_names_to_NRs(["__ARM_NR_set_tls"], | 
| Paul Lawrence | 89fa81f | 2017-02-17 10:22:03 -0800 | [diff] [blame] | 103 |                                                       self.get_headers("arm"), | 
 | 104 |                                                       self.get_switches("arm")), | 
| Paul Lawrence | 7ea4090 | 2017-02-14 13:32:23 -0800 | [diff] [blame] | 105 |                       [('__ARM_NR_set_tls', 983045)]) | 
 | 106 |  | 
 | 107 |     self.assertEquals(genseccomp.convert_names_to_NRs(["openat"], | 
| Paul Lawrence | 89fa81f | 2017-02-17 10:22:03 -0800 | [diff] [blame] | 108 |                                                       self.get_headers("arm64"), | 
 | 109 |                                                       self.get_switches("arm64")), | 
| Paul Lawrence | 7ea4090 | 2017-02-14 13:32:23 -0800 | [diff] [blame] | 110 |                       [("openat", 56)]) | 
 | 111 |  | 
| Paul Lawrence | 89fa81f | 2017-02-17 10:22:03 -0800 | [diff] [blame] | 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 |  | 
| Paul Lawrence | 7ea4090 | 2017-02-14 13:32:23 -0800 | [diff] [blame] | 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) | 
| Paul Lawrence | 65b47c9 | 2017-03-22 08:03:51 -0700 | [diff] [blame] | 163 |     self.assertEquals(bpf, ['BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 1, 0, 2),', | 
| Paul Lawrence | 7ea4090 | 2017-02-14 13:32:23 -0800 | [diff] [blame] | 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) | 
| Paul Lawrence | 65b47c9 | 2017-03-22 08:03:51 -0700 | [diff] [blame] | 169 |     self.assertEquals(bpf, ['BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 1, 0, 4),', | 
| Paul Lawrence | 7ea4090 | 2017-02-14 13:32:23 -0800 | [diff] [blame] | 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 |  | 
| Paul Lawrence | dfe8434 | 2017-02-16 09:24:39 -0800 | [diff] [blame] | 183 |     #include "seccomp_bpfs.h" | 
 | 184 |     const sock_filter arm_filter[] = { | 
| Paul Lawrence | 7ea4090 | 2017-02-14 13:32:23 -0800 | [diff] [blame] | 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 |  | 
| Paul Lawrence | 3dd3d55 | 2017-04-12 10:02:54 -0700 | [diff] [blame] | 203 |     blacklist = cStringIO.StringIO(textwrap.dedent("""\ | 
 | 204 |     """)) | 
 | 205 |  | 
 | 206 |     syscall_files = [syscalls, whitelist, blacklist] | 
| Paul Lawrence | 89fa81f | 2017-02-17 10:22:03 -0800 | [diff] [blame] | 207 |     output = genseccomp.construct_bpf(syscall_files, "arm", self.get_headers("arm"), | 
 | 208 |                                       self.get_switches("arm")) | 
| Paul Lawrence | 7ea4090 | 2017-02-14 13:32:23 -0800 | [diff] [blame] | 209 |  | 
 | 210 |     expected_output = textwrap.dedent("""\ | 
 | 211 |     // Autogenerated file - edit at your peril!! | 
 | 212 |  | 
 | 213 |     #include <linux/filter.h> | 
 | 214 |     #include <errno.h> | 
 | 215 |  | 
| Paul Lawrence | dfe8434 | 2017-02-16 09:24:39 -0800 | [diff] [blame] | 216 |     #include "seccomp_bpfs.h" | 
 | 217 |     const sock_filter arm_filter[] = { | 
| Paul Lawrence | 65b47c9 | 2017-03-22 08:03:51 -0700 | [diff] [blame] | 218 |     BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 3, 0, 4), | 
| Paul Lawrence | 7ea4090 | 2017-02-14 13:32:23 -0800 | [diff] [blame] | 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 |  | 
 | 230 | if __name__ == '__main__': | 
 | 231 |   unittest.main() |