OSDN Git Service

Add Zygote.startChildZygote() to fork a new process that itself is a zygote.
authorRobert Sesek <rsesek@google.com>
Mon, 12 Feb 2018 23:46:01 +0000 (18:46 -0500)
committerRobert Sesek <rsesek@google.com>
Fri, 16 Feb 2018 19:17:41 +0000 (14:17 -0500)
This adds a new --start-child-zygote argument that instructs the main
zygote to create a new child process that will also be a zygote. The
system_server generates a random name in the abstract socket namespace
for it and the child-zygote to communicate over, and that is passed as
an argument to the new process.

A child-zygote bypasses the normal post-fork-child of the zygote process
in order to preserve itself as a zygote. This means not starting the
Binder threadpool nor launching into ActivityThread. Instead, a
child-zygote calls into its own main function. The main function runs a
ZygoteServer select loop, listening on the socket name specified by the
system_server when it was forked.

Unlike the system zygotes, a child-zygote can be killed without bringing
down the system. Killing a child-zygote will not terminate its child
processes, which will be reparented to init for reaping when they
eventually exit.

Bug: 63749735
Test: m (with multi-project commits landed)
Change-Id: I3e7ebbdba498f8fec1d84cdf927dc43a92be4b68

core/java/android/os/ChildZygoteProcess.java [new file with mode: 0644]
core/java/android/os/ZygoteProcess.java
core/java/com/android/internal/os/RuntimeInit.java
core/java/com/android/internal/os/WebViewZygoteInit.java
core/java/com/android/internal/os/Zygote.java
core/java/com/android/internal/os/ZygoteConnection.java
core/java/com/android/internal/os/ZygoteInit.java
core/java/com/android/internal/os/ZygoteServer.java
core/jni/com_android_internal_os_Zygote.cpp
core/jni/fd_utils.cpp

diff --git a/core/java/android/os/ChildZygoteProcess.java b/core/java/android/os/ChildZygoteProcess.java
new file mode 100644 (file)
index 0000000..337a3e2
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import android.net.LocalSocketAddress;
+
+/**
+ * Represents a connection to a child-zygote process. A child-zygote is spawend from another
+ * zygote process using {@link startChildZygote()}.
+ *
+ * {@hide}
+ */
+public class ChildZygoteProcess extends ZygoteProcess {
+    /**
+     * The PID of the child zygote process.
+     */
+    private final int mPid;
+
+    ChildZygoteProcess(LocalSocketAddress socketAddress, int pid) {
+        super(socketAddress, null);
+        mPid = pid;
+    }
+
+    /**
+     * Returns the PID of the child-zygote process.
+     */
+    public int getPid() {
+        return mPid;
+    }
+}
index 4a97640..57418c8 100644 (file)
@@ -33,6 +33,7 @@ import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
+import java.util.UUID;
 
 /*package*/ class ZygoteStartFailedEx extends Exception {
     ZygoteStartFailedEx(String s) {
@@ -217,7 +218,8 @@ public class ZygoteProcess {
         try {
             return startViaZygote(processClass, niceName, uid, gid, gids,
                     runtimeFlags, mountExternal, targetSdkVersion, seInfo,
-                    abi, instructionSet, appDataDir, invokeWith, zygoteArgs);
+                    abi, instructionSet, appDataDir, invokeWith, false /* startChildZygote */,
+                    zygoteArgs);
         } catch (ZygoteStartFailedEx ex) {
             Log.e(LOG_TAG,
                     "Starting VM process through Zygote failed");
@@ -333,6 +335,8 @@ public class ZygoteProcess {
      * @param abi the ABI the process should use.
      * @param instructionSet null-ok the instruction set to use.
      * @param appDataDir null-ok the data directory of the app.
+     * @param startChildZygote Start a sub-zygote. This creates a new zygote process
+     * that has its state cloned from this zygote process.
      * @param extraArgs Additional arguments to supply to the zygote process.
      * @return An object that describes the result of the attempt to start the process.
      * @throws ZygoteStartFailedEx if process start failed for any reason
@@ -348,6 +352,7 @@ public class ZygoteProcess {
                                                       String instructionSet,
                                                       String appDataDir,
                                                       String invokeWith,
+                                                      boolean startChildZygote,
                                                       String[] extraArgs)
                                                       throws ZygoteStartFailedEx {
         ArrayList<String> argsForZygote = new ArrayList<String>();
@@ -404,6 +409,10 @@ public class ZygoteProcess {
             argsForZygote.add(invokeWith);
         }
 
+        if (startChildZygote) {
+            argsForZygote.add("--start-child-zygote");
+        }
+
         argsForZygote.add(processClass);
 
         if (extraArgs != null) {
@@ -418,6 +427,18 @@ public class ZygoteProcess {
     }
 
     /**
+     * Closes the connections to the zygote, if they exist.
+     */
+    public void close() {
+        if (primaryZygoteState != null) {
+            primaryZygoteState.close();
+        }
+        if (secondaryZygoteState != null) {
+            secondaryZygoteState.close();
+        }
+    }
+
+    /**
      * Tries to establish a connection to the zygote that handles a given {@code abi}. Might block
      * and retry if the zygote is unresponsive. This method is a no-op if a connection is
      * already open.
@@ -549,4 +570,36 @@ public class ZygoteProcess {
         }
         Slog.wtf(LOG_TAG, "Failed to connect to Zygote through socket " + address.getName());
     }
+
+    /**
+     * Starts a new zygote process as a child of this zygote. This is used to create
+     * secondary zygotes that inherit data from the zygote that this object
+     * communicates with. This returns a new ZygoteProcess representing a connection
+     * to the newly created zygote. Throws an exception if the zygote cannot be started.
+     */
+    public ChildZygoteProcess startChildZygote(final String processClass,
+                                               final String niceName,
+                                               int uid, int gid, int[] gids,
+                                               int runtimeFlags,
+                                               String seInfo,
+                                               String abi,
+                                               String instructionSet) {
+        // Create an unguessable address in the global abstract namespace.
+        final LocalSocketAddress serverAddress = new LocalSocketAddress(
+                processClass + "/" + UUID.randomUUID().toString());
+
+        final String[] extraArgs = {Zygote.CHILD_ZYGOTE_SOCKET_NAME_ARG + serverAddress.getName()};
+
+        Process.ProcessStartResult result;
+        try {
+            result = startViaZygote(processClass, niceName, uid, gid,
+                    gids, runtimeFlags, 0 /* mountExternal */, 0 /* targetSdkVersion */, seInfo,
+                    abi, instructionSet, null /* appDataDir */, null /* invokeWith */,
+                    true /* startChildZygote */, extraArgs);
+        } catch (ZygoteStartFailedEx ex) {
+            throw new RuntimeException("Starting child-zygote through Zygote failed", ex);
+        }
+
+        return new ChildZygoteProcess(serverAddress, result.pid);
+    }
 }
index 895be08..bb5a0ad 100644 (file)
@@ -230,7 +230,7 @@ public class RuntimeInit {
      * @param argv Argument vector for main()
      * @param classLoader the classLoader to load {@className} with
      */
-    private static Runnable findStaticMain(String className, String[] argv,
+    protected static Runnable findStaticMain(String className, String[] argv,
             ClassLoader classLoader) {
         Class<?> cl;
 
index cadb66a..b38c851 100644 (file)
@@ -129,7 +129,7 @@ class WebViewZygoteInit {
 
         final Runnable caller;
         try {
-            sServer.registerServerSocket("webview_zygote");
+            sServer.registerServerSocketFromEnv("webview_zygote");
             // The select loop returns early in the child process after a fork and
             // loops forever in the zygote.
             caller = sServer.runSelectLoop(TextUtils.join(",", Build.SUPPORTED_ABIS));
index 89d63f6..e23cbf8 100644 (file)
@@ -71,6 +71,13 @@ public final class Zygote {
 
     private static final ZygoteHooks VM_HOOKS = new ZygoteHooks();
 
+    /**
+     * An extraArg passed when a zygote process is forking a child-zygote, specifying a name
+     * in the abstract socket namespace. This socket name is what the new child zygote
+     * should listen for connections on.
+     */
+    public static final String CHILD_ZYGOTE_SOCKET_NAME_ARG = "--zygote-socket=";
+
     private Zygote() {}
 
     /** Called for some security initialization before any fork. */
@@ -102,6 +109,8 @@ public final class Zygote {
      * @param fdsToIgnore null-ok an array of ints, either null or holding
      * one or more POSIX file descriptor numbers that are to be ignored
      * in the file descriptor table check.
+     * @param startChildZygote if true, the new child process will itself be a
+     * new zygote process.
      * @param instructionSet null-ok the instruction set to use.
      * @param appDataDir null-ok the data directory of the app.
      *
@@ -110,13 +119,13 @@ public final class Zygote {
      */
     public static int forkAndSpecialize(int uid, int gid, int[] gids, int runtimeFlags,
           int[][] rlimits, int mountExternal, String seInfo, String niceName, int[] fdsToClose,
-          int[] fdsToIgnore, String instructionSet, String appDataDir) {
+          int[] fdsToIgnore, boolean startChildZygote, String instructionSet, String appDataDir) {
         VM_HOOKS.preFork();
         // Resets nice priority for zygote process.
         resetNicePriority();
         int pid = nativeForkAndSpecialize(
                   uid, gid, gids, runtimeFlags, rlimits, mountExternal, seInfo, niceName, fdsToClose,
-                  fdsToIgnore, instructionSet, appDataDir);
+                  fdsToIgnore, startChildZygote, instructionSet, appDataDir);
         // Enable tracing as soon as possible for the child process.
         if (pid == 0) {
             Trace.setTracingEnabled(true, runtimeFlags);
@@ -130,7 +139,7 @@ public final class Zygote {
 
     native private static int nativeForkAndSpecialize(int uid, int gid, int[] gids,int runtimeFlags,
           int[][] rlimits, int mountExternal, String seInfo, String niceName, int[] fdsToClose,
-          int[] fdsToIgnore, String instructionSet, String appDataDir);
+          int[] fdsToIgnore, boolean startChildZygote, String instructionSet, String appDataDir);
 
     /**
      * Called to do any initialization before starting an application.
@@ -190,8 +199,8 @@ public final class Zygote {
     native protected static void nativeUnmountStorageOnInit();
 
     private static void callPostForkChildHooks(int runtimeFlags, boolean isSystemServer,
-            String instructionSet) {
-        VM_HOOKS.postForkChild(runtimeFlags, isSystemServer, instructionSet);
+            boolean isZygote, String instructionSet) {
+        VM_HOOKS.postForkChild(runtimeFlags, isSystemServer, isZygote, instructionSet);
     }
 
     /**
index 6a87b1f..a32fb43 100644 (file)
@@ -221,8 +221,8 @@ class ZygoteConnection {
 
         pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid, parsedArgs.gids,
                 parsedArgs.runtimeFlags, rlimits, parsedArgs.mountExternal, parsedArgs.seInfo,
-                parsedArgs.niceName, fdsToClose, fdsToIgnore, parsedArgs.instructionSet,
-                parsedArgs.appDataDir);
+                parsedArgs.niceName, fdsToClose, fdsToIgnore, parsedArgs.startChildZygote,
+                parsedArgs.instructionSet, parsedArgs.appDataDir);
 
         try {
             if (pid == 0) {
@@ -233,7 +233,8 @@ class ZygoteConnection {
                 IoUtils.closeQuietly(serverPipeFd);
                 serverPipeFd = null;
 
-                return handleChildProc(parsedArgs, descriptors, childPipeFd);
+                return handleChildProc(parsedArgs, descriptors, childPipeFd,
+                        parsedArgs.startChildZygote);
             } else {
                 // In the parent. A pid < 0 indicates a failure and will be handled in
                 // handleParentProc.
@@ -415,6 +416,14 @@ class ZygoteConnection {
         boolean preloadDefault;
 
         /**
+         * Whether this is a request to start a zygote process as a child of this zygote.
+         * Set with --start-child-zygote. The remaining arguments must include the
+         * CHILD_ZYGOTE_SOCKET_NAME_ARG flag to indicate the abstract socket name that
+         * should be used for communication.
+         */
+        boolean startChildZygote;
+
+        /**
          * Constructs instance and parses args
          * @param args zygote command-line args
          * @throws IllegalArgumentException
@@ -565,6 +574,8 @@ class ZygoteConnection {
                     preloadPackageCacheKey = args[++curArg];
                 } else if (arg.equals("--preload-default")) {
                     preloadDefault = true;
+                } else if (arg.equals("--start-child-zygote")) {
+                    startChildZygote = true;
                 } else {
                     break;
                 }
@@ -587,6 +598,20 @@ class ZygoteConnection {
                 remainingArgs = new String[args.length - curArg];
                 System.arraycopy(args, curArg, remainingArgs, 0, remainingArgs.length);
             }
+
+            if (startChildZygote) {
+                boolean seenChildSocketArg = false;
+                for (String arg : remainingArgs) {
+                    if (arg.startsWith(Zygote.CHILD_ZYGOTE_SOCKET_NAME_ARG)) {
+                        seenChildSocketArg = true;
+                        break;
+                    }
+                }
+                if (!seenChildSocketArg) {
+                    throw new IllegalArgumentException("--start-child-zygote specified " +
+                            "without " + Zygote.CHILD_ZYGOTE_SOCKET_NAME_ARG);
+                }
+            }
         }
     }
 
@@ -739,9 +764,10 @@ class ZygoteConnection {
      * @param parsedArgs non-null; zygote args
      * @param descriptors null-ok; new file descriptors for stdio if available.
      * @param pipeFd null-ok; pipe for communication back to Zygote.
+     * @param isZygote whether this new child process is itself a new Zygote.
      */
     private Runnable handleChildProc(Arguments parsedArgs, FileDescriptor[] descriptors,
-            FileDescriptor pipeFd) {
+            FileDescriptor pipeFd, boolean isZygote) {
         /**
          * By the time we get here, the native code has closed the two actual Zygote
          * socket connections, and substituted /dev/null in their place.  The LocalSocket
@@ -778,8 +804,13 @@ class ZygoteConnection {
             // Should not get here.
             throw new IllegalStateException("WrapperInit.execApplication unexpectedly returned");
         } else {
-            return ZygoteInit.zygoteInit(parsedArgs.targetSdkVersion, parsedArgs.remainingArgs,
-                    null /* classLoader */);
+            if (!isZygote) {
+                return ZygoteInit.zygoteInit(parsedArgs.targetSdkVersion, parsedArgs.remainingArgs,
+                        null /* classLoader */);
+            } else {
+                return ZygoteInit.childZygoteInit(parsedArgs.targetSdkVersion,
+                        parsedArgs.remainingArgs, null /* classLoader */);
+            }
         }
     }
 
index 21f1fb6..f603f4f 100644 (file)
@@ -755,7 +755,7 @@ public class ZygoteInit {
                 throw new RuntimeException("No ABI list supplied.");
             }
 
-            zygoteServer.registerServerSocket(socketName);
+            zygoteServer.registerServerSocketFromEnv(socketName);
             // In some configurations, we avoid preloading resources and classes eagerly.
             // In such cases, we will preload things prior to our first fork.
             if (!enableLazyPreload) {
@@ -870,5 +870,16 @@ public class ZygoteInit {
         return RuntimeInit.applicationInit(targetSdkVersion, argv, classLoader);
     }
 
+    /**
+     * The main function called when starting a child zygote process. This is used as an
+     * alternative to zygoteInit(), which skips calling into initialization routines that
+     * start the Binder threadpool.
+     */
+    static final Runnable childZygoteInit(
+            int targetSdkVersion, String[] argv, ClassLoader classLoader) {
+        RuntimeInit.Arguments args = new RuntimeInit.Arguments(argv);
+        return RuntimeInit.findStaticMain(args.startClass, args.startArgs, classLoader);
+    }
+
     private static final native void nativeZygoteInit();
 }
index 8baa15a..fecf9b9 100644 (file)
@@ -44,9 +44,21 @@ class ZygoteServer {
 
     private static final String ANDROID_SOCKET_PREFIX = "ANDROID_SOCKET_";
 
+    /**
+     * Listening socket that accepts new server connections.
+     */
     private LocalServerSocket mServerSocket;
 
     /**
+     * Whether or not mServerSocket's underlying FD should be closed directly.
+     * If mServerSocket is created with an existing FD, closing the socket does
+     * not close the FD and it must be closed explicitly. If the socket is created
+     * with a name instead, then closing the socket will close the underlying FD
+     * and it should not be double-closed.
+     */
+    private boolean mCloseSocketFd;
+
+    /**
      * Set by the child process, immediately after a call to {@code Zygote.forkAndSpecialize}.
      */
     private boolean mIsForkChild;
@@ -59,11 +71,12 @@ class ZygoteServer {
     }
 
     /**
-     * Registers a server socket for zygote command connections
+     * Registers a server socket for zygote command connections. This locates the server socket
+     * file descriptor through an ANDROID_SOCKET_ environment variable.
      *
      * @throws RuntimeException when open fails
      */
-    void registerServerSocket(String socketName) {
+    void registerServerSocketFromEnv(String socketName) {
         if (mServerSocket == null) {
             int fileDesc;
             final String fullSocketName = ANDROID_SOCKET_PREFIX + socketName;
@@ -78,6 +91,7 @@ class ZygoteServer {
                 FileDescriptor fd = new FileDescriptor();
                 fd.setInt$(fileDesc);
                 mServerSocket = new LocalServerSocket(fd);
+                mCloseSocketFd = true;
             } catch (IOException ex) {
                 throw new RuntimeException(
                         "Error binding to local socket '" + fileDesc + "'", ex);
@@ -86,6 +100,22 @@ class ZygoteServer {
     }
 
     /**
+     * Registers a server socket for zygote command connections. This opens the server socket
+     * at the specified name in the abstract socket namespace.
+     */
+    void registerServerSocketAtAbstractName(String socketName) {
+        if (mServerSocket == null) {
+            try {
+                mServerSocket = new LocalServerSocket(socketName);
+                mCloseSocketFd = false;
+            } catch (IOException ex) {
+                throw new RuntimeException(
+                        "Error binding to abstract socket '" + socketName + "'", ex);
+            }
+        }
+    }
+
+    /**
      * Waits for and accepts a single command connection. Throws
      * RuntimeException on failure.
      */
@@ -112,7 +142,7 @@ class ZygoteServer {
             if (mServerSocket != null) {
                 FileDescriptor fd = mServerSocket.getFileDescriptor();
                 mServerSocket.close();
-                if (fd != null) {
+                if (fd != null && mCloseSocketFd) {
                     Os.close(fd);
                 }
             }
@@ -219,6 +249,11 @@ class ZygoteServer {
                             Log.e(TAG, "Caught post-fork exception in child process.", e);
                             throw e;
                         }
+                    } finally {
+                        // Reset the child flag, in the event that the child process is a child-
+                        // zygote. The flag will not be consulted this loop pass after the Runnable
+                        // is returned.
+                        mIsForkChild = false;
                     }
                 }
             }
index fa86c75..3f95cf4 100644 (file)
@@ -516,7 +516,7 @@ static pid_t ForkAndSpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArra
                                      jint mount_external,
                                      jstring java_se_info, jstring java_se_name,
                                      bool is_system_server, jintArray fdsToClose,
-                                     jintArray fdsToIgnore,
+                                     jintArray fdsToIgnore, bool is_child_zygote,
                                      jstring instructionSet, jstring dataDir) {
   SetSignalHandlers();
 
@@ -699,7 +699,7 @@ static pid_t ForkAndSpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArra
     UnsetChldSignalHandler();
 
     env->CallStaticVoidMethod(gZygoteClass, gCallPostForkChildHooks, runtime_flags,
-                              is_system_server, instructionSet);
+                              is_system_server, is_child_zygote, instructionSet);
     if (env->ExceptionCheck()) {
       RuntimeAbort(env, __LINE__, "Error calling post fork hooks.");
     }
@@ -748,8 +748,7 @@ static jint com_android_internal_os_Zygote_nativeForkAndSpecialize(
         JNIEnv* env, jclass, jint uid, jint gid, jintArray gids,
         jint runtime_flags, jobjectArray rlimits,
         jint mount_external, jstring se_info, jstring se_name,
-        jintArray fdsToClose,
-        jintArray fdsToIgnore,
+        jintArray fdsToClose, jintArray fdsToIgnore, jboolean is_child_zygote,
         jstring instructionSet, jstring appDataDir) {
     jlong capabilities = 0;
 
@@ -786,13 +785,22 @@ static jint com_android_internal_os_Zygote_nativeForkAndSpecialize(
       capabilities |= (1LL << CAP_BLOCK_SUSPEND);
     }
 
+    // If forking a child zygote process, that zygote will need to be able to change
+    // the UID and GID of processes it forks, as well as drop those capabilities.
+    if (is_child_zygote) {
+      capabilities |= (1LL << CAP_SETUID);
+      capabilities |= (1LL << CAP_SETGID);
+      capabilities |= (1LL << CAP_SETPCAP);
+    }
+
     // Containers run without some capabilities, so drop any caps that are not
     // available.
     capabilities &= GetEffectiveCapabilityMask(env);
 
     return ForkAndSpecializeCommon(env, uid, gid, gids, runtime_flags,
             rlimits, capabilities, capabilities, mount_external, se_info,
-            se_name, false, fdsToClose, fdsToIgnore, instructionSet, appDataDir);
+            se_name, false, fdsToClose, fdsToIgnore, is_child_zygote == JNI_TRUE,
+            instructionSet, appDataDir);
 }
 
 static jint com_android_internal_os_Zygote_nativeForkSystemServer(
@@ -803,7 +811,7 @@ static jint com_android_internal_os_Zygote_nativeForkSystemServer(
                                       runtime_flags, rlimits,
                                       permittedCapabilities, effectiveCapabilities,
                                       MOUNT_EXTERNAL_DEFAULT, NULL, NULL, true, NULL,
-                                      NULL, NULL, NULL);
+                                      NULL, false, NULL, NULL);
   if (pid > 0) {
       // The zygote process checks whether the child process has died or not.
       ALOGI("System server process %d has been created", pid);
@@ -877,7 +885,7 @@ static const JNINativeMethod gMethods[] = {
     { "nativeSecurityInit", "()V",
       (void *) com_android_internal_os_Zygote_nativeSecurityInit },
     { "nativeForkAndSpecialize",
-      "(II[II[[IILjava/lang/String;Ljava/lang/String;[I[ILjava/lang/String;Ljava/lang/String;)I",
+      "(II[II[[IILjava/lang/String;Ljava/lang/String;[I[IZLjava/lang/String;Ljava/lang/String;)I",
       (void *) com_android_internal_os_Zygote_nativeForkAndSpecialize },
     { "nativeForkSystemServer", "(II[II[[IJJ)I",
       (void *) com_android_internal_os_Zygote_nativeForkSystemServer },
@@ -892,7 +900,7 @@ static const JNINativeMethod gMethods[] = {
 int register_com_android_internal_os_Zygote(JNIEnv* env) {
   gZygoteClass = MakeGlobalRefOrDie(env, FindClassOrDie(env, kZygoteClassName));
   gCallPostForkChildHooks = GetStaticMethodIDOrDie(env, gZygoteClass, "callPostForkChildHooks",
-                                                   "(IZLjava/lang/String;)V");
+                                                   "(IZZLjava/lang/String;)V");
 
   return RegisterMethodsOrDie(env, "com/android/internal/os/Zygote", gMethods, NELEM(gMethods));
 }
index 956b724..1383bbd 100644 (file)
@@ -313,10 +313,12 @@ bool FileDescriptorInfo::GetSocketName(const int fd, std::string* result) {
     return false;
   }
 
-  // This is a local socket with an abstract address, we do not accept it.
+  // This is a local socket with an abstract address. Remove the leading NUL byte and
+  // add a human-readable "ABSTRACT/" prefix.
   if (unix_addr->sun_path[0] == '\0') {
-    LOG(ERROR) << "Unsupported AF_UNIX socket (fd=" << fd << ") with abstract address.";
-    return false;
+    *result = "ABSTRACT/";
+    result->append(&unix_addr->sun_path[1], path_len - 1);
+    return true;
   }
 
   // If we're here, sun_path must refer to a null terminated filesystem