| #!/usr/bin/env python3 |
| # |
| # This script generates syscall name to number mapping for supported |
| # architectures. To update the output, runs: |
| # |
| # $ app/gen_blocklist.py --allowed app/assets/syscalls_allowed.json \ |
| # --blocked app/assets/syscalls_blocked.json |
| # |
| # Note that these are just syscalls that explicitly allowed and blocked in CTS |
| # currently. |
| # |
| # TODO: Consider generating it in Android.mk/bp. |
| |
| import argparse |
| import glob |
| import json |
| import os |
| import subprocess |
| |
| _SUPPORTED_ARCHS = ['arm', 'arm64', 'x86', 'x86_64', 'mips', 'mips64'] |
| |
| # Syscalls that are currently explicitly allowed in CTS |
| _SYSCALLS_ALLOWED_IN_CTS = { |
| 'openat': 'all', |
| |
| # b/35034743 - do not remove test without reading bug. |
| 'syncfs': 'arm64', |
| |
| # b/35906875 - do not remove test without reading bug |
| 'inotify_init': 'arm', |
| } |
| |
| # Syscalls that are currently explicitly blocked in CTS |
| _SYSCALLS_BLOCKED_IN_CTS = { |
| 'acct': 'all', |
| 'add_key': 'all', |
| 'adjtimex': 'all', |
| 'chroot': 'all', |
| 'clock_adjtime': 'all', |
| 'clock_settime': 'all', |
| 'delete_module': 'all', |
| 'init_module': 'all', |
| 'keyctl': 'all', |
| 'mount': 'all', |
| 'reboot': 'all', |
| 'setdomainname': 'all', |
| 'sethostname': 'all', |
| 'settimeofday': 'all', |
| 'setfsgid': 'all', |
| 'setfsuid': 'all', |
| 'setgid': 'all', |
| 'setgid32': 'x86,arm', |
| 'setgroups': 'all', |
| 'setgroups32': 'x86,arm', |
| 'setregid': 'all', |
| 'setregid32': 'x86,arm', |
| 'setresgid': 'all', |
| 'setresgid32': 'x86,arm', |
| 'setreuid': 'all', |
| 'setreuid32': 'x86,arm', |
| 'setuid': 'all', |
| 'setuid32': 'x86,arm', |
| 'swapoff': 'all', |
| 'swapoff': 'all', |
| 'swapon': 'all', |
| 'swapon': 'all', |
| 'syslog': 'all', |
| 'umount2': 'all', |
| } |
| |
| def create_syscall_name_to_number_map(arch, names): |
| arch_config = { |
| 'arm': { |
| 'uapi_class': 'asm-arm', |
| 'extra_cflags': [], |
| }, |
| 'arm64': { |
| 'uapi_class': 'asm-arm64', |
| 'extra_cflags': [], |
| }, |
| 'x86': { |
| 'uapi_class': 'asm-x86', |
| 'extra_cflags': ['-D__i386__'], |
| }, |
| 'x86_64': { |
| 'uapi_class': 'asm-x86', |
| 'extra_cflags': [], |
| }, |
| 'mips': { |
| 'uapi_class': 'asm-mips', |
| 'extra_cflags': ['-D_MIPS_SIM=_MIPS_SIM_ABI32'], |
| }, |
| 'mips64': { |
| 'uapi_class': 'asm-mips', |
| 'extra_cflags': ['-D_MIPS_SIM=_MIPS_SIM_ABI64'], |
| } |
| } |
| |
| # Run preprocessor over the __NR_syscall symbols, including unistd.h, |
| # to get the actual numbers |
| # TODO: The following code is forked from bionic/libc/tools/genseccomp.py. |
| # Figure out if we can de-duplicate them crossing cts project boundary. |
| prefix = '__SECCOMP_' # prefix to ensure no name collisions |
| kernel_uapi_path = os.path.join(os.getenv('ANDROID_BUILD_TOP'), |
| 'bionic/libc/kernel/uapi') |
| cpp = subprocess.Popen( |
| [get_latest_clang_path(), |
| '-E', '-nostdinc', |
| '-I' + os.path.join(kernel_uapi_path, |
| arch_config[arch]['uapi_class']), |
| '-I' + os.path.join(kernel_uapi_path) |
| ] |
| + arch_config[arch]['extra_cflags'] |
| + ['-'], |
| universal_newlines=True, |
| stdin=subprocess.PIPE, stdout=subprocess.PIPE) |
| cpp.stdin.write('#include <asm/unistd.h>\n') |
| for name in names: |
| # In SYSCALLS.TXT, there are two arm-specific syscalls whose names start |
| # with __ARM__NR_. These we must simply write out as is. |
| if not name.startswith('__ARM_NR_'): |
| cpp.stdin.write(prefix + name + ', __NR_' + name + '\n') |
| else: |
| cpp.stdin.write(prefix + name + ', ' + name + '\n') |
| content = cpp.communicate()[0].split('\n') |
| |
| # The input is now the preprocessed source file. This will contain a lot |
| # of junk from the preprocessor, but our lines will be in the format: |
| # |
| # __SECCOMP_${NAME}, (0 + value) |
| syscalls = {} |
| for line in content: |
| if not line.startswith(prefix): |
| continue |
| # We might pick up extra whitespace during preprocessing, so best to strip. |
| name, value = [w.strip() for w in line.split(',')] |
| name = name[len(prefix):] |
| # Note that some of the numbers were expressed as base + offset, so we |
| # need to eval, not just int |
| value = eval(value) |
| if name in syscalls: |
| raise Exception('syscall %s is re-defined' % name) |
| syscalls[name] = value |
| return syscalls |
| |
| def get_latest_clang_path(): |
| candidates = sorted(glob.glob(os.path.join(os.getenv('ANDROID_BUILD_TOP'), |
| 'prebuilts/clang/host/linux-x86/clang-*')), reverse=True) |
| for clang_dir in candidates: |
| clang_exe = os.path.join(clang_dir, 'bin/clang') |
| if os.path.exists(clang_exe): |
| return clang_exe |
| raise FileNotFoundError('Cannot locate clang executable') |
| |
| def collect_syscall_names_for_arch(syscall_map, arch): |
| syscall_names = [] |
| for syscall in syscall_map.keys(): |
| if (arch in syscall_map[syscall] or |
| 'all' == syscall_map[syscall]): |
| syscall_names.append(syscall) |
| return syscall_names |
| |
| def main(): |
| parser = argparse.ArgumentParser('syscall name to number generator') |
| parser.add_argument('--allowed', metavar='path/to/json', type=str) |
| parser.add_argument('--blocked', metavar='path/to/json', type=str) |
| args = parser.parse_args() |
| |
| allowed = {} |
| blocked = {} |
| for arch in _SUPPORTED_ARCHS: |
| blocked[arch] = create_syscall_name_to_number_map( |
| arch, |
| collect_syscall_names_for_arch(_SYSCALLS_BLOCKED_IN_CTS, arch)) |
| allowed[arch] = create_syscall_name_to_number_map( |
| arch, |
| collect_syscall_names_for_arch(_SYSCALLS_ALLOWED_IN_CTS, arch)) |
| |
| msg_do_not_modify = '# DO NOT MODIFY. CHANGE gen_blocklist.py INSTEAD.' |
| with open(args.allowed, 'w') as f: |
| print(msg_do_not_modify, file=f) |
| json.dump(allowed, f, sort_keys=True, indent=2) |
| |
| with open(args.blocked, 'w') as f: |
| print(msg_do_not_modify, file=f) |
| json.dump(blocked, f, sort_keys=True, indent=2) |
| |
| if __name__ == '__main__': |
| main() |