From d47b241c726dff8f06319b9643a3574b0d299d09 Mon Sep 17 00:00:00 2001
From: Simon McVittie <smcv@collabora.com>
Date: Mon, 6 Jan 2020 19:00:44 +0000
Subject: [PATCH] wrap: Tell child process to take out its own lock if
 necessary

Non-OFD locks don't propagate across fork(), and bwrap needs to clone()
itself (which behaves like fork() in this respect) to separate itself
into a parent outside the container and a child inside the container.

This change adds a weak dependency on Linux 3.15. If we run on an older
version, everything should still *work*, but there will be a short
period of time during which we have already decided to use the runtime,
but it is not locked (and in particular not protected from deletion).

Fixes: 959fd338 "wrap: Take out a lock on the container's runtime for the duration"
Signed-off-by: Simon McVittie <smcv@collabora.com>
---
 src/bwrap.c |  3 ++-
 src/wrap.c  | 45 ++++++++++++++++++++++++++++++++++++++-------
 2 files changed, 40 insertions(+), 8 deletions(-)

diff --git a/src/bwrap.c b/src/bwrap.c
index dcd2b11b9..7bdef02f9 100644
--- a/src/bwrap.c
+++ b/src/bwrap.c
@@ -226,7 +226,8 @@ pv_bwrap_bind_usr (FlatpakBwrap *bwrap,
     {
       if (g_str_has_prefix (member, "lib")
           || g_str_equal (member, "bin")
-          || g_str_equal (member, "sbin"))
+          || g_str_equal (member, "sbin")
+          || g_str_equal (member, ".ref"))
         {
           dest = g_build_filename (mount_point, member, NULL);
 
diff --git a/src/wrap.c b/src/wrap.c
index e43dce780..7d0c86449 100644
--- a/src/wrap.c
+++ b/src/wrap.c
@@ -4,7 +4,7 @@
  * Contains code taken from Flatpak.
  *
  * Copyright © 2014-2019 Red Hat, Inc
- * Copyright © 2017-2019 Collabora Ltd.
+ * Copyright © 2017-2020 Collabora Ltd.
  *
  * SPDX-License-Identifier: LGPL-2.1-or-later
  *
@@ -2568,15 +2568,46 @@ main (int argc,
    * can't do that without breaking gameoverlayrender.so's assumptions. */
   if (runtime_lock != NULL)
     {
-      g_autofree gchar *fd_str = NULL;
-      int fd = pv_bwrap_lock_steal_fd (runtime_lock);
-
-      fd_str = g_strdup_printf ("%d", fd);
-      flatpak_bwrap_add_fd (bwrap, fd);
       flatpak_bwrap_add_args (bwrap,
                               "/run/pressure-vessel/bin/pressure-vessel-with-lock",
                               "--subreaper",
-                              "--fd", fd_str,
+                              NULL);
+
+      if (pv_bwrap_lock_is_ofd (runtime_lock))
+        {
+          int fd = pv_bwrap_lock_steal_fd (runtime_lock);
+          g_autofree gchar *fd_str = NULL;
+
+          g_debug ("Passing lock fd %d down to with-lock", fd);
+          flatpak_bwrap_add_fd (bwrap, fd);
+          fd_str = g_strdup_printf ("%d", fd);
+          flatpak_bwrap_add_args (bwrap,
+                                  "--fd", fd_str,
+                                  NULL);
+        }
+      else
+        {
+          /*
+           * We were unable to take out an open file descriptor lock,
+           * so it will be released on fork(). Tell the with-lock process
+           * to take out its own compatible lock instead. There will be
+           * a short window during which we have lost our lock but the
+           * with-lock process has not taken its lock - that's unavoidable
+           * if we want to use exec() to replace ourselves with the
+           * container.
+           *
+           * pv_bwrap_bind_usr() arranges for /.ref to either be a
+           * symbolic link to /usr/.ref which is the runtime_lock
+           * (if opt_runtime is a merged /usr), or the runtime_lock
+           * itself (otherwise).
+           */
+          g_debug ("Telling process in container to lock /.ref");
+          flatpak_bwrap_add_args (bwrap,
+                                  "--lock-file", "/.ref",
+                                  NULL);
+        }
+
+      flatpak_bwrap_add_args (bwrap,
                               "--",
                               NULL);
     }
-- 
GitLab