dir: Use fd-based traversal for marker files in deploy directories

The paths up to files/ are not attacker-controlled, and
g_file_replace_contents with G_FILE_CREATE_REPLACE_DESTINATION already
replaces symlinks rather than following them, so this is not a security
fix. The only reason why is that the file is in the root of the attacker
controlled directory.

This is a bit subtle, so lets do the right thing and concistently use
fd-relative operations when doing anything inside attacker/app
controlled directories.

Helps: https://github.com/flatpak/flatpak/security/advisories/GHSA-w69g-9x8j-7p8f
This commit is contained in:
Sebastian Wick committed 2026-08-11 01:22:28 +02:00
1 parent ead0ca6b53
commit e07a5b5ac0
1 file changed
+29 -17
+29 -17
View File
@@ -9637,7 +9637,6 @@ flatpak_dir_deploy (FlatpakDir *self,
g_autofree char *checkoutdirpath = NULL;
const char *checkoutdir_basename;
g_autoptr(GFile) real_checkoutdir = NULL;
g_autoptr(GFile) dotref = NULL;
g_autoptr(GFile) files_etc = NULL;
g_autoptr(GFile) deploy_data_file = NULL;
g_autoptr(GVariant) commit_data = NULL;
@@ -9897,10 +9896,19 @@ flatpak_dir_deploy (FlatpakDir *self,
metadata_contents, metadata_size, error))
return FALSE;
dotref = g_file_resolve_relative_path (checkoutdir, "files/.ref");
if (!g_file_replace_contents (dotref, "", 0, NULL, FALSE,
G_FILE_CREATE_REPLACE_DESTINATION, NULL, cancellable, error))
return FALSE;
{
g_autofree char *files_path = g_build_filename (checkoutdir_basename, "files", NULL);
glnx_autofd int files_dfd = -1;
if (!glnx_opendirat (deploy_base_dfd, files_path, FALSE, &files_dfd, error))
return FALSE;
if (!glnx_file_replace_contents_at (files_dfd, ".ref",
(const guint8 *) "", 0,
GLNX_FILE_REPLACE_NODATASYNC,
cancellable, error))
return FALSE;
}
export = g_file_get_child (checkoutdir, "export");
@@ -12150,7 +12158,6 @@ flatpak_dir_undeploy (FlatpakDir *self,
g_autofree char *id = NULL;
g_autofree char *dirname = NULL;
g_autofree char *current_active = NULL;
g_autoptr(GFile) change_file = NULL;
g_autoptr(GError) child_error = NULL;
int i, retry;
@@ -12242,18 +12249,23 @@ flatpak_dir_undeploy (FlatpakDir *self,
}
if (is_update)
change_file = g_file_resolve_relative_path (removed_subdir, "files/.updated");
else
change_file = g_file_resolve_relative_path (removed_subdir, "files/.removed");
{
glnx_autofd int files_dfd = -1;
const char *marker = is_update ? ".updated" : ".removed";
g_autofree char *files_path = g_build_filename (flatpak_file_get_path_cached (removed_subdir), "files", NULL);
if (!g_file_replace_contents (change_file, "", 0, NULL, FALSE,
G_FILE_CREATE_REPLACE_DESTINATION, NULL, NULL, &child_error))
{
g_autofree gchar *path = g_file_get_path (change_file);
g_warning ("Unable to clear %s: %s", path, child_error->message);
g_clear_error (&child_error);
}
if (!glnx_opendirat (AT_FDCWD, files_path,
FALSE, &files_dfd, &child_error) ||
!glnx_file_replace_contents_at (files_dfd, marker,
(const guint8 *) "", 0,
GLNX_FILE_REPLACE_NODATASYNC,
NULL, &child_error))
{
g_warning ("Unable to write %s marker: %s", marker,
child_error ? child_error->message : g_strerror (errno));
g_clear_error (&child_error);
}
}
if (force_remove || !dir_is_locked (removed_subdir))
{