import CS git flatpak-1.12.9-4.el8_10

This commit is contained in:
AlmaLinux RelEng Bot 2026-05-28 14:34:56 -04:00
parent e9a45618bd
commit 1e0192aaea
34 changed files with 10075 additions and 1 deletions

View File

@ -0,0 +1,61 @@
From fbe7a80a9e49ed364af2d7caf5902e3088f54587 Mon Sep 17 00:00:00 2001
From: Sebastian Wick <sebastian.wick@redhat.com>
Date: Fri, 6 Feb 2026 17:56:21 +0100
Subject: [PATCH] flatpak-bwrap: Add dup-ing variant
flatpak_bwrap_add_args_data_fd_dup
---
common/flatpak-bwrap-private.h | 5 +++++
common/flatpak-bwrap.c | 20 ++++++++++++++++++++
2 files changed, 25 insertions(+)
diff --git a/common/flatpak-bwrap-private.h b/common/flatpak-bwrap-private.h
index 207d23a0..64bd26b6 100644
--- a/common/flatpak-bwrap-private.h
+++ b/common/flatpak-bwrap-private.h
@@ -62,6 +62,11 @@ void flatpak_bwrap_append_bwrap (FlatpakBwrap *bwrap,
FlatpakBwrap *other); /* Steals the fds */
void flatpak_bwrap_append_args (FlatpakBwrap *bwrap,
GPtrArray *other_array);
+gboolean flatpak_bwrap_add_args_data_fd_dup (FlatpakBwrap *bwrap,
+ const char *op,
+ int fd,
+ const char *path_optional,
+ GError **error);
void flatpak_bwrap_add_args_data_fd (FlatpakBwrap *bwrap,
const char *op,
int fd,
diff --git a/common/flatpak-bwrap.c b/common/flatpak-bwrap.c
index cda0dbfb..1694ffd4 100644
--- a/common/flatpak-bwrap.c
+++ b/common/flatpak-bwrap.c
@@ -141,6 +141,26 @@ flatpak_bwrap_add_fd (FlatpakBwrap *bwrap,
g_array_append_val (bwrap->fds, fd);
}
+gboolean
+flatpak_bwrap_add_args_data_fd_dup (FlatpakBwrap *bwrap,
+ const char *op,
+ int fd,
+ const char *path_optional,
+ GError **error)
+{
+ glnx_autofd int fd_dup = -1;
+
+ fd_dup = fcntl (fd, F_DUPFD_CLOEXEC, 3);
+ if (fd_dup < 0)
+ return glnx_throw_errno_prefix (error, "Failed to dup fd %d", fd);
+
+ flatpak_bwrap_add_args_data_fd (bwrap,
+ op,
+ g_steal_fd (&fd_dup),
+ path_optional);
+ return TRUE;
+}
+
void
flatpak_bwrap_add_arg_printf (FlatpakBwrap *bwrap, const char *format, ...)
{
--
2.54.0

View File

@ -0,0 +1,34 @@
From 5096a974bcba5dfeddaea197b2ac63f558100858 Mon Sep 17 00:00:00 2001
From: Simon McVittie <smcv@collabora.com>
Date: Wed, 8 Apr 2026 09:44:55 +0100
Subject: [PATCH] run: Mount original app on /run/parent/app when using
--app-path=""
Before addressing CVE-2026-34078, we would always mount the original app
*somewhere*, either /app (in the normal case) or /run/parent/app (when
using a custom or empty /app for the subsandbox). The empty-app case
regressed during the fix for CVE-2026-34078; bring back previous behaviour.
Fixes: ac62ebe3 "run: Use O_PATH fds for the runtime and app deploy directories"
Resolves: https://github.com/flatpak/flatpak/issues/6568
Signed-off-by: Simon McVittie <smcv@collabora.com>
(cherry picked from commit fde4716f67b6620da57fd74481694eb58795d589)
---
common/flatpak-run.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/common/flatpak-run.c b/common/flatpak-run.c
index 4ffc5fa3..a80d0050 100644
--- a/common/flatpak-run.c
+++ b/common/flatpak-run.c
@@ -4476,6 +4476,7 @@ flatpak_run_app (FlatpakDecomposed *app_ref,
}
else if (custom_app_fd == FLATPAK_RUN_APP_DEPLOY_APP_EMPTY)
{
+ original_app_target_path = "/run/parent/app";
app_fd = -1;
app_files = NULL;
}
--
2.54.0

View File

@ -0,0 +1,31 @@
From d05aeb399ba56e46ab3b7b4eed3669ab98a0df56 Mon Sep 17 00:00:00 2001
From: Alberto Garcia <berto@igalia.com>
Date: Wed, 8 Apr 2026 19:28:32 +0200
Subject: [PATCH] portal: update max_fd after creating the instance ID pipe
fd_map_remap_fd() is called several times after this, and without this
change it can allocate a target fd that collides with instance_id_fd.
Only the write end of the pipe needs to be considered because that's
the one passed to the child.
Closes: https://github.com/flatpak/flatpak/issues/6570
---
portal/flatpak-portal.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/portal/flatpak-portal.c b/portal/flatpak-portal.c
index a4378f77..3110071c 100644
--- a/portal/flatpak-portal.c
+++ b/portal/flatpak-portal.c
@@ -1075,6 +1075,7 @@ handle_spawn (PortalFlatpak *object,
g_ptr_array_add (flatpak_argv, g_strdup_printf ("--instance-id-fd=%d", pipe_fds[1]));
child_setup_data.instance_id_fd = pipe_fds[1];
+ max_fd = MAX(max_fd, pipe_fds[1]);
}
if (devel)
--
2.54.0

View File

@ -0,0 +1,122 @@
From 559c415b517c2533cf6b7294fb3f4cf99ed8e2ae Mon Sep 17 00:00:00 2001
From: Sebastian Wick <sebastian.wick@redhat.com>
Date: Wed, 8 Apr 2026 17:47:48 +0200
Subject: [PATCH] run: Fix fd tracking in flatpak_run_add_app_info_args
Calls to flatpak_bwrap_add_args_data_fd take ownership over the fd they
take. Closing them while they are still in the bwrap struct will abort
later when the bwrap struct gets freed and it tries to close the already
closed fd.
Fix this by using glnx_autofd and g_steal_fd.
---
common/flatpak-run.c | 34 +++++++++++++++++-----------------
1 file changed, 17 insertions(+), 17 deletions(-)
diff --git a/common/flatpak-run.c b/common/flatpak-run.c
index a80d0050..a11e26d6 100644
--- a/common/flatpak-run.c
+++ b/common/flatpak-run.c
@@ -2602,13 +2602,17 @@ flatpak_run_add_app_info_args (FlatpakBwrap *bwrap,
gboolean build,
gboolean devel,
char **app_info_path_out,
- int instance_id_fd,
+ int instance_id_fd_arg,
char **instance_id_host_dir_out,
GError **error)
{
g_autofree char *info_path = NULL;
g_autofree char *bwrapinfo_path = NULL;
- int fd, fd2, fd3;
+ glnx_autofd int fd1 = -1;
+ glnx_autofd int fd2 = -1;
+ glnx_autofd int fd3 = -1;
+ int info_fd;
+ glnx_autofd int instance_id_fd = instance_id_fd_arg;
g_autoptr(GKeyFile) keyfile = NULL;
g_autofree char *runtime_path = NULL;
const char *group;
@@ -2754,8 +2758,8 @@ flatpak_run_add_app_info_args (FlatpakBwrap *bwrap,
This way even if the bind-mount is unmounted we can find the real data.
*/
- fd = open (info_path, O_RDONLY);
- if (fd == -1)
+ fd1 = info_fd = open (info_path, O_RDONLY);
+ if (fd1 == -1)
{
int errsv = errno;
g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errsv),
@@ -2766,7 +2770,6 @@ flatpak_run_add_app_info_args (FlatpakBwrap *bwrap,
fd2 = open (info_path, O_RDONLY);
if (fd2 == -1)
{
- close (fd);
int errsv = errno;
g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errsv),
_("Failed to open flatpak-info file: %s"), g_strerror (errsv));
@@ -2774,9 +2777,9 @@ flatpak_run_add_app_info_args (FlatpakBwrap *bwrap,
}
flatpak_bwrap_add_args_data_fd (bwrap,
- "--file", fd, "/.flatpak-info");
+ "--file", g_steal_fd (&fd1), "/.flatpak-info");
flatpak_bwrap_add_args_data_fd (bwrap,
- "--ro-bind-data", fd2, "/.flatpak-info");
+ "--ro-bind-data", g_steal_fd (&fd2), "/.flatpak-info");
/* Tell the application that it's running under Flatpak in a generic way. */
flatpak_bwrap_add_args (bwrap,
@@ -2793,8 +2796,6 @@ flatpak_run_add_app_info_args (FlatpakBwrap *bwrap,
fd3 = open (bwrapinfo_path, O_RDWR | O_CREAT, 0644);
if (fd3 == -1)
{
- close (fd);
- close (fd2);
int errsv = errno;
g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errsv),
_("Failed to open bwrapinfo.json file: %s"), g_strerror (errsv));
@@ -2817,10 +2818,6 @@ flatpak_run_add_app_info_args (FlatpakBwrap *bwrap,
if (errsv == EINTR)
continue;
- close (fd);
- close (fd2);
- close (fd3);
-
g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errsv),
_("Failed to write to instance id fd: %s"), g_strerror (errsv));
return FALSE;
@@ -2830,13 +2827,14 @@ flatpak_run_add_app_info_args (FlatpakBwrap *bwrap,
instance_id_size -= bytes_written;
}
- close (instance_id_fd);
+ /* explicitly close this as soon as we're done to notify the other side */
+ g_clear_fd (&instance_id_fd, NULL);
}
- flatpak_bwrap_add_args_data_fd (bwrap, "--info-fd", fd3, NULL);
+ flatpak_bwrap_add_args_data_fd (bwrap, "--info-fd", g_steal_fd (&fd3), NULL);
if (app_info_path_out != NULL)
- *app_info_path_out = g_strdup_printf ("/proc/self/fd/%d", fd);
+ *app_info_path_out = g_strdup_printf ("/proc/self/fd/%d", info_fd);
if (instance_id_host_dir_out != NULL)
*instance_id_host_dir_out = g_steal_pointer (&instance_id_host_dir);
@@ -4697,7 +4695,9 @@ flatpak_run_app (FlatpakDecomposed *app_ref,
app_id, flatpak_decomposed_get_branch (app_ref),
runtime_ref, app_id_dir, app_context, extra_context,
sandboxed, FALSE, flags & FLATPAK_RUN_FLAG_DEVEL,
- &app_info_path, instance_id_fd, &instance_id_host_dir,
+ &app_info_path,
+ g_steal_fd (&instance_id_fd),
+ &instance_id_host_dir,
error))
return FALSE;
--
2.54.0

View File

@ -0,0 +1,28 @@
From 9462b0f980aa0718a41601aaa87125f545c424eb Mon Sep 17 00:00:00 2001
From: Sebastian Wick <sebastian.wick@redhat.com>
Date: Wed, 8 Apr 2026 18:15:42 +0200
Subject: [PATCH] utils: Improve error message when passing an FD numer which
is not a FD
---
common/flatpak-utils.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/common/flatpak-utils.c b/common/flatpak-utils.c
index c2015941..938d8131 100644
--- a/common/flatpak-utils.c
+++ b/common/flatpak-utils.c
@@ -9205,8 +9205,8 @@ flatpak_parse_fd (const char *fd_string,
fd = (int) parsed;
- if (!glnx_fstat (fd, &stbuf, error))
- return -1;
+ if (!glnx_fstat (fd, &stbuf, NULL))
+ return glnx_fd_throw (error, "Not an open file descriptor: %d", fd);
return fd;
}
--
2.54.0

View File

@ -0,0 +1,32 @@
From 9e145899fe0a28ce79d905b5efabc253b5c8050b Mon Sep 17 00:00:00 2001
From: Sebastian Wick <sebastian.wick@redhat.com>
Date: Wed, 8 Apr 2026 18:14:19 +0200
Subject: [PATCH] run: Do not close --bind/--ro-bind
---
app/flatpak-builtins-run.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/app/flatpak-builtins-run.c b/app/flatpak-builtins-run.c
index 7edd7326..701e3454 100644
--- a/app/flatpak-builtins-run.c
+++ b/app/flatpak-builtins-run.c
@@ -82,6 +82,7 @@ option_bind_fd_cb (const char *option_name,
return glnx_throw (error, "File descriptors 0, 1, 2 are reserved");
g_array_append_val (opt_bind_fds, fd);
+ fd = -1; /* ownership transferred to GArray */
return TRUE;
}
@@ -101,6 +102,7 @@ option_ro_bind_fd_cb (const char *option_name,
return glnx_throw (error, "File descriptors 0, 1, 2 are reserved");
g_array_append_val (opt_ro_bind_fds, fd);
+ fd = -1; /* ownership transferred to GArray */
return TRUE;
}
--
2.54.0

View File

@ -0,0 +1,95 @@
From 8708ab052d884cc84e6e71ea012e8d11eba41982 Mon Sep 17 00:00:00 2001
From: Sebastian Wick <sebastian.wick@redhat.com>
Date: Wed, 8 Apr 2026 18:19:20 +0200
Subject: [PATCH] run: Use the same FD validation for all FD options
---
app/flatpak-builtins-run.c | 63 ++++++++++++++++++++++++++++++++++++--
1 file changed, 60 insertions(+), 3 deletions(-)
diff --git a/app/flatpak-builtins-run.c b/app/flatpak-builtins-run.c
index 701e3454..d97dc0bf 100644
--- a/app/flatpak-builtins-run.c
+++ b/app/flatpak-builtins-run.c
@@ -106,6 +106,63 @@ option_ro_bind_fd_cb (const char *option_name,
return TRUE;
}
+static gboolean
+opt_instance_id_fd_cb (const char *option_name,
+ const char *value,
+ gpointer data,
+ GError **error)
+{
+ glnx_autofd int fd = -1;
+
+ fd = flatpak_parse_fd (value, error);
+ if (fd < 0)
+ return FALSE;
+
+ if (fd < 3)
+ return glnx_throw (error, "File descriptors 0, 1, 2 are reserved");
+
+ opt_instance_id_fd = g_steal_fd (&fd);
+ return TRUE;
+}
+
+static gboolean
+opt_app_fd_cb (const char *option_name,
+ const char *value,
+ gpointer data,
+ GError **error)
+{
+ glnx_autofd int fd = -1;
+
+ fd = flatpak_parse_fd (value, error);
+ if (fd < 0)
+ return FALSE;
+
+ if (fd < 3)
+ return glnx_throw (error, "File descriptors 0, 1, 2 are reserved");
+
+ opt_app_fd = g_steal_fd (&fd);
+ return TRUE;
+}
+
+static gboolean
+opt_usr_fd_cb (const char *option_name,
+ const char *value,
+ gpointer data,
+ GError **error)
+{
+ glnx_autofd int fd = -1;
+
+ fd = flatpak_parse_fd (value, error);
+ if (fd < 0)
+ return FALSE;
+
+ if (fd < 3)
+ return glnx_throw (error, "File descriptors 0, 1, 2 are reserved");
+
+ opt_usr_fd = g_steal_fd (&fd);
+ return TRUE;
+}
+
static GOptionEntry options[] = {
{ "arch", 0, 0, G_OPTION_ARG_STRING, &opt_arch, N_("Arch to use"), N_("ARCH") },
{ "command", 0, 0, G_OPTION_ARG_STRING, &opt_command, N_("Command to run"), N_("COMMAND") },
@@ -130,11 +187,11 @@ static GOptionEntry options[] = {
{ "parent-pid", 0, 0, G_OPTION_ARG_INT, &opt_parent_pid, N_("Use PID as parent pid for sharing namespaces"), N_("PID") },
{ "parent-expose-pids", 0, 0, G_OPTION_ARG_NONE, &opt_parent_expose_pids, N_("Make processes visible in parent namespace"), NULL },
{ "parent-share-pids", 0, 0, G_OPTION_ARG_NONE, &opt_parent_share_pids, N_("Share process ID namespace with parent"), NULL },
- { "instance-id-fd", 0, 0, G_OPTION_ARG_INT, &opt_instance_id_fd, N_("Write the instance ID to the given file descriptor"), NULL },
+ { "instance-id-fd", 0, 0, G_OPTION_ARG_CALLBACK, &opt_instance_id_fd_cb, N_("Write the instance ID to the given file descriptor"), NULL },
{ "app-path", 0, 0, G_OPTION_ARG_FILENAME, &opt_app_path, N_("Use PATH instead of the app's /app"), N_("PATH") },
- { "app-fd", 0, 0, G_OPTION_ARG_INT, &opt_app_fd, N_("Use FD instead of the app's /app"), N_("FD") },
+ { "app-fd", 0, 0, G_OPTION_ARG_CALLBACK, &opt_app_fd_cb, N_("Use FD instead of the app's /app"), N_("FD") },
{ "usr-path", 0, 0, G_OPTION_ARG_FILENAME, &opt_usr_path, N_("Use PATH instead of the runtime's /usr"), N_("PATH") },
- { "usr-fd", 0, 0, G_OPTION_ARG_INT, &opt_usr_fd, N_("Use FD instead of the runtime's /usr"), N_("FD") },
+ { "usr-fd", 0, 0, G_OPTION_ARG_INT, &opt_usr_fd_cb, N_("Use FD instead of the runtime's /usr"), N_("FD") },
{ "bind-fd", 0, 0, G_OPTION_ARG_CALLBACK | G_OPTION_FLAG_HIDDEN, &option_bind_fd_cb, N_("Bind mount the file or directory referred to by FD to its canonicalized path"), N_("FD") },
{ "ro-bind-fd", 0, 0, G_OPTION_ARG_CALLBACK | G_OPTION_FLAG_HIDDEN, &option_ro_bind_fd_cb, N_("Bind mount the file or directory referred to by FD read-only to its canonicalized path"), N_("FD") },
{ NULL }
--
2.54.0

View File

@ -0,0 +1,101 @@
From 3469414dbb068f770b594e6a3f47cd143aff6172 Mon Sep 17 00:00:00 2001
From: Sebastian Wick <sebastian.wick@redhat.com>
Date: Wed, 8 Apr 2026 21:59:19 +0200
Subject: [PATCH] run: Add bind-fd and ro-bind-fd binds after all other binds
This is only moving it a bit down because
flatpak_run_add_environment_args still adds a whole bunch of binds which
then can over-mount the user requested binds (bind-fd, ro-bind-fd).
---
common/flatpak-run.c | 68 ++++++++++++++++++++++----------------------
1 file changed, 34 insertions(+), 34 deletions(-)
diff --git a/common/flatpak-run.c b/common/flatpak-run.c
index a11e26d6..58b96dc1 100644
--- a/common/flatpak-run.c
+++ b/common/flatpak-run.c
@@ -4720,40 +4720,6 @@ flatpak_run_app (FlatpakDecomposed *app_ref,
flatpak_bwrap_add_arg_printf (bwrap, "/run/user/%d", getuid ());
}
- for (i = 0; bind_fds && i < bind_fds->len; i++)
- {
- int fd = g_array_index (bind_fds, int, i);
- g_autofree char *path = NULL;
-
- /* We get the path the fd refers to, to determine to mount point
- * destination inside the sandbox */
- path = get_path_for_fd (fd, error);
- if (!path)
- return FALSE;
-
- if (!flatpak_bwrap_add_args_data_fd_dup (bwrap,
- "--bind-fd", fd, path,
- error))
- return FALSE;
- }
-
- for (i = 0; ro_bind_fds && i < ro_bind_fds->len; i++)
- {
- int fd = g_array_index (ro_bind_fds, int, i);
- g_autofree char *path = NULL;
-
- /* We get the path the fd refers to, to determine to mount point
- * destination inside the sandbox */
- path = get_path_for_fd (fd, error);
- if (!path)
- return FALSE;
-
- if (!flatpak_bwrap_add_args_data_fd_dup (bwrap,
- "--ro-bind-fd", fd, path,
- error))
- return FALSE;
- }
-
if (!flatpak_run_add_dconf_args (bwrap, app_id, metakey, error))
return FALSE;
@@ -4789,6 +4755,40 @@ flatpak_run_app (FlatpakDecomposed *app_ref,
"--symlink", "/usr/lib/debug/source", "/run/build-runtime",
NULL);
+ for (i = 0; bind_fds && i < bind_fds->len; i++)
+ {
+ int fd = g_array_index (bind_fds, int, i);
+ g_autofree char *path = NULL;
+
+ /* We get the path the fd refers to, to determine to mount point
+ * destination inside the sandbox */
+ path = get_path_for_fd (fd, error);
+ if (!path)
+ return FALSE;
+
+ if (!flatpak_bwrap_add_args_data_fd_dup (bwrap,
+ "--bind-fd", fd, path,
+ error))
+ return FALSE;
+ }
+
+ for (i = 0; ro_bind_fds && i < ro_bind_fds->len; i++)
+ {
+ int fd = g_array_index (ro_bind_fds, int, i);
+ g_autofree char *path = NULL;
+
+ /* We get the path the fd refers to, to determine to mount point
+ * destination inside the sandbox */
+ path = get_path_for_fd (fd, error);
+ if (!path)
+ return FALSE;
+
+ if (!flatpak_bwrap_add_args_data_fd_dup (bwrap,
+ "--ro-bind-fd", fd, path,
+ error))
+ return FALSE;
+ }
+
if (cwd)
flatpak_bwrap_add_args (bwrap, "--chdir", cwd, NULL);
--
2.54.0

View File

@ -0,0 +1,38 @@
From dd0a1c25299907b69085aae0fab32becbfca884e Mon Sep 17 00:00:00 2001
From: Alberto Garcia <berto@igalia.com>
Date: Wed, 8 Apr 2026 19:44:29 +0200
Subject: [PATCH] portal: use g_array_index() to read from expose_fds /
expose_fds_ro
The data field of a GArray is a gchar* but we're storing integers
here, so use the proper method to ensure that we're getting the
element at the right offset and with the correct type.
---
portal/flatpak-portal.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/portal/flatpak-portal.c b/portal/flatpak-portal.c
index 3110071c..59a17b2b 100644
--- a/portal/flatpak-portal.c
+++ b/portal/flatpak-portal.c
@@ -1219,7 +1219,7 @@ handle_spawn (PortalFlatpak *object,
{
int remapped_fd;
- remapped_fd = fd_map_remap_fd (fd_map, &max_fd, expose_fds->data[i]);
+ remapped_fd = fd_map_remap_fd (fd_map, &max_fd, g_array_index (expose_fds, int, i));
g_ptr_array_add (flatpak_argv, g_strdup_printf ("--bind-fd=%d",
remapped_fd));
@@ -1229,7 +1229,7 @@ handle_spawn (PortalFlatpak *object,
{
int remapped_fd;
- remapped_fd = fd_map_remap_fd (fd_map, &max_fd, expose_fds_ro->data[i]);
+ remapped_fd = fd_map_remap_fd (fd_map, &max_fd, g_array_index (expose_fds_ro, int, i));
g_ptr_array_add (flatpak_argv, g_strdup_printf ("--ro-bind-fd=%d",
remapped_fd));
--
2.54.0

View File

@ -0,0 +1,28 @@
From 37a73712d0e5b20d31fd1a1bd4df1b66e53b32ee Mon Sep 17 00:00:00 2001
From: Sebastian Wick <sebastian.wick@redhat.com>
Date: Thu, 9 Apr 2026 00:56:40 +0200
Subject: [PATCH] run: Fix backport mistake
Not even sure how this happened. Whoops. It's time to get some sleep.
Fixes: c89a0c50 ("run: Use the same FD validation for all FD options")
---
app/flatpak-builtins-run.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/flatpak-builtins-run.c b/app/flatpak-builtins-run.c
index d97dc0bf..e174848e 100644
--- a/app/flatpak-builtins-run.c
+++ b/app/flatpak-builtins-run.c
@@ -191,7 +191,7 @@ static GOptionEntry options[] = {
{ "app-path", 0, 0, G_OPTION_ARG_FILENAME, &opt_app_path, N_("Use PATH instead of the app's /app"), N_("PATH") },
{ "app-fd", 0, 0, G_OPTION_ARG_CALLBACK, &opt_app_fd_cb, N_("Use FD instead of the app's /app"), N_("FD") },
{ "usr-path", 0, 0, G_OPTION_ARG_FILENAME, &opt_usr_path, N_("Use PATH instead of the runtime's /usr"), N_("PATH") },
- { "usr-fd", 0, 0, G_OPTION_ARG_INT, &opt_usr_fd_cb, N_("Use FD instead of the runtime's /usr"), N_("FD") },
+ { "usr-fd", 0, 0, G_OPTION_ARG_CALLBACK, &opt_usr_fd_cb, N_("Use FD instead of the runtime's /usr"), N_("FD") },
{ "bind-fd", 0, 0, G_OPTION_ARG_CALLBACK | G_OPTION_FLAG_HIDDEN, &option_bind_fd_cb, N_("Bind mount the file or directory referred to by FD to its canonicalized path"), N_("FD") },
{ "ro-bind-fd", 0, 0, G_OPTION_ARG_CALLBACK | G_OPTION_FLAG_HIDDEN, &option_ro_bind_fd_cb, N_("Bind mount the file or directory referred to by FD read-only to its canonicalized path"), N_("FD") },
{ NULL }
--
2.54.0

View File

@ -0,0 +1,100 @@
From dc40c215b8678a5eabdb3b3ff8556d29c67f8fde Mon Sep 17 00:00:00 2001
From: Simon McVittie <smcv@collabora.com>
Date: Thu, 9 Apr 2026 18:45:04 +0100
Subject: [PATCH] run: Cope with an empty runtime
When FlatpakDir runs extra-data helpers in apply_extra_data(),
if the helper is statically linked, it might not need a runtime at all.
For example the helper for openh264 falls into this category.
Fixes: ac62ebe3 "run: Use O_PATH fds for the runtime and app deploy directories"
Helps: https://github.com/flatpak/flatpak/issues/6583
Signed-off-by: Simon McVittie <smcv@collabora.com>
(cherry picked from commit aa1a54c9dae25fd13ebc936e06996f8db39f4aa5)
---
common/flatpak-run.c | 30 +++++++++++++++++++++++++-----
1 file changed, 25 insertions(+), 5 deletions(-)
diff --git a/common/flatpak-run.c b/common/flatpak-run.c
index 58b96dc1..bd6536ac 100644
--- a/common/flatpak-run.c
+++ b/common/flatpak-run.c
@@ -2842,6 +2842,10 @@ flatpak_run_add_app_info_args (FlatpakBwrap *bwrap,
return TRUE;
}
+/*
+ * @runtime_fd: the /usr for the runtime, or -1 if running with no runtime,
+ * perhaps to unpack extra-data
+ */
static void
add_tzdata_args (FlatpakBwrap *bwrap,
int runtime_fd)
@@ -2853,14 +2857,19 @@ add_tzdata_args (FlatpakBwrap *bwrap,
g_autofree char *runtime_zoneinfo = NULL;
g_autoptr(GError) error = NULL;
+ g_return_if_fail (runtime_fd >= -1);
+
raw_timezone = flatpak_get_timezone ();
timezone_content = g_strdup_printf ("%s\n", raw_timezone);
localtime_content = g_strconcat ("../usr/share/zoneinfo/", raw_timezone, NULL);
- zoneinfo_fd = glnx_chaseat (runtime_fd, "share/zoneinfo",
- GLNX_CHASE_RESOLVE_BENEATH |
- GLNX_CHASE_MUST_BE_DIRECTORY,
- NULL);
+ if (runtime_fd >= 0)
+ {
+ zoneinfo_fd = glnx_chaseat (runtime_fd, "share/zoneinfo",
+ GLNX_CHASE_RESOLVE_BENEATH |
+ GLNX_CHASE_MUST_BE_DIRECTORY,
+ NULL);
+ }
runtime_zoneinfo = g_strconcat ("share/zoneinfo/", raw_timezone, NULL);
@@ -3370,6 +3379,10 @@ setup_seccomp (FlatpakBwrap *bwrap,
}
#endif
+/*
+ * @runtime_fd: the /usr for the runtime, or -1 if running with no runtime,
+ * perhaps to unpack extra-data
+ */
static void
flatpak_run_setup_usr_links (FlatpakBwrap *bwrap,
int runtime_fd,
@@ -3423,6 +3436,10 @@ flatpak_run_setup_usr_links (FlatpakBwrap *bwrap,
}
}
+/*
+ * @runtime_fd: the /usr for the runtime, or -1 if running with no runtime,
+ * perhaps to unpack extra-data
+ */
gboolean
flatpak_run_setup_base_argv (FlatpakBwrap *bwrap,
int runtime_fd,
@@ -3439,6 +3456,8 @@ flatpak_run_setup_base_argv (FlatpakBwrap *bwrap,
gulong pers;
gid_t gid = getgid ();
+ g_return_val_if_fail (runtime_fd >= -1, FALSE);
+
run_dir = g_strdup_printf ("/run/user/%d", getuid ());
passwd_contents = g_strdup_printf ("%s:x:%d:%d:%s:%s:%s\n"
@@ -3512,7 +3531,8 @@ flatpak_run_setup_base_argv (FlatpakBwrap *bwrap,
else if (g_file_test ("/var/lib/dbus/machine-id", G_FILE_TEST_EXISTS))
flatpak_bwrap_add_args (bwrap, "--ro-bind", "/var/lib/dbus/machine-id", "/etc/machine-id", NULL);
- if ((flags & FLATPAK_RUN_FLAG_WRITABLE_ETC) == 0)
+ if (runtime_fd >= 0
+ && (flags & FLATPAK_RUN_FLAG_WRITABLE_ETC) == 0)
{
g_auto(GLnxDirFdIterator) dfd_iter = { 0, };
struct dirent *dent;
--
2.54.0

View File

@ -0,0 +1,94 @@
From 8ce576a3b950d6ab735a5e049342d91b36685f94 Mon Sep 17 00:00:00 2001
From: Sebastian Wick <sebastian.wick@redhat.com>
Date: Fri, 6 Feb 2026 17:14:49 +0100
Subject: [PATCH] utils: Add flatpak_parse_fd
This is meant to parse file descriptor strings passed via the command
line. It is not a security mechanism and will happily accept fds 0-3 as
well.
---
common/flatpak-context.c | 19 +++++++------------
common/flatpak-utils-private.h | 3 +++
common/flatpak-utils.c | 22 ++++++++++++++++++++++
3 files changed, 32 insertions(+), 12 deletions(-)
diff --git a/common/flatpak-context.c b/common/flatpak-context.c
index 53b79807..992243e3 100644
--- a/common/flatpak-context.c
+++ b/common/flatpak-context.c
@@ -1304,21 +1304,16 @@ option_env_fd_cb (const gchar *option_name,
GError **error)
{
FlatpakContext *context = data;
- guint64 fd;
- gchar *endptr;
- gboolean ret;
+ glnx_autofd int fd = -1;
- fd = g_ascii_strtoull (value, &endptr, 10);
-
- if (endptr == NULL || *endptr != '\0' || fd > G_MAXINT)
- return glnx_throw (error, "Not a valid file descriptor: %s", value);
-
- ret = flatpak_context_parse_env_fd (context, (int) fd, error);
+ fd = flatpak_parse_fd (value, error);
+ if (fd < 0)
+ return FALSE;
- if (fd >= 3)
- close (fd);
+ if (fd < 3)
+ return glnx_throw (error, "File descriptors 0, 1, 2 are reserved");
- return ret;
+ return flatpak_context_parse_env_fd (context, fd, error);
}
static gboolean
diff --git a/common/flatpak-utils-private.h b/common/flatpak-utils-private.h
index f79b22c8..754fe412 100644
--- a/common/flatpak-utils-private.h
+++ b/common/flatpak-utils-private.h
@@ -927,6 +927,9 @@ void flatpak_print_escaped_string (const char *s,
gboolean flatpak_validate_path_characters (const char *path,
GError **error);
+int flatpak_parse_fd (const char *fd_string,
+ GError **error);
+
#define FLATPAK_MESSAGE_ID "c7b39b1e006b464599465e105b361485"
#endif /* __FLATPAK_UTILS_H__ */
diff --git a/common/flatpak-utils.c b/common/flatpak-utils.c
index 0ab84064..c2015941 100644
--- a/common/flatpak-utils.c
+++ b/common/flatpak-utils.c
@@ -9188,3 +9188,25 @@ flatpak_validate_path_characters (const char *path,
return TRUE;
}
+
+int
+flatpak_parse_fd (const char *fd_string,
+ GError **error)
+{
+ guint64 parsed;
+ char *endptr;
+ int fd;
+ struct stat stbuf;
+
+ parsed = g_ascii_strtoull (fd_string, &endptr, 10);
+
+ if (endptr == NULL || *endptr != '\0' || parsed > G_MAXINT)
+ return glnx_fd_throw (error, "Not a valid file descriptor: %s", fd_string);
+
+ fd = (int) parsed;
+
+ if (!glnx_fstat (fd, &stbuf, error))
+ return -1;
+
+ return fd;
+}
--
2.54.0

View File

@ -0,0 +1,44 @@
From 630fcba57765ac64a0cebc31257e63a5bc530961 Mon Sep 17 00:00:00 2001
From: Simon McVittie <smcv@collabora.com>
Date: Thu, 9 Apr 2026 18:47:40 +0100
Subject: [PATCH] dir: In apply_extra_data(), don't assume there is always a
runtime
org.freedesktop.Platform.openh264 is one example of an extension that
runs a statically-linked extra-data helper, with no runtime. Only open
the runtime if there is one.
Fixes: ac62ebe3 "run: Use O_PATH fds for the runtime and app deploy directories"
Resolves: https://github.com/flatpak/flatpak/issues/6583
Signed-off-by: Simon McVittie <smcv@collabora.com>
(cherry picked from commit c14ad3722940706730a76997c6925f9998106f90)
---
common/flatpak-dir.c | 12 ++++++++----
1 file changed, 8 insertions(+), 4 deletions(-)
diff --git a/common/flatpak-dir.c b/common/flatpak-dir.c
index 94d2d4f2..adc9bf62 100644
--- a/common/flatpak-dir.c
+++ b/common/flatpak-dir.c
@@ -8015,10 +8015,14 @@ apply_extra_data (FlatpakDir *self,
NULL);
glnx_autofd int usr_fd = -1;
- usr_fd = open (flatpak_file_get_path_cached (runtime_files),
- O_PATH | O_CLOEXEC | O_NOFOLLOW);
- if (usr_fd < 0)
- return glnx_throw_errno_prefix (error, "Failed to open runtime files");
+
+ if (runtime_files != NULL)
+ {
+ usr_fd = open (flatpak_file_get_path_cached (runtime_files),
+ O_PATH | O_CLOEXEC | O_NOFOLLOW);
+ if (usr_fd < 0)
+ return glnx_throw_errno_prefix (error, "Failed to open runtime files");
+ }
if (!flatpak_run_setup_base_argv (bwrap, usr_fd, NULL, runtime_arch,
/* Might need multiarch in apply_extra (see e.g. #3742). Should be pretty safe in this limited context */
--
2.54.0

View File

@ -0,0 +1,52 @@
From 0ac58a6734a3d5f6a0445cfe3310596e382c092b Mon Sep 17 00:00:00 2001
From: Simon McVittie <smcv@collabora.com>
Date: Fri, 10 Apr 2026 09:58:05 +0100
Subject: [PATCH] utils: Add flatpak_set_cloexec()
Helps: https://github.com/flatpak/flatpak/issues/6582
Signed-off-by: Simon McVittie <smcv@collabora.com>
(cherry picked from commit 8a989c790d9121f53ada88fd001a3997b9e40632)
---
common/flatpak-utils-private.h | 2 ++
common/flatpak-utils.c | 17 +++++++++++++++++
2 files changed, 19 insertions(+)
diff --git a/common/flatpak-utils-private.h b/common/flatpak-utils-private.h
index 754fe412..fd819c71 100644
--- a/common/flatpak-utils-private.h
+++ b/common/flatpak-utils-private.h
@@ -932,4 +932,6 @@ int flatpak_parse_fd (const char *fd_string,
#define FLATPAK_MESSAGE_ID "c7b39b1e006b464599465e105b361485"
+gboolean flatpak_set_cloexec (int fd);
+
#endif /* __FLATPAK_UTILS_H__ */
diff --git a/common/flatpak-utils.c b/common/flatpak-utils.c
index 938d8131..446b3325 100644
--- a/common/flatpak-utils.c
+++ b/common/flatpak-utils.c
@@ -9210,3 +9210,20 @@ flatpak_parse_fd (const char *fd_string,
return fd;
}
+
+/* Sets errno on failure. */
+gboolean
+flatpak_set_cloexec (int fd)
+{
+ int flags = fcntl (fd, F_GETFD);
+
+ if (flags == -1)
+ return FALSE;
+
+ flags |= FD_CLOEXEC;
+
+ if (fcntl (fd, F_SETFD, flags) < 0)
+ return FALSE;
+
+ return TRUE;
+}
--
2.54.0

View File

@ -0,0 +1,102 @@
From 5807aa3d7712d2fb5c294056dd90431cd863fded Mon Sep 17 00:00:00 2001
From: Simon McVittie <smcv@collabora.com>
Date: Fri, 10 Apr 2026 10:07:14 +0100
Subject: [PATCH] run, context: Mark fd arguments as close-on-exec
On entry to `flatpak run`, these fds have been inheritable (not
FD_CLOEXEC), otherwise they would not have been inherited; but we don't
want the "payload" command to inherit them, so set them as
non-close-on-exec as soon as we receive them. In the cases where we pass
them down to the underlying bwrap command, we'll either dup them, or
set them to be inheritable again (in practice we dup them).
In particular, Chromium-derived web browsers get very upset when their
subsandbox processes inherit unexpected fds, which has been causing crashes
with no useful diagnostic information since CVE-2026-34078 was fixed.
Fixes: 1b5e886d "run: Add --usr-fd and --app-fd options"
Fixes: b5ae89ed "run: Add --(ro-)bind-fd options"
Resolves: https://github.com/flatpak/flatpak/issues/6582
Signed-off-by: Simon McVittie <smcv@collabora.com>
(cherry picked from commit 0902090726c2e51b1c6f22c64d708a4895a196e7)
---
app/flatpak-builtins-run.c | 15 +++++++++++++++
common/flatpak-context.c | 8 ++++++++
2 files changed, 23 insertions(+)
diff --git a/app/flatpak-builtins-run.c b/app/flatpak-builtins-run.c
index e174848e..cc69423e 100644
--- a/app/flatpak-builtins-run.c
+++ b/app/flatpak-builtins-run.c
@@ -81,6 +81,9 @@ option_bind_fd_cb (const char *option_name,
if (fd < 3)
return glnx_throw (error, "File descriptors 0, 1, 2 are reserved");
+ if (!flatpak_set_cloexec (fd))
+ return glnx_throw_errno_prefix (error, "--bind-fd");
+
g_array_append_val (opt_bind_fds, fd);
fd = -1; /* ownership transferred to GArray */
return TRUE;
@@ -101,6 +104,9 @@ option_ro_bind_fd_cb (const char *option_name,
if (fd < 3)
return glnx_throw (error, "File descriptors 0, 1, 2 are reserved");
+ if (!flatpak_set_cloexec (fd))
+ return glnx_throw_errno_prefix (error, "--ro-bind-fd");
+
g_array_append_val (opt_ro_bind_fds, fd);
fd = -1; /* ownership transferred to GArray */
return TRUE;
@@ -121,6 +127,9 @@ opt_instance_id_fd_cb (const char *option_name,
if (fd < 3)
return glnx_throw (error, "File descriptors 0, 1, 2 are reserved");
+ if (!flatpak_set_cloexec (fd))
+ return glnx_throw_errno_prefix (error, "--instance-id-fd");
+
opt_instance_id_fd = g_steal_fd (&fd);
return TRUE;
}
@@ -140,6 +149,9 @@ opt_app_fd_cb (const char *option_name,
if (fd < 3)
return glnx_throw (error, "File descriptors 0, 1, 2 are reserved");
+ if (!flatpak_set_cloexec (fd))
+ return glnx_throw_errno_prefix (error, "--app-fd");
+
opt_app_fd = g_steal_fd (&fd);
return TRUE;
}
@@ -159,6 +171,9 @@ opt_usr_fd_cb (const char *option_name,
if (fd < 3)
return glnx_throw (error, "File descriptors 0, 1, 2 are reserved");
+ if (!flatpak_set_cloexec (fd))
+ return glnx_throw_errno_prefix (error, "--usr-fd");
+
opt_usr_fd = g_steal_fd (&fd);
return TRUE;
}
diff --git a/common/flatpak-context.c b/common/flatpak-context.c
index 992243e3..3ff4ac16 100644
--- a/common/flatpak-context.c
+++ b/common/flatpak-context.c
@@ -1313,6 +1313,14 @@ option_env_fd_cb (const gchar *option_name,
if (fd < 3)
return glnx_throw (error, "File descriptors 0, 1, 2 are reserved");
+ /* This is not strictly necessary, because we're going to close it after
+ * parsing the environment block, but let's be consistent with other fd
+ * arguments that we need to avoid being inherited by the "payload"
+ * command. This is also a convenient place to validate that it's an
+ * open fd. */
+ if (!flatpak_set_cloexec (fd))
+ return glnx_throw_errno_prefix (error, "--env-fd");
+
return flatpak_context_parse_env_fd (context, fd, error);
}
--
2.54.0

View File

@ -0,0 +1,179 @@
From 5bd5827412a440b074f9f79ae34073e2554ad76c Mon Sep 17 00:00:00 2001
From: Simon McVittie <smcv@collabora.com>
Date: Thu, 9 Apr 2026 20:06:18 +0100
Subject: [PATCH] utils: Move flatpak_get_path_for_fd to here
This was originally in flatpak-portal, then was duplicated into
flatpak-run in commit ac62ebe3 "run: Use O_PATH fds for the runtime and
app deploy directories", and subsequently removed from the portal in
commit 3c500145 "portal: Use --bind-fd, --app-fd and --usr-fd options to
avoid races". Now we want to use it in the portal again.
Helps: https://github.com/flatpak/flatpak/issues/6584
Co-authored-by: Sebastian Wick <sebastian.wick@redhat.com>
Signed-off-by: Simon McVittie <smcv@collabora.com>
(cherry picked from commit 15dc818874514ffbece4c080405353ed396b54a9)
---
common/flatpak-run.c | 44 +++---------------------------
common/flatpak-utils-private.h | 3 ++
common/flatpak-utils.c | 50 ++++++++++++++++++++++++++++++++++
3 files changed, 57 insertions(+), 40 deletions(-)
diff --git a/common/flatpak-run.c b/common/flatpak-run.c
index bd6536ac..8ce6978c 100644
--- a/common/flatpak-run.c
+++ b/common/flatpak-run.c
@@ -4108,42 +4108,6 @@ check_sudo (GError **error)
return TRUE;
}
-static char *
-get_path_for_fd (int fd,
- GError **error)
-{
- g_autofree char *proc_path = NULL;
- g_autofree char *path = NULL;
-
- proc_path = g_strdup_printf ("/proc/self/fd/%d", fd);
- path = glnx_readlinkat_malloc (AT_FDCWD, proc_path, NULL, error);
- if (path == NULL)
- return NULL;
-
- /* All normal paths start with /, but some weird things
- don't, such as socket:[27345] or anon_inode:[eventfd].
- We don't support any of these */
- if (path[0] != '/')
- {
- return glnx_null_throw (error, "%s resolves to non-absolute path %s",
- proc_path, path);
- }
-
- /* File descriptors to actually deleted files have " (deleted)"
- appended to them. This also happens to some fake fd types
- like shmem which are "/<name> (deleted)". All such
- files are considered invalid. Unfortunately this also
- matches files with filenames that actually end in " (deleted)",
- but there is not much to do about this. */
- if (g_str_has_suffix (path, " (deleted)"))
- {
- return glnx_null_throw (error, "%s resolves to deleted path %s",
- proc_path, path);
- }
-
- return g_steal_pointer (&path);
-}
-
gboolean
flatpak_run_app (FlatpakDecomposed *app_ref,
FlatpakDeploy *app_deploy,
@@ -4354,7 +4318,7 @@ flatpak_run_app (FlatpakDecomposed *app_ref,
{
g_autofree char *path = NULL;
- path = get_path_for_fd (custom_runtime_fd, &my_error);
+ path = flatpak_get_path_for_fd (custom_runtime_fd, &my_error);
if (path == NULL)
{
return flatpak_fail_error (error, FLATPAK_ERROR,
@@ -4476,7 +4440,7 @@ flatpak_run_app (FlatpakDecomposed *app_ref,
{
g_autofree char *path = NULL;
- path = get_path_for_fd (custom_app_fd, error);
+ path = flatpak_get_path_for_fd (custom_app_fd, error);
if (path == NULL)
return glnx_prefix_error (error, "Cannot convert custom app fd to path");
@@ -4782,7 +4746,7 @@ flatpak_run_app (FlatpakDecomposed *app_ref,
/* We get the path the fd refers to, to determine to mount point
* destination inside the sandbox */
- path = get_path_for_fd (fd, error);
+ path = flatpak_get_path_for_fd (fd, error);
if (!path)
return FALSE;
@@ -4799,7 +4763,7 @@ flatpak_run_app (FlatpakDecomposed *app_ref,
/* We get the path the fd refers to, to determine to mount point
* destination inside the sandbox */
- path = get_path_for_fd (fd, error);
+ path = flatpak_get_path_for_fd (fd, error);
if (!path)
return FALSE;
diff --git a/common/flatpak-utils-private.h b/common/flatpak-utils-private.h
index fd819c71..efe8eb2b 100644
--- a/common/flatpak-utils-private.h
+++ b/common/flatpak-utils-private.h
@@ -930,6 +930,9 @@ gboolean flatpak_validate_path_characters (const char *path,
int flatpak_parse_fd (const char *fd_string,
GError **error);
+char * flatpak_get_path_for_fd (int fd,
+ GError **error);
+
#define FLATPAK_MESSAGE_ID "c7b39b1e006b464599465e105b361485"
gboolean flatpak_set_cloexec (int fd);
diff --git a/common/flatpak-utils.c b/common/flatpak-utils.c
index 446b3325..6f09fa09 100644
--- a/common/flatpak-utils.c
+++ b/common/flatpak-utils.c
@@ -9227,3 +9227,53 @@ flatpak_set_cloexec (int fd)
return TRUE;
}
+
+/*
+ * Attempt to discover the filesystem path corresponding to @fd.
+ *
+ * If @fd points to an existing file, return the absolute path of that
+ * file in the environment where it was opened. Note that this is not
+ * necessarily a valid path in the current namespace, if it was
+ * transferred via fd-passing from a process in a different filesystem
+ * namespace.
+ *
+ * If @fd points to a deleted file, or to a socket, fifo, memfd or similar
+ * non-filesystem object, set an error and return %NULL.
+ *
+ * Returns: (type filename) (transfer full) (nullable):
+ */
+char *
+flatpak_get_path_for_fd (int fd,
+ GError **error)
+{
+ g_autofree char *proc_path = NULL;
+ g_autofree char *path = NULL;
+
+ proc_path = g_strdup_printf ("/proc/self/fd/%d", fd);
+ path = glnx_readlinkat_malloc (AT_FDCWD, proc_path, NULL, error);
+ if (path == NULL)
+ return NULL;
+
+ /* All normal paths start with /, but some weird things
+ don't, such as socket:[27345] or anon_inode:[eventfd].
+ We don't support any of these */
+ if (path[0] != '/')
+ {
+ return glnx_null_throw (error, "%s resolves to non-absolute path %s",
+ proc_path, path);
+ }
+
+ /* File descriptors to actually deleted files have " (deleted)"
+ appended to them. This also happens to some fake fd types
+ like shmem which are "/<name> (deleted)". All such
+ files are considered invalid. Unfortunately this also
+ matches files with filenames that actually end in " (deleted)",
+ but there is not much to do about this. */
+ if (g_str_has_suffix (path, " (deleted)"))
+ {
+ return glnx_null_throw (error, "%s resolves to deleted path %s",
+ proc_path, path);
+ }
+
+ return g_steal_pointer (&path);
+}
--
2.54.0

View File

@ -0,0 +1,66 @@
From 0663652213e742df38528f6c929789189831d40a Mon Sep 17 00:00:00 2001
From: Simon McVittie <smcv@collabora.com>
Date: Thu, 9 Apr 2026 20:16:16 +0100
Subject: [PATCH] portal: Avoid crash if sandbox-expose-[ro-]fd is out of range
If the handle is not in the range `0 <= handle < fds_len`, but no
GError is set, we'd have crashed when we dereferenced error->message.
Instead, log an error and early-return, matching what we do for
app-fd, usr-fd and the array of inheritable fds.
Fixes: 3c500145 "portal: Use --bind-fd, --app-fd and --usr-fd options to avoid races"
Helps: https://github.com/flatpak/flatpak/issues/6584
Co-authored-by: Sebastian Wick <sebastian.wick@redhat.com>
Signed-off-by: Simon McVittie <smcv@collabora.com>
(cherry picked from commit 4ef2421bd22d8fbf8f17cf9bf5da1dd95aedef8d)
---
portal/flatpak-portal.c | 26 ++++++++++++++++++++++----
1 file changed, 22 insertions(+), 4 deletions(-)
diff --git a/portal/flatpak-portal.c b/portal/flatpak-portal.c
index 59a17b2b..c7310f89 100644
--- a/portal/flatpak-portal.c
+++ b/portal/flatpak-portal.c
@@ -1172,8 +1172,17 @@ handle_spawn (PortalFlatpak *object,
gint32 handle;
g_variant_get_child (sandbox_expose_fd, i, "h", &handle);
- if (handle >= 0 && handle < fds_len &&
- validate_opath_fd (fds[handle], TRUE, &error))
+ if (handle >= fds_len || handle < 0)
+ {
+ g_debug ("Invalid sandbox-expose-fd handle %d", handle);
+ g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR,
+ G_DBUS_ERROR_INVALID_ARGS,
+ "No file descriptor for handle %d",
+ handle);
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
+ }
+
+ if (validate_opath_fd (fds[handle], TRUE, &error))
{
g_array_append_val (expose_fds, fds[handle]);
}
@@ -1198,8 +1207,17 @@ handle_spawn (PortalFlatpak *object,
gint32 handle;
g_variant_get_child (sandbox_expose_fd_ro, i, "h", &handle);
- if (handle >= 0 && handle < fds_len &&
- validate_opath_fd (fds[handle], FALSE, &error))
+ if (handle >= fds_len || handle < 0)
+ {
+ g_debug ("Invalid sandbox-expose-ro-fd handle %d", handle);
+ g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR,
+ G_DBUS_ERROR_INVALID_ARGS,
+ "No file descriptor for handle %d",
+ handle);
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
+ }
+
+ if (validate_opath_fd (fds[handle], FALSE, &error))
{
g_array_append_val (expose_fds_ro, fds[handle]);
}
--
2.54.0

View File

@ -0,0 +1,65 @@
From db8a36948a0b9d7bc0b7572a969c148f4a704ed4 Mon Sep 17 00:00:00 2001
From: Simon McVittie <smcv@collabora.com>
Date: Thu, 9 Apr 2026 20:24:48 +0100
Subject: [PATCH] portal: Log and ignore unusable sandbox-expose fds instead of
erroring
For the sandbox expose fds, a historical quirk of this code is that if
the checks in get_path_for_fd() failed, we would merely log at g_info()
level (usually only shown when debugging the portal), and otherwise
silently ignore the request to expose the fd in the sandbox.
With hindsight this was probably not the right thing to do, but apps
could well be relying on it now. For example, there are indications
that Epiphany might send a memfd from the main instance to a subsandbox,
which never actually worked, but will break that subsandbox process
if that's treated as a fatal error.
Fixes: 3c500145 "portal: Use --bind-fd, --app-fd and --usr-fd options to avoid races"
Helps: https://github.com/flatpak/flatpak/issues/6584
Co-authored-by: Sebastian Wick <sebastian.wick@redhat.com>
Signed-off-by: Simon McVittie <smcv@collabora.com>
(cherry picked from commit 75ab6eebb857fd26172613b69e55f04830ad0d82)
---
portal/flatpak-portal.c | 18 ++++++------------
1 file changed, 6 insertions(+), 12 deletions(-)
diff --git a/portal/flatpak-portal.c b/portal/flatpak-portal.c
index c7310f89..9f8c7918 100644
--- a/portal/flatpak-portal.c
+++ b/portal/flatpak-portal.c
@@ -1188,12 +1188,9 @@ handle_spawn (PortalFlatpak *object,
}
else
{
- g_debug ("Invalid sandbox expose fd: %s", error->message);
- g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR,
- G_DBUS_ERROR_INVALID_ARGS,
- "No valid file descriptor for handle %d",
- handle);
- return G_DBUS_METHOD_INVOCATION_HANDLED;
+ g_info ("unable to validate sandbox-expose-fd %d, ignoring: %s",
+ fds[handle], error->message);
+ g_clear_error (&error);
}
}
}
@@ -1223,12 +1220,9 @@ handle_spawn (PortalFlatpak *object,
}
else
{
- g_debug ("Invalid sandbox expose ro fd: %s", error->message);
- g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR,
- G_DBUS_ERROR_INVALID_ARGS,
- "No file descriptor for handle %d",
- handle);
- return G_DBUS_METHOD_INVOCATION_HANDLED;
+ g_info ("unable to validate sandbox-expose-ro-fd %d, ignoring: %s",
+ fds[handle], error->message);
+ g_clear_error (&error);
}
}
}
--
2.54.0

View File

@ -0,0 +1,69 @@
From df7cfbbed0a0576c35f236ab6ba3620cbfeb900f Mon Sep 17 00:00:00 2001
From: Simon McVittie <smcv@collabora.com>
Date: Thu, 9 Apr 2026 20:28:57 +0100
Subject: [PATCH] portal: Reinstate flatpak_get_path_for_fd() checks
As with the previous commit, historically we would debug-log but
otherwise silently ignore attempts to expose a file in a sandboxed
subsandbox that doesn't have a suitable path.
For example, org.gnome.Epiphany (or possibly WebKitGTK) asks to expose
files from /app and /usr in the subsandbox. When we ignored those
requests (because /app and /usr have a different meaning on the host
system), the app worked as intended anyway, because the subsandbox has
access to the app's /app and the runtime's /usr whether they're
explicitly added or not, so it all worked out OK. However, treating
this as a fatal error (as it arguably should have been) broke
Epiphany's subsandboxes.
Fixes: 3c500145 "portal: Use --bind-fd, --app-fd and --usr-fd options to avoid races"
Resolves: https://github.com/flatpak/flatpak/issues/6584
Co-authored-by: Sebastian Wick <sebastian.wick@redhat.com>
Signed-off-by: Simon McVittie <smcv@collabora.com>
(cherry picked from commit 28634c7f52e57df7091007973d1bb5e1f87f1e9d)
---
portal/flatpak-portal.c | 20 ++++++++++++++++++++
1 file changed, 20 insertions(+)
diff --git a/portal/flatpak-portal.c b/portal/flatpak-portal.c
index 9f8c7918..185ed676 100644
--- a/portal/flatpak-portal.c
+++ b/portal/flatpak-portal.c
@@ -556,7 +556,9 @@ validate_opath_fd (int fd,
{
int fd_flags;
struct stat st_buf;
+ struct stat real_st_buf;
int access_mode;
+ g_autofree char *path = NULL;
/* Must be able to get fd flags */
fd_flags = fcntl (fd, F_GETFL);
@@ -575,6 +577,24 @@ validate_opath_fd (int fd,
if (fstat (fd, &st_buf) < 0)
return glnx_throw_errno_prefix (error, "Failed to fstat");
+ path = flatpak_get_path_for_fd (fd, error);
+ if (path == NULL)
+ return FALSE;
+
+ /* Verify that this is the same file as the app opened.
+ * Note that this is not security relevant because flatpak-run/bwrap will
+ * check things and abort if something is off. We do this only for backwards
+ * compatibility reasons: we need to be able to ignore the issue instead of
+ * aborting the entire sandbox setup later. */
+ if (stat (path, &real_st_buf) < 0 ||
+ st_buf.st_dev != real_st_buf.st_dev ||
+ st_buf.st_ino != real_st_buf.st_ino)
+ {
+ /* Different files on the inside and the outside, reject the request */
+ return glnx_throw (error,
+ "different file inside and outside sandbox");
+ }
+
access_mode = R_OK;
if (S_ISDIR (st_buf.st_mode))
access_mode |= X_OK;
--
2.54.0

View File

@ -0,0 +1,37 @@
From 313ceb0cac8d28f941421fabb24e7371b2f3cc8d Mon Sep 17 00:00:00 2001
From: Simon McVittie <smcv@collabora.com>
Date: Fri, 10 Apr 2026 11:38:12 +0100
Subject: [PATCH] libtest: Allow adding a new ref to an existing temporary
ostree repo
When we run `tests/test-run-custom.sh` as a build-time test,
we expect to already have the necessary runtimes, apps, etc. in
`${builddir}/tests/runtime-repo`. However, when running "as-installed"
tests, we're using a fresh temporary ostree repo for each test.
Merely having the repo exist is not enough: for some tests, and in
particular `tests/test-run-custom.sh`, it needs to have more than one
runtime available.
Resolves: https://github.com/flatpak/flatpak/issues/6591
Signed-off-by: Simon McVittie <smcv@collabora.com>
(cherry picked from commit 50dda82eb054695b3d3758d0a88ef68c8dd79dc4)
---
tests/libtest.sh | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tests/libtest.sh b/tests/libtest.sh
index 4a9fd481..5dc4011e 100644
--- a/tests/libtest.sh
+++ b/tests/libtest.sh
@@ -302,7 +302,7 @@ make_runtime () {
RUNTIME_REPO=${TEST_DATA_DIR}/runtime-repo
(
flock -s 200
- if [ ! -d ${RUNTIME_REPO} ]; then
+ if [ ! -f "${RUNTIME_REPO}/refs/heads/${RUNTIME_REF}" ]; then
$(dirname $0)/make-test-runtime.sh ${RUNTIME_REPO} org.test.Platform ${BRANCH} "" "" > /dev/null
fi
) 200>${TEST_DATA_DIR}/runtime-repo-lock
--
2.54.0

View File

@ -0,0 +1,103 @@
From 263497107d68e5aa44856b6d1d375fd1adf77879 Mon Sep 17 00:00:00 2001
From: Simon McVittie <smcv@collabora.com>
Date: Fri, 10 Apr 2026 14:00:14 +0100
Subject: [PATCH] app, context: Never close fds 0, 1 or 2
These fds are stdin, stdout and stderr respectively, and are expected
to remain open at all times (if they are not needed then they can point
to /dev/null, but they should always be open). If the user gives us
`--env-fd=2` or similar, we don't want to close fd 2 before exiting
unsuccessfully: that would give us nowhere to display the error message.
Signed-off-by: Simon McVittie <smcv@collabora.com>
(cherry picked from commit c4ab58cd2e66c4bcf193919ef9cbdce1dac042da)
---
app/flatpak-builtins-run.c | 26 +++++++++++++++++++++-----
common/flatpak-context.c | 6 +++++-
2 files changed, 26 insertions(+), 6 deletions(-)
diff --git a/app/flatpak-builtins-run.c b/app/flatpak-builtins-run.c
index cc69423e..82725119 100644
--- a/app/flatpak-builtins-run.c
+++ b/app/flatpak-builtins-run.c
@@ -79,7 +79,11 @@ option_bind_fd_cb (const char *option_name,
return FALSE;
if (fd < 3)
- return glnx_throw (error, "File descriptors 0, 1, 2 are reserved");
+ {
+ /* Don't close these fds! */
+ fd = -1;
+ return glnx_throw (error, "File descriptors 0, 1, 2 are reserved");
+ }
if (!flatpak_set_cloexec (fd))
return glnx_throw_errno_prefix (error, "--bind-fd");
@@ -102,7 +106,10 @@ option_ro_bind_fd_cb (const char *option_name,
return FALSE;
if (fd < 3)
- return glnx_throw (error, "File descriptors 0, 1, 2 are reserved");
+ {
+ fd = -1;
+ return glnx_throw (error, "File descriptors 0, 1, 2 are reserved");
+ }
if (!flatpak_set_cloexec (fd))
return glnx_throw_errno_prefix (error, "--ro-bind-fd");
@@ -125,7 +132,10 @@ opt_instance_id_fd_cb (const char *option_name,
return FALSE;
if (fd < 3)
- return glnx_throw (error, "File descriptors 0, 1, 2 are reserved");
+ {
+ fd = -1;
+ return glnx_throw (error, "File descriptors 0, 1, 2 are reserved");
+ }
if (!flatpak_set_cloexec (fd))
return glnx_throw_errno_prefix (error, "--instance-id-fd");
@@ -147,7 +157,10 @@ opt_app_fd_cb (const char *option_name,
return FALSE;
if (fd < 3)
- return glnx_throw (error, "File descriptors 0, 1, 2 are reserved");
+ {
+ fd = -1;
+ return glnx_throw (error, "File descriptors 0, 1, 2 are reserved");
+ }
if (!flatpak_set_cloexec (fd))
return glnx_throw_errno_prefix (error, "--app-fd");
@@ -169,7 +182,10 @@ opt_usr_fd_cb (const char *option_name,
return FALSE;
if (fd < 3)
- return glnx_throw (error, "File descriptors 0, 1, 2 are reserved");
+ {
+ fd = -1;
+ return glnx_throw (error, "File descriptors 0, 1, 2 are reserved");
+ }
if (!flatpak_set_cloexec (fd))
return glnx_throw_errno_prefix (error, "--usr-fd");
diff --git a/common/flatpak-context.c b/common/flatpak-context.c
index 3ff4ac16..3dc8f533 100644
--- a/common/flatpak-context.c
+++ b/common/flatpak-context.c
@@ -1311,7 +1311,11 @@ option_env_fd_cb (const gchar *option_name,
return FALSE;
if (fd < 3)
- return glnx_throw (error, "File descriptors 0, 1, 2 are reserved");
+ {
+ /* Don't close these fds! */
+ fd = -1;
+ return glnx_throw (error, "File descriptors 0, 1, 2 are reserved");
+ }
/* This is not strictly necessary, because we're going to close it after
* parsing the environment block, but let's be consistent with other fd
--
2.54.0

View File

@ -0,0 +1,235 @@
From 9c357f7f9d4d781bf04dafab9559343e9ec44f55 Mon Sep 17 00:00:00 2001
From: Simon McVittie <smcv@collabora.com>
Date: Fri, 10 Apr 2026 15:02:43 +0100
Subject: [PATCH] app, context: Factor out flatpak_accept_fd_argument()
Signed-off-by: Simon McVittie <smcv@collabora.com>
(cherry picked from commit d42037c5267ac7967ce285b9052b25fe7a968368)
---
app/flatpak-builtins-run.c | 61 ++++++----------------------------
common/flatpak-context.c | 18 ++--------
common/flatpak-utils-private.h | 4 +++
common/flatpak-utils.c | 47 ++++++++++++++++++++++++++
4 files changed, 63 insertions(+), 67 deletions(-)
diff --git a/app/flatpak-builtins-run.c b/app/flatpak-builtins-run.c
index 82725119..c9e7e071 100644
--- a/app/flatpak-builtins-run.c
+++ b/app/flatpak-builtins-run.c
@@ -74,20 +74,11 @@ option_bind_fd_cb (const char *option_name,
{
glnx_autofd int fd = -1;
- fd = flatpak_parse_fd (value, error);
+ fd = flatpak_accept_fd_argument (option_name, value, error);
+
if (fd < 0)
return FALSE;
- if (fd < 3)
- {
- /* Don't close these fds! */
- fd = -1;
- return glnx_throw (error, "File descriptors 0, 1, 2 are reserved");
- }
-
- if (!flatpak_set_cloexec (fd))
- return glnx_throw_errno_prefix (error, "--bind-fd");
-
g_array_append_val (opt_bind_fds, fd);
fd = -1; /* ownership transferred to GArray */
return TRUE;
@@ -101,19 +92,11 @@ option_ro_bind_fd_cb (const char *option_name,
{
glnx_autofd int fd = -1;
- fd = flatpak_parse_fd (value, error);
+ fd = flatpak_accept_fd_argument (option_name, value, error);
+
if (fd < 0)
return FALSE;
- if (fd < 3)
- {
- fd = -1;
- return glnx_throw (error, "File descriptors 0, 1, 2 are reserved");
- }
-
- if (!flatpak_set_cloexec (fd))
- return glnx_throw_errno_prefix (error, "--ro-bind-fd");
-
g_array_append_val (opt_ro_bind_fds, fd);
fd = -1; /* ownership transferred to GArray */
return TRUE;
@@ -127,19 +110,11 @@ opt_instance_id_fd_cb (const char *option_name,
{
glnx_autofd int fd = -1;
- fd = flatpak_parse_fd (value, error);
+ fd = flatpak_accept_fd_argument (option_name, value, error);
+
if (fd < 0)
return FALSE;
- if (fd < 3)
- {
- fd = -1;
- return glnx_throw (error, "File descriptors 0, 1, 2 are reserved");
- }
-
- if (!flatpak_set_cloexec (fd))
- return glnx_throw_errno_prefix (error, "--instance-id-fd");
-
opt_instance_id_fd = g_steal_fd (&fd);
return TRUE;
}
@@ -152,19 +127,11 @@ opt_app_fd_cb (const char *option_name,
{
glnx_autofd int fd = -1;
- fd = flatpak_parse_fd (value, error);
+ fd = flatpak_accept_fd_argument (option_name, value, error);
+
if (fd < 0)
return FALSE;
- if (fd < 3)
- {
- fd = -1;
- return glnx_throw (error, "File descriptors 0, 1, 2 are reserved");
- }
-
- if (!flatpak_set_cloexec (fd))
- return glnx_throw_errno_prefix (error, "--app-fd");
-
opt_app_fd = g_steal_fd (&fd);
return TRUE;
}
@@ -177,19 +144,11 @@ opt_usr_fd_cb (const char *option_name,
{
glnx_autofd int fd = -1;
- fd = flatpak_parse_fd (value, error);
+ fd = flatpak_accept_fd_argument (option_name, value, error);
+
if (fd < 0)
return FALSE;
- if (fd < 3)
- {
- fd = -1;
- return glnx_throw (error, "File descriptors 0, 1, 2 are reserved");
- }
-
- if (!flatpak_set_cloexec (fd))
- return glnx_throw_errno_prefix (error, "--usr-fd");
-
opt_usr_fd = g_steal_fd (&fd);
return TRUE;
}
diff --git a/common/flatpak-context.c b/common/flatpak-context.c
index 3dc8f533..2165631d 100644
--- a/common/flatpak-context.c
+++ b/common/flatpak-context.c
@@ -1306,25 +1306,11 @@ option_env_fd_cb (const gchar *option_name,
FlatpakContext *context = data;
glnx_autofd int fd = -1;
- fd = flatpak_parse_fd (value, error);
+ fd = flatpak_accept_fd_argument (option_name, value, error);
+
if (fd < 0)
return FALSE;
- if (fd < 3)
- {
- /* Don't close these fds! */
- fd = -1;
- return glnx_throw (error, "File descriptors 0, 1, 2 are reserved");
- }
-
- /* This is not strictly necessary, because we're going to close it after
- * parsing the environment block, but let's be consistent with other fd
- * arguments that we need to avoid being inherited by the "payload"
- * command. This is also a convenient place to validate that it's an
- * open fd. */
- if (!flatpak_set_cloexec (fd))
- return glnx_throw_errno_prefix (error, "--env-fd");
-
return flatpak_context_parse_env_fd (context, fd, error);
}
diff --git a/common/flatpak-utils-private.h b/common/flatpak-utils-private.h
index efe8eb2b..215b3c38 100644
--- a/common/flatpak-utils-private.h
+++ b/common/flatpak-utils-private.h
@@ -937,4 +937,8 @@ char * flatpak_get_path_for_fd (int fd,
gboolean flatpak_set_cloexec (int fd);
+int flatpak_accept_fd_argument (const char *option_name,
+ const char *value,
+ GError **error);
+
#endif /* __FLATPAK_UTILS_H__ */
diff --git a/common/flatpak-utils.c b/common/flatpak-utils.c
index 6f09fa09..63a2e325 100644
--- a/common/flatpak-utils.c
+++ b/common/flatpak-utils.c
@@ -9228,6 +9228,53 @@ flatpak_set_cloexec (int fd)
return TRUE;
}
+/*
+ * flatpak_accept_fd_argument:
+ * @option_name: Name of a command-line option such as `--env-fd`
+ * @value: Value of the command-line option
+ *
+ * Parse a command-line argument whose value is a file descriptor to be
+ * used internally by Flatpak.
+ *
+ * The file descriptor must be 3 or higher (cannot be stdin, stdout
+ * or stderr).
+ *
+ * The file descriptor is set to be close-on-execute (CLOEXEC).
+ * If child processes are meant to inherit it, the caller must clear the
+ * close-on-execute flag, or duplicate the fd.
+ *
+ * Returns: A file descriptor to be closed by the caller, or -1 on error
+ */
+int
+flatpak_accept_fd_argument (const char *option_name,
+ const char *value,
+ GError **error)
+{
+ glnx_autofd int fd = -1;
+
+ fd = flatpak_parse_fd (value, error);
+
+ if (fd < 0)
+ {
+ g_prefix_error (error, "%s: ", option_name);
+ return -1;
+ }
+
+ if (fd < 3)
+ {
+ /* We don't want to close stdin, stdout or stderr */
+ fd = -1;
+ return glnx_fd_throw (error,
+ "%s: Cannot use reserved file descriptor 0, 1 or 2",
+ option_name);
+ }
+
+ if (!flatpak_set_cloexec (fd))
+ return glnx_fd_throw_errno_prefix (error, "%s", option_name);
+
+ return g_steal_fd (&fd);
+}
+
/*
* Attempt to discover the filesystem path corresponding to @fd.
*
--
2.54.0

View File

@ -0,0 +1,48 @@
From ba43f074af594705b0aea9f53261d3b61fc3c866 Mon Sep 17 00:00:00 2001
From: Sebastian Wick <sebastian.wick@redhat.com>
Date: Fri, 6 Feb 2026 16:32:50 +0100
Subject: [PATCH] flatpak-bwrap: Use glnx_close_fd as clear func
We already have a function which clears a fd that a pointer points to,
so let's use it instead of duplicating the code.
Will become useful in a later commit as well.
---
common/flatpak-bwrap.c | 13 ++-----------
1 file changed, 2 insertions(+), 11 deletions(-)
diff --git a/common/flatpak-bwrap.c b/common/flatpak-bwrap.c
index 1694ffd4..693a1981 100644
--- a/common/flatpak-bwrap.c
+++ b/common/flatpak-bwrap.c
@@ -41,15 +41,6 @@
#include "flatpak-utils-private.h"
#include "flatpak-utils-base-private.h"
-static void
-clear_fd (gpointer data)
-{
- int *fd_p = data;
-
- if (fd_p != NULL && *fd_p != -1)
- close (*fd_p);
-}
-
char *flatpak_bwrap_empty_env[] = { NULL };
FlatpakBwrap *
@@ -59,9 +50,9 @@ flatpak_bwrap_new (char **env)
bwrap->argv = g_ptr_array_new_with_free_func (g_free);
bwrap->noinherit_fds = g_array_new (FALSE, TRUE, sizeof (int));
- g_array_set_clear_func (bwrap->noinherit_fds, clear_fd);
+ g_array_set_clear_func (bwrap->noinherit_fds, (GDestroyNotify) glnx_close_fd);
bwrap->fds = g_array_new (FALSE, TRUE, sizeof (int));
- g_array_set_clear_func (bwrap->fds, clear_fd);
+ g_array_set_clear_func (bwrap->fds, (GDestroyNotify) glnx_close_fd);
if (env)
bwrap->envp = g_strdupv (env);
--
2.54.0

View File

@ -0,0 +1,912 @@
From f41f12c98896aced62830bf5f3e21c40ee24a6d7 Mon Sep 17 00:00:00 2001
From: Sebastian Wick <sebastian.wick@redhat.com>
Date: Fri, 6 Feb 2026 20:54:22 +0100
Subject: [PATCH] run: Use O_PATH fds for the runtime and app deploy
directories
This also allows us to use glnx_chaseat, and other at-functions to
traverse the filesystem tree in a safe way.
This is important because the app and runtime deploy directories can be
under an attackers control. The flatpak portal for example allows
sandboxed apps to provide them.
In particular, attacks where the deploy dirs get replaced by a symlink
pointing into the host system will be stopped by this.
Note that this change alone is not enough to avoid the attack, and the
portal has to be changed as well.
---
app/flatpak-builtins-build.c | 8 +-
app/flatpak-builtins-run.c | 37 ++-
common/Makefile.am.inc | 2 +-
common/flatpak-dir.c | 8 +-
common/flatpak-installation.c | 3 +-
common/flatpak-run-private.h | 11 +-
common/flatpak-run.c | 452 +++++++++++++++++++++++-----------
7 files changed, 372 insertions(+), 149 deletions(-)
diff --git a/app/flatpak-builtins-build.c b/app/flatpak-builtins-build.c
index 4a1e762..09b469a 100644
--- a/app/flatpak-builtins-build.c
+++ b/app/flatpak-builtins-build.c
@@ -457,7 +457,13 @@ flatpak_builtin_build (int argc, char **argv, GCancellable *cancellable, GError
/* Never set up an a11y bus for builds */
run_flags |= FLATPAK_RUN_FLAG_NO_A11Y_BUS_PROXY;
- if (!flatpak_run_setup_base_argv (bwrap, runtime_files, app_id_dir, arch,
+ glnx_autofd int usr_fd = -1;
+ usr_fd = open (flatpak_file_get_path_cached (runtime_files),
+ O_PATH | O_CLOEXEC | O_NOFOLLOW);
+ if (usr_fd < 0)
+ return glnx_throw_errno_prefix (error, "Failed to open runtime files");
+
+ if (!flatpak_run_setup_base_argv (bwrap, usr_fd, app_id_dir, arch,
run_flags, error))
return FALSE;
diff --git a/app/flatpak-builtins-run.c b/app/flatpak-builtins-run.c
index ba2ae67..9403d8a 100644
--- a/app/flatpak-builtins-run.c
+++ b/app/flatpak-builtins-run.c
@@ -110,6 +110,8 @@ flatpak_builtin_run (int argc, char **argv, GCancellable *cancellable, GError **
g_autoptr(GError) local_error = NULL;
g_autoptr(GPtrArray) dirs = NULL;
FlatpakRunFlags flags = 0;
+ glnx_autofd int app_fd = -1;
+ glnx_autofd int usr_fd = -1;
context = g_option_context_new (_("APP [ARGUMENT…] - Run an app"));
g_option_context_set_translation_domain (context, GETTEXT_PACKAGE);
@@ -305,14 +307,45 @@ flatpak_builtin_run (int argc, char **argv, GCancellable *cancellable, GError **
if (!opt_session_bus)
flags |= FLATPAK_RUN_FLAG_NO_SESSION_BUS_PROXY;
+ if (opt_app_path != NULL)
+ {
+ if (g_strcmp0 (opt_app_path, "") == 0)
+ {
+ app_fd = FLATPAK_RUN_APP_DEPLOY_APP_EMPTY;
+ }
+ else
+ {
+ app_fd = open (opt_app_path, O_PATH | O_CLOEXEC | O_NOFOLLOW);
+
+ if (app_fd < 0)
+ return glnx_throw_errno_prefix (error, "Failed to open app-path");
+ }
+ }
+ else
+ {
+ app_fd = FLATPAK_RUN_APP_DEPLOY_APP_ORIGINAL;
+ }
+
+ if (opt_usr_path != NULL)
+ {
+ usr_fd = open (opt_usr_path, O_PATH | O_CLOEXEC | O_NOFOLLOW);
+
+ if (usr_fd < 0)
+ return glnx_throw_errno_prefix (error, "Failed to open usr-path");
+ }
+ else
+ {
+ usr_fd = FLATPAK_RUN_APP_DEPLOY_USR_ORIGINAL;
+ }
+
if (!flatpak_run_app (app_deploy ? app_ref : runtime_ref,
app_deploy,
- opt_app_path,
+ app_fd,
arg_context,
opt_runtime,
opt_runtime_version,
opt_runtime_commit,
- opt_usr_path,
+ usr_fd,
opt_parent_pid,
flags,
opt_cwd,
diff --git a/common/Makefile.am.inc b/common/Makefile.am.inc
index 892ee4c..3147cc7 100644
--- a/common/Makefile.am.inc
+++ b/common/Makefile.am.inc
@@ -205,6 +205,7 @@ libflatpak_common_la_CFLAGS = \
$(NULL)
libflatpak_common_la_LIBADD = \
$(AM_LIBADD) \
+ libglnx.la \
$(ARCHIVE_LIBS) \
$(ZSTD_LIBS) \
$(BASE_LIBS) \
@@ -249,7 +250,6 @@ libflatpak_la_LIBADD = \
$(AM_LIBADD) \
libflatpak-common.la \
libflatpak-common-base.la \
- libglnx.la \
$(BASE_LIBS) \
$(OSTREE_LIBS) \
$(SOUP_LIBS) \
diff --git a/common/flatpak-dir.c b/common/flatpak-dir.c
index 978df03..c18fd44 100644
--- a/common/flatpak-dir.c
+++ b/common/flatpak-dir.c
@@ -8014,7 +8014,13 @@ apply_extra_data (FlatpakDir *self,
"--cap-drop", "ALL",
NULL);
- if (!flatpak_run_setup_base_argv (bwrap, runtime_files, NULL, runtime_arch,
+ glnx_autofd int usr_fd = -1;
+ usr_fd = open (flatpak_file_get_path_cached (runtime_files),
+ O_PATH | O_CLOEXEC | O_NOFOLLOW);
+ if (usr_fd < 0)
+ return glnx_throw_errno_prefix (error, "Failed to open runtime files");
+
+ if (!flatpak_run_setup_base_argv (bwrap, usr_fd, NULL, runtime_arch,
/* Might need multiarch in apply_extra (see e.g. #3742). Should be pretty safe in this limited context */
FLATPAK_RUN_FLAG_MULTIARCH |
FLATPAK_RUN_FLAG_NO_SESSION_HELPER | FLATPAK_RUN_FLAG_NO_PROC,
diff --git a/common/flatpak-installation.c b/common/flatpak-installation.c
index 3c5a12a..9254252 100644
--- a/common/flatpak-installation.c
+++ b/common/flatpak-installation.c
@@ -700,9 +700,10 @@ flatpak_installation_launch_full (FlatpakInstallation *self,
if (!flatpak_run_app (app_ref,
app_deploy,
+ FLATPAK_RUN_APP_DEPLOY_APP_ORIGINAL,
NULL,
- NULL, NULL,
NULL, NULL, NULL,
+ FLATPAK_RUN_APP_DEPLOY_USR_ORIGINAL,
0,
run_flags,
NULL,
diff --git a/common/flatpak-run-private.h b/common/flatpak-run-private.h
index eac25cc..5fc1f29 100644
--- a/common/flatpak-run-private.h
+++ b/common/flatpak-run-private.h
@@ -28,6 +28,11 @@
#include "flatpak-utils-private.h"
#include "flatpak-exports-private.h"
+#define FLATPAK_RUN_APP_DEPLOY_APP_ORIGINAL (-2)
+#define FLATPAK_RUN_APP_DEPLOY_APP_EMPTY (-3)
+
+#define FLATPAK_RUN_APP_DEPLOY_USR_ORIGINAL (-2)
+
gboolean flatpak_run_in_transient_unit (const char *app_id,
GError **error);
@@ -151,7 +156,7 @@ gboolean flatpak_ensure_data_dir (GFile *app_id_dir,
GError **error);
gboolean flatpak_run_setup_base_argv (FlatpakBwrap *bwrap,
- GFile *runtime_files,
+ int runtime_fd,
GFile *app_id_dir,
const char *arch,
FlatpakRunFlags flags,
@@ -181,12 +186,12 @@ gboolean flatpak_run_add_app_info_args (FlatpakBwrap *bwrap,
gboolean flatpak_run_app (FlatpakDecomposed *app_ref,
FlatpakDeploy *app_deploy,
- const char *custom_app_path,
+ int custom_app_fd,
FlatpakContext *extra_context,
const char *custom_runtime,
const char *custom_runtime_version,
const char *custom_runtime_commit,
- const char *custom_usr_path,
+ int custom_runtime_fd,
int parent_pid,
FlatpakRunFlags flags,
const char *cwd,
diff --git a/common/flatpak-run.c b/common/flatpak-run.c
index 4fdb56f..0e1d16f 100644
--- a/common/flatpak-run.c
+++ b/common/flatpak-run.c
@@ -2846,19 +2846,37 @@ flatpak_run_add_app_info_args (FlatpakBwrap *bwrap,
static void
add_tzdata_args (FlatpakBwrap *bwrap,
- GFile *runtime_files)
+ int runtime_fd)
{
- g_autofree char *raw_timezone = flatpak_get_timezone ();
- g_autofree char *timezone_content = g_strdup_printf ("%s\n", raw_timezone);
- g_autofree char *localtime_content = g_strconcat ("../usr/share/zoneinfo/", raw_timezone, NULL);
- g_autoptr(GFile) runtime_zoneinfo = NULL;
+ g_autofree char *raw_timezone = NULL;
+ g_autofree char *timezone_content = NULL;
+ g_autofree char *localtime_content = NULL;
+ glnx_autofd int zoneinfo_fd = -1;
+ g_autofree char *runtime_zoneinfo = NULL;
+ g_autoptr(GError) error = NULL;
+
+ raw_timezone = flatpak_get_timezone ();
+ timezone_content = g_strdup_printf ("%s\n", raw_timezone);
+ localtime_content = g_strconcat ("../usr/share/zoneinfo/", raw_timezone, NULL);
+
+ zoneinfo_fd = glnx_chaseat (runtime_fd, "share/zoneinfo",
+ GLNX_CHASE_RESOLVE_BENEATH |
+ GLNX_CHASE_MUST_BE_DIRECTORY,
+ NULL);
- if (runtime_files)
- runtime_zoneinfo = g_file_resolve_relative_path (runtime_files, "share/zoneinfo");
+ runtime_zoneinfo = g_strconcat ("share/zoneinfo/", raw_timezone, NULL);
/* Check for runtime /usr/share/zoneinfo */
- if (runtime_zoneinfo != NULL && g_file_query_exists (runtime_zoneinfo, NULL))
+ if (zoneinfo_fd >= 0)
{
+ glnx_autofd int runtime_zoneinfo_fd = -1;
+
+ /* Check for runtime /usr/share/zoneinfo */
+ runtime_zoneinfo_fd = glnx_chaseat (runtime_fd, runtime_zoneinfo,
+ GLNX_CHASE_RESOLVE_BENEATH |
+ GLNX_CHASE_MUST_BE_REGULAR,
+ NULL);
+
/* Check for host /usr/share/zoneinfo */
if (g_file_test ("/usr/share/zoneinfo", G_FILE_TEST_IS_DIR))
{
@@ -2868,15 +2886,12 @@ add_tzdata_args (FlatpakBwrap *bwrap,
"--symlink", localtime_content, "/etc/localtime",
NULL);
}
- else
+ /* Check if host timezone file exist in the runtime tzdata */
+ else if (runtime_zoneinfo_fd >= 0)
{
- g_autoptr(GFile) runtime_tzfile = g_file_resolve_relative_path (runtime_zoneinfo, raw_timezone);
-
- /* Check if host timezone file exist in the runtime tzdata */
- if (g_file_query_exists (runtime_tzfile, NULL))
- flatpak_bwrap_add_args (bwrap,
- "--symlink", localtime_content, "/etc/localtime",
- NULL);
+ flatpak_bwrap_add_args (bwrap,
+ "--symlink", localtime_content, "/etc/localtime",
+ NULL);
}
}
@@ -3359,24 +3374,41 @@ setup_seccomp (FlatpakBwrap *bwrap,
static void
flatpak_run_setup_usr_links (FlatpakBwrap *bwrap,
- GFile *runtime_files,
+ int runtime_fd,
const char *sysroot)
{
int i;
- if (runtime_files == NULL)
+ g_return_if_fail (runtime_fd >= -1);
+
+ if (runtime_fd < 0)
return;
for (i = 0; flatpak_abs_usrmerged_dirs[i] != NULL; i++)
{
const char *subdir = flatpak_abs_usrmerged_dirs[i];
- g_autoptr(GFile) runtime_subdir = NULL;
+ glnx_autofd int runtime_subdir_fd = -1;
+ g_autoptr(GError) local_error = NULL;
g_assert (subdir[0] == '/');
+
/* Skip the '/' when using as a subdirectory of the runtime */
- runtime_subdir = g_file_get_child (runtime_files, subdir + 1);
+ runtime_subdir_fd = glnx_chaseat (runtime_fd, subdir + 1,
+ GLNX_CHASE_RESOLVE_BENEATH |
+ GLNX_CHASE_NOFOLLOW,
+ &local_error);
- if (g_file_query_exists (runtime_subdir, NULL))
+ if (runtime_subdir_fd < 0 &&
+ !g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
+ {
+ g_warning ("Checking for usrmerged dir %s failed: %s",
+ subdir, local_error->message);
+ }
+ else if (runtime_subdir_fd < 0)
+ {
+ g_debug ("%s does not exist in runtime", subdir);
+ }
+ else
{
g_autofree char *link = g_strconcat ("usr", subdir, NULL);
g_autofree char *create = NULL;
@@ -3390,17 +3422,12 @@ flatpak_run_setup_usr_links (FlatpakBwrap *bwrap,
"--symlink", link, create,
NULL);
}
- else
- {
- g_debug ("%s does not exist",
- flatpak_file_get_path_cached (runtime_subdir));
- }
}
}
gboolean
flatpak_run_setup_base_argv (FlatpakBwrap *bwrap,
- GFile *runtime_files,
+ int runtime_fd,
GFile *app_id_dir,
const char *arch,
FlatpakRunFlags flags,
@@ -3413,7 +3440,6 @@ flatpak_run_setup_base_argv (FlatpakBwrap *bwrap,
struct group *g;
gulong pers;
gid_t gid = getgid ();
- g_autoptr(GFile) etc = NULL;
run_dir = g_strdup_printf ("/run/user/%d", getuid ());
@@ -3488,22 +3514,25 @@ flatpak_run_setup_base_argv (FlatpakBwrap *bwrap,
else if (g_file_test ("/var/lib/dbus/machine-id", G_FILE_TEST_EXISTS))
flatpak_bwrap_add_args (bwrap, "--ro-bind", "/var/lib/dbus/machine-id", "/etc/machine-id", NULL);
- if (runtime_files)
- etc = g_file_get_child (runtime_files, "etc");
- if (etc != NULL &&
- (flags & FLATPAK_RUN_FLAG_WRITABLE_ETC) == 0 &&
- g_file_query_exists (etc, NULL))
+ if ((flags & FLATPAK_RUN_FLAG_WRITABLE_ETC) == 0)
{
g_auto(GLnxDirFdIterator) dfd_iter = { 0, };
struct dirent *dent;
gboolean inited;
+ g_autoptr(GError) local_error = NULL;
- inited = glnx_dirfd_iterator_init_at (AT_FDCWD, flatpak_file_get_path_cached (etc), FALSE, &dfd_iter, NULL);
+ inited = glnx_dirfd_iterator_init_at (runtime_fd, "etc", FALSE, &dfd_iter, &local_error);
+ if (!inited && !g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
+ {
+ g_propagate_error (error, g_steal_pointer (&local_error));
+ return FALSE;
+ }
while (inited)
{
- g_autofree char *src = NULL;
g_autofree char *dest = NULL;
+ glnx_autofd int src_fd = -1;
+ struct stat statbuf;
if (!glnx_dirfd_iterator_next_dent_ensure_dtype (&dfd_iter, &dent, NULL, NULL) || dent == NULL)
break;
@@ -3520,9 +3549,19 @@ flatpak_run_setup_base_argv (FlatpakBwrap *bwrap,
strcmp (dent->d_name, "pkcs11") == 0)
continue;
- src = g_build_filename (flatpak_file_get_path_cached (etc), dent->d_name, NULL);
dest = g_build_filename ("/etc", dent->d_name, NULL);
- if (dent->d_type == DT_LNK)
+
+ src_fd = glnx_chaseat (dfd_iter.fd, dent->d_name,
+ GLNX_CHASE_NOFOLLOW |
+ GLNX_CHASE_RESOLVE_BENEATH,
+ error);
+ if (src_fd < 0)
+ return FALSE;
+
+ if (!glnx_fstat (src_fd, &statbuf, error))
+ return FALSE;
+
+ if (S_ISLNK (statbuf.st_mode))
{
g_autofree char *target = NULL;
@@ -3533,9 +3572,12 @@ flatpak_run_setup_base_argv (FlatpakBwrap *bwrap,
flatpak_bwrap_add_args (bwrap, "--symlink", target, dest, NULL);
}
- else
+ else if (src_fd >= 0)
{
- flatpak_bwrap_add_args (bwrap, "--ro-bind", src, dest, NULL);
+ flatpak_bwrap_add_args_data_fd (bwrap,
+ "--ro-bind-fd",
+ g_steal_fd (&src_fd),
+ dest);
}
}
}
@@ -3556,9 +3598,9 @@ flatpak_run_setup_base_argv (FlatpakBwrap *bwrap,
NULL);
}
- flatpak_run_setup_usr_links (bwrap, runtime_files, NULL);
+ flatpak_run_setup_usr_links (bwrap, runtime_fd, NULL);
- add_tzdata_args (bwrap, runtime_files);
+ add_tzdata_args (bwrap, runtime_fd);
pers = PER_LINUX;
@@ -3785,7 +3827,7 @@ regenerate_ld_cache (GPtrArray *base_argv_array,
GArray *base_fd_array,
GFile *app_id_dir,
const char *checksum,
- GFile *runtime_files,
+ int runtime_fd,
gboolean generate_ld_so_conf,
GCancellable *cancellable,
GError **error)
@@ -3825,7 +3867,7 @@ regenerate_ld_cache (GPtrArray *base_argv_array,
flatpak_bwrap_append_args (bwrap, base_argv_array);
- flatpak_run_setup_usr_links (bwrap, runtime_files, NULL);
+ flatpak_run_setup_usr_links (bwrap, runtime_fd, NULL);
if (generate_ld_so_conf)
{
@@ -4048,15 +4090,51 @@ check_sudo (GError **error)
return TRUE;
}
+static char *
+get_path_for_fd (int fd,
+ GError **error)
+{
+ g_autofree char *proc_path = NULL;
+ g_autofree char *path = NULL;
+
+ proc_path = g_strdup_printf ("/proc/self/fd/%d", fd);
+ path = glnx_readlinkat_malloc (AT_FDCWD, proc_path, NULL, error);
+ if (path == NULL)
+ return NULL;
+
+ /* All normal paths start with /, but some weird things
+ don't, such as socket:[27345] or anon_inode:[eventfd].
+ We don't support any of these */
+ if (path[0] != '/')
+ {
+ return glnx_null_throw (error, "%s resolves to non-absolute path %s",
+ proc_path, path);
+ }
+
+ /* File descriptors to actually deleted files have " (deleted)"
+ appended to them. This also happens to some fake fd types
+ like shmem which are "/<name> (deleted)". All such
+ files are considered invalid. Unfortunately this also
+ matches files with filenames that actually end in " (deleted)",
+ but there is not much to do about this. */
+ if (g_str_has_suffix (path, " (deleted)"))
+ {
+ return glnx_null_throw (error, "%s resolves to deleted path %s",
+ proc_path, path);
+ }
+
+ return g_steal_pointer (&path);
+}
+
gboolean
flatpak_run_app (FlatpakDecomposed *app_ref,
FlatpakDeploy *app_deploy,
- const char *custom_app_path,
+ int custom_app_fd,
FlatpakContext *extra_context,
const char *custom_runtime,
const char *custom_runtime_version,
const char *custom_runtime_commit,
- const char *custom_usr_path,
+ int custom_runtime_fd,
int parent_pid,
FlatpakRunFlags flags,
const char *cwd,
@@ -4071,11 +4149,6 @@ flatpak_run_app (FlatpakDecomposed *app_ref,
g_autoptr(FlatpakDeploy) runtime_deploy = NULL;
g_autoptr(GBytes) runtime_deploy_data = NULL;
g_autoptr(GBytes) app_deploy_data = NULL;
- g_autoptr(GFile) app_files = NULL;
- g_autoptr(GFile) original_app_files = NULL;
- g_autoptr(GFile) runtime_files = NULL;
- g_autoptr(GFile) original_runtime_files = NULL;
- g_autoptr(GFile) bin_ldconfig = NULL;
g_autoptr(GFile) app_id_dir = NULL;
g_autoptr(GFile) real_app_id_dir = NULL;
g_autofree char *default_runtime_pref = NULL;
@@ -4107,18 +4180,39 @@ flatpak_run_app (FlatpakDecomposed *app_ref,
g_autofree char *per_app_dir_lock_path = NULL;
g_autofree char *shared_xdg_runtime_dir = NULL;
int ld_so_fd = -1;
- g_autoptr(GFile) runtime_ld_so_conf = NULL;
gboolean generate_ld_so_conf = TRUE;
gboolean use_ld_so_cache = TRUE;
gboolean sandboxed = (flags & FLATPAK_RUN_FLAG_SANDBOX) != 0;
gboolean parent_expose_pids = (flags & FLATPAK_RUN_FLAG_PARENT_EXPOSE_PIDS) != 0;
gboolean parent_share_pids = (flags & FLATPAK_RUN_FLAG_PARENT_SHARE_PIDS) != 0;
- const char *app_target_path = "/app";
- const char *runtime_target_path = "/usr";
- struct stat s;
+ glnx_autofd int original_runtime_fd = -1;
+ g_autoptr(GFile) original_runtime_files = NULL;
+ g_autoptr(GFile) custom_runtime_files = NULL;
+ /* borrows from either original_runtime_fd or custom_runtime_fd */
+ int runtime_fd = -1;
+ /* borrows from either original_runtime_files or custom_runtime_files */
+ GFile *runtime_files = NULL;
+ const char *original_runtime_target_path = NULL;
+ glnx_autofd int original_app_fd = -1;
+ g_autoptr(GFile) original_app_files = NULL;
+ g_autoptr(GFile) custom_app_files = NULL;
+ /* borrows from either original_app_fd or custom_app_fd */
+ int app_fd = -1;
+ /* borrows from either original_app_files or custom_app_files */
+ GFile *app_files = NULL;
+ const char *original_app_target_path = NULL;
g_return_val_if_fail (app_ref != NULL, FALSE);
+ g_return_val_if_fail (custom_app_fd == FLATPAK_RUN_APP_DEPLOY_APP_ORIGINAL ||
+ custom_app_fd == FLATPAK_RUN_APP_DEPLOY_APP_EMPTY ||
+ custom_app_fd >= 0,
+ FALSE);
+
+ g_return_val_if_fail (custom_runtime_fd == FLATPAK_RUN_APP_DEPLOY_USR_ORIGINAL ||
+ custom_runtime_fd >= 0,
+ FALSE);
+
if (!check_sudo (error))
return FALSE;
@@ -4231,38 +4325,53 @@ flatpak_run_app (FlatpakDecomposed *app_ref,
flatpak_context_merge (app_context, extra_context);
original_runtime_files = flatpak_deploy_get_files (runtime_deploy);
+ original_runtime_fd = open (flatpak_file_get_path_cached (original_runtime_files),
+ O_PATH | O_CLOEXEC);
+ if (original_runtime_fd < 0)
+ return glnx_throw_errno_prefix (error, "Failed to open original runtime");
- if (custom_usr_path != NULL)
+ if (custom_runtime_fd >= 0)
{
- runtime_files = g_file_new_for_path (custom_usr_path);
- /* Mount the original runtime below here instead of /usr */
- runtime_target_path = "/run/parent/usr";
+ g_autofree char *path = NULL;
+
+ path = get_path_for_fd (custom_runtime_fd, &my_error);
+ if (path == NULL)
+ {
+ return flatpak_fail_error (error, FLATPAK_ERROR,
+ "Cannot convert custom usr fd to path: %s",
+ my_error->message);
+ }
+
+ custom_runtime_files = g_file_new_for_path (path);
+
+ original_runtime_target_path = "/run/parent/usr";
+ runtime_fd = custom_runtime_fd;
+ runtime_files = custom_runtime_files;
+ }
+ else if (custom_app_fd == FLATPAK_RUN_APP_DEPLOY_USR_ORIGINAL)
+ {
+ original_runtime_target_path = "/usr";
+ runtime_fd = original_runtime_fd;
+ runtime_files = original_runtime_files;
}
else
{
- runtime_files = g_object_ref (original_runtime_files);
+ g_assert_not_reached ();
}
- bin_ldconfig = g_file_resolve_relative_path (runtime_files, "bin/ldconfig");
- if (!g_file_query_exists (bin_ldconfig, NULL))
- use_ld_so_cache = FALSE;
-
- /* We can't use the ld.so cache if we are using a custom /usr or /app,
- * because we don't have a unique ID for the /usr or /app, so we can't
- * do cache-invalidation correctly. The caller can either build their
- * own ld.so.cache before supplying us with the runtime, or supply
- * their own LD_LIBRARY_PATH. */
- if (custom_usr_path != NULL || custom_app_path != NULL)
- use_ld_so_cache = FALSE;
-
if (app_deploy != NULL)
{
g_autofree const char **previous_ids = NULL;
gsize len = 0;
gboolean do_migrate;
- real_app_id_dir = flatpak_get_data_dir (app_id);
original_app_files = flatpak_deploy_get_files (app_deploy);
+ original_app_fd = open (flatpak_file_get_path_cached (original_app_files),
+ O_PATH | O_CLOEXEC | O_NOFOLLOW);
+ if (original_app_fd < 0)
+ return glnx_throw_errno_prefix (error, "Failed to open original runtime");
+
+ real_app_id_dir = flatpak_get_data_dir (app_id);
previous_app_id_dirs = g_ptr_array_new_with_free_func (g_object_unref);
previous_ids = flatpak_deploy_data_get_previous_ids (app_deploy_data, &len);
@@ -4343,19 +4452,60 @@ flatpak_run_app (FlatpakDecomposed *app_ref,
app_id_dir = g_object_ref (real_app_id_dir);
}
- if (custom_app_path != NULL)
+ if (custom_app_fd >= 0)
{
- if (strcmp (custom_app_path, "") == 0)
- app_files = NULL;
- else
- app_files = g_file_new_for_path (custom_app_path);
+ g_autofree char *path = NULL;
- /* Mount the original app below here */
- app_target_path = "/run/parent/app";
+ path = get_path_for_fd (custom_app_fd, error);
+ if (path == NULL)
+ return glnx_prefix_error (error, "Cannot convert custom app fd to path");
+
+ custom_app_files = g_file_new_for_path (path);
+
+ original_app_target_path = "/run/parent/app";
+ app_fd = custom_app_fd;
+ app_files = custom_app_files;
+ }
+ else if (custom_app_fd == FLATPAK_RUN_APP_DEPLOY_APP_ORIGINAL)
+ {
+ original_app_target_path = "/app";
+ app_fd = original_app_fd;
+ app_files = original_app_files;
+ }
+ else if (custom_app_fd == FLATPAK_RUN_APP_DEPLOY_APP_EMPTY)
+ {
+ app_fd = -1;
+ app_files = NULL;
+ }
+ else
+ {
+ g_assert_not_reached ();
}
- else if (original_app_files != NULL)
+
+ /* We can't use the ld.so cache if we are using a custom /usr or /app,
+ * because we don't have a unique ID for the /usr or /app, so we can't
+ * do cache-invalidation correctly. The caller can either build their
+ * own ld.so.cache before supplying us with the runtime, or supply
+ * their own LD_LIBRARY_PATH. */
+ if (runtime_fd == custom_runtime_fd || app_fd == custom_app_fd)
{
- app_files = g_object_ref (original_app_files);
+ use_ld_so_cache = FALSE;
+ }
+ else
+ {
+ glnx_autofd int ldconfig_fd = -1;
+
+ ldconfig_fd = glnx_chaseat (runtime_fd, "bin/ldconfig",
+ GLNX_CHASE_RESOLVE_BENEATH |
+ GLNX_CHASE_MUST_BE_REGULAR,
+ &my_error);
+ if (ldconfig_fd < 0)
+ {
+ use_ld_so_cache = FALSE;
+ g_debug ("bin/ldconfig not found in runtime: %s", my_error->message);
+ }
+
+ g_clear_error (&my_error);
}
flatpak_run_apply_env_default (bwrap, use_ld_so_cache);
@@ -4368,75 +4518,86 @@ flatpak_run_app (FlatpakDecomposed *app_ref,
flatpak_bwrap_set_env (bwrap, "FLATPAK_SANDBOX_DIR", flatpak_file_get_path_cached (sandbox_dir), TRUE);
}
- flatpak_bwrap_add_args (bwrap,
- "--ro-bind", flatpak_file_get_path_cached (runtime_files), "/usr",
- NULL);
-
- if (runtime_files == original_runtime_files)
- {
- /* All true Flatpak runtimes have files/.ref */
- flatpak_bwrap_add_args (bwrap,
- "--lock-file", "/usr/.ref",
- NULL);
- }
- else
- {
- g_autoptr(GFile) runtime_child = NULL;
+ if (!flatpak_bwrap_add_args_data_fd_dup (bwrap,
+ "--ro-bind-fd", runtime_fd, "/usr",
+ error))
+ return FALSE;
- runtime_child = g_file_get_child (runtime_files, ".ref");
+ {
+ glnx_autofd int runtime_ref_fd = -1;
- /* Lock ${usr}/.ref if it exists */
- if (g_file_query_exists (runtime_child, NULL))
+ runtime_ref_fd = glnx_chaseat (runtime_fd, ".ref",
+ GLNX_CHASE_RESOLVE_BENEATH |
+ GLNX_CHASE_MUST_BE_REGULAR,
+ NULL);
+ if (runtime_ref_fd >= 0)
+ {
flatpak_bwrap_add_args (bwrap,
"--lock-file", "/usr/.ref",
NULL);
+ }
+ }
+
+ if (runtime_fd == custom_runtime_fd)
+ {
+ glnx_autofd int original_runtime_ref_fd = -1;
+ glnx_autofd int original_runtime_etc_fd = -1;
/* Put the real Flatpak runtime in /run/parent, so that the
* replacement /usr can have symlinks into /run/parent in order
* to use the Flatpak runtime's graphics drivers etc. if desired */
- flatpak_bwrap_add_args (bwrap,
- "--ro-bind",
- flatpak_file_get_path_cached (original_runtime_files),
- "/run/parent/usr",
- "--lock-file", "/run/parent/usr/.ref",
- NULL);
- flatpak_run_setup_usr_links (bwrap, original_runtime_files,
- "/run/parent");
+ if (!flatpak_bwrap_add_args_data_fd_dup (bwrap,
+ "--ro-bind-fd",
+ original_runtime_fd,
+ "/run/parent/usr",
+ error))
+ return FALSE;
- g_clear_object (&runtime_child);
- runtime_child = g_file_get_child (original_runtime_files, "etc");
+ original_runtime_ref_fd = glnx_chaseat (original_runtime_fd, ".ref",
+ GLNX_CHASE_RESOLVE_BENEATH |
+ GLNX_CHASE_MUST_BE_REGULAR,
+ NULL);
+ if (original_runtime_ref_fd >= 0)
+ {
+ flatpak_bwrap_add_args (bwrap,
+ "--lock-file", "/run/parent/usr/.ref",
+ NULL);
+ }
- if (g_file_query_exists (runtime_child, NULL))
- flatpak_bwrap_add_args (bwrap,
- "--symlink", "usr/etc", "/run/parent/etc",
- NULL);
+ original_runtime_etc_fd = glnx_chaseat (original_runtime_fd, "etc",
+ GLNX_CHASE_RESOLVE_BENEATH |
+ GLNX_CHASE_MUST_BE_REGULAR,
+ NULL);
+ if (original_runtime_etc_fd >= 0)
+ {
+ flatpak_bwrap_add_args (bwrap,
+ "--symlink", "usr/etc", "/run/parent/etc",
+ NULL);
+ }
+
+ flatpak_run_setup_usr_links (bwrap, original_runtime_fd,
+ "/run/parent");
}
- if (app_files != NULL)
+ if (app_fd >= 0)
{
- flatpak_bwrap_add_args (bwrap,
- "--ro-bind", flatpak_file_get_path_cached (app_files), "/app",
- NULL);
+ glnx_autofd int app_ref_fd = -1;
- if (app_files == original_app_files)
+ if (!flatpak_bwrap_add_args_data_fd_dup (bwrap,
+ "--ro-bind-fd", app_fd, "/app",
+ error))
+ return FALSE;
+
+ app_ref_fd = glnx_chaseat (app_fd, ".ref",
+ GLNX_CHASE_RESOLVE_BENEATH |
+ GLNX_CHASE_MUST_BE_REGULAR,
+ NULL);
+ if (app_ref_fd >= 0)
{
- /* All true Flatpak apps have files/.ref */
flatpak_bwrap_add_args (bwrap,
"--lock-file", "/app/.ref",
NULL);
}
- else
- {
- g_autoptr(GFile) app_child = NULL;
-
- app_child = g_file_get_child (app_files, ".ref");
-
- /* Lock ${app}/.ref if it exists */
- if (g_file_query_exists (app_child, NULL))
- flatpak_bwrap_add_args (bwrap,
- "--lock-file", "/app/.ref",
- NULL);
- }
}
else
{
@@ -4445,7 +4606,7 @@ flatpak_run_app (FlatpakDecomposed *app_ref,
NULL);
}
- if (original_app_files != NULL && app_files != original_app_files)
+ if (original_app_fd >= 0 && original_app_fd != app_fd)
{
/* Put the real Flatpak app in /run/parent/app */
flatpak_bwrap_add_args (bwrap,
@@ -4458,26 +4619,37 @@ flatpak_run_app (FlatpakDecomposed *app_ref,
if (metakey != NULL &&
!flatpak_run_add_extension_args (bwrap, metakey, app_ref,
- use_ld_so_cache, app_target_path,
+ use_ld_so_cache, original_app_target_path,
&app_extensions, &app_ld_path,
cancellable, error))
return FALSE;
if (!flatpak_run_add_extension_args (bwrap, runtime_metakey, runtime_ref,
- use_ld_so_cache, runtime_target_path,
+ use_ld_so_cache, original_runtime_target_path,
&runtime_extensions, &runtime_ld_path,
cancellable, error))
return FALSE;
- if (custom_usr_path == NULL)
+ if (runtime_fd == original_runtime_fd)
flatpak_run_extend_ld_path (bwrap, NULL, runtime_ld_path);
- if (custom_app_path == NULL)
+ if (app_fd == original_app_fd)
flatpak_run_extend_ld_path (bwrap, app_ld_path, NULL);
- runtime_ld_so_conf = g_file_resolve_relative_path (runtime_files, "etc/ld.so.conf");
- if (lstat (flatpak_file_get_path_cached (runtime_ld_so_conf), &s) == 0)
- generate_ld_so_conf = S_ISREG (s.st_mode) && s.st_size == 0;
+ {
+ glnx_autofd int ld_so_conf_fd = -1;
+ struct glnx_statx stx;
+
+ ld_so_conf_fd = glnx_chase_and_statxat (runtime_fd, "etc/ld.so.conf",
+ GLNX_CHASE_RESOLVE_BENEATH |
+ GLNX_CHASE_MUST_BE_REGULAR,
+ GLNX_STATX_SIZE,
+ &stx, NULL);
+ if (ld_so_conf_fd < 0 ||
+ !(stx.stx_mask & GLNX_STATX_SIZE) ||
+ stx.stx_size != 0)
+ generate_ld_so_conf = FALSE;
+ }
/* At this point we have the minimal argv set up, with just the app, runtime and extensions.
We can reuse this to generate the ld.so.cache (if needed) */
@@ -4489,7 +4661,7 @@ flatpak_run_app (FlatpakDecomposed *app_ref,
bwrap->fds,
app_id_dir,
checksum,
- runtime_files,
+ runtime_fd,
generate_ld_so_conf,
cancellable, error);
if (ld_so_fd == -1)
@@ -4499,7 +4671,7 @@ flatpak_run_app (FlatpakDecomposed *app_ref,
flags |= flatpak_context_get_run_flags (app_context);
- if (!flatpak_run_setup_base_argv (bwrap, runtime_files, app_id_dir, app_arch, flags, error))
+ if (!flatpak_run_setup_base_argv (bwrap, runtime_fd, app_id_dir, app_arch, flags, error))
return FALSE;
if (generate_ld_so_conf)
--
2.54.0

View File

@ -0,0 +1,78 @@
From 7140fee959d82d1c5913167398e6d559f3a13ee3 Mon Sep 17 00:00:00 2001
From: Sebastian Wick <sebastian.wick@redhat.com>
Date: Fri, 6 Feb 2026 20:55:46 +0100
Subject: [PATCH] run: Add --usr-fd and --app-fd options
Exposes options to pass in a fd for the runtime and app deploy. The
flatpak portal will make use of this in a following commit.
---
app/flatpak-builtins-run.c | 30 ++++++++++++++++++++++++++++--
1 file changed, 28 insertions(+), 2 deletions(-)
diff --git a/app/flatpak-builtins-run.c b/app/flatpak-builtins-run.c
index 9403d8ae..a2d71420 100644
--- a/app/flatpak-builtins-run.c
+++ b/app/flatpak-builtins-run.c
@@ -60,7 +60,9 @@ static gboolean opt_parent_expose_pids;
static gboolean opt_parent_share_pids;
static int opt_instance_id_fd = -1;
static char *opt_app_path;
+static int opt_app_fd = -1;
static char *opt_usr_path;
+static int opt_usr_fd = -1;
static GOptionEntry options[] = {
{ "arch", 0, 0, G_OPTION_ARG_STRING, &opt_arch, N_("Arch to use"), N_("ARCH") },
@@ -88,7 +90,9 @@ static GOptionEntry options[] = {
{ "parent-share-pids", 0, 0, G_OPTION_ARG_NONE, &opt_parent_share_pids, N_("Share process ID namespace with parent"), NULL },
{ "instance-id-fd", 0, 0, G_OPTION_ARG_INT, &opt_instance_id_fd, N_("Write the instance ID to the given file descriptor"), NULL },
{ "app-path", 0, 0, G_OPTION_ARG_FILENAME, &opt_app_path, N_("Use PATH instead of the app's /app"), N_("PATH") },
+ { "app-fd", 0, 0, G_OPTION_ARG_INT, &opt_app_fd, N_("Use FD instead of the app's /app"), N_("FD") },
{ "usr-path", 0, 0, G_OPTION_ARG_FILENAME, &opt_usr_path, N_("Use PATH instead of the runtime's /usr"), N_("PATH") },
+ { "usr-fd", 0, 0, G_OPTION_ARG_INT, &opt_usr_fd, N_("Use FD instead of the runtime's /usr"), N_("FD") },
{ NULL }
};
@@ -307,7 +311,18 @@ flatpak_builtin_run (int argc, char **argv, GCancellable *cancellable, GError **
if (!opt_session_bus)
flags |= FLATPAK_RUN_FLAG_NO_SESSION_BUS_PROXY;
- if (opt_app_path != NULL)
+ if (opt_app_fd >= 0 && opt_app_path != NULL)
+ {
+ flatpak_fail_error (error, FLATPAK_ERROR,
+ _("app-fd and app-path cannot both be used"));
+ return FALSE;
+ }
+
+ if (opt_app_fd >= 0)
+ {
+ app_fd = opt_app_fd;
+ }
+ else if (opt_app_path != NULL)
{
if (g_strcmp0 (opt_app_path, "") == 0)
{
@@ -326,7 +341,18 @@ flatpak_builtin_run (int argc, char **argv, GCancellable *cancellable, GError **
app_fd = FLATPAK_RUN_APP_DEPLOY_APP_ORIGINAL;
}
- if (opt_usr_path != NULL)
+ if (opt_usr_fd >= 0 && opt_usr_path != NULL)
+ {
+ flatpak_fail_error (error, FLATPAK_ERROR,
+ _("usr-fd and usr-path cannot both be used"));
+ return FALSE;
+ }
+
+ if (opt_usr_fd >= 0)
+ {
+ usr_fd = opt_usr_fd;
+ }
+ else if (opt_usr_path != NULL)
{
usr_fd = open (opt_usr_path, O_PATH | O_CLOEXEC | O_NOFOLLOW);
--
2.54.0

View File

@ -0,0 +1,116 @@
From 3a55408e13066f39f78158d3026ea3581f0b28c6 Mon Sep 17 00:00:00 2001
From: Sebastian Wick <sebastian.wick@redhat.com>
Date: Fri, 6 Feb 2026 21:02:47 +0100
Subject: [PATCH] run: Add (ro-)bind fds to flatpak_run_app
The flatpak portal allows apps to expose files and folders from within
the sandbox to a side-sandbox using flatpak-spawn. So far it has used
the --filesystem option to mount those files and folders, but it takes a
path. Paths are inherently racy and they allow the app to swap out any
component of the path with a symlink after handing it off. If they win
the race, flatpak will mount a completely different directory.
This adds a new way to mount files and directories based on O_PATH
file descriptor that needs to provided when execing the flatpak binary.
---
app/flatpak-builtins-run.c | 2 ++
common/flatpak-installation.c | 1 +
common/flatpak-run-private.h | 2 ++
common/flatpak-run.c | 36 +++++++++++++++++++++++++++++++++++
4 files changed, 41 insertions(+)
diff --git a/app/flatpak-builtins-run.c b/app/flatpak-builtins-run.c
index a2d71420..7190f1f7 100644
--- a/app/flatpak-builtins-run.c
+++ b/app/flatpak-builtins-run.c
@@ -380,6 +380,8 @@ flatpak_builtin_run (int argc, char **argv, GCancellable *cancellable, GError **
rest_argc - 1,
opt_instance_id_fd,
NULL,
+ NULL,
+ NULL,
cancellable,
error))
return FALSE;
diff --git a/common/flatpak-installation.c b/common/flatpak-installation.c
index 9254252d..6eb16459 100644
--- a/common/flatpak-installation.c
+++ b/common/flatpak-installation.c
@@ -710,6 +710,7 @@ flatpak_installation_launch_full (FlatpakInstallation *self,
NULL,
NULL, 0, -1,
&instance_dir,
+ NULL, NULL,
cancellable, error))
return FALSE;
diff --git a/common/flatpak-run-private.h b/common/flatpak-run-private.h
index 5fc1f296..23477bd8 100644
--- a/common/flatpak-run-private.h
+++ b/common/flatpak-run-private.h
@@ -200,6 +200,8 @@ gboolean flatpak_run_app (FlatpakDecomposed *app_ref,
int n_args,
int instance_id_fd,
char **instance_dir_out,
+ GArray *bind_fds,
+ GArray *ro_bind_fds,
GCancellable *cancellable,
GError **error);
diff --git a/common/flatpak-run.c b/common/flatpak-run.c
index 0e1d16f9..9087be23 100644
--- a/common/flatpak-run.c
+++ b/common/flatpak-run.c
@@ -4143,6 +4143,8 @@ flatpak_run_app (FlatpakDecomposed *app_ref,
int n_args,
int instance_id_fd,
char **instance_dir_out,
+ GArray *bind_fds,
+ GArray *ro_bind_fds,
GCancellable *cancellable,
GError **error)
{
@@ -4717,6 +4719,40 @@ flatpak_run_app (FlatpakDecomposed *app_ref,
flatpak_bwrap_add_arg_printf (bwrap, "/run/user/%d", getuid ());
}
+ for (i = 0; bind_fds && i < bind_fds->len; i++)
+ {
+ int fd = g_array_index (bind_fds, int, i);
+ g_autofree char *path = NULL;
+
+ /* We get the path the fd refers to, to determine to mount point
+ * destination inside the sandbox */
+ path = get_path_for_fd (fd, error);
+ if (!path)
+ return FALSE;
+
+ if (!flatpak_bwrap_add_args_data_fd_dup (bwrap,
+ "--bind-fd", fd, path,
+ error))
+ return FALSE;
+ }
+
+ for (i = 0; ro_bind_fds && i < ro_bind_fds->len; i++)
+ {
+ int fd = g_array_index (ro_bind_fds, int, i);
+ g_autofree char *path = NULL;
+
+ /* We get the path the fd refers to, to determine to mount point
+ * destination inside the sandbox */
+ path = get_path_for_fd (fd, error);
+ if (!path)
+ return FALSE;
+
+ if (!flatpak_bwrap_add_args_data_fd_dup (bwrap,
+ "--ro-bind-fd", fd, path,
+ error))
+ return FALSE;
+ }
+
if (!flatpak_run_add_dconf_args (bwrap, app_id, metakey, error))
return FALSE;
--
2.54.0

View File

@ -0,0 +1,95 @@
From 9ca818511d96dadbdb7b8deee1f8ef99f10914e6 Mon Sep 17 00:00:00 2001
From: Sebastian Wick <sebastian.wick@redhat.com>
Date: Fri, 6 Feb 2026 21:03:34 +0100
Subject: [PATCH] run: Add --(ro-)bind-fd options
Exposes the functionality added to flatpak_run_app in the previous
commit with two new options.
---
app/flatpak-builtins-run.c | 49 ++++++++++++++++++++++++++++++++++++--
1 file changed, 47 insertions(+), 2 deletions(-)
diff --git a/app/flatpak-builtins-run.c b/app/flatpak-builtins-run.c
index 7190f1f7..7edd7326 100644
--- a/app/flatpak-builtins-run.c
+++ b/app/flatpak-builtins-run.c
@@ -63,6 +63,46 @@ static char *opt_app_path;
static int opt_app_fd = -1;
static char *opt_usr_path;
static int opt_usr_fd = -1;
+static GArray *opt_bind_fds = NULL;
+static GArray *opt_ro_bind_fds = NULL;
+
+static gboolean
+option_bind_fd_cb (const char *option_name,
+ const char *value,
+ gpointer data,
+ GError **error)
+{
+ glnx_autofd int fd = -1;
+
+ fd = flatpak_parse_fd (value, error);
+ if (fd < 0)
+ return FALSE;
+
+ if (fd < 3)
+ return glnx_throw (error, "File descriptors 0, 1, 2 are reserved");
+
+ g_array_append_val (opt_bind_fds, fd);
+ return TRUE;
+}
+
+static gboolean
+option_ro_bind_fd_cb (const char *option_name,
+ const char *value,
+ gpointer data,
+ GError **error)
+{
+ glnx_autofd int fd = -1;
+
+ fd = flatpak_parse_fd (value, error);
+ if (fd < 0)
+ return FALSE;
+
+ if (fd < 3)
+ return glnx_throw (error, "File descriptors 0, 1, 2 are reserved");
+
+ g_array_append_val (opt_ro_bind_fds, fd);
+ return TRUE;
+}
static GOptionEntry options[] = {
{ "arch", 0, 0, G_OPTION_ARG_STRING, &opt_arch, N_("Arch to use"), N_("ARCH") },
@@ -93,6 +133,8 @@ static GOptionEntry options[] = {
{ "app-fd", 0, 0, G_OPTION_ARG_INT, &opt_app_fd, N_("Use FD instead of the app's /app"), N_("FD") },
{ "usr-path", 0, 0, G_OPTION_ARG_FILENAME, &opt_usr_path, N_("Use PATH instead of the runtime's /usr"), N_("PATH") },
{ "usr-fd", 0, 0, G_OPTION_ARG_INT, &opt_usr_fd, N_("Use FD instead of the runtime's /usr"), N_("FD") },
+ { "bind-fd", 0, 0, G_OPTION_ARG_CALLBACK | G_OPTION_FLAG_HIDDEN, &option_bind_fd_cb, N_("Bind mount the file or directory referred to by FD to its canonicalized path"), N_("FD") },
+ { "ro-bind-fd", 0, 0, G_OPTION_ARG_CALLBACK | G_OPTION_FLAG_HIDDEN, &option_ro_bind_fd_cb, N_("Bind mount the file or directory referred to by FD read-only to its canonicalized path"), N_("FD") },
{ NULL }
};
@@ -117,6 +159,9 @@ flatpak_builtin_run (int argc, char **argv, GCancellable *cancellable, GError **
glnx_autofd int app_fd = -1;
glnx_autofd int usr_fd = -1;
+ opt_bind_fds = g_array_new (FALSE, FALSE, sizeof (int));
+ opt_ro_bind_fds = g_array_new (FALSE, FALSE, sizeof (int));
+
context = g_option_context_new (_("APP [ARGUMENT…] - Run an app"));
g_option_context_set_translation_domain (context, GETTEXT_PACKAGE);
@@ -380,8 +425,8 @@ flatpak_builtin_run (int argc, char **argv, GCancellable *cancellable, GError **
rest_argc - 1,
opt_instance_id_fd,
NULL,
- NULL,
- NULL,
+ opt_bind_fds,
+ opt_ro_bind_fds,
cancellable,
error))
return FALSE;
--
2.54.0

View File

@ -0,0 +1,601 @@
From 9958b547bea1e9335cb91af61a239d1a7039387e Mon Sep 17 00:00:00 2001
From: Sebastian Wick <sebastian.wick@redhat.com>
Date: Fri, 6 Feb 2026 21:03:58 +0100
Subject: [PATCH] portal: Use --bind-fd, --app-fd and --usr-fd options to avoid
races
Now that flatpak_run_app accepts fds for app and runtime deploy, as well
as bind and ro-bind fds, and flatpak-run exposes the functionality, we
can finally hook this all up to the flatpak portal!
---
portal/flatpak-portal.c | 438 +++++++++++++++-------------------------
1 file changed, 162 insertions(+), 276 deletions(-)
diff --git a/portal/flatpak-portal.c b/portal/flatpak-portal.c
index e5701895..a4378f77 100644
--- a/portal/flatpak-portal.c
+++ b/portal/flatpak-portal.c
@@ -550,195 +550,60 @@ child_setup_func (gpointer user_data)
}
static gboolean
-is_valid_expose (const char *expose,
- GError **error)
+validate_opath_fd (int fd,
+ gboolean needs_writable,
+ GError **error)
{
- /* No subdirs or absolute paths */
- if (expose[0] == '/')
- {
- g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS,
- "Invalid sandbox expose: absolute paths not allowed");
- return FALSE;
- }
- else if (strchr (expose, '/'))
- {
- g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS,
- "Invalid sandbox expose: subdirectories not allowed");
- return FALSE;
- }
-
- return TRUE;
-}
-
-static char *
-filesystem_arg (const char *path,
- gboolean readonly)
-{
- g_autoptr(GString) s = g_string_new ("--filesystem=");
- const char *p;
-
- for (p = path; *p != 0; p++)
- {
- if (*p == ':')
- g_string_append (s, "\\:");
- else
- g_string_append_c (s, *p);
- }
-
- if (readonly)
- g_string_append (s, ":ro");
-
- return g_string_free (g_steal_pointer (&s), FALSE);
-}
-
-
-static char *
-filesystem_sandbox_arg (const char *path,
- const char *sandbox,
- gboolean readonly)
-{
- g_autoptr(GString) s = g_string_new ("--filesystem=");
- const char *p;
-
- for (p = path; *p != 0; p++)
- {
- if (*p == ':')
- g_string_append (s, "\\:");
- else
- g_string_append_c (s, *p);
- }
-
- g_string_append (s, "/sandbox/");
-
- for (p = sandbox; *p != 0; p++)
- {
- if (*p == ':')
- g_string_append (s, "\\:");
- else
- g_string_append_c (s, *p);
- }
-
- if (readonly)
- g_string_append (s, ":ro");
-
- return g_string_free (g_steal_pointer (&s), FALSE);
-}
-
-static char *
-bubblewrap_remap_path (const char *path)
-{
- if (g_str_has_prefix (path, "/newroot/"))
- path = path + strlen ("/newroot");
- return g_strdup (path);
-}
-
-static char *
-verify_proc_self_fd (const char *proc_path,
- GError **error)
-{
- char path_buffer[PATH_MAX + 1];
- ssize_t symlink_size;
-
- symlink_size = readlink (proc_path, path_buffer, PATH_MAX);
- if (symlink_size < 0)
- return glnx_null_throw_errno_prefix (error, "readlink");
-
- path_buffer[symlink_size] = 0;
-
- /* All normal paths start with /, but some weird things
- don't, such as socket:[27345] or anon_inode:[eventfd].
- We don't support any of these */
- if (path_buffer[0] != '/')
- return glnx_null_throw (error, "%s resolves to non-absolute path %s",
- proc_path, path_buffer);
-
- /* File descriptors to actually deleted files have " (deleted)"
- appended to them. This also happens to some fake fd types
- like shmem which are "/<name> (deleted)". All such
- files are considered invalid. Unfortunatelly this also
- matches files with filenames that actually end in " (deleted)",
- but there is not much to do about this. */
- if (g_str_has_suffix (path_buffer, " (deleted)"))
- return glnx_null_throw (error, "%s resolves to deleted path %s",
- proc_path, path_buffer);
-
- /* remap from sandbox to host if needed */
- return bubblewrap_remap_path (path_buffer);
-}
-
-static char *
-get_path_for_fd (int fd,
- gboolean *writable_out,
- GError **error)
-{
- g_autofree char *proc_path = NULL;
int fd_flags;
struct stat st_buf;
- struct stat real_st_buf;
- g_autofree char *path = NULL;
- gboolean writable = FALSE;
- int read_access_mode;
+ int access_mode;
/* Must be able to get fd flags */
fd_flags = fcntl (fd, F_GETFL);
- if (fd_flags == -1)
- return glnx_null_throw_errno_prefix (error, "fcntl F_GETFL");
+ if (fd_flags < 0)
+ return glnx_throw_errno_prefix (error, "Failed to get fd flags");
/* Must be O_PATH */
if ((fd_flags & O_PATH) != O_PATH)
- return glnx_null_throw (error, "not opened with O_PATH");
-
- /* We don't want to allow exposing symlinks, because if they are
- * under the callers control they could be changed between now and
- * starting the child allowing it to point anywhere, so enforce NOFOLLOW.
- * and verify that stat is not a link.
- */
- if ((fd_flags & O_NOFOLLOW) != O_NOFOLLOW)
- return glnx_null_throw (error, "not opened with O_NOFOLLOW");
+ {
+ g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "File descriptor is not O_PATH");
+ return FALSE;
+ }
/* Must be able to fstat */
if (fstat (fd, &st_buf) < 0)
- return glnx_null_throw_errno_prefix (error, "fstat");
-
- /* As per above, no symlinks */
- if (S_ISLNK (st_buf.st_mode))
- return glnx_null_throw (error, "is a symbolic link");
+ return glnx_throw_errno_prefix (error, "Failed to fstat");
- proc_path = g_strdup_printf ("/proc/self/fd/%d", fd);
-
- /* Must be able to read valid path from /proc/self/fd */
- /* This is an absolute and (at least at open time) symlink-expanded path */
- path = verify_proc_self_fd (proc_path, error);
- if (path == NULL)
- return NULL;
+ access_mode = R_OK;
+ if (S_ISDIR (st_buf.st_mode))
+ access_mode |= X_OK;
- /* Verify that this is the same file as the app opened */
- if (stat (path, &real_st_buf) < 0 ||
- st_buf.st_dev != real_st_buf.st_dev ||
- st_buf.st_ino != real_st_buf.st_ino)
- {
- /* Different files on the inside and the outside, reject the request */
- return glnx_null_throw (error,
- "different file inside and outside sandbox");
- }
+ if (needs_writable)
+ access_mode |= W_OK;
- read_access_mode = R_OK;
- if (S_ISDIR (st_buf.st_mode))
- read_access_mode |= X_OK;
+ /* Must be able to access readable and potentially writable */
+ if (faccessat (fd, "", access_mode, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW) != 0)
+ return glnx_throw_errno_prefix (error, "Bad access mode");
- /* Must be able to access the path via the sandbox supplied O_PATH fd,
- which applies the sandbox side mount options (like readonly). */
- if (access (proc_path, read_access_mode) != 0)
- return glnx_null_throw (error, "not %s in sandbox",
- read_access_mode & X_OK ? "accessible" : "readable");
+ return TRUE;
+}
- if (access (proc_path, W_OK) == 0)
- writable = TRUE;
+static int
+fd_map_remap_fd (GArray *fd_map,
+ int *max_fd_in_out,
+ int fd)
+{
+ FdMapEntry fd_map_entry;
- if (writable_out != NULL)
- *writable_out = writable;
+ /* Use a fd that hasn't been used yet. We might have to reshuffle
+ * fd_map_entry.to, a bit later. */
+ fd_map_entry.from = fd;
+ fd_map_entry.to = ++(*max_fd_in_out);
+ fd_map_entry.final = fd_map_entry.to;
+ g_array_append_val (fd_map, fd_map_entry);
- return g_steal_pointer (&path);
+ return fd_map_entry.final;
}
static gboolean
@@ -793,9 +658,12 @@ handle_spawn (PortalFlatpak *object,
gboolean devel;
gboolean empty_app;
g_autoptr(GString) env_string = g_string_new ("");
- glnx_autofd int env_fd = -1;
const char *flatpak;
gboolean testing = FALSE;
+ g_autoptr(GArray) owned_fds = NULL;
+ g_autoptr(GArray) expose_fds = NULL;
+ g_autoptr(GArray) expose_fds_ro = NULL;
+ glnx_autofd int instance_sandbox_fd = -1;
child_setup_data.instance_id_fd = -1;
child_setup_data.env_fd = -1;
@@ -918,29 +786,6 @@ handle_spawn (PortalFlatpak *object,
return G_DBUS_METHOD_INVOCATION_HANDLED;
}
- for (i = 0; sandbox_expose != NULL && sandbox_expose[i] != NULL; i++)
- {
- const char *expose = sandbox_expose[i];
-
- g_debug ("exposing %s", expose);
- if (!is_valid_expose (expose, &error))
- {
- g_dbus_method_invocation_return_gerror (invocation, error);
- return G_DBUS_METHOD_INVOCATION_HANDLED;
- }
- }
-
- for (i = 0; sandbox_expose_ro != NULL && sandbox_expose_ro[i] != NULL; i++)
- {
- const char *expose = sandbox_expose_ro[i];
- g_debug ("exposing %s", expose);
- if (!is_valid_expose (expose, &error))
- {
- g_dbus_method_invocation_return_gerror (invocation, error);
- return G_DBUS_METHOD_INVOCATION_HANDLED;
- }
- }
-
g_debug ("Running spawn command %s", arg_argv[0]);
n_fds = 0;
@@ -1112,10 +957,14 @@ handle_spawn (PortalFlatpak *object,
g_string_append_c (env_string, '\0');
}
+ owned_fds = g_array_new (FALSE, FALSE, sizeof (int));
+ g_array_set_clear_func (owned_fds, (GDestroyNotify) glnx_close_fd);
+
if (env_string->len > 0)
{
- FdMapEntry fd_map_entry;
g_auto(GLnxTmpfile) env_tmpf = { 0, };
+ int env_fd = -1;
+ int remapped_fd;
if (!flatpak_buffer_to_sealed_memfd_or_tmpfile (&env_tmpf, "environ",
env_string->str,
@@ -1126,16 +975,12 @@ handle_spawn (PortalFlatpak *object,
}
env_fd = glnx_steal_fd (&env_tmpf.fd);
+ g_array_append_val (owned_fds, env_fd);
- /* Use a fd that hasn't been used yet. We might have to reshuffle
- * fd_map_entry.to, a bit later. */
- fd_map_entry.from = env_fd;
- fd_map_entry.to = ++max_fd;
- fd_map_entry.final = fd_map_entry.to;
- g_array_append_val (fd_map, fd_map_entry);
+ remapped_fd = fd_map_remap_fd (fd_map, &max_fd, env_fd);
g_ptr_array_add (flatpak_argv,
- g_strdup_printf ("--env-fd=%d", fd_map_entry.final));
+ g_strdup_printf ("--env-fd=%d", remapped_fd));
}
for (i = 0; unset_env != NULL && unset_env[i] != NULL; i++)
@@ -1243,54 +1088,100 @@ handle_spawn (PortalFlatpak *object,
else
g_ptr_array_add (flatpak_argv, g_strdup ("--unshare=network"));
+ expose_fds = g_array_new (FALSE, FALSE, sizeof (int));
+ expose_fds_ro = g_array_new (FALSE, FALSE, sizeof (int));
+
+ if (instance_path != NULL)
+ {
+ glnx_autofd int instance_fd = -1;
+
+ instance_fd = glnx_chaseat (AT_FDCWD, instance_path,
+ GLNX_CHASE_DEFAULT,
+ &error);
+ if (instance_fd < 0)
+ {
+ g_dbus_method_invocation_return_gerror (invocation, error);
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
+ }
+
+ if (!glnx_ensure_dir (instance_fd, "sandbox", 0700, &error))
+ {
+ g_warning ("Unable to create %s/sandbox: %s", instance_path, error->message);
+ g_clear_error (&error);
+ }
+
+ instance_sandbox_fd = glnx_chaseat (instance_fd, "sandbox",
+ GLNX_CHASE_RESOLVE_NO_SYMLINKS,
+ &error);
+ if (instance_sandbox_fd < 0)
+ {
+ g_dbus_method_invocation_return_gerror (invocation, error);
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
+ }
+ }
- if (instance_path)
+ for (i = 0; sandbox_expose != NULL && sandbox_expose[i] != NULL; i++)
{
- for (i = 0; sandbox_expose != NULL && sandbox_expose[i] != NULL; i++)
- g_ptr_array_add (flatpak_argv,
- filesystem_sandbox_arg (instance_path, sandbox_expose[i], FALSE));
- for (i = 0; sandbox_expose_ro != NULL && sandbox_expose_ro[i] != NULL; i++)
- g_ptr_array_add (flatpak_argv,
- filesystem_sandbox_arg (instance_path, sandbox_expose_ro[i], TRUE));
+ int expose_fd;
+
+ g_assert (instance_sandbox_fd >= 0);
+
+ expose_fd = glnx_chaseat (instance_sandbox_fd, sandbox_expose[i],
+ GLNX_CHASE_RESOLVE_NO_SYMLINKS |
+ GLNX_CHASE_RESOLVE_BENEATH,
+ &error);
+ if (expose_fd < 0)
+ {
+ g_dbus_method_invocation_return_gerror (invocation, error);
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
+ }
+
+ g_array_append_val (expose_fds, expose_fd);
+ /* transfers ownership, can't g_steal_fd with g_array_append_val */
+ g_array_append_val (owned_fds, expose_fd);
}
for (i = 0; sandbox_expose_ro != NULL && sandbox_expose_ro[i] != NULL; i++)
{
- const char *expose = sandbox_expose_ro[i];
- g_debug ("exposing %s", expose);
+ int expose_fd;
+
+ g_assert (instance_sandbox_fd >= 0);
+
+ expose_fd = glnx_chaseat (instance_sandbox_fd, sandbox_expose_ro[i],
+ GLNX_CHASE_RESOLVE_NO_SYMLINKS |
+ GLNX_CHASE_RESOLVE_BENEATH,
+ &error);
+ if (expose_fd < 0)
+ {
+ g_dbus_method_invocation_return_gerror (invocation, error);
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
+ }
+
+ g_array_append_val (expose_fds_ro, expose_fd);
+ /* transfers ownership, can't g_steal_fd with g_array_append_val */
+ g_array_append_val (owned_fds, expose_fd);
}
if (sandbox_expose_fd != NULL)
{
gsize len = g_variant_n_children (sandbox_expose_fd);
+
for (i = 0; i < len; i++)
{
gint32 handle;
+
g_variant_get_child (sandbox_expose_fd, i, "h", &handle);
- if (handle >= 0 && handle < fds_len)
+ if (handle >= 0 && handle < fds_len &&
+ validate_opath_fd (fds[handle], TRUE, &error))
{
- int handle_fd = fds[handle];
- g_autofree char *path = NULL;
- gboolean writable = FALSE;
-
- path = get_path_for_fd (handle_fd, &writable, &error);
-
- if (path)
- {
- g_ptr_array_add (flatpak_argv, filesystem_arg (path, !writable));
- }
- else
- {
- g_debug ("unable to get path for sandbox-exposed fd %d, ignoring: %s",
- handle_fd, error->message);
- g_clear_error (&error);
- }
+ g_array_append_val (expose_fds, fds[handle]);
}
else
{
+ g_debug ("Invalid sandbox expose fd: %s", error->message);
g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR,
G_DBUS_ERROR_INVALID_ARGS,
- "No file descriptor for handle %d",
+ "No valid file descriptor for handle %d",
handle);
return G_DBUS_METHOD_INVOCATION_HANDLED;
}
@@ -1300,31 +1191,20 @@ handle_spawn (PortalFlatpak *object,
if (sandbox_expose_fd_ro != NULL)
{
gsize len = g_variant_n_children (sandbox_expose_fd_ro);
+
for (i = 0; i < len; i++)
{
gint32 handle;
+
g_variant_get_child (sandbox_expose_fd_ro, i, "h", &handle);
- if (handle >= 0 && handle < fds_len)
+ if (handle >= 0 && handle < fds_len &&
+ validate_opath_fd (fds[handle], FALSE, &error))
{
- int handle_fd = fds[handle];
- g_autofree char *path = NULL;
- gboolean writable = FALSE;
-
- path = get_path_for_fd (handle_fd, &writable, &error);
-
- if (path)
- {
- g_ptr_array_add (flatpak_argv, filesystem_arg (path, TRUE));
- }
- else
- {
- g_debug ("unable to get path for sandbox-exposed fd %d, ignoring: %s",
- handle_fd, error->message);
- g_clear_error (&error);
- }
+ g_array_append_val (expose_fds_ro, fds[handle]);
}
else
{
+ g_debug ("Invalid sandbox expose ro fd: %s", error->message);
g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR,
G_DBUS_ERROR_INVALID_ARGS,
"No file descriptor for handle %d",
@@ -1334,20 +1214,40 @@ handle_spawn (PortalFlatpak *object,
}
}
+ for (i = 0; i < expose_fds->len; i++)
+ {
+ int remapped_fd;
+
+ remapped_fd = fd_map_remap_fd (fd_map, &max_fd, expose_fds->data[i]);
+
+ g_ptr_array_add (flatpak_argv, g_strdup_printf ("--bind-fd=%d",
+ remapped_fd));
+ }
+
+ for (i = 0; i < expose_fds_ro->len; i++)
+ {
+ int remapped_fd;
+
+ remapped_fd = fd_map_remap_fd (fd_map, &max_fd, expose_fds_ro->data[i]);
+
+ g_ptr_array_add (flatpak_argv, g_strdup_printf ("--ro-bind-fd=%d",
+ remapped_fd));
+ }
+
empty_app = (arg_flags & FLATPAK_SPAWN_FLAGS_EMPTY_APP) != 0;
+ if (empty_app && app_fd != NULL)
+ {
+ g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR,
+ G_DBUS_ERROR_INVALID_ARGS,
+ "app-fd and EMPTY_APP cannot both be used");
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
+ }
+
if (app_fd != NULL)
{
+ int remapped_fd;
gint32 handle = g_variant_get_handle (app_fd);
- g_autofree char *path = NULL;
-
- if (empty_app)
- {
- g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR,
- G_DBUS_ERROR_INVALID_ARGS,
- "app-fd and EMPTY_APP cannot both be used");
- return G_DBUS_METHOD_INVOCATION_HANDLED;
- }
if (handle >= fds_len || handle < 0)
{
@@ -1359,18 +1259,11 @@ handle_spawn (PortalFlatpak *object,
}
g_assert (fds != NULL); /* otherwise fds_len would be 0 */
- path = get_path_for_fd (fds[handle], NULL, &error);
- if (path == NULL)
- {
- g_prefix_error (&error, "Unable to convert /app fd %d into path: ",
- fds[handle]);
- g_dbus_method_invocation_return_gerror (invocation, error);
- return G_DBUS_METHOD_INVOCATION_HANDLED;
- }
+ remapped_fd = fd_map_remap_fd (fd_map, &max_fd, fds[handle]);
- g_debug ("Using %s as /app instead of app", path);
- g_ptr_array_add (flatpak_argv, g_strdup_printf ("--app-path=%s", path));
+ g_ptr_array_add (flatpak_argv, g_strdup_printf ("--app-fd=%d",
+ remapped_fd));
}
else if (empty_app)
{
@@ -1379,8 +1272,8 @@ handle_spawn (PortalFlatpak *object,
if (usr_fd != NULL)
{
+ int remapped_fd;
gint32 handle = g_variant_get_handle (usr_fd);
- g_autofree char *path = NULL;
if (handle >= fds_len || handle < 0)
{
@@ -1392,18 +1285,11 @@ handle_spawn (PortalFlatpak *object,
}
g_assert (fds != NULL); /* otherwise fds_len would be 0 */
- path = get_path_for_fd (fds[handle], NULL, &error);
- if (path == NULL)
- {
- g_prefix_error (&error, "Unable to convert /usr fd %d into path: ",
- fds[handle]);
- g_dbus_method_invocation_return_gerror (invocation, error);
- return G_DBUS_METHOD_INVOCATION_HANDLED;
- }
+ remapped_fd = fd_map_remap_fd (fd_map, &max_fd, fds[handle]);
- g_debug ("Using %s as /usr instead of runtime", path);
- g_ptr_array_add (flatpak_argv, g_strdup_printf ("--usr-path=%s", path));
+ g_ptr_array_add (flatpak_argv, g_strdup_printf ("--usr-fd=%d",
+ remapped_fd));
}
g_ptr_array_add (flatpak_argv, g_strdup_printf ("--runtime=%s", runtime_parts[1]));
--
2.54.0

View File

@ -0,0 +1,38 @@
From 8d1e978c6a582b1603dccb7fe9340e15787f6297 Mon Sep 17 00:00:00 2001
From: Xiangzhe <xiangzhedev@gmail.com>
Date: Wed, 8 Apr 2026 12:27:28 +0800
Subject: [PATCH] run: Fix checking wrong variable in runtime fd selection
In flatpak_run_app(), the else-if branch that handles
FLATPAK_RUN_APP_DEPLOY_USR_ORIGINAL was checking custom_app_fd instead
of custom_runtime_fd. When custom_app_fd is APP_EMPTY (-3) and
custom_runtime_fd is USR_ORIGINAL (-2), the condition would not match
and fall through to g_assert_not_reached(), aborting the process.
This broke sub-sandbox spawning with --app-path="" (empty app), which
is used by steam-runtime-check-requirements to verify that Flatpak's
sub-sandbox mechanism works.
Fixes: ac62ebe3 "run: Use O_PATH fds for the runtime and app deploy directories"
Helps: https://github.com/flatpak/flatpak/issues/6568
(cherry picked from commit 066babba75d355d077ea11091e5f65d3b0e0d818)
---
common/flatpak-run.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/common/flatpak-run.c b/common/flatpak-run.c
index 9087be23..4ffc5fa3 100644
--- a/common/flatpak-run.c
+++ b/common/flatpak-run.c
@@ -4350,7 +4350,7 @@ flatpak_run_app (FlatpakDecomposed *app_ref,
runtime_fd = custom_runtime_fd;
runtime_files = custom_runtime_files;
}
- else if (custom_app_fd == FLATPAK_RUN_APP_DEPLOY_USR_ORIGINAL)
+ else if (custom_runtime_fd == FLATPAK_RUN_APP_DEPLOY_USR_ORIGINAL)
{
original_runtime_target_path = "/usr";
runtime_fd = original_runtime_fd;
--
2.54.0

View File

@ -0,0 +1,90 @@
From a13a7415517cbf1d5073bde05e5b9c3c95de6b16 Mon Sep 17 00:00:00 2001
From: Sebastian Wick <sebastian.wick@redhat.com>
Date: Fri, 9 Jan 2026 19:24:44 +0100
Subject: [PATCH] utils: Only remove cached files in the cache directory
The function flatpak_switch_symlink_and_remove is used to implement a
cache for ld.so (regenerate_ld_cache). If the active symlink changes to
a new cache file, the old cache file is supposed to get removed.
The symlink still points to the old cache file, so we would remove the
file that it points to and then point at the new file.
Because the symlink is under the app's control, the symlink can point
anywhere, and the removal happens in the host context, which allows an
app to remove arbitrary files on the host.
The filename of the cache files are checksums, which means that we can
ensure that the link is a file in the same directory of the link by
checking that it only contains the chars a-zA-Z0-9.
(cherry picked from commit c97905c8188ddaad01ee146b57bba6c3fa294113):
---
common/flatpak-utils.c | 36 +++++++++++++++++++++++++++++++++---
1 file changed, 33 insertions(+), 3 deletions(-)
diff --git a/common/flatpak-utils.c b/common/flatpak-utils.c
index 999d88fd..0ab84064 100644
--- a/common/flatpak-utils.c
+++ b/common/flatpak-utils.c
@@ -1344,6 +1344,22 @@ out:
return ret;
}
+static gboolean
+flatpak_str_is_alphanumeric (const char *arg)
+{
+ while (*arg != '\0')
+ {
+ char c = *arg;
+
+ if (!g_ascii_isalnum (c))
+ return FALSE;
+
+ arg++;
+ }
+
+ return TRUE;
+}
+
/* This atomically replaces a symlink with a new value, removing the
* existing symlink target, if it exstis and is different from
* @target. This is atomic in the sense that we're guaranteed to
@@ -1353,6 +1369,9 @@ out:
* symlink for some reason, ending up with neither the old or the new
* target. That is fine if the reason for the symlink is keeping a
* cache though.
+ * The target shall only be a file in the same directory as the symlink, and
+ * shall only contain the characters a-zA-Z0-9. This is so that the target of
+ * the symlink that gets removed is in the same directory as the link.
*/
gboolean
flatpak_switch_symlink_and_remove (const char *symlink_path,
@@ -1396,10 +1415,21 @@ flatpak_switch_symlink_and_remove (const char *symlink_path,
g_autofree char *old_target = flatpak_readlink (tmp_path, error);
if (old_target == NULL)
return FALSE;
- if (strcmp (old_target, target) != 0) /* Don't remove old file if its the same as the new one */
+
+ /* Don't remove old file if its the same as the new one */
+ if (strcmp (old_target, target) != 0)
{
- g_autofree char *old_target_path = g_build_filename (symlink_dir, old_target, NULL);
- unlink (old_target_path);
+ if (flatpak_str_is_alphanumeric (old_target))
+ {
+ g_autofree char *old_target_path = NULL;
+
+ old_target_path = g_build_filename (symlink_dir, old_target, NULL);
+ unlink (old_target_path);
+ }
+ else
+ {
+ g_warning ("Refusing to delete old link target %s", old_target);
+ }
}
}
else if (errno != ENOENT)
--
2.54.0

View File

@ -0,0 +1,50 @@
From 6a85e0591b6fbc01d9afe6d81bf6748ce2f3e8ac Mon Sep 17 00:00:00 2001
From: Sebastian Wick <sebastian.wick@redhat.com>
Date: Mon, 12 Jan 2026 17:38:02 +0100
Subject: [PATCH] utils: Do not follow symlinks in local_open_file
We use local_open_file in the context of the system helper to open
files written by a user. This means that we want to prevent DOS and
exposing files which only the system helper has access to.
To prevent DOS and avoid side-effects, the file is opened with
O_NONBLOCK and O_NOCTTY.
To prevent leaking files, the file is supposed to not open symlinks.
This part, we failed at. We check if the opened file is a regular file,
but what we actually checked is, if the file a symlink might point at is
a regular file.
Fix this by also specifying O_NOFOLLOW in openat.
(cherry picked from commit 4a678f463b455c585d38ac4cf4d994e7ce710f8e):
---
common/flatpak-oci-registry.c | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/common/flatpak-oci-registry.c b/common/flatpak-oci-registry.c
index 2dbd46b5..56853142 100644
--- a/common/flatpak-oci-registry.c
+++ b/common/flatpak-oci-registry.c
@@ -251,6 +251,9 @@ flatpak_oci_registry_new (const char *uri,
return oci_registry;
}
+/* Carefully opens a file from a base directory and subpath,
+ * making sure that its not a symlink, pipe, etc.
+ */
static int
local_open_file (int dfd,
const char *subpath,
@@ -262,7 +265,7 @@ local_open_file (int dfd,
struct stat tmp_st_buf;
do
- fd = openat (dfd, subpath, O_RDONLY | O_NONBLOCK | O_CLOEXEC | O_NOCTTY);
+ fd = openat (dfd, subpath, O_NOFOLLOW | O_RDONLY | O_NONBLOCK | O_CLOEXEC | O_NOCTTY);
while (G_UNLIKELY (fd == -1 && errno == EINTR));
if (fd == -1)
{
--
2.54.0

View File

@ -0,0 +1,152 @@
From 640c9c2725f67220dd25a5148d6f2bb6fc7ddcf5 Mon Sep 17 00:00:00 2001
From: Sebastian Wick <sebastian.wick@redhat.com>
Date: Sat, 7 Feb 2026 21:57:30 +0100
Subject: [PATCH] system-helper: Only remove an ongoing pull if users match
The code would always remove a pull from the hashtable, and then check if the
users match and abort if they don't. Either way, the pull gets dropped.
Fix this by only removing the pull if the dir and the user match.
(cherry picked from commit a27ec46e8c0ab0ae162f2aa3142dccb6b79d9211):
---
system-helper/flatpak-system-helper.c | 85 ++++++++++++---------------
1 file changed, 36 insertions(+), 49 deletions(-)
diff --git a/system-helper/flatpak-system-helper.c b/system-helper/flatpak-system-helper.c
index 03410e84..5aa3e2ab 100644
--- a/system-helper/flatpak-system-helper.c
+++ b/system-helper/flatpak-system-helper.c
@@ -355,23 +355,31 @@ get_connection_uid (GDBusMethodInvocation *invocation, uid_t *out_uid, GError **
}
static OngoingPull *
-take_ongoing_pull_by_dir (const gchar *src_dir)
+take_ongoing_pull_by_dir (const char *src_dir,
+ uid_t uid)
{
OngoingPull *pull = NULL;
- gpointer key, value;
+ char *cache_dir_name = NULL;
G_LOCK (cache_dirs_in_use);
- /* Keep src_dir key inside hashtable but remove its OngoingPull
- * value and set it to NULL. This way src_dir is still marked
- * as in-use (as Deploy or CancelPull might be executing on it,
- * whereas OngoingPull ownership is transferred to respective
- * callers. */
- if (g_hash_table_steal_extended (cache_dirs_in_use, src_dir, &key, &value))
- {
- if (value)
+ if (g_hash_table_steal_extended (cache_dirs_in_use, src_dir,
+ (gpointer) &cache_dir_name,
+ (gpointer) &pull))
+ {
+ if (pull && pull->uid == uid)
{
- g_hash_table_insert (cache_dirs_in_use, key, NULL);
- pull = value;
+ /* Keep src_dir key inside hashtable but remove its OngoingPull
+ * value and set it to NULL. This way src_dir is still marked
+ * as in-use (as Deploy or CancelPull might be executing on it,
+ * whereas OngoingPull ownership is transferred to respective
+ * callers. */
+ g_hash_table_insert (cache_dirs_in_use, cache_dir_name, NULL);
+ }
+ else
+ {
+ /* Otherwise, re-insert what is currently there and return NULL */
+ g_hash_table_insert (cache_dirs_in_use, cache_dir_name, pull);
+ pull = NULL;
}
}
G_UNLOCK (cache_dirs_in_use);
@@ -423,6 +431,9 @@ handle_deploy (FlatpakSystemHelper *object,
if (strlen (arg_repo_path) > 0)
{
+ g_autoptr(GError) local_error = NULL;
+ uid_t uid;
+
if (!g_file_query_exists (repo_file, NULL))
{
g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS,
@@ -430,30 +441,17 @@ handle_deploy (FlatpakSystemHelper *object,
return G_DBUS_METHOD_INVOCATION_HANDLED;
}
+ /* Ensure that pull's uid is same as the caller's uid */
+ if (!get_connection_uid (invocation, &uid, &local_error))
+ {
+ g_dbus_method_invocation_return_gerror (invocation, local_error);
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
+ }
+
src_dir = g_path_get_dirname (arg_repo_path);
- ongoing_pull = take_ongoing_pull_by_dir (src_dir);
+ ongoing_pull = take_ongoing_pull_by_dir (src_dir, uid);
if (ongoing_pull != NULL)
{
- g_autoptr(GError) local_error = NULL;
- uid_t uid;
-
- /* Ensure that pull's uid is same as the caller's uid */
- if (!get_connection_uid (invocation, &uid, &local_error))
- {
- g_dbus_method_invocation_return_gerror (invocation, local_error);
- return G_DBUS_METHOD_INVOCATION_HANDLED;
- }
- else
- {
- if (ongoing_pull->uid != uid)
- {
- g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_FAILED,
- "Ongoing pull's uid(%d) does not match with peer uid(%d)",
- ongoing_pull->uid, uid);
- return G_DBUS_METHOD_INVOCATION_HANDLED;
- }
- }
-
terminate_revokefs_backend (ongoing_pull);
if (!flatpak_canonicalize_permissions (AT_FDCWD,
@@ -735,31 +733,20 @@ handle_cancel_pull (FlatpakSystemHelper *object,
return G_DBUS_METHOD_INVOCATION_HANDLED;
}
- ongoing_pull = take_ongoing_pull_by_dir (arg_src_dir);
- if (ongoing_pull == NULL)
+ if (!get_connection_uid (invocation, &uid, &error))
{
- g_set_error (&error, G_DBUS_ERROR, G_DBUS_ERROR_FAILED,
- "Cannot find ongoing pull to cancel at %s", arg_src_dir);
g_dbus_method_invocation_return_gerror (invocation, error);
return G_DBUS_METHOD_INVOCATION_HANDLED;
}
- /* Ensure that pull's uid is same as the caller's uid */
- if (!get_connection_uid (invocation, &uid, &error))
+ ongoing_pull = take_ongoing_pull_by_dir (arg_src_dir, uid);
+ if (ongoing_pull == NULL)
{
+ g_set_error (&error, G_DBUS_ERROR, G_DBUS_ERROR_FAILED,
+ "Cannot find ongoing pull to cancel at %s", arg_src_dir);
g_dbus_method_invocation_return_gerror (invocation, error);
return G_DBUS_METHOD_INVOCATION_HANDLED;
}
- else
- {
- if (ongoing_pull->uid != uid)
- {
- g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_FAILED,
- "Ongoing pull's uid(%d) does not match with peer uid(%d)",
- ongoing_pull->uid, uid);
- return G_DBUS_METHOD_INVOCATION_HANDLED;
- }
- }
ongoing_pull->preserve_pull = (arg_flags & FLATPAK_HELPER_CANCEL_PULL_FLAGS_PRESERVE_PULL) != 0;
ongoing_pull_free (ongoing_pull);
--
2.54.0

File diff suppressed because it is too large Load Diff

View File

@ -3,7 +3,7 @@
Name: flatpak
Version: 1.12.9
Release: 3%{?dist}
Release: 4%{?dist}
Summary: Application deployment framework for desktop apps
License: LGPLv2+
@ -21,6 +21,44 @@ Patch0: flatpak-Revert-selinux-Permit-using-systemd-userdbd.patch
# Backported upstream patch for CVE-2024-42472
Patch1: flatpak-1.12.x-CVE-2024-42472.patch
# CVE-2026-34078
Patch100: flatpak-1.12.x-update-libglnx-for-glnx-chaseseat.patch
Patch101: CVE-2026-34078-1-flatpak-bwrap-add-dup-ing-variant-flatpak-bwrap-add-args-data-fd-dup.patch
Patch102: CVE-2026-34078-2-utils-add-flatpak-parse-fd.patch
Patch103: CVE-2026-34078-3-flatpak-bwrap-use-glnx-close-fd-as-clear-func.patch
Patch104: CVE-2026-34078-4-run-use-o-path-fds-for-the-runtime-and-app-deploy-directories.patch
Patch105: CVE-2026-34078-5-run-add-usr-fd-and-app-fd-options.patch
Patch106: CVE-2026-34078-6-run-add-ro-bind-fds-to-flatpak-run-app.patch
Patch107: CVE-2026-34078-7-run-add-ro-bind-fd-options.patch
Patch108: CVE-2026-34078-8-portal-use-bind-fd-app-fd-and-usr-fd-options-to-avoid-races.patch
Patch109: CVE-2026-34078-9-run-fix-checking-wrong-variable-in-runtime-fd-selection.patch
Patch110: CVE-2026-34078-10-run-mount-original-app-on-run-parent-app-when-using-app-path.patch
Patch111: CVE-2026-34078-11-portal-update-max-fd-after-creating-the-instance-id-pipe.patch
Patch112: CVE-2026-34078-12-run-fix-fd-tracking-in-flatpak-run-add-app-info-args.patch
Patch113: CVE-2026-34078-13-utils-improve-error-message-when-passing-an-fd-numer-which-is-not-a-fd.patch
Patch114: CVE-2026-34078-14-run-do-not-close-bind-ro-bind.patch
Patch115: CVE-2026-34078-15-run-use-the-same-fd-validation-for-all-fd-options.patch
Patch116: CVE-2026-34078-16-run-add-bind-fd-and-ro-bind-fd-binds-after-all-other-binds.patch
Patch117: CVE-2026-34078-17-portal-use-g-array-index-to-read-from-expose-fds-expose-fds-ro.patch
Patch118: CVE-2026-34078-18-run-fix-backport-mistake.patch
Patch119: CVE-2026-34078-19-run-cope-with-an-empty-runtime.patch
Patch120: CVE-2026-34078-20-dir-in-apply-extra-data-don-t-assume-there-is-always-a-runtime.patch
Patch121: CVE-2026-34078-21-utils-add-flatpak-set-cloexec.patch
Patch122: CVE-2026-34078-22-run-context-mark-fd-arguments-as-close-on-exec.patch
Patch123: CVE-2026-34078-23-utils-move-flatpak-get-path-for-fd-to-here.patch
Patch124: CVE-2026-34078-24-portal-avoid-crash-if-sandbox-expose-ro-fd-is-out-of-range.patch
Patch125: CVE-2026-34078-25-portal-log-and-ignore-unusable-sandbox-expose-fds-instead-of-erroring.patch
Patch126: CVE-2026-34078-26-portal-reinstate-flatpak-get-path-for-fd-checks.patch
Patch127: CVE-2026-34078-27-libtest-allow-adding-a-new-ref-to-an-existing-temporary-ostree-repo.patch
Patch128: CVE-2026-34078-28-app-context-never-close-fds-0-1-or-2.patch
Patch129: CVE-2026-34078-29-app-context-factor-out-flatpak-accept-fd-argument.patch
# CVE-2026-34079
Patch130: CVE-2026-34079-1-utils-only-remove-cached-files-in-the-cache-directory.patch
Patch131: CVE-2026-34079-2-utils-do-not-follow-symlinks-in-local-open-file.patch
Patch132: CVE-2026-34079-3-system-helper-only-remove-an-ongoing-pull-if-users-match.patch
BuildRequires: pkgconfig(appstream-glib)
BuildRequires: pkgconfig(dconf)
BuildRequires: pkgconfig(fuse)
@ -280,6 +318,12 @@ fi
%changelog
* Wed May 20 2026 Jan Grulich <jgrulich@redhat.com> - 1.12.9-4
- Fix arbitrary code execution via crafted symlinks in sandbox-expose options
Resolves: RHEL-165633
- Fix arbitrary file deletion on host via improper cache file path validation
Resolves: RHEL-170160
* Wed Sep 04 2024 Kalev Lember <klember@redhat.com> - 1.12.9-3
- Fix previous changelog entry