aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuis Hector Chavez <lhchavez@google.com>2019-03-27 11:14:14 -0700
committerandroid-build-merger <android-build-merger@google.com>2019-03-27 11:14:14 -0700
commit1b4ea70c5f0e6e2c5a2204033cb4b3f2c724190f (patch)
treea5c1cbfd869ad35a60071b85a5511790cf99ec2e
parentde90bdab74bef72596fb9f9289c28cf0e4f04c2d (diff)
parent5fdc780fc19245e15b140a919bbc315073912b8f (diff)
downloadminijail-1b4ea70c5f0e6e2c5a2204033cb4b3f2c724190f.tar.gz
tools/compile_seccomp_policy: Increase the code coverage to 93% am: 89a2710839
am: 5fdc780fc1 Change-Id: I98775f8c84a89fbe71d4048214394e08dde81cd4
-rwxr-xr-xtools/compiler_unittest.py63
-rwxr-xr-xtools/parser_unittest.py130
-rw-r--r--tools/testdata/arch_64.json158
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,