Merge "Revert "Disable dlfcn#dlopen_system_libicuuc_ tests due to test failures on cuttlefish""
diff --git a/docs/32-bit-abi.md b/docs/32-bit-abi.md
index 81afd14..3be6b1a 100644
--- a/docs/32-bit-abi.md
+++ b/docs/32-bit-abi.md
@@ -81,13 +81,23 @@
 in the 64-bit ABI even though they're identical to the non-`64` names.
 
 
-## `time_t` is 32-bit
+## `time_t` is 32-bit on LP32 (y2038)
 
-On 32-bit Android, `time_t` is 32-bit. The header `<time64.h>` and type
-`time64_t` exist as a workaround, but the kernel interfaces exposed on 32-bit
-Android all use the 32-bit `time_t`.
+On 32-bit Android, `time_t` is 32-bit, which will overflow in 2038.
 
-In the 64-bit ABI, `time_t` is 64-bit.
+In the 64-bit ABI, `time_t` is 64-bit, which will not overflow until
+long after the death of the star around which we currently circle.
+
+The header `<time64.h>` and type `time64_t` exist as a workaround,
+but the kernel interfaces exposed on 32-bit Android all use the 32-bit
+`time_t` and `struct timespec`/`struct timeval`. Linux 5.x kernels
+do offer extra interfaces so that 32-bit processes can pass 64-bit
+times to/from the kernel, but we do not plan on adding support for
+these to the C library. Convenient use of the new calls would require
+an equivalent to `_FILE_OFFSET_BITS=64`, which we wouldn't be able
+to globally flip for reasons similar to `_FILE_OFFSET_BITS`, mentioned
+above. All apps are already required to offer 64-bit variants, and we
+expect 64-bit-only devices within the next few years.
 
 
 ## `pthread_mutex_t` is too small for large pids
diff --git a/libc/Android.bp b/libc/Android.bp
index 31767ef..ff3b3b1 100644
--- a/libc/Android.bp
+++ b/libc/Android.bp
@@ -2387,6 +2387,7 @@
         "SECCOMP_WHITELIST_COMMON.TXT",
         "SECCOMP_WHITELIST_APP.TXT",
         "SECCOMP_BLACKLIST_COMMON.TXT",
+        "SECCOMP_PRIORITY.TXT",
         ":generate_app_zygote_blacklist",
         ":libseccomp_gen_syscall_nrs_arm",
         ":libseccomp_gen_syscall_nrs_arm64",
@@ -2415,6 +2416,7 @@
         "SECCOMP_WHITELIST_APP.TXT",
         "SECCOMP_BLACKLIST_COMMON.TXT",
         "SECCOMP_BLACKLIST_APP.TXT",
+        "SECCOMP_PRIORITY.TXT",
         ":libseccomp_gen_syscall_nrs_arm",
         ":libseccomp_gen_syscall_nrs_arm64",
         ":libseccomp_gen_syscall_nrs_x86",
@@ -2441,6 +2443,7 @@
         "SECCOMP_WHITELIST_COMMON.TXT",
         "SECCOMP_WHITELIST_SYSTEM.TXT",
         "SECCOMP_BLACKLIST_COMMON.TXT",
+        "SECCOMP_PRIORITY.TXT",
         ":libseccomp_gen_syscall_nrs_arm",
         ":libseccomp_gen_syscall_nrs_arm64",
         ":libseccomp_gen_syscall_nrs_x86",
diff --git a/libc/SECCOMP_PRIORITY.TXT b/libc/SECCOMP_PRIORITY.TXT
new file mode 100644
index 0000000..fb5ad4a
--- /dev/null
+++ b/libc/SECCOMP_PRIORITY.TXT
@@ -0,0 +1,10 @@
+# This file is used to populate seccomp's whitelist policy in combination with SYSCALLS.TXT.
+# Note that the resultant policy is applied only to zygote spawned processes.
+#
+# This file is processed by a python script named genseccomp.py.
+#
+# The syscalls below are prioritized above other syscalls when checking seccomp policy, in
+# the order of appearance in this file.
+
+futex
+ioctl
\ No newline at end of file
diff --git a/libc/tools/genseccomp.py b/libc/tools/genseccomp.py
index cc0ff99..ba7e2ca 100755
--- a/libc/tools/genseccomp.py
+++ b/libc/tools/genseccomp.py
@@ -12,6 +12,7 @@
 
 
 BPF_JGE = "BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, {0}, {1}, {2})"
+BPF_JEQ = "BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, {0}, {1}, {2})"
 BPF_ALLOW = "BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW)"
 
 
@@ -37,6 +38,24 @@
   return set([x["name"] for x in parser.syscalls if x.get(architecture)])
 
 
+def load_syscall_priorities_from_file(file_path):
+  format_re = re.compile(r'^\s*([A-Za-z_][A-Za-z0-9_]+)\s*$')
+  priorities = []
+  with open(file_path) as f:
+    for line in f:
+      m = format_re.match(line)
+      if not m:
+        continue
+      try:
+        name = m.group(1)
+        priorities.append(name)
+      except:
+        logging.debug('Failed to parse %s from %s', (line, file_path))
+        pass
+
+  return priorities
+
+
 def merge_names(base_names, whitelist_names, blacklist_names):
   if bool(blacklist_names - base_names):
     raise RuntimeError("Blacklist item not in bionic - aborting " + str(
@@ -45,6 +64,20 @@
   return (base_names - blacklist_names) | whitelist_names
 
 
+def extract_priority_syscalls(syscalls, priorities):
+  # Extract syscalls that are not in the priority list
+  other_syscalls = \
+    [syscall for syscall in syscalls if syscall[0] not in priorities]
+  # For prioritized syscalls, keep the order in which they appear in th
+  # priority list
+  syscall_dict = {syscall[0]: syscall[1] for syscall in syscalls}
+  priority_syscalls = []
+  for name in priorities:
+    if name in syscall_dict.keys():
+      priority_syscalls.append((name, syscall_dict[name]))
+  return priority_syscalls, other_syscalls
+
+
 def parse_syscall_NRs(names_path):
   # 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:
@@ -123,8 +156,21 @@
     return jump + first + second
 
 
-def convert_ranges_to_bpf(ranges):
-  bpf = convert_to_intermediate_bpf(ranges)
+# Converts the prioritized syscalls to a bpf list that  is prepended to the
+# tree generated by convert_to_intermediate_bpf(). If we hit one of these
+# syscalls, shortcut to the allow statement at the bottom of the tree
+# immediately
+def convert_priority_to_intermediate_bpf(priority_syscalls):
+  result = []
+  for i, syscall in enumerate(priority_syscalls):
+    result.append(BPF_JEQ.format(syscall[1], "{allow}", 0) +
+                  ", //" + syscall[0])
+  return result
+
+
+def convert_ranges_to_bpf(ranges, priority_syscalls):
+  bpf = convert_priority_to_intermediate_bpf(priority_syscalls) + \
+    convert_to_intermediate_bpf(ranges)
 
   # Now we know the size of the tree, we can substitute the {fail} and {allow}
   # placeholders
@@ -135,9 +181,8 @@
     # With bpfs jmp 0 means the next statement, so the distance to the end is
     # len(bpf) - i - 1, which is where we will put the kill statement, and
     # then the statement after that is the allow statement
-    if "{fail}" in statement and "{allow}" in statement:
-      bpf[i] = statement.format(fail=str(len(bpf) - i),
-                                allow=str(len(bpf) - i - 1))
+    bpf[i] = statement.format(fail=str(len(bpf) - i),
+                              allow=str(len(bpf) - i - 1))
 
   # Add the allow calls at the end. If the syscall is not matched, we will
   # continue. This allows the user to choose to match further syscalls, and
@@ -174,13 +219,15 @@
   return header + "\n".join(bpf) + footer
 
 
-def construct_bpf(syscalls, architecture, name_modifier):
-  ranges = convert_NRs_to_ranges(syscalls)
-  bpf = convert_ranges_to_bpf(ranges)
+def construct_bpf(syscalls, architecture, name_modifier, priorities):
+  priority_syscalls, other_syscalls = \
+    extract_priority_syscalls(syscalls, priorities)
+  ranges = convert_NRs_to_ranges(other_syscalls)
+  bpf = convert_ranges_to_bpf(ranges, priority_syscalls)
   return convert_bpf_to_output(bpf, architecture, name_modifier)
 
 
-def gen_policy(name_modifier, out_dir, base_syscall_file, syscall_files, syscall_NRs):
+def gen_policy(name_modifier, out_dir, base_syscall_file, syscall_files, syscall_NRs, priority_file):
   for arch in SupportedArchitectures:
     base_names = load_syscall_names_from_file(base_syscall_file, arch)
     whitelist_names = set()
@@ -190,6 +237,9 @@
         blacklist_names |= load_syscall_names_from_file(f, arch)
       else:
         whitelist_names |= load_syscall_names_from_file(f, arch)
+    priorities = []
+    if priority_file:
+      priorities = load_syscall_priorities_from_file(priority_file)
 
     allowed_syscalls = []
     for name in merge_names(base_names, whitelist_names, blacklist_names):
@@ -198,7 +248,7 @@
       except:
         logging.exception("Failed to find %s in %s", name, arch)
         raise
-    output = construct_bpf(allowed_syscalls, arch, name_modifier)
+    output = construct_bpf(allowed_syscalls, arch, name_modifier, priorities)
 
     # And output policy
     existing = ""
@@ -226,6 +276,7 @@
                             "following files: \n"
                             "* /blacklist.*\.txt$/ syscall blacklist.\n"
                             "* /whitelist.*\.txt$/ syscall whitelist.\n"
+                            "* /priority.txt$/ priorities for bpf rules.\n"
                             "* otherwise, syscall name-number mapping.\n"))
   args = parser.parse_args()
 
@@ -235,17 +286,21 @@
     logging.basicConfig(level=logging.INFO)
 
   syscall_files = []
+  priority_file = None
   syscall_NRs = {}
   for filename in args.files:
     if filename.lower().endswith('.txt'):
-      syscall_files.append(filename)
+      if filename.lower().endswith('priority.txt'):
+        priority_file = filename
+      else:
+        syscall_files.append(filename)
     else:
       m = re.search(r"libseccomp_gen_syscall_nrs_([^/]+)", filename)
       syscall_NRs[m.group(1)] = parse_syscall_NRs(filename)
 
   gen_policy(name_modifier=args.name_modifier, out_dir=args.out_dir,
              syscall_NRs=syscall_NRs, base_syscall_file=args.base_file,
-             syscall_files=args.files)
+             syscall_files=syscall_files, priority_file=priority_file)
 
 
 if __name__ == "__main__":
diff --git a/linker/linker_translate_path.cpp b/linker/linker_translate_path.cpp
index df7d0aa..4f3fdfb 100644
--- a/linker/linker_translate_path.cpp
+++ b/linker/linker_translate_path.cpp
@@ -31,13 +31,14 @@
 #include "linker_utils.h"
 
 #if defined(__LP64__)
-static const char* const kSystemLibDir        = "/system/lib64";
-static const char* const kI18nApexLibDir      = "/apex/com.android.i18n/lib64";
+#define APEX_LIB(apex, name) \
+  { "/system/lib64/" name, "/apex/" apex "/lib64/" name }
 #else
-static const char* const kSystemLibDir        = "/system/lib";
-static const char* const kI18nApexLibDir      = "/apex/com.android.i18n/lib";
+#define APEX_LIB(apex, name) \
+  { "/system/lib/" name, "/apex/" apex "/lib/" name }
 #endif
 
+
 // Workaround for dlopen(/system/lib(64)/<soname>) when .so is in /apex. http://b/121248172
 /**
  * Translate /system path to /apex path if needed
@@ -47,27 +48,22 @@
  * return true if translation is needed
  */
 bool translateSystemPathToApexPath(const char* name, std::string* out_name_to_apex) {
-  static const char* const kSystemToArtApexLibs[] = {
-      "libicuuc.so",
-      "libicui18n.so",
+  static constexpr const char* kPathTranslationQ[][2] = {
+      APEX_LIB("com.android.i18n", "libicui18n.so"),
+      APEX_LIB("com.android.i18n", "libicuuc.so")
   };
-  // New mapping for new apex should be added below
 
-  // Nothing to do if target sdk version is Q or above
-  if (get_application_target_sdk_version() >= 29) {
+  if (name == nullptr) {
     return false;
   }
 
-  // If the path isn't /system/lib, there's nothing to do.
-  if (name == nullptr || dirname(name) != kSystemLibDir) {
-    return false;
-  }
+  auto comparator = [name](auto p) { return strcmp(name, p[0]) == 0; };
 
-  const char* base_name = basename(name);
-
-  for (const char* soname : kSystemToArtApexLibs) {
-    if (strcmp(base_name, soname) == 0) {
-      *out_name_to_apex = std::string(kI18nApexLibDir) + "/" + base_name;
+  if (get_application_target_sdk_version() < __ANDROID_API_Q__) {
+    if (auto it =
+            std::find_if(std::begin(kPathTranslationQ), std::end(kPathTranslationQ), comparator);
+        it != std::end(kPathTranslationQ)) {
+      *out_name_to_apex = (*it)[1];
       return true;
     }
   }
diff --git a/tests/fdsan_test.cpp b/tests/fdsan_test.cpp
index 9932b21..134d621 100644
--- a/tests/fdsan_test.cpp
+++ b/tests/fdsan_test.cpp
@@ -194,7 +194,7 @@
 #endif
 }
 
-TEST_F(FdsanTest, DISABLED_vfork) {
+TEST_F(FdsanTest, vfork) {
   android::base::unique_fd fd(open("/dev/null", O_RDONLY));
 
   pid_t rc = vfork();