| 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 |
| |