Bug 1440206 - Allow brokered access to a subset of connect() in the Linux content sandbox. r?gcp draft
authorJed Davis <jld@mozilla.com>
Fri, 23 Feb 2018 18:03:13 -0700
changeset 765138 e0a59535475f8716cba39d37009cd92affa3c3f1
parent 765132 31a33fc619562e5b326585c6864d86832dac5126
push id101981
push userbmo:jld@mozilla.com
push dateFri, 09 Mar 2018 04:08:04 +0000
reviewersgcp
bugs1440206, 1066750
milestone60.0a1
Bug 1440206 - Allow brokered access to a subset of connect() in the Linux content sandbox. r?gcp This is to support WebGL with hybrid graphics drivers that connect to a secondary X server for GL (Primus and VirtualGL), without allowing access to arbitrary sockets. In addition to local X11 connections, Primus needs to connect to the Bumblebee daemon (otherwise it will exit the calling process). The broker support is limited to AF_UNIX, to non-datagram sockets (see bug 1066750), and to pathname addresses. Abstract addresses could theoretically be handled but there isn't currently a compelling reason to, and the broker very much assumes it's dealing with a C-style string referring to a filesystem path and not an arbitrary byte sequence (including NULs). At a higher level: If the GPU X server is remote then it won't work, but it won't work anyway because WebGL requires features that aren't supported by indirect GLX. If the GPU X server is local but the browser is inside a chroot, it will fail to connect unless /tmp/.X11-unix is bind-mounted into the chroot; hopefully this use case is not common. MozReview-Commit-ID: IvI2jYDRZZ2
security/sandbox/common/SandboxSettings.cpp
security/sandbox/linux/SandboxBrokerClient.cpp
security/sandbox/linux/SandboxBrokerClient.h
security/sandbox/linux/SandboxFilter.cpp
security/sandbox/linux/broker/SandboxBroker.cpp
security/sandbox/linux/broker/SandboxBroker.h
security/sandbox/linux/broker/SandboxBrokerCommon.cpp
security/sandbox/linux/broker/SandboxBrokerCommon.h
security/sandbox/linux/broker/SandboxBrokerPolicyFactory.cpp
--- a/security/sandbox/common/SandboxSettings.cpp
+++ b/security/sandbox/common/SandboxSettings.cpp
@@ -23,19 +23,17 @@ int GetEffectiveContentSandboxLevel() {
 #if !defined(NIGHTLY_BUILD) && (defined(XP_WIN) || defined(XP_MACOSX))
   if (level < 1) {
     level = 1;
   }
 #endif
 #ifdef XP_LINUX
   // Level 4 and up will break direct access to audio.
   // Bug 1438391: also VirtualGL lazily connecting to X.
-  if (level > 3 &&
-      (!Preferences::GetBool("media.cubeb.sandbox") ||
-       PR_GetEnv("VGL_ISACTIVE") != nullptr)) {
+  if (level > 3 && !Preferences::GetBool("media.cubeb.sandbox")) {
     level = 3;
   }
 #endif
 
   return level;
 }
 
 bool IsContentSandboxEnabled() {
--- a/security/sandbox/linux/SandboxBrokerClient.cpp
+++ b/security/sandbox/linux/SandboxBrokerClient.cpp
@@ -10,16 +10,17 @@
 
 #include <errno.h>
 #include <fcntl.h>
 #include <stdio.h>
 #include <string.h>
 #include <sys/socket.h>
 #include <sys/stat.h>
 #include <sys/types.h>
+#include <sys/un.h>
 #include <unistd.h>
 
 #include "mozilla/Assertions.h"
 #include "mozilla/NullPtr.h"
 #include "base/strings/safe_sprintf.h"
 
 namespace mozilla {
 
@@ -238,10 +239,42 @@ SandboxBrokerClient::Rmdir(const char* a
 
 int
 SandboxBrokerClient::Readlink(const char* aPath, void* aBuff, size_t aSize)
 {
   Request req = {SANDBOX_FILE_READLINK, 0, aSize};
   return DoCall(&req, aPath, nullptr, aBuff, false);
 }
 
+int
+SandboxBrokerClient::Connect(const sockaddr_un* aAddr, size_t aLen, int aType)
+{
+  static const size_t maxLen = sizeof(aAddr->sun_path);
+  const char* path = aAddr->sun_path;
+  const auto addrEnd = reinterpret_cast<const char*>(aAddr) + aLen;
+  // Ensure that the length covers the non-path member(s).
+  if (addrEnd < path) {
+    return -EINVAL;
+  }
+  // Unix domain only
+  if (aAddr->sun_family != AF_UNIX) {
+    return -EAFNOSUPPORT;
+  }
+  // How much of sun_path may be accessed?
+  auto bufLen = static_cast<size_t>(addrEnd - path);
+  if (bufLen > maxLen) {
+    bufLen = maxLen;
+  }
+  // Require null-termination.
+  const size_t pathLen = strnlen(path, bufLen);
+  if (pathLen == bufLen) {
+    return -ENAMETOOLONG;
+  }
+  // Abstract addresses aren't handled (yet?).
+  if (pathLen == 0) {
+    return -ECONNREFUSED;
+  }
+
+  const Request req = { SANDBOX_SOCKET_CONNECT, aType, 0 };
+  return DoCall(&req, path, nullptr, nullptr, true);
+}
+
 } // namespace mozilla
-
--- a/security/sandbox/linux/SandboxBrokerClient.h
+++ b/security/sandbox/linux/SandboxBrokerClient.h
@@ -17,16 +17,17 @@
 // returned by SandboxBroker::Create, passed to the child over IPC.
 //
 // The operations exposed here can be called from any thread and in
 // async signal handlers, like the corresponding system calls.  The
 // intended use is from a seccomp-bpf SIGSYS handler, to transparently
 // replace those syscalls, but they could also be used directly.
 
 struct stat;
+struct sockaddr_un;
 
 namespace mozilla {
 
 class SandboxBrokerClient final : private SandboxBrokerCommon {
  public:
   explicit SandboxBrokerClient(int aFd);
   ~SandboxBrokerClient();
 
@@ -37,16 +38,17 @@ class SandboxBrokerClient final : privat
   int Chmod(const char* aPath, int aMode);
   int Link(const char* aPath, const char* aPath2);
   int Mkdir(const char* aPath, int aMode);
   int Symlink(const char* aOldPath, const char* aNewPath);
   int Rename(const char* aOldPath, const char* aNewPath);
   int Unlink(const char* aPath);
   int Rmdir(const char* aPath);
   int Readlink(const char* aPath, void* aBuf, size_t aBufSize);
+  int Connect(const struct sockaddr_un* aAddr, size_t aLen, int aType);
 
  private:
   int mFileDesc;
 
   int DoCall(const Request* aReq,
              const char* aPath,
              const char* aPath2,
              void *aReponseBuff,
--- a/security/sandbox/linux/SandboxFilter.cpp
+++ b/security/sandbox/linux/SandboxFilter.cpp
@@ -28,16 +28,17 @@
 #include <linux/net.h>
 #include <linux/prctl.h>
 #include <linux/sched.h>
 #include <string.h>
 #include <sys/ioctl.h>
 #include <sys/mman.h>
 #include <sys/socket.h>
 #include <sys/syscall.h>
+#include <sys/un.h>
 #include <sys/utsname.h>
 #include <time.h>
 #include <unistd.h>
 #include <vector>
 #include <algorithm>
 
 #include "sandbox/linux/bpf_dsl/bpf_dsl.h"
 #include "sandbox/linux/system_headers/linux_seccomp.h"
@@ -617,16 +618,126 @@ private:
       MOZ_ASSERT(false);
       rv = -ENOSYS;
     }
 
     close(fd);
     return rv;
   }
 
+  // This just needs to return something to stand in for the
+  // unconnected socket until ConnectTrap, below, and keep track of
+  // the socket type somehow.  Half a socketpair *is* a socket, so it
+  // should result in minimal confusion in the caller.
+  static intptr_t FakeSocketTrapCommon(int domain, int type, int protocol) {
+    int fds[2];
+    // X11 client libs will still try to getaddrinfo() even for a
+    // local connection.  Also, WebRTC still has vestigial network
+    // code trying to do things in the content process.  Politely tell
+    // them no.
+    if (domain != AF_UNIX) {
+      return -EAFNOSUPPORT;
+    }
+    if (socketpair(domain, type, protocol, fds) != 0) {
+      return -errno;
+    }
+    close(fds[1]);
+    return fds[0];
+  }
+
+  static intptr_t FakeSocketTrap(ArgsRef aArgs, void* aux) {
+    return FakeSocketTrapCommon(static_cast<int>(aArgs.args[0]),
+                                static_cast<int>(aArgs.args[1]),
+                                static_cast<int>(aArgs.args[2]));
+  }
+
+  static intptr_t FakeSocketTrapLegacy(ArgsRef aArgs, void* aux) {
+    const auto innerArgs = reinterpret_cast<unsigned long*>(aArgs.args[1]);
+
+    return FakeSocketTrapCommon(static_cast<int>(innerArgs[0]),
+                                static_cast<int>(innerArgs[1]),
+                                static_cast<int>(innerArgs[2]));
+  }
+
+  static Maybe<int>
+  DoGetSockOpt(int fd, int optname) {
+    int optval;
+    socklen_t optlen = sizeof(optval);
+
+    if (getsockopt(fd, SOL_SOCKET, optname, &optval, &optlen) != 0) {
+      return Nothing();
+    }
+    MOZ_RELEASE_ASSERT(static_cast<size_t>(optlen) == sizeof(optval));
+    return Some(optval);
+  }
+
+  // Substitute the newly connected socket from the broker for the
+  // original socket.  This is meant to be used on a fd from
+  // FakeSocketTrap, above, but it should also work to simulate
+  // re-connect()ing a real connected socket.
+  //
+  // Warning: This isn't quite right if the socket is dup()ed, because
+  // other duplicates will still be the original socket, but hopefully
+  // nothing we're dealing with does that.
+  static intptr_t ConnectTrapCommon(SandboxBrokerClient* aBroker, int aFd,
+                                    const struct sockaddr_un* aAddr,
+                                    socklen_t aLen) {
+    if (aFd < 0) {
+      return -EBADF;
+    }
+    const auto maybeDomain = DoGetSockOpt(aFd, SO_DOMAIN);
+    if (!maybeDomain) {
+      return -errno;
+    }
+    if (*maybeDomain != AF_UNIX) {
+      return -EAFNOSUPPORT;
+    }
+    const auto maybeType = DoGetSockOpt(aFd, SO_TYPE);
+    if (!maybeType) {
+      return -errno;
+    }
+    const int oldFlags = fcntl(aFd, F_GETFL);
+    if (oldFlags == -1) {
+      return -errno;
+    }
+    const int newFd = aBroker->Connect(aAddr, aLen, *maybeType);
+    if (newFd < 0) {
+      return newFd;
+    }
+    if (fcntl(newFd, F_SETFL, oldFlags & O_NONBLOCK) != 0) {
+      close(newFd);
+      return -errno;
+    }
+    if (dup2(newFd, aFd) < 0) {
+      close(newFd);
+      return -errno;
+    }
+    close(newFd);
+    return 0;
+  }
+
+  static intptr_t ConnectTrap(ArgsRef aArgs, void* aux) {
+    typedef const struct sockaddr_un* AddrPtr;
+
+    return ConnectTrapCommon(static_cast<SandboxBrokerClient*>(aux),
+                             static_cast<int>(aArgs.args[0]),
+                             reinterpret_cast<AddrPtr>(aArgs.args[1]),
+                             static_cast<socklen_t>(aArgs.args[2]));
+  }
+
+  static intptr_t ConnectTrapLegacy(ArgsRef aArgs, void* aux) {
+    const auto innerArgs = reinterpret_cast<unsigned long*>(aArgs.args[1]);
+    typedef const struct sockaddr_un* AddrPtr;
+
+    return ConnectTrapCommon(static_cast<SandboxBrokerClient*>(aux),
+                             static_cast<int>(innerArgs[0]),
+                             reinterpret_cast<AddrPtr>(innerArgs[1]),
+                             static_cast<socklen_t>(innerArgs[2]));
+  }
+
 public:
   ContentSandboxPolicy(SandboxBrokerClient* aBroker,
                        ContentProcessSandboxParams&& aParams)
     : mBroker(aBroker)
     , mParams(Move(aParams))
     , mAllowSysV(PR_GetEnv("MOZ_SANDBOX_ALLOW_SYSV") != nullptr)
     { }
 
@@ -649,35 +760,36 @@ public:
           return Some(Trap(SocketpairUnpackTrap, nullptr));
         }
         // Otherwise, we can't filter the args if the platform passes
         // them by pointer.
         return Some(Allow());
       }
       Arg<int> domain(0), type(1);
       return Some(If(domain == AF_UNIX,
-                     Switch(type & ~SOCK_CLOEXEC)
+                     Switch(type & ~(SOCK_CLOEXEC | SOCK_NONBLOCK))
                      .Case(SOCK_STREAM, Allow())
                      .Case(SOCK_SEQPACKET, Allow())
                      .Case(SOCK_DGRAM, Trap(SocketpairDatagramTrap, nullptr))
                      .Default(InvalidSyscall()))
                   .Else(InvalidSyscall()));
     }
 
 #ifdef ANDROID
     case SYS_SOCKET:
       return Some(Error(EACCES));
 #else // #ifdef DESKTOP
-    case SYS_SOCKET: // DANGEROUS
-      // Some things try to get a socket but can work without one,
-      // like sctp_userspace_get_mtu_from_ifn in WebRTC, so this is
-      // silently disallowed.
-      return Some(AllowBelowLevel(4, Error(EACCES)));
-    case SYS_CONNECT: // DANGEROUS
-      return Some(AllowBelowLevel(4));
+    case SYS_SOCKET: { // DANGEROUS
+      const auto trapFn = aHasArgs ? FakeSocketTrap : FakeSocketTrapLegacy;
+      return Some(AllowBelowLevel(4, Trap(trapFn, nullptr)));
+    }
+    case SYS_CONNECT: { // DANGEROUS
+      const auto trapFn = aHasArgs ? ConnectTrap : ConnectTrapLegacy;
+      return Some(AllowBelowLevel(4, Trap(trapFn, mBroker)));
+    }
     case SYS_RECV:
     case SYS_SEND:
     case SYS_GETSOCKOPT:
     case SYS_SETSOCKOPT:
     case SYS_GETSOCKNAME:
     case SYS_GETPEERNAME:
     case SYS_SHUTDOWN:
       return Some(Allow());
@@ -936,16 +1048,17 @@ public:
       return Allow();
 #endif
 
     case __NR_getrusage:
     case __NR_times:
       return Allow();
 
     case __NR_dup:
+    case __NR_dup2: // See ConnectTrapCommon
       return Allow();
 
     CASES_FOR_getuid:
     CASES_FOR_getgid:
     CASES_FOR_geteuid:
     CASES_FOR_getegid:
       return Allow();
 
--- a/security/sandbox/linux/broker/SandboxBroker.cpp
+++ b/security/sandbox/linux/broker/SandboxBroker.cpp
@@ -10,16 +10,17 @@
 #include "SandboxBrokerUtils.h"
 
 #include <dirent.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <sys/socket.h>
 #include <sys/stat.h>
 #include <sys/types.h>
+#include <sys/un.h>
 #include <unistd.h>
 
 #ifdef XP_LINUX
 #include <sys/prctl.h>
 #endif
 
 #include "base/string_util.h"
 #include "mozilla/Assertions.h"
@@ -500,16 +501,55 @@ DoLink(const char* aPath, const char* aP
     return link(aPath, aPath2);
   }
   if (aOper == SandboxBrokerCommon::Operation::SANDBOX_FILE_SYMLINK) {
     return symlink(aPath, aPath2);
   }
   MOZ_CRASH("SandboxBroker: Unknown link operation");
 }
 
+static int
+DoConnect(const char* aPath, size_t aLen, int aType)
+{
+  // Deny SOCK_DGRAM for the same reason it's denied for socketpair.
+  if (aType != SOCK_STREAM && aType != SOCK_SEQPACKET) {
+    errno = EACCES;
+    return -1;
+  }
+  // Ensure that the address is a pathname.  (An empty string
+  // resulting from an abstract address probably shouldn't have made
+  // it past the policy check, but check explicitly just in case.)
+  if (aPath[0] == '\0') {
+    errno = ECONNREFUSED;
+    return -1;
+  }
+
+  // Try to copy the name into a normal-sized sockaddr_un, with
+  // null-termination:
+  struct sockaddr_un sun;
+  memset(&sun, 0, sizeof(sun));
+  sun.sun_family = AF_UNIX;
+  if (aLen + 1 > sizeof(sun.sun_path)) {
+    errno = ENAMETOOLONG;
+    return -1;
+  }
+  memcpy(&sun.sun_path, aPath, aLen);
+
+  // Finally, the actual socket connection.
+  const int fd = socket(AF_UNIX, aType | SOCK_CLOEXEC, 0);
+  if (fd < 0) {
+    return -1;
+  }
+  if (connect(fd, reinterpret_cast<struct sockaddr*>(&sun), sizeof(sun)) < 0) {
+    close(fd);
+    return -1;
+  }
+  return fd;
+}
+
 size_t
 SandboxBroker::ConvertToRealPath(char* aPath, size_t aBufSize, size_t aPathLen)
 {
   if (strstr(aPath, "..") != nullptr) {
     char* result = realpath(aPath, nullptr);
     if (result != nullptr) {
       base::strlcpy(aPath, result, aBufSize);
       free(result);
@@ -742,31 +782,31 @@ SandboxBroker::ThreadMain(void)
 
       // First string is guaranteed to be 0-terminated.
       pathLen = first_len;
 
       // Look up the first pathname but first translate relative paths.
       pathLen = ConvertToRealPath(pathBuf, sizeof(pathBuf), pathLen);
       perms = mPolicy->Lookup(nsDependentCString(pathBuf, pathLen));
 
-      // We don't have read permissions on the requested dir.
-      if (!(perms & MAY_READ)) {
-          // Was it a tempdir that we can remap?
-          pathLen = RemapTempDirs(pathBuf, sizeof(pathBuf), pathLen);
-          perms = mPolicy->Lookup(nsDependentCString(pathBuf, pathLen));
-          if (!(perms & MAY_READ)) {
-            // Did we arrive from a symlink in a path that is not writable?
-            // Then try to figure out the original path and see if that is
-            // readable. Work on the original path, this reverses
-            // ConvertToRealPath above.
-            int symlinkPerms = SymlinkPermissions(recvBuf, first_len);
-            if (symlinkPerms > 0) {
-              perms = symlinkPerms;
-            }
+      // We don't have permissions on the requested dir.
+      if (!perms) {
+        // Was it a tempdir that we can remap?
+        pathLen = RemapTempDirs(pathBuf, sizeof(pathBuf), pathLen);
+        perms = mPolicy->Lookup(nsDependentCString(pathBuf, pathLen));
+        if (!perms) {
+          // Did we arrive from a symlink in a path that is not writable?
+          // Then try to figure out the original path and see if that is
+          // readable. Work on the original path, this reverses
+          // ConvertToRealPath above.
+          int symlinkPerms = SymlinkPermissions(recvBuf, first_len);
+          if (symlinkPerms > 0) {
+            perms = symlinkPerms;
           }
+        }
       }
 
       // Same for the second path.
       pathLen2 = strnlen(pathBuf2, kMaxPathLen);
       if (pathLen2 > 0) {
         // Force 0 termination.
         pathBuf2[pathLen2] = '\0';
         pathLen2 = ConvertToRealPath(pathBuf2, sizeof(pathBuf2), pathLen2);
@@ -947,16 +987,29 @@ SandboxBroker::ThreadMain(void)
             ios[1].iov_len = respSize;
           } else {
             resp.mError = -errno;
           }
         } else {
           AuditDenial(req.mOp, req.mFlags, perms, pathBuf);
         }
         break;
+
+      case SANDBOX_SOCKET_CONNECT:
+        if (permissive || (perms & MAY_CONNECT) != 0) {
+          openedFd = DoConnect(pathBuf, pathLen, req.mFlags);
+          if (openedFd >= 0) {
+            resp.mError = 0;
+          } else {
+            resp.mError = -errno;
+          }
+        } else {
+          AuditDenial(req.mOp, req.mFlags, perms, pathBuf);
+        }
+        break;
       }
     } else {
       MOZ_ASSERT(perms == 0);
       AuditDenial(req.mOp, req.mFlags, perms, pathBuf);
     }
 
     const size_t numIO = ios[1].iov_len > 0 ? 2 : 1;
     const ssize_t sent = SendWithFd(respfd, ios, numIO, openedFd);
--- a/security/sandbox/linux/broker/SandboxBroker.h
+++ b/security/sandbox/linux/broker/SandboxBroker.h
@@ -48,16 +48,18 @@ class SandboxBroker final
     // This flag is for testing policy changes -- when the client is
     // used with the seccomp-bpf integration, an access to this file
     // will invoke a crash dump with the context of the syscall.
     // (This overrides all other flags.)
     CRASH_INSTEAD = 1 << 4,
     // Applies to everything below this path, including subdirs created
     // at runtime
     RECURSIVE     = 1 << 5,
+    // Allow Unix-domain socket connections to a path
+    MAY_CONNECT   = 1 << 6,
   };
   // Bitwise operations on enum values return ints, so just use int in
   // the hash table type (and below) to avoid cluttering code with casts.
   typedef nsDataHashtable<nsCStringHashKey, int> PathPermissionMap;
 
   class Policy {
     PathPermissionMap mMap;
   public:
--- a/security/sandbox/linux/broker/SandboxBrokerCommon.cpp
+++ b/security/sandbox/linux/broker/SandboxBrokerCommon.cpp
@@ -36,17 +36,18 @@ const char* SandboxBrokerCommon::Operati
   "stat",
   "chmod",
   "link",
   "symlink",
   "mkdir",
   "rename",
   "rmdir",
   "unlink",
-  "readlink"
+  "readlink",
+  "connect"
 };
 
 /* static */ ssize_t
 SandboxBrokerCommon::RecvWithFd(int aFd, const iovec* aIO, size_t aNumIO,
                                     int* aPassedFdPtr)
 {
   struct msghdr msg = {};
   msg.msg_iov = const_cast<iovec*>(aIO);
--- a/security/sandbox/linux/broker/SandboxBrokerCommon.h
+++ b/security/sandbox/linux/broker/SandboxBrokerCommon.h
@@ -32,23 +32,25 @@ public:
     SANDBOX_FILE_CHMOD,
     SANDBOX_FILE_LINK,
     SANDBOX_FILE_SYMLINK,
     SANDBOX_FILE_MKDIR,
     SANDBOX_FILE_RENAME,
     SANDBOX_FILE_RMDIR,
     SANDBOX_FILE_UNLINK,
     SANDBOX_FILE_READLINK,
+    SANDBOX_SOCKET_CONNECT,
   };
   // String versions of the above
   static const char* OperationDescription[];
 
   struct Request {
     Operation mOp;
     // For open, flags; for access, "mode"; for stat, O_NOFOLLOW for lstat.
+    // For connect, the socket type.
     int mFlags;
     // Size of return value buffer, if any
     size_t mBufSize;
     // The rest of the packet is the pathname.
     // SCM_RIGHTS for response socket attached.
   };
 
   struct Response {
--- a/security/sandbox/linux/broker/SandboxBrokerPolicyFactory.cpp
+++ b/security/sandbox/linux/broker/SandboxBrokerPolicyFactory.cpp
@@ -365,16 +365,31 @@ SandboxBrokerPolicyFactory::SandboxBroke
     if (bloatLen >= 4) {
       nsAutoCString bloatStr(bloatLog);
       bloatStr.Truncate(bloatLen - 4);
       policy->AddPrefix(rdwrcr, bloatStr.get());
     }
   }
 #endif
 
+  // Allow Primus to contact the Bumblebee daemon to manage GPU
+  // switching on NVIDIA Optimus systems.
+  const char* bumblebeeSocket = PR_GetEnv("BUMBLEBEE_SOCKET");
+  if (bumblebeeSocket == nullptr) {
+    bumblebeeSocket = "/var/run/bumblebee.socket";
+  }
+  policy->AddPath(SandboxBroker::MAY_CONNECT, bumblebeeSocket);
+
+  // Allow local X11 connections, for Primus and VirtualGL to contact
+  // the secondary X server.
+  policy->AddPrefix(SandboxBroker::MAY_CONNECT, "/tmp/.X11-unix/X");
+  if (const auto xauth = PR_GetEnv("XAUTHORITY")) {
+    policy->AddPath(rdonly, xauth);
+  }
+
   mCommonContentPolicy.reset(policy);
 #endif
 }
 
 #ifdef MOZ_CONTENT_SANDBOX
 UniquePtr<SandboxBroker::Policy>
 SandboxBrokerPolicyFactory::GetContentPolicy(int aPid, bool aFileProcess)
 {
@@ -502,20 +517,19 @@ SandboxBrokerPolicyFactory::GetContentPo
       // case we know it already exists).  See bug 1335329.
       nsPrintfCString pulsePath("%s/pulse", userDir);
       policy->AddPath(rdonly, pulsePath.get());
     }
   }
 #endif // MOZ_WIDGET_GTK
 
   if (allowPulse) {
-    // See bug 1384986 comment #1.
-    if (const auto xauth = PR_GetEnv("XAUTHORITY")) {
-      policy->AddPath(rdonly, xauth);
-    }
+    // PulseAudio also needs access to read the $XAUTHORITY file (see
+    // bug 1384986 comment #1), but that's already allowed for hybrid
+    // GPU drivers (see above).
     policy->AddPath(rdonly, "/var/lib/dbus/machine-id");
   }
 
   // Return the common policy.
   policy->FixRecursivePermissions();
   return policy;
 }