blob: 967d4b58e78a65e2dd7e637a7b30df0f75ab8cb2 [file] [log] [blame]
From 7a232d72dec087828e678547b3147cbaed7faa49 Mon Sep 17 00:00:00 2001
From: Zong-Cing Lin <zclin@google.com>
Date: Tue, 25 Jan 2022 11:20:38 +0800
Subject: [PATCH 2/2] Backport "Merge branch '2325-symlink-replace-file' into
'master'"
https://gitlab.gnome.org/GNOME/glib/-/commit/c80528f17ba25ea7d7089946926b93a98bd1479e
It's for CVE-2021-28153
Bug: 215545684
Test: generate new glib so and verify functionalities on Qv1
diff --git a/gio/glocalfileoutputstream.c b/gio/glocalfileoutputstream.c
index 65044729d..db6708ead 100644
--- a/gio/glocalfileoutputstream.c
+++ b/gio/glocalfileoutputstream.c
@@ -58,6 +58,12 @@
#define O_BINARY 0
#endif
+#ifndef O_CLOEXEC
+#define O_CLOEXEC 0
+#else
+#define HAVE_O_CLOEXEC 1
+#endif
+
struct _GLocalFileOutputStreamPrivate {
char *tmp_filename;
char *original_filename;
@@ -752,11 +758,12 @@ handle_overwrite_open (const char *filename,
int open_flags;
int res;
int mode;
+ gboolean replace_destination_set = (flags & G_FILE_CREATE_REPLACE_DESTINATION);
mode = mode_from_flags_or_info (flags, reference_info);
/* We only need read access to the original file if we are creating a backup.
- * We also add O_CREATE to avoid a race if the file was just removed */
+ * We also add O_CREAT to avoid a race if the file was just removed */
if (create_backup || readable)
open_flags = O_RDWR | O_CREAT | O_BINARY;
else
@@ -772,15 +779,22 @@ handle_overwrite_open (const char *filename,
/* Could be a symlink, or it could be a regular ELOOP error,
* but then the next open will fail too. */
is_symlink = TRUE;
- fd = g_open (filename, open_flags, mode);
+ if (!replace_destination_set)
+ fd = g_open (filename, open_flags, mode);
}
-#else
- fd = g_open (filename, open_flags, mode);
+#else /* if !O_NOFOLLOW */
/* This is racy, but we do it as soon as possible to minimize the race */
is_symlink = g_file_test (filename, G_FILE_TEST_IS_SYMLINK);
+
+ if (!is_symlink || !replace_destination_set)
+ {
+ fd = g_open (filename, open_flags, mode);
+ errsv = errno;
+ }
#endif
- if (fd == -1)
+ if (fd == -1 &&
+ (!is_symlink || !replace_destination_set))
{
int errsv = errno;
char *display_name = g_filename_display_name (filename);
@@ -814,16 +828,27 @@ handle_overwrite_open (const char *filename,
if (!S_ISREG (original_stat.st_mode))
{
if (S_ISDIR (original_stat.st_mode))
- g_set_error_literal (error,
- G_IO_ERROR,
- G_IO_ERROR_IS_DIRECTORY,
- _("Target file is a directory"));
- else
- g_set_error_literal (error,
+ {
+ g_set_error_literal (error,
+ G_IO_ERROR,
+ G_IO_ERROR_IS_DIRECTORY,
+ _("Target file is a directory"));
+ goto err_out;
+ }
+ else if (!is_symlink ||
+#ifdef S_ISLNK
+ !S_ISLNK (original_stat.st_mode)
+#else
+ FALSE
+#endif
+ )
+ {
+ g_set_error_literal (error,
G_IO_ERROR,
G_IO_ERROR_NOT_REGULAR_FILE,
_("Target file is not a regular file"));
- goto err_out;
+ goto err_out;
+ }
}
if (etag != NULL)
@@ -852,7 +877,7 @@ handle_overwrite_open (const char *filename,
* to a backup file and rewrite the contents of the file.
*/
- if ((flags & G_FILE_CREATE_REPLACE_DESTINATION) ||
+ if (replace_destination_set ||
(!(original_stat.st_nlink > 1) && !is_symlink))
{
char *dirname, *tmp_filename;
@@ -871,7 +896,7 @@ handle_overwrite_open (const char *filename,
/* try to keep permissions (unless replacing) */
- if ( ! (flags & G_FILE_CREATE_REPLACE_DESTINATION) &&
+ if (!replace_destination_set &&
(
#ifdef HAVE_FCHOWN
fchown (tmpfd, original_stat.st_uid, original_stat.st_gid) == -1 ||
@@ -904,7 +929,8 @@ handle_overwrite_open (const char *filename,
}
}
- (void) g_close (fd, NULL);
+ if (fd >= 0)
+ (void) g_close (fd, NULL);
*temp_filename = tmp_filename;
return tmpfd;
}
@@ -1009,7 +1035,7 @@ handle_overwrite_open (const char *filename,
}
}
- if (flags & G_FILE_CREATE_REPLACE_DESTINATION)
+ if (replace_destination_set)
{
(void) g_close (fd, NULL);
@@ -1094,7 +1120,7 @@ _g_local_file_output_stream_replace (const char *filename,
sync_on_close = FALSE;
/* If the file doesn't exist, create it */
- open_flags = O_CREAT | O_EXCL | O_BINARY;
+ open_flags = O_CREAT | O_EXCL | O_BINARY | O_CLOEXEC;
if (readable)
open_flags |= O_RDWR;
else
@@ -1124,8 +1150,11 @@ _g_local_file_output_stream_replace (const char *filename,
set_error_from_open_errno (filename, error);
return NULL;
}
-
-
+#if !defined(HAVE_O_CLOEXEC) && defined(F_SETFD)
+ else
+ fcntl (fd, F_SETFD, FD_CLOEXEC);
+#endif
+
stream = g_object_new (G_TYPE_LOCAL_FILE_OUTPUT_STREAM, NULL);
stream->priv->fd = fd;
stream->priv->sync_on_close = sync_on_close;
--
2.35.0.rc0.227.g00780c9af4-goog