#!/usr/bin/env python3
#
# This script generates syscall name to number mapping for supported
# architectures. To update the output, runs:
#
# $ app/gen_blacklist.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_blacklist.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()