1# Copyright (c) 2015 The Chromium OS Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5import logging
6import os
7
8from autotest_lib.client.bin import test, utils
9from autotest_lib.client.common_lib import error
10from autotest_lib.client.cros import kernel_config
11
12class security_AltSyscall(test.test):
13    """
14    Verify that alt_syscall allows/blocks system calls as expected using
15    minijail.
16    """
17    version = 1
18
19    def initialize(self):
20        """Initializes the test."""
21        self.job.require_gcc()
22
23    def setup(self):
24        """Compiles the test binaries."""
25        os.chdir(self.srcdir)
26        utils.make('clean')
27        utils.make()
28
29    def run_test(self, exe, table, expected_ret, pretty_msg):
30        """
31        Runs a single alt_syscall test case.
32
33        Runs the executable with the specified alt_syscall table using minijail.
34        Fails the test if the return value does not match what we expected.
35
36        @param exe Test executable
37        @param table Alt_syscall table name
38        @param expected_ret Expected return value from the test
39        @param pretty_msg Message to display on failue
40        """
41        exe_path = os.path.join(self.srcdir, exe)
42        flags = '-a %s' % table
43        cmdline = '/sbin/minijail0 %s -- %s' % (flags, exe_path)
44
45        logging.info("Command line: %s", cmdline)
46        ret = utils.system(cmdline, ignore_status=True)
47
48        if ret != expected_ret:
49            logging.error("ret: %d, expected: %d", ret, expected_ret)
50            raise error.TestFail(pretty_msg)
51
52    def alt_syscall_supported(self):
53        """Checks that alt_syscall is supported by the kernel."""
54        config = kernel_config.KernelConfig()
55        config.initialize()
56        config.is_enabled('ALT_SYSCALL')
57        config.is_enabled('ALT_SYSCALL_CHROMIUMOS')
58        return len(config.failures()) == 0
59
60    def run_once(self):
61        """Main entrypoint of the test."""
62        if not self.alt_syscall_supported():
63            logging.warning("ALT_SYSCALL not supported")
64            return
65
66        case_allow = ("read", "read_write_test", 0,
67                      "Allowed system calls failed")
68        case_deny_blocked = ("mmap", "read_write_test", 2,
69                             "Blocked system calls succeeded")
70        case_deny_alt_syscall = ("alt_syscall", "read_write_test", 1,
71                                 "Changing alt_syscall table succeeded")
72        case_adjtimex = ("adjtimex", "android", 0,
73                         "android_adjtimex() filtering didn't work.")
74        case_clock_adjtime = ("clock_adjtime", "android", 0,
75                              "android_clock_adjtime() filtering didn't work.")
76
77        for case in [case_allow, case_deny_blocked, case_deny_alt_syscall,
78                     case_adjtimex, case_clock_adjtime]:
79            self.run_test(*case)
80