Reopen whitelisted zygote file descriptors after a fork.

We don't want these descriptors to be shared post-fork, so we'll
have to close and reopen them when the zygote forks. The set of
open descriptors is checked against a whitelist and it is a fatal
error if a non whitelisted FD is opened. It is also a fatal error
if anything other than a regular file / character device or socket
is opened at the time of forking.

This work is done in two stages :
- An initial list of FDs is constructed and cached prior to the
  first zygote fork.

- On each subsequent fork, we check whether the list of open FDs
  has changed. We are currently tolerant of changes, but in the
  longer term, it should be a fatal error if the set of open file
  descriptors in the zygote changes.

- Post fork, we traverse the list of open descriptors and reopen
  them if necessary.

bug: 30963384
Change-Id: Icfd45c96c660491b554d146a9d70d97dbcc712bc
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index a04fc2a..3207b3c 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -54,6 +54,7 @@
 #include "ScopedLocalRef.h"
 #include "ScopedPrimitiveArray.h"
 #include "ScopedUtfChars.h"
+#include "fd_utils-inl.h"
 
 #include "nativebridge/native_bridge.h"
 
@@ -436,6 +437,9 @@
 }
 #endif
 
+// The list of open zygote file descriptors.
+static FileDescriptorTable* gOpenFdTable = NULL;
+
 // Utility routine to fork zygote and specialize the child process.
 static pid_t ForkAndSpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray javaGids,
                                      jint debug_flags, jobjectArray javaRlimits,
@@ -450,6 +454,18 @@
   SetForkLoad(true);
 #endif
 
+  // If this is the first fork for this zygote, create the open FD table.
+  // If it isn't, we just need to check whether the list of open files has
+  // changed (and it shouldn't in the normal case).
+  if (gOpenFdTable == NULL) {
+    gOpenFdTable = FileDescriptorTable::Create();
+    if (gOpenFdTable == NULL) {
+      RuntimeAbort(env, __LINE__, "Unable to construct file descriptor table.");
+    }
+  } else if (!gOpenFdTable->Restat()) {
+    RuntimeAbort(env, __LINE__, "Unable to restat file descriptor table.");
+  }
+
   pid_t pid = fork();
 
   if (pid == 0) {
@@ -459,6 +475,12 @@
     // Clean up any descriptors which must be closed immediately
     DetachDescriptors(env, fdsToClose);
 
+    // Re-open all remaining open file descriptors so that they aren't shared
+    // with the zygote across a fork.
+    if (!gOpenFdTable->Reopen()) {
+      RuntimeAbort(env, __LINE__, "Unable to reopen whitelisted descriptors.");
+    }
+
     // Keep capabilities across UID change, unless we're staying root.
     if (uid != 0) {
       EnableKeepCapabilities(env);