From 704772bda034448165d071f68b6aeca716f4220e Mon Sep 17 00:00:00 2001 From: Elliott Hughes Date: Mon, 10 Oct 2022 17:06:43 +0000 Subject: [PATCH] riscv64 syscall stub and seccomp filter generation. These are sufficiently intertwined that they need to be done together. riscv64 is our first primary-only architecture, so that required some changes. The .bp changes are to support this --- we need to only show the python scripts the architectures they'll actually be using, rather than showing them everything and ignoring some of the results. riscv64 is also the first architecture that post-dates the kernel's 64-bit time work, so there's a bit of extra fiddling needed to handle the __NR3264_ indirection in the uapi headers. Signed-off-by: Mao Han Signed-off-by: Xia Lifang Signed-off-by: Chen Guoyin Signed-off-by: Wang Chen Signed-off-by: Lu Xufan Test: local builds for x86-64 and riscv64 Change-Id: I74044744e80b312088f805c44fbd667c9bfcdc69 --- libc/Android.bp | 222 +++++++++++++++++++++++++----- libc/SYSCALLS.TXT | 5 +- libc/seccomp/seccomp_bpfs.h | 7 + libc/seccomp/seccomp_policy.cpp | 54 ++++++-- libc/tools/genfunctosyscallnrs.py | 4 +- libc/tools/genseccomp.py | 63 +++++---- libc/tools/gensyscalls.py | 27 +++- 7 files changed, 304 insertions(+), 78 deletions(-) diff --git a/libc/Android.bp b/libc/Android.bp index 52f40a838..890df95fa 100644 --- a/libc/Android.bp +++ b/libc/Android.bp @@ -1304,6 +1304,14 @@ genrule { cmd: "$(location gensyscalls) arm64 $(in) > $(out)", } +genrule { + name: "syscalls-riscv64.S", + out: ["syscalls-riscv64.S"], + srcs: ["SYSCALLS.TXT"], + tools: ["gensyscalls"], + cmd: "$(location gensyscalls) riscv64 $(in) > $(out)", +} + genrule { name: "syscalls-x86.S", out: ["syscalls-x86.S"], @@ -1330,6 +1338,9 @@ cc_library_static { arm64: { srcs: [":syscalls-arm64.S"], }, + riscv64: { + srcs: [":syscalls-riscv64.S"], + }, x86: { srcs: [":syscalls-x86.S"], }, @@ -2353,6 +2364,15 @@ cc_object { ], } +cc_object { + name: "libseccomp_gen_syscall_nrs_riscv64", + defaults: ["libseccomp_gen_syscall_nrs_defaults"], + local_include_dirs: [ + "kernel/uapi/asm-riscv", + "kernel/uapi", + ], +} + cc_object { name: "libseccomp_gen_syscall_nrs_x86", defaults: ["libseccomp_gen_syscall_nrs_defaults"], @@ -2390,12 +2410,38 @@ cc_genrule { srcs: [ "SYSCALLS.TXT", - ":libseccomp_gen_syscall_nrs_arm", - ":libseccomp_gen_syscall_nrs_arm64", - ":libseccomp_gen_syscall_nrs_x86", - ":libseccomp_gen_syscall_nrs_x86_64", ], + arch: { + arm: { + srcs: [ + ":libseccomp_gen_syscall_nrs_arm", + ":libseccomp_gen_syscall_nrs_arm64", + ], + }, + arm64: { + srcs: [ + ":libseccomp_gen_syscall_nrs_arm", + ":libseccomp_gen_syscall_nrs_arm64", + ], + }, + riscv64: { + srcs: [":libseccomp_gen_syscall_nrs_riscv64"], + }, + x86: { + srcs: [ + ":libseccomp_gen_syscall_nrs_x86", + ":libseccomp_gen_syscall_nrs_x86_64", + ], + }, + x86_64: { + srcs: [ + ":libseccomp_gen_syscall_nrs_x86", + ":libseccomp_gen_syscall_nrs_x86_64", + ], + }, + }, + out: [ "func_to_syscall_nrs.h", ], @@ -2423,18 +2469,54 @@ cc_genrule { "SECCOMP_BLOCKLIST_COMMON.TXT", "SECCOMP_PRIORITY.TXT", ":generate_app_zygote_blocklist", - ":libseccomp_gen_syscall_nrs_arm", - ":libseccomp_gen_syscall_nrs_arm64", - ":libseccomp_gen_syscall_nrs_x86", - ":libseccomp_gen_syscall_nrs_x86_64", ], - out: [ - "arm64_app_zygote_policy.cpp", - "arm_app_zygote_policy.cpp", - "x86_64_app_zygote_policy.cpp", - "x86_app_zygote_policy.cpp", - ], + arch: { + arm: { + srcs: [ + ":libseccomp_gen_syscall_nrs_arm", + ":libseccomp_gen_syscall_nrs_arm64", + ], + out: [ + "arm_app_zygote_policy.cpp", + "arm64_app_zygote_policy.cpp", + ], + }, + arm64: { + srcs: [ + ":libseccomp_gen_syscall_nrs_arm", + ":libseccomp_gen_syscall_nrs_arm64", + ], + out: [ + "arm_app_zygote_policy.cpp", + "arm64_app_zygote_policy.cpp", + ], + }, + riscv64: { + srcs: [":libseccomp_gen_syscall_nrs_riscv64"], + out: ["riscv64_app_zygote_policy.cpp"], + }, + x86: { + srcs: [ + ":libseccomp_gen_syscall_nrs_x86", + ":libseccomp_gen_syscall_nrs_x86_64", + ], + out: [ + "x86_app_zygote_policy.cpp", + "x86_64_app_zygote_policy.cpp", + ], + }, + x86_64: { + srcs: [ + ":libseccomp_gen_syscall_nrs_x86", + ":libseccomp_gen_syscall_nrs_x86_64", + ], + out: [ + "x86_app_zygote_policy.cpp", + "x86_64_app_zygote_policy.cpp", + ], + }, + }, } cc_genrule { @@ -2451,18 +2533,54 @@ cc_genrule { "SECCOMP_BLOCKLIST_COMMON.TXT", "SECCOMP_BLOCKLIST_APP.TXT", "SECCOMP_PRIORITY.TXT", - ":libseccomp_gen_syscall_nrs_arm", - ":libseccomp_gen_syscall_nrs_arm64", - ":libseccomp_gen_syscall_nrs_x86", - ":libseccomp_gen_syscall_nrs_x86_64", ], - out: [ - "arm64_app_policy.cpp", - "arm_app_policy.cpp", - "x86_64_app_policy.cpp", - "x86_app_policy.cpp", - ], + arch: { + arm: { + srcs: [ + ":libseccomp_gen_syscall_nrs_arm", + ":libseccomp_gen_syscall_nrs_arm64", + ], + out: [ + "arm_app_policy.cpp", + "arm64_app_policy.cpp", + ], + }, + arm64: { + srcs: [ + ":libseccomp_gen_syscall_nrs_arm", + ":libseccomp_gen_syscall_nrs_arm64", + ], + out: [ + "arm_app_policy.cpp", + "arm64_app_policy.cpp", + ], + }, + riscv64: { + srcs: [":libseccomp_gen_syscall_nrs_riscv64"], + out: ["riscv64_app_policy.cpp"], + }, + x86: { + srcs: [ + ":libseccomp_gen_syscall_nrs_x86", + ":libseccomp_gen_syscall_nrs_x86_64", + ], + out: [ + "x86_app_policy.cpp", + "x86_64_app_policy.cpp", + ], + }, + x86_64: { + srcs: [ + ":libseccomp_gen_syscall_nrs_x86", + ":libseccomp_gen_syscall_nrs_x86_64", + ], + out: [ + "x86_app_policy.cpp", + "x86_64_app_policy.cpp", + ], + }, + }, } cc_genrule { @@ -2478,18 +2596,54 @@ cc_genrule { "SECCOMP_ALLOWLIST_SYSTEM.TXT", "SECCOMP_BLOCKLIST_COMMON.TXT", "SECCOMP_PRIORITY.TXT", - ":libseccomp_gen_syscall_nrs_arm", - ":libseccomp_gen_syscall_nrs_arm64", - ":libseccomp_gen_syscall_nrs_x86", - ":libseccomp_gen_syscall_nrs_x86_64", ], - out: [ - "arm64_system_policy.cpp", - "arm_system_policy.cpp", - "x86_64_system_policy.cpp", - "x86_system_policy.cpp", - ], + arch: { + arm: { + srcs: [ + ":libseccomp_gen_syscall_nrs_arm", + ":libseccomp_gen_syscall_nrs_arm64", + ], + out: [ + "arm_system_policy.cpp", + "arm64_system_policy.cpp", + ], + }, + arm64: { + srcs: [ + ":libseccomp_gen_syscall_nrs_arm", + ":libseccomp_gen_syscall_nrs_arm64", + ], + out: [ + "arm_system_policy.cpp", + "arm64_system_policy.cpp", + ], + }, + riscv64: { + srcs: [":libseccomp_gen_syscall_nrs_riscv64"], + out: ["riscv64_system_policy.cpp"], + }, + x86: { + srcs: [ + ":libseccomp_gen_syscall_nrs_x86", + ":libseccomp_gen_syscall_nrs_x86_64", + ], + out: [ + "x86_system_policy.cpp", + "x86_64_system_policy.cpp", + ], + }, + x86_64: { + srcs: [ + ":libseccomp_gen_syscall_nrs_x86", + ":libseccomp_gen_syscall_nrs_x86_64", + ], + out: [ + "x86_system_policy.cpp", + "x86_64_system_policy.cpp", + ], + }, + }, } cc_library { diff --git a/libc/SYSCALLS.TXT b/libc/SYSCALLS.TXT index fe01ab9a4..1b9054b2a 100644 --- a/libc/SYSCALLS.TXT +++ b/libc/SYSCALLS.TXT @@ -7,7 +7,7 @@ # where: # arch_list ::= "all" | arches # arches ::= arch | arch "," arches -# arch ::= "arm" | "arm64" | "x86" | "x86_64" | "lp32" | "lp64" +# arch ::= "arm" | "arm64" | "riscv64" | "x86" | "x86_64" | "lp32" | "lp64" # # Note: # - syscall_name corresponds to the name of the syscall, which may differ from @@ -352,6 +352,9 @@ int __waitid:waitid(int, pid_t, siginfo_t*, int, void*) all int __set_tls:__ARM_NR_set_tls(void*) arm int cacheflush:__ARM_NR_cacheflush(long start, long end, long flags) arm +# riscv64-specific +int _flush_icache:riscv_flush_icache(void*, void*, unsigned long) riscv64 + # x86-specific int __set_thread_area:set_thread_area(void*) x86 diff --git a/libc/seccomp/seccomp_bpfs.h b/libc/seccomp/seccomp_bpfs.h index 3bdffa932..34219e241 100644 --- a/libc/seccomp/seccomp_bpfs.h +++ b/libc/seccomp/seccomp_bpfs.h @@ -33,6 +33,13 @@ extern const size_t arm64_app_zygote_filter_size; extern const struct sock_filter arm64_system_filter[]; extern const size_t arm64_system_filter_size; +extern const struct sock_filter riscv64_app_filter[]; +extern const size_t riscv64_app_filter_size; +extern const struct sock_filter riscv64_app_zygote_filter[]; +extern const size_t riscv64_app_zygote_filter_size; +extern const struct sock_filter riscv64_system_filter[]; +extern const size_t riscv64_system_filter_size; + extern const struct sock_filter x86_app_filter[]; extern const size_t x86_app_filter_size; extern const struct sock_filter x86_app_zygote_filter[]; diff --git a/libc/seccomp/seccomp_policy.cpp b/libc/seccomp/seccomp_policy.cpp index a42816eb6..1ca4eec2e 100644 --- a/libc/seccomp/seccomp_policy.cpp +++ b/libc/seccomp/seccomp_policy.cpp @@ -25,13 +25,13 @@ #include #include +#include #include "func_to_syscall_nrs.h" #include "seccomp_bpfs.h" #if defined __arm__ || defined __aarch64__ -#define DUAL_ARCH #define PRIMARY_ARCH AUDIT_ARCH_AARCH64 static const struct sock_filter* primary_app_filter = arm64_app_filter; static const size_t primary_app_filter_size = arm64_app_filter_size; @@ -52,9 +52,9 @@ static const size_t secondary_system_filter_size = arm_system_filter_size; static const long secondary_setresgid = __arm_setresgid; static const long secondary_setresuid = __arm_setresuid; + #elif defined __i386__ || defined __x86_64__ -#define DUAL_ARCH #define PRIMARY_ARCH AUDIT_ARCH_X86_64 static const struct sock_filter* primary_app_filter = x86_64_app_filter; static const size_t primary_app_filter_size = x86_64_app_filter_size; @@ -75,6 +75,20 @@ static const size_t secondary_system_filter_size = x86_system_filter_size; static const long secondary_setresgid = __x86_setresgid; static const long secondary_setresuid = __x86_setresuid; + +#elif defined(__riscv) + +#define PRIMARY_ARCH AUDIT_ARCH_RISCV64 +static const struct sock_filter* primary_app_filter = riscv64_app_filter; +static const size_t primary_app_filter_size = riscv64_app_filter_size; +static const struct sock_filter* primary_app_zygote_filter = riscv64_app_zygote_filter; +static const size_t primary_app_zygote_filter_size = riscv64_app_zygote_filter_size; +static const struct sock_filter* primary_system_filter = riscv64_system_filter; +static const size_t primary_system_filter_size = riscv64_system_filter_size; + +static const long primary_setresgid = __riscv64_setresgid; +static const long primary_setresuid = __riscv64_setresuid; + #else #error No architecture was defined! #endif @@ -98,7 +112,7 @@ static void ExamineSyscall(filter& f) { f.push_back(BPF_STMT(BPF_LD|BPF_W|BPF_ABS, syscall_nr)); } -#ifdef DUAL_ARCH +#if defined(SECONDARY_ARCH) static bool SetValidateArchitectureJumpTarget(size_t offset, filter& f) { size_t jump_length = f.size() - offset - 1; auto u8_jump_length = (__u8) jump_length; @@ -149,9 +163,17 @@ static void ValidateSyscallArgInRange(filter& f, __u32 arg_num, __u32 range_min, // that's the only system call we need to check here. A CTS test ensures the other // calls will remain blocked. static void ValidateSetUidGid(filter& f, uint32_t uid_gid_min, uint32_t uid_gid_max, bool primary) { +#if defined(SECONDARY_ARCH) + __u32 setresuid_nr = primary ? primary_setresuid : secondary_setresuid; + __u32 setresgid_nr = primary ? primary_setresgid : secondary_setresgid; +#else + __u32 setresuid_nr = primary_setresuid; + __u32 setresgid_nr = primary_setresgid; + UNUSED(primary); +#endif + // Check setresuid(ruid, euid, sguid) fall within range ExamineSyscall(f); - __u32 setresuid_nr = primary ? primary_setresuid : secondary_setresuid; f.push_back(BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, setresuid_nr, 0, 12)); for (int arg = 0; arg < 3; arg++) { ValidateSyscallArgInRange(f, arg, uid_gid_min, uid_gid_max); @@ -159,7 +181,6 @@ static void ValidateSetUidGid(filter& f, uint32_t uid_gid_min, uint32_t uid_gid_ // Check setresgid(rgid, egid, sgid) fall within range ExamineSyscall(f); - __u32 setresgid_nr = primary ? primary_setresgid : secondary_setresgid; f.push_back(BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, setresgid_nr, 0, 12)); for (int arg = 0; arg < 3; arg++) { ValidateSyscallArgInRange(f, arg, uid_gid_min, uid_gid_max); @@ -184,7 +205,7 @@ static bool install_filter(filter const& f) { bool _install_setuidgid_filter(uint32_t uid_gid_min, uint32_t uid_gid_max) { filter f; -#ifdef DUAL_ARCH +#if defined(SECONDARY_ARCH) // Note that for mixed 64/32 bit architectures, ValidateArchitecture inserts a // jump that must be changed to point to the start of the 32-bit policy // 32 bit syscalls will not hit the policy between here and the call to SetJump @@ -195,7 +216,7 @@ bool _install_setuidgid_filter(uint32_t uid_gid_min, uint32_t uid_gid_max) { ValidateSetUidGid(f, uid_gid_min, uid_gid_max, true /* primary */); -#ifdef DUAL_ARCH +#if defined(SECONDARY_ARCH) if (!SetValidateArchitectureJumpTarget(offset_to_secondary_filter, f)) { return false; } @@ -213,32 +234,43 @@ enum FilterType { }; bool _set_seccomp_filter(FilterType type) { - const sock_filter *p, *s; - size_t p_size, s_size; filter f; + const sock_filter* p; + size_t p_size; +#if defined(SECONDARY_ARCH) + const sock_filter* s; + size_t s_size; +#endif + switch (type) { case APP: p = primary_app_filter; p_size = primary_app_filter_size; +#if defined(SECONDARY_ARCH) s = secondary_app_filter; s_size = secondary_app_filter_size; +#endif break; case APP_ZYGOTE: p = primary_app_zygote_filter; p_size = primary_app_zygote_filter_size; +#if defined(SECONDARY_ARCH) s = secondary_app_zygote_filter; s_size = secondary_app_zygote_filter_size; +#endif break; case SYSTEM: p = primary_system_filter; p_size = primary_system_filter_size; +#if defined(SECONDARY_ARCH) s = secondary_system_filter; s_size = secondary_system_filter_size; +#endif break; } -#ifdef DUAL_ARCH +#if defined(SECONDARY_ARCH) // Note that for mixed 64/32 bit architectures, ValidateArchitecture inserts a // jump that must be changed to point to the start of the 32-bit policy // 32 bit syscalls will not hit the policy between here and the call to SetJump @@ -254,7 +286,7 @@ bool _set_seccomp_filter(FilterType type) { } Disallow(f); -#ifdef DUAL_ARCH +#if defined(SECONDARY_ARCH) if (!SetValidateArchitectureJumpTarget(offset_to_secondary_filter, f)) { return false; } diff --git a/libc/tools/genfunctosyscallnrs.py b/libc/tools/genfunctosyscallnrs.py index fa488447a..9b8f7ee08 100755 --- a/libc/tools/genfunctosyscallnrs.py +++ b/libc/tools/genfunctosyscallnrs.py @@ -21,12 +21,12 @@ def load_syscall_names_from_file(file_path, architecture): def gen_syscall_nrs(out_file, base_syscall_file, syscall_NRs): - for arch in SupportedArchitectures: + for arch in syscall_NRs.keys(): base_names = load_syscall_names_from_file(base_syscall_file, arch) for func, syscall in base_names.items(): out_file.write("#define __" + arch + "_" + func + " " + - str(syscall_NRs[arch][syscall]) + ";\n") + str(syscall_NRs[arch][syscall]) + "\n") def main(): diff --git a/libc/tools/genseccomp.py b/libc/tools/genseccomp.py index 33bf47074..8a07caffc 100755 --- a/libc/tools/genseccomp.py +++ b/libc/tools/genseccomp.py @@ -5,9 +5,10 @@ import logging import operator import os import re +import sys import textwrap -from gensyscalls import SupportedArchitectures, SysCallsTxtParser +from gensyscalls import SysCallsTxtParser BPF_JGE = "BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, {0}, {1}, {2})" @@ -84,42 +85,46 @@ def parse_syscall_NRs(names_path): # #define __(ARM_)?NR_${NAME} ${VALUE} # # Where ${VALUE} is a preprocessor expression. + # + # Newer architectures have things like this though: + # + # #define __NR3264_fcntl 25 + # #define __NR_fcntl __NR3264_fcntl + # + # So we need to keep track of the __NR3264_* constants and substitute them. - constant_re = re.compile( - r'^\s*#define\s+([A-Za-z_][A-Za-z0-9_]+)\s+(.+)\s*$') + line_re = re.compile(r'^# \d+ ".*".*') + undef_re = re.compile(r'^#undef\s.*') + define_re = re.compile(r'^\s*#define\s+([A-Za-z0-9_(,)]+)(?:\s+(.+))?\s*$') token_re = re.compile(r'\b[A-Za-z_][A-Za-z0-9_]+\b') constants = {} + nr3264s = {} with open(names_path) as f: for line in f: - m = constant_re.match(line) - if m is None: - continue - try: + line = line.strip() + m = define_re.match(line) + if m: name = m.group(1) - # eval() takes care of any arithmetic that may be done - value = eval(token_re.sub(lambda x: str(constants[x.group(0)]), - m.group(2))) + value = m.group(2) + if name.startswith('__NR3264'): + nr3264s[name] = value + elif name.startswith('__NR_') or name.startswith('__ARM_NR_'): + if value in nr3264s: + value = nr3264s[value] + # eval() takes care of any arithmetic that may be done + value = eval(token_re.sub(lambda x: str(constants[x.group(0)]), value)) - constants[name] = value - except: # pylint: disable=bare-except - # TODO: This seems wrong. - # Key error doesn't seem like the error the original author was trying - # to catch. It looks like the intent was to catch IndexError from - # match.group() for non-matching lines, but that's impossible because - # the match object is checked and continued if not matched. What - # actually happens is that KeyError is thrown by constants[x.group(0)] - # on at least the first run because the dict is empty. - # - # It's also matching syntax errors because not all C integer literals - # are valid Python integer literals, e.g. 10L. - logging.debug('Failed to parse %s', line) + constants[name] = value + else: + if not line_re.match(line) and not undef_re.match(line) and line: + print('%s: failed to parse line `%s`' % (names_path, line)) + sys.exit(1) syscalls = {} for name, value in constants.items(): - if not name.startswith("__NR_") and not name.startswith("__ARM_NR"): - continue + # Remove the __NR_ prefix. + # TODO: why not __ARM_NR too? if name.startswith("__NR_"): - # Remote the __NR_ prefix name = name[len("__NR_"):] syscalls[name] = value @@ -237,7 +242,7 @@ def construct_bpf(syscalls, architecture, name_modifier, priorities): def gen_policy(name_modifier, out_dir, base_syscall_file, syscall_files, syscall_NRs, priority_file): - for arch in SupportedArchitectures: + for arch in syscall_NRs.keys(): base_names = load_syscall_names_from_file(base_syscall_file, arch) allowlist_names = set() blocklist_names = set() @@ -251,11 +256,11 @@ def gen_policy(name_modifier, out_dir, base_syscall_file, syscall_files, priorities = load_syscall_priorities_from_file(priority_file) allowed_syscalls = [] - for name in merge_names(base_names, allowlist_names, blocklist_names): + for name in sorted(merge_names(base_names, allowlist_names, blocklist_names)): try: allowed_syscalls.append((name, syscall_NRs[arch][name])) except: - logging.exception("Failed to find %s in %s", name, arch) + logging.exception("Failed to find %s in %s (%s)", name, arch, syscall_NRs[arch]) raise output = construct_bpf(allowed_syscalls, arch, name_modifier, priorities) diff --git a/libc/tools/gensyscalls.py b/libc/tools/gensyscalls.py index baaa52d25..d3e6ef4d5 100755 --- a/libc/tools/gensyscalls.py +++ b/libc/tools/gensyscalls.py @@ -15,7 +15,7 @@ import sys import tempfile -SupportedArchitectures = [ "arm", "arm64", "x86", "x86_64" ] +SupportedArchitectures = [ "arm", "arm64", "riscv64", "x86", "x86_64" ] syscall_stub_header = \ """ @@ -79,6 +79,24 @@ END(%(func)s) """ +# +# RISC-V64 assembler templates for each syscall stub +# + +riscv64_call = syscall_stub_header + """\ + li a7, %(__NR_name)s + ecall + + li a7, -MAX_ERRNO + bgtu a0, a7, 1f + + ret +1: + neg a0, a0 + j __set_errno_internal +END(%(func)s) +""" + # # x86 assembler templates for each syscall stub # @@ -228,6 +246,10 @@ def arm64_genstub(syscall): return arm64_call % syscall +def riscv64_genstub(syscall): + return riscv64_call % syscall + + def x86_genstub(syscall): result = syscall_stub_header % syscall @@ -440,6 +462,9 @@ def main(arch, syscall_file): if "arm64" in syscall: syscall["asm-arm64"] = add_footer(64, arm64_genstub(syscall), syscall) + if "riscv64" in syscall: + syscall["asm-riscv64"] = add_footer(64, riscv64_genstub(syscall), syscall) + if "x86" in syscall: if syscall["socketcall_id"] >= 0: syscall["asm-x86"] = add_footer(32, x86_genstub_socketcall(syscall), syscall)