Add seccomp blacklist, and exclude swap functions

Bug: 37253880
Test: Make sure device boots
      Run pylint on genseccomp.py, test_genseccomp.py
      Run test_genseccomp.py
      Run new CTS test
      cts-tradefed run cts -m CtsSecurityTestCases -t android.security.cts.SeccompTest

Change-Id: I833a5364a1481d65173e77654da1798dc45a3f9d
diff --git a/libc/tools/genseccomp.py b/libc/tools/genseccomp.py
index a8e551e..79968ae 100755
--- a/libc/tools/genseccomp.py
+++ b/libc/tools/genseccomp.py
@@ -27,11 +27,22 @@
 
 
 def get_names(syscall_files, architecture):
-  syscalls = []
+  syscall_lists = []
   for syscall_file in syscall_files:
     parser = SysCallsTxtParser()
     parser.parse_open_file(syscall_file)
-    syscalls += parser.syscalls
+    syscall_lists.append(parser.syscalls)
+
+  bionic, whitelist, blacklist = syscall_lists[0], syscall_lists[1], syscall_lists[2]
+  for x in blacklist:
+    if not x in bionic:
+      raise RuntimeError("Blacklist item not in bionic - aborting " + str(x))
+
+    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
 
   # Select only elements matching required architecture
   syscalls = [x for x in syscalls if architecture in x and x[architecture]]
@@ -47,8 +58,7 @@
     dups.remove("socketcall")
 
   if len(dups) > 0:
-    print "Duplicate entries found - aborting ", dups
-    exit(-1)
+    raise RuntimeError("Duplicate entries found - aborting " + str(dups))
 
   # Remove remaining duplicates
   return list(set(names))
@@ -188,7 +198,9 @@
   return convert_bpf_to_output(bpf, architecture)
 
 
-ANDROID_SYSCALL_FILES = ["SYSCALLS.TXT", "SECCOMP_WHITELIST.TXT"]
+ANDROID_SYSCALL_FILES = ["SYSCALLS.TXT",
+                         "SECCOMP_WHITELIST.TXT",
+                         "SECCOMP_BLACKLIST.TXT"]
 
 
 POLICY_CONFIGS = [("arm", "kernel/uapi/asm-arm", []),
diff --git a/libc/tools/test_genseccomp.py b/libc/tools/test_genseccomp.py
index 73f768d..71a78d1 100755
--- a/libc/tools/test_genseccomp.py
+++ b/libc/tools/test_genseccomp.py
@@ -24,7 +24,7 @@
     return self.get_config(arch)[2]
 
   def test_get_names(self):
-    syscalls = cStringIO.StringIO(textwrap.dedent("""\
+    bionic = cStringIO.StringIO(textwrap.dedent("""\
 int __llseek:_llseek(int, unsigned long, unsigned long, off64_t*, int) arm,mips,x86
 int         fchown:fchown(int, uid_t, gid_t)    arm64,mips,mips64,x86_64
     """))
@@ -33,11 +33,17 @@
 ssize_t     read(int, void*, size_t)        all
     """))
 
-    syscall_files = [syscalls, whitelist]
-    names = genseccomp.get_names(syscall_files, "arm")
-    for f in syscall_files:
-      f.seek(0)
-    names64 = genseccomp.get_names(syscall_files, "arm64")
+    empty = cStringIO.StringIO(textwrap.dedent("""\
+    """))
+
+    names = genseccomp.get_names([bionic, whitelist, empty], "arm")
+    bionic.seek(0)
+    whitelist.seek(0)
+    empty.seek(0)
+    names64 = genseccomp.get_names([bionic, whitelist, empty], "arm64")
+    bionic.seek(0)
+    whitelist.seek(0)
+    empty.seek(0)
 
     self.assertIn("fchown", names64)
     self.assertNotIn("fchown", names)
@@ -46,6 +52,47 @@
     self.assertIn("read", names)
     self.assertIn("read", names64)
 
+    # Blacklist item must be in bionic
+    blacklist = cStringIO.StringIO(textwrap.dedent("""\
+int         fchown2:fchown2(int, uid_t, gid_t)    arm64,mips,mips64,x86_64
+    """))
+    with self.assertRaises(RuntimeError):
+      genseccomp.get_names([bionic, whitelist, blacklist], "arm")
+    bionic.seek(0)
+    whitelist.seek(0)
+    blacklist.seek(0)
+
+    # Test blacklist item is removed
+    blacklist = cStringIO.StringIO(textwrap.dedent("""\
+int         fchown:fchown(int, uid_t, gid_t)    arm64,mips,mips64,x86_64
+    """))
+    names = genseccomp.get_names([bionic, whitelist, blacklist], "arm64")
+    bionic.seek(0)
+    whitelist.seek(0)
+    blacklist.seek(0)
+    self.assertIn("read", names)
+    self.assertNotIn("fchown", names)
+
+    # Blacklist item must not be in whitelist
+    whitelist = cStringIO.StringIO(textwrap.dedent("""\
+int         fchown:fchown(int, uid_t, gid_t)    arm64,mips,mips64,x86_64
+    """))
+    with self.assertRaises(RuntimeError):
+      genseccomp.get_names([empty, whitelist, blacklist], "arm")
+    empty.seek(0)
+    whitelist.seek(0)
+    blacklist.seek(0)
+
+    # No dups in bionic and whitelist
+    whitelist = cStringIO.StringIO(textwrap.dedent("""\
+int __llseek:_llseek(int, unsigned long, unsigned long, off64_t*, int) arm,mips,x86
+    """))
+    with self.assertRaises(RuntimeError):
+      genseccomp.get_names([bionic, whitelist, empty], "arm")
+    bionic.seek(0)
+    whitelist.seek(0)
+    empty.seek(0)
+
   def test_convert_names_to_NRs(self):
     self.assertEquals(genseccomp.convert_names_to_NRs(["open"],
                                                       self.get_headers("arm"),
@@ -153,7 +200,10 @@
     ssize_t     read(int, void*, size_t)        all
     """))
 
-    syscall_files = [syscalls, whitelist]
+    blacklist = cStringIO.StringIO(textwrap.dedent("""\
+    """))
+
+    syscall_files = [syscalls, whitelist, blacklist]
     output = genseccomp.construct_bpf(syscall_files, "arm", self.get_headers("arm"),
                                       self.get_switches("arm"))