diff options
author | Luis Hector Chavez <lhchavez@google.com> | 2019-03-27 11:14:14 -0700 |
---|---|---|
committer | android-build-merger <android-build-merger@google.com> | 2019-03-27 11:14:14 -0700 |
commit | 1b4ea70c5f0e6e2c5a2204033cb4b3f2c724190f (patch) | |
tree | a5c1cbfd869ad35a60071b85a5511790cf99ec2e | |
parent | de90bdab74bef72596fb9f9289c28cf0e4f04c2d (diff) | |
parent | 5fdc780fc19245e15b140a919bbc315073912b8f (diff) | |
download | minijail-1b4ea70c5f0e6e2c5a2204033cb4b3f2c724190f.tar.gz |
tools/compile_seccomp_policy: Increase the code coverage to 93% am: 89a2710839
am: 5fdc780fc1
Change-Id: I98775f8c84a89fbe71d4048214394e08dde81cd4
-rwxr-xr-x | tools/compiler_unittest.py | 63 | ||||
-rwxr-xr-x | tools/parser_unittest.py | 130 | ||||
-rw-r--r-- | tools/testdata/arch_64.json | 158 |
3 files changed, 349 insertions, 2 deletions
diff --git a/tools/compiler_unittest.py b/tools/compiler_unittest.py index ae00d7f..3cc0e6a 100755 --- a/tools/compiler_unittest.py +++ b/tools/compiler_unittest.py @@ -233,6 +233,14 @@ class CompileFilterStatementTests(unittest.TestCase): block.simulate(self.arch.arch_nr, self.arch.syscalls['read'], 1, 1)[1], 'ALLOW') + def test_trap(self): + """Accept lines that trap unconditionally.""" + block = self._compile('read: trap') + + self.assertEqual( + block.simulate(self.arch.arch_nr, self.arch.syscalls['read'], + 0)[1], 'TRAP') + def test_ret_errno(self): """Accept lines that return errno.""" block = self._compile('read : arg0 == 0 || arg0 == 1 ; return 1') @@ -255,6 +263,22 @@ class CompileFilterStatementTests(unittest.TestCase): block.simulate(self.arch.arch_nr, self.arch.syscalls['read'], 0)[1:], ('ERRNO', 1)) + def test_trace(self): + """Accept lines that trace unconditionally.""" + block = self._compile('read: trace') + + self.assertEqual( + block.simulate(self.arch.arch_nr, self.arch.syscalls['read'], + 0)[1], 'TRACE') + + def test_log(self): + """Accept lines that log unconditionally.""" + block = self._compile('read: log') + + self.assertEqual( + block.simulate(self.arch.arch_nr, self.arch.syscalls['read'], + 0)[1], 'LOG') + def test_mmap_write_xor_exec(self): """Accept the idiomatic filter for mmap.""" block = self._compile( @@ -369,7 +393,7 @@ class CompileFileTests(unittest.TestCase): """Ensure policy reflects script by testing some random scripts.""" iterations = 5 for i in range(iterations): - num_entries = len(self.arch.syscalls) * (i + 1) // iterations + num_entries = 64 * (i + 1) // iterations syscalls = dict( zip( random.sample(self.arch.syscalls.keys(), num_entries), @@ -399,6 +423,43 @@ class CompileFileTests(unittest.TestCase): 'strategy: %s, policy:\n%s') % (name, number, strategy, policy_contents)) + @unittest.skipIf(not int(os.getenv('SLOW_TESTS', '0')), 'slow') + def test_compile_huge_policy(self): + """Ensure jumps while compiling a huge policy are still valid.""" + # Given that the BST strategy is O(n^3), don't choose a crazy large + # value, but it still needs to be around 128 so that we exercise the + # codegen paths that depend on the length of the jump. + # + # Immediate jump offsets in BPF comparison instructions are limited to + # 256 instructions, so given that every syscall filter consists of a + # load and jump instructions, with 128 syscalls there will be at least + # one jump that's further than 256 instructions. + num_entries = 128 + syscalls = dict(random.sample(self.arch.syscalls.items(), num_entries)) + # Here we force every single filter to be distinct. Otherwise the + # codegen layer will coalesce filters that compile to the same + # instructions. + policy_contents = '\n'.join( + '%s: arg0 == %d' % s for s in syscalls.items()) + + path = self._write_file('test.policy', policy_contents) + + program = self.compiler.compile_file( + path, + optimization_strategy=compiler.OptimizationStrategy.BST, + kill_action=bpf.KillProcess()) + for name, number in self.arch.syscalls.items(): + expected_result = ('ALLOW' + if name in syscalls else 'KILL_PROCESS') + self.assertEqual( + bpf.simulate(program.instructions, self.arch.arch_nr, + self.arch.syscalls[name], number)[1], + expected_result) + self.assertEqual( + bpf.simulate(program.instructions, self.arch.arch_nr, + self.arch.syscalls[name], number + 1)[1], + 'KILL_PROCESS') + if __name__ == '__main__': unittest.main() diff --git a/tools/parser_unittest.py b/tools/parser_unittest.py index f13a109..1173f89 100755 --- a/tools/parser_unittest.py +++ b/tools/parser_unittest.py @@ -236,6 +236,11 @@ class ParseConstantTests(unittest.TestCase): with self.assertRaisesRegex(parser.ParseException, 'empty constant'): self.parser.parse_value(self._tokenize('0|')) + def test_parse_invalid_constant(self): + """Reject parsing invalid constants.""" + with self.assertRaisesRegex(parser.ParseException, 'invalid constant'): + self.parser.parse_value(self._tokenize('foo')) + class ParseFilterExpressionTests(unittest.TestCase): """Tests for PolicyParser.parse_argument_expression.""" @@ -287,6 +292,16 @@ class ParseFilterExpressionTests(unittest.TestCase): self.parser.parse_argument_expression( self._tokenize('arg0 = 0xffff')) + def test_parse_missing_operator(self): + """Reject missing operator.""" + with self.assertRaisesRegex(parser.ParseException, 'missing operator'): + self.parser.parse_argument_expression(self._tokenize('arg0')) + + def test_parse_missing_operand(self): + """Reject missing operand.""" + with self.assertRaisesRegex(parser.ParseException, 'empty constant'): + self.parser.parse_argument_expression(self._tokenize('arg0 ==')) + class ParseFilterTests(unittest.TestCase): """Tests for PolicyParser.parse_filter.""" @@ -567,6 +582,28 @@ class ParseFileTests(unittest.TestCase): ]), ])) + def test_parse_other_arch(self): + """Allow entries that only target another architecture.""" + path = self._write_file( + 'test.policy', """ + # Comment. + read[arch=nonexistent]: allow + write: allow + """) + + self.assertEqual( + self.parser.parse_file(path), + parser.ParsedPolicy( + default_action=bpf.KillProcess(), + filter_statements=[ + parser.FilterStatement( + syscall=parser.Syscall('write', 1), + frequency=1, + filters=[ + parser.Filter(None, bpf.Allow()), + ]), + ])) + def test_parse_include(self): """Allow including policy files.""" path = self._write_file( @@ -605,6 +642,40 @@ class ParseFileTests(unittest.TestCase): ]), ])) + def test_parse_invalid_include(self): + """Reject including invalid policy files.""" + with self.assertRaisesRegex(parser.ParseException, + r'empty include path'): + path = self._write_file( + 'test.policy', """ + @include + """) + self.parser.parse_file(path) + + with self.assertRaisesRegex(parser.ParseException, + r'invalid include path'): + path = self._write_file( + 'test.policy', """ + @include arg0 + """) + self.parser.parse_file(path) + + with self.assertRaisesRegex(parser.ParseException, + r'@include statement nested too deep'): + path = self._write_file( + 'test.policy', """ + @include ./test.policy + """) + self.parser.parse_file(path) + + with self.assertRaisesRegex(parser.ParseException, + r'Could not @include .*'): + path = self._write_file( + 'test.policy', """ + @include ./nonexistent.policy + """) + self.parser.parse_file(path) + def test_parse_frequency(self): """Allow including frequency files.""" self._write_file( @@ -631,6 +702,65 @@ class ParseFileTests(unittest.TestCase): ]), ])) + def test_parse_invalid_frequency(self): + """Reject including invalid frequency files.""" + path = self._write_file('test.policy', + """@frequency ./test.frequency""") + + with self.assertRaisesRegex(parser.ParseException, r'missing colon'): + self._write_file('test.frequency', """ + read + """) + self.parser.parse_file(path) + + with self.assertRaisesRegex(parser.ParseException, r'invalid colon'): + self._write_file('test.frequency', """ + read foo + """) + self.parser.parse_file(path) + + with self.assertRaisesRegex(parser.ParseException, r'missing number'): + self._write_file('test.frequency', """ + read: + """) + self.parser.parse_file(path) + + with self.assertRaisesRegex(parser.ParseException, r'invalid number'): + self._write_file('test.frequency', """ + read: foo + """) + self.parser.parse_file(path) + + with self.assertRaisesRegex(parser.ParseException, r'invalid number'): + self._write_file('test.frequency', """ + read: -1 + """) + self.parser.parse_file(path) + + with self.assertRaisesRegex(parser.ParseException, + r'empty frequency path'): + path = self._write_file( + 'test.policy', """ + @frequency + """) + self.parser.parse_file(path) + + with self.assertRaisesRegex(parser.ParseException, + r'invalid frequency path'): + path = self._write_file( + 'test.policy', """ + @frequency arg0 + """) + self.parser.parse_file(path) + + with self.assertRaisesRegex(parser.ParseException, + r'Could not open frequency file.*'): + path = self._write_file( + 'test.policy', """ + @frequency ./nonexistent.frequency + """) + self.parser.parse_file(path) + def test_parse_multiple_unconditional(self): """Reject actions after an unconditional action.""" path = self._write_file( diff --git a/tools/testdata/arch_64.json b/tools/testdata/arch_64.json index 385da94..1286ee4 100644 --- a/tools/testdata/arch_64.json +++ b/tools/testdata/arch_64.json @@ -102,7 +102,163 @@ "syscall_96": 96, "syscall_97": 97, "syscall_98": 98, - "syscall_99": 99 + "syscall_99": 99, + "syscall_100": 100, + "syscall_101": 101, + "syscall_102": 102, + "syscall_103": 103, + "syscall_104": 104, + "syscall_105": 105, + "syscall_106": 106, + "syscall_107": 107, + "syscall_108": 108, + "syscall_109": 109, + "syscall_110": 110, + "syscall_111": 111, + "syscall_112": 112, + "syscall_113": 113, + "syscall_114": 114, + "syscall_115": 115, + "syscall_116": 116, + "syscall_117": 117, + "syscall_118": 118, + "syscall_119": 119, + "syscall_120": 120, + "syscall_121": 121, + "syscall_122": 122, + "syscall_123": 123, + "syscall_124": 124, + "syscall_125": 125, + "syscall_126": 126, + "syscall_127": 127, + "syscall_128": 128, + "syscall_129": 129, + "syscall_130": 130, + "syscall_131": 131, + "syscall_132": 132, + "syscall_133": 133, + "syscall_134": 134, + "syscall_135": 135, + "syscall_136": 136, + "syscall_137": 137, + "syscall_138": 138, + "syscall_139": 139, + "syscall_140": 140, + "syscall_141": 141, + "syscall_142": 142, + "syscall_143": 143, + "syscall_144": 144, + "syscall_145": 145, + "syscall_146": 146, + "syscall_147": 147, + "syscall_148": 148, + "syscall_149": 149, + "syscall_150": 150, + "syscall_151": 151, + "syscall_152": 152, + "syscall_153": 153, + "syscall_154": 154, + "syscall_155": 155, + "syscall_156": 156, + "syscall_157": 157, + "syscall_158": 158, + "syscall_159": 159, + "syscall_160": 160, + "syscall_161": 161, + "syscall_162": 162, + "syscall_163": 163, + "syscall_164": 164, + "syscall_165": 165, + "syscall_166": 166, + "syscall_167": 167, + "syscall_168": 168, + "syscall_169": 169, + "syscall_170": 170, + "syscall_171": 171, + "syscall_172": 172, + "syscall_173": 173, + "syscall_174": 174, + "syscall_175": 175, + "syscall_176": 176, + "syscall_177": 177, + "syscall_178": 178, + "syscall_179": 179, + "syscall_180": 180, + "syscall_181": 181, + "syscall_182": 182, + "syscall_183": 183, + "syscall_184": 184, + "syscall_185": 185, + "syscall_186": 186, + "syscall_187": 187, + "syscall_188": 188, + "syscall_189": 189, + "syscall_190": 190, + "syscall_191": 191, + "syscall_192": 192, + "syscall_193": 193, + "syscall_194": 194, + "syscall_195": 195, + "syscall_196": 196, + "syscall_197": 197, + "syscall_198": 198, + "syscall_199": 199, + "syscall_200": 200, + "syscall_201": 201, + "syscall_202": 202, + "syscall_203": 203, + "syscall_204": 204, + "syscall_205": 205, + "syscall_206": 206, + "syscall_207": 207, + "syscall_208": 208, + "syscall_209": 209, + "syscall_210": 210, + "syscall_211": 211, + "syscall_212": 212, + "syscall_213": 213, + "syscall_214": 214, + "syscall_215": 215, + "syscall_216": 216, + "syscall_217": 217, + "syscall_218": 218, + "syscall_219": 219, + "syscall_220": 220, + "syscall_221": 221, + "syscall_222": 222, + "syscall_223": 223, + "syscall_224": 224, + "syscall_225": 225, + "syscall_226": 226, + "syscall_227": 227, + "syscall_228": 228, + "syscall_229": 229, + "syscall_230": 230, + "syscall_231": 231, + "syscall_232": 232, + "syscall_233": 233, + "syscall_234": 234, + "syscall_235": 235, + "syscall_236": 236, + "syscall_237": 237, + "syscall_238": 238, + "syscall_239": 239, + "syscall_240": 240, + "syscall_241": 241, + "syscall_242": 242, + "syscall_243": 243, + "syscall_244": 244, + "syscall_245": 245, + "syscall_246": 246, + "syscall_247": 247, + "syscall_248": 248, + "syscall_249": 249, + "syscall_250": 250, + "syscall_251": 251, + "syscall_252": 252, + "syscall_253": 253, + "syscall_254": 254, + "syscall_255": 255 }, "constants": { "ENOSYS": 38, |