Two patches.

Patch 1a+1b: Set capabilities sooner.

(cherry-pick from gingerbread I22fde728bd5d65774f8fdf1fa45956fe18358c4c)
(hand-pick from gingerbread Ief58bdfff2cb82d7824ef796c2c1031f525f6ee9,
 plus minor tweak to avoid altering the API)

Patch 3: If there's an error dropping privileges, abort.

(cherry-pick from gingerbread I85ce12e93cb8c297add756dc3b872b8346eda00b)

Bug 3176774

Change-Id: If8285d7bfd5a466bd77c0f7ffadf1bf96d31db06
diff --git a/libcore/dalvik/src/main/java/dalvik/system/Zygote.java b/libcore/dalvik/src/main/java/dalvik/system/Zygote.java
index 641e856..653e4d0 100644
--- a/libcore/dalvik/src/main/java/dalvik/system/Zygote.java
+++ b/libcore/dalvik/src/main/java/dalvik/system/Zygote.java
@@ -109,12 +109,27 @@
      * dimension having a length of 3 and representing
      * (resource, rlim_cur, rlim_max). These are set via the posix
      * setrlimit(2) call.
+     * @param permittedCapabilities argument for setcap()
+     * @param effectiveCapabilities argument for setcap()
      *
      * @return 0 if this is the child, pid of the child
      * if this is the parent, or -1 on error.
+     *
+     * @hide
      */
-    native public static int forkSystemServer(int uid, int gid, 
-            int[] gids, int debugFlags, int[][] rlimits);
+    native public static int forkSystemServer(int uid, int gid,
+            int[] gids, int debugFlags, int[][] rlimits,
+            long permittedCapabilities, long effectiveCapabilities);
+
+    /*
+     * For bug 3176774, we needed to update forkSystemServer() after
+     * the API was locked down.  To avoid going out of sync with the
+     * API description file, we provide a dummy function here.
+     */
+    public static int forkSystemServer(int uid, int gid,
+            int[] gids, int debugFlags, int[][] rlimits) {
+        throw new UnsupportedOperationException();
+    }
 
     /**
      * Special method to start the system server process.
diff --git a/vm/native/dalvik_system_Zygote.c b/vm/native/dalvik_system_Zygote.c
index f8e8250..f501255 100644
--- a/vm/native/dalvik_system_Zygote.c
+++ b/vm/native/dalvik_system_Zygote.c
@@ -324,10 +324,38 @@
 #endif
 }
 
-/* 
+/*
+ * Set Linux capability flags.
+ *
+ * Returns 0 on success, errno on failure.
+ */
+static int setCapabilities(int64_t permitted, int64_t effective)
+{
+#ifdef HAVE_ANDROID_OS
+    struct __user_cap_header_struct capheader;
+    struct __user_cap_data_struct capdata;
+
+    memset(&capheader, 0, sizeof(capheader));
+    memset(&capdata, 0, sizeof(capdata));
+
+    capheader.version = _LINUX_CAPABILITY_VERSION;
+    capheader.pid = 0;
+
+    capdata.effective = effective;
+    capdata.permitted = permitted;
+
+    LOGV("CAPSET perm=%llx eff=%llx\n", permitted, effective);
+    if (capset(&capheader, &capdata) != 0)
+        return errno;
+#endif /*HAVE_ANDROID_OS*/
+
+    return 0;
+}
+
+/*
  * Utility routine to fork zygote and specialize the child process.
  */
-static pid_t forkAndSpecializeCommon(const u4* args)
+static pid_t forkAndSpecializeCommon(const u4* args, bool isSystemServer)
 {
     pid_t pid;
 
@@ -336,6 +364,21 @@
     ArrayObject* gids = (ArrayObject *)args[2];
     u4 debugFlags = args[3];
     ArrayObject *rlimits = (ArrayObject *)args[4];
+    int64_t permittedCapabilities, effectiveCapabilities;
+
+    if (isSystemServer) {
+        /*
+         * Don't use GET_ARG_LONG here for now.  gcc is generating code
+         * that uses register d8 as a temporary, and that's coming out
+         * scrambled in the child process.  b/3138621
+         */
+        //permittedCapabilities = GET_ARG_LONG(args, 5);
+        //effectiveCapabilities = GET_ARG_LONG(args, 7);
+        permittedCapabilities = args[5] | (int64_t) args[6] << 32;
+        effectiveCapabilities = args[7] | (int64_t) args[8] << 32;
+    } else {
+        permittedCapabilities = effectiveCapabilities = 0;
+    }
 
     if (!gDvm.zygote) {
         dvmThrowException("Ljava/lang/IllegalStateException;",
@@ -367,7 +410,8 @@
             err = prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0);
 
             if (err < 0) {
-                LOGW("cannot PR_SET_KEEPCAPS errno: %d", errno);
+                LOGE("cannot PR_SET_KEEPCAPS: %s", strerror(errno));
+                dvmAbort();
             }
         }
 
@@ -376,23 +420,34 @@
         err = setgroupsIntarray(gids);
 
         if (err < 0) {
-            LOGW("cannot setgroups() errno: %d", errno);
+            LOGE("cannot setgroups(): %s", strerror(errno));
+            dvmAbort();
         }
 
         err = setrlimitsFromArray(rlimits);
 
         if (err < 0) {
-            LOGW("cannot setrlimit() errno: %d", errno);
+            LOGE("cannot setrlimit(): %s", strerror(errno));
+            dvmAbort();
         }
 
         err = setgid(gid);
         if (err < 0) {
-            LOGW("cannot setgid(%d) errno: %d", gid, errno);
+            LOGE("cannot setgid(%d): %s", gid, strerror(errno));
+            dvmAbort();
         }
 
         err = setuid(uid);
         if (err < 0) {
-            LOGW("cannot setuid(%d) errno: %d", uid, errno);
+            LOGE("cannot setuid(%d): %s", uid, strerror(errno));
+            dvmAbort();
+        }
+
+        err = setCapabilities(permittedCapabilities, effectiveCapabilities);
+        if (err != 0) {
+            LOGE("cannot set capabilities (%llx,%llx): %s\n",
+                permittedCapabilities, effectiveCapabilities, strerror(err));
+            dvmAbort();
         }
 
         /*
@@ -425,19 +480,20 @@
 {
     pid_t pid;
 
-    pid = forkAndSpecializeCommon(args);
+    pid = forkAndSpecializeCommon(args, false);
 
     RETURN_INT(pid);
 }
 
-/* native public static int forkSystemServer(int uid, int gid, 
- *     int[] gids, int debugFlags); 
+/* native public static int forkSystemServer(int uid, int gid,
+ *     int[] gids, int debugFlags, long permittedCapabilities,
+ *     long effectiveCapabilities);
  */
 static void Dalvik_dalvik_system_Zygote_forkSystemServer(
         const u4* args, JValue* pResult)
 {
     pid_t pid;
-    pid = forkAndSpecializeCommon(args);
+    pid = forkAndSpecializeCommon(args, true);
 
     /* The zygote process checks whether the child process has died or not. */
     if (pid > 0) {
@@ -462,7 +518,7 @@
         Dalvik_dalvik_system_Zygote_fork },
     { "forkAndSpecialize",            "(II[II[[I)I",
         Dalvik_dalvik_system_Zygote_forkAndSpecialize },
-    { "forkSystemServer",            "(II[II[[I)I",
+    { "forkSystemServer",            "(II[II[[IJJ)I",
         Dalvik_dalvik_system_Zygote_forkSystemServer },
     { NULL, NULL, NULL },
 };