Split zygote's seccomp filter into two
To pave the way to reducing app's kernel attack surface, this change
split the single filter into one for system and one for apps. Note that
there is current no change between them.
Zygote will apply these filters appropriately to system server and apps.
Keep set_seccomp_filter() for now until the caller has switched to the
new API, which I will do immediately after this before the two filters
diverse.
Also remove get_seccomp_filter() since it doesn't seem to be used
anyway.
Test: diff the generated code, no difference except the variable names
Test: cts -m CtsSecurityTestCases -t android.security.cts.SeccompTest
Bug: 63944145
Change-Id: Id8ba05a87332c92ec697926af77bc5742eb04b23
diff --git a/libc/tools/genseccomp.py b/libc/tools/genseccomp.py
index f3a232e..dad9113 100755
--- a/libc/tools/genseccomp.py
+++ b/libc/tools/genseccomp.py
@@ -26,47 +26,18 @@
self.names.append(name)
-def get_names(syscall_files, architecture, global_policy):
- syscall_lists = []
- for syscall_file in syscall_files:
- parser = SysCallsTxtParser()
- parser.parse_open_file(syscall_file)
- syscall_lists.append(parser.syscalls)
+def load_syscall_names_from_file(file_path, architecture):
+ parser = SysCallsTxtParser()
+ parser.parse_open_file(open(file_path))
+ return set([x["name"] for x in parser.syscalls if x.get(architecture)])
- bionic, whitelist, blacklist = syscall_lists[0], syscall_lists[1], syscall_lists[2]
- if global_policy:
- global_whitelist = syscall_lists[-1]
- else:
- global_whitelist = []
- for x in blacklist:
- if not x in bionic:
- raise RuntimeError("Blacklist item not in bionic - aborting " + str(x))
+def merge_names(base_names, whitelist_names, blacklist_names):
+ if bool(blacklist_names - base_names):
+ raise RuntimeError("Blacklist item not in bionic - aborting " + str(
+ blacklist_name - base_names))
- if x in whitelist:
- raise RuntimeError("Blacklist item in whitelist - aborting " + str(x))
-
- bionic_minus_blacklist = [x for x in bionic if x not in blacklist]
- syscalls = bionic_minus_blacklist + whitelist + global_whitelist
-
- # Select only elements matching required architecture
- syscalls = [x for x in syscalls if architecture in x and x[architecture]]
-
- # We only want the name
- names = [x["name"] for x in syscalls]
-
- # Check for duplicates
- dups = [name for name, count in collections.Counter(names).items() if count > 1]
-
- # x86 has duplicate socketcall entries, so hard code for this
- if architecture == "x86":
- dups.remove("socketcall")
-
- if len(dups) > 0:
- raise RuntimeError("Duplicate entries found - aborting " + str(dups))
-
- # Remove remaining duplicates
- return list(set(names))
+ return (base_names - blacklist_names) | whitelist_names
def convert_names_to_NRs(names, header_dir, extra_switches):
@@ -175,8 +146,11 @@
return bpf
-def convert_bpf_to_output(bpf, architecture, global_policy):
- suffix = "global_" if global_policy else ""
+def convert_bpf_to_output(bpf, architecture, name_modifier):
+ if name_modifier:
+ name_modifier = name_modifier + "_"
+ else:
+ name_modifier = ""
header = textwrap.dedent("""\
// Autogenerated file - edit at your peril!!
@@ -185,29 +159,51 @@
#include "seccomp_bpfs.h"
const sock_filter {architecture}_{suffix}filter[] = {{
- """).format(architecture=architecture,suffix=suffix)
+ """).format(architecture=architecture,suffix=name_modifier)
footer = textwrap.dedent("""\
}};
const size_t {architecture}_{suffix}filter_size = sizeof({architecture}_{suffix}filter) / sizeof(struct sock_filter);
- """).format(architecture=architecture,suffix=suffix)
+ """).format(architecture=architecture,suffix=name_modifier)
return header + "\n".join(bpf) + footer
-def construct_bpf(syscall_files, architecture, header_dir, extra_switches,
- global_policy):
- names = get_names(syscall_files, architecture, global_policy)
+def construct_bpf(names, architecture, header_dir, extra_switches,
+ name_modifier):
syscalls = convert_names_to_NRs(names, header_dir, extra_switches)
ranges = convert_NRs_to_ranges(syscalls)
bpf = convert_ranges_to_bpf(ranges)
- return convert_bpf_to_output(bpf, architecture, global_policy)
+ return convert_bpf_to_output(bpf, architecture, name_modifier)
-ANDROID_SYSCALL_FILES = ["SYSCALLS.TXT",
- "SECCOMP_WHITELIST.TXT",
- "SECCOMP_BLACKLIST.TXT"]
+# final syscalls = base - blacklists + whitelists
+ANDROID_SYSTEM_SYSCALL_FILES = {
+ "base": "SYSCALLS.TXT",
+ "whitelists": [
+ "SECCOMP_WHITELIST_COMMON.TXT",
+ "SECCOMP_WHITELIST_SYSTEM.TXT"],
+ "blacklists": ["SECCOMP_BLACKLIST_COMMON.TXT"]
+}
+
+ANDROID_APP_SYSCALL_FILES = {
+ "base": "SYSCALLS.TXT",
+ "whitelists": [
+ "SECCOMP_WHITELIST_COMMON.TXT",
+ "SECCOMP_WHITELIST_APP.TXT"],
+ "blacklists": ["SECCOMP_BLACKLIST_COMMON.TXT"]
+}
+
+ANDROID_GLOBAL_SYSCALL_FILES = {
+ "base": "SYSCALLS.TXT",
+ "whitelists": [
+ "SECCOMP_WHITELIST_COMMON.TXT",
+ "SECCOMP_WHITELIST_SYSTEM.TXT",
+ "SECCOMP_WHITELIST_APP.TXT",
+ "SECCOMP_WHITELIST_GLOBAL.TXT"],
+ "blacklists": ["SECCOMP_BLACKLIST_COMMON.TXT"]
+}
POLICY_CONFIGS = [("arm", "kernel/uapi/asm-arm", []),
@@ -223,18 +219,23 @@
os.chdir(os.path.join(os.environ["ANDROID_BUILD_TOP"], "bionic/libc"))
-def gen_policy(global_policy):
- if global_policy:
- ANDROID_SYSCALL_FILES.append("SECCOMP_WHITELIST_GLOBAL.TXT")
-
+def gen_policy(syscall_files, name_modifier):
for arch, header_path, switches in POLICY_CONFIGS:
- files = [open(filename) for filename in ANDROID_SYSCALL_FILES]
- output = construct_bpf(files, arch, header_path, switches, global_policy)
+ base_names = load_syscall_names_from_file(syscall_files["base"], arch)
+ whitelist_names = set()
+ for f in syscall_files["whitelists"]:
+ whitelist_names |= load_syscall_names_from_file(f, arch)
+ blacklist_names = set()
+ for f in syscall_files["blacklists"]:
+ blacklist_names |= load_syscall_names_from_file(f, arch)
+
+ names = merge_names(base_names, whitelist_names, blacklist_names)
+ output = construct_bpf(names, arch, header_path, switches, name_modifier)
# And output policy
existing = ""
- global_string = "_global" if global_policy else ""
- output_path = "seccomp/{}{}_policy.cpp".format(arch, global_string)
+ filename_modifier = "_" + name_modifier if name_modifier else ""
+ output_path = "seccomp/{}{}_policy.cpp".format(arch, filename_modifier)
if os.path.isfile(output_path):
existing = open(output_path).read()
if output == existing:
@@ -247,8 +248,10 @@
def main():
set_dir()
- gen_policy(False)
- gen_policy(True)
+ gen_policy(ANDROID_SYSTEM_SYSCALL_FILES, 'system')
+ gen_policy(ANDROID_APP_SYSCALL_FILES, 'app')
+ gen_policy(ANDROID_GLOBAL_SYSCALL_FILES, 'global')
+
if __name__ == "__main__":
main()