LAKITU: Fix CVE-2021-28153 in glib and glib-utils.
This CL is adding fixes for CVE-2021-28153 from upstream:
https://gitlab.gnome.org/GNOME/glib/-/merge_requests/1982/diffs?commit_id=317b3b587058a05dca95d56dac26568c5b098d33
BUG=b/183876723,b/183876588
TEST=presubmit
RELEASE_NOTE=Fixed CVE-2021-28153 in glib and glib-utils.
cos-patch: security-moderate
Change-Id: I9f44a2cef02f4a84dc8bb4590e70eca799c46ef3
Reviewed-on: https://cos-review.googlesource.com/c/third_party/overlays/chromiumos-overlay/+/22830
Main-Branch-Verified: Cusky Presubmit Bot <presubmit@cos-infra-prod.iam.gserviceaccount.com>
Tested-by: Cusky Presubmit Bot <presubmit@cos-infra-prod.iam.gserviceaccount.com>
Reviewed-by: Meena Shanmugam <meenashanmugam@google.com>
diff --git a/dev-libs/glib/files/glib-2.66.7-fix-CVE-2021-28153.patch b/dev-libs/glib/files/glib-2.66.7-fix-CVE-2021-28153.patch
new file mode 100644
index 0000000..6732bff
--- /dev/null
+++ b/dev-libs/glib/files/glib-2.66.7-fix-CVE-2021-28153.patch
@@ -0,0 +1,232 @@
+From b0ee8d82e470a6365bf7201cfd093f7b953ec899 Mon Sep 17 00:00:00 2001
+From: Vaibhav Rustagi <vaibhavrustagi@google.com>
+Date: Tue, 21 Sep 2021 15:56:04 -0700
+Subject: [PATCH] Fixes for CVE-2021-28153.
+
+Cherry-pick from
+https://gitlab.gnome.org/GNOME/glib/-/merge_requests/1982/diffs?commit_id=317b3b587058a05dca95d56dac26568c5b098d33
+---
+ gio/glocalfileoutputstream.c | 20 +++++--
+ gio/tests/file.c | 112 ++++++++++++++++++++++++++++++++++-
+ 2 files changed, 124 insertions(+), 8 deletions(-)
+
+diff --git a/gio/glocalfileoutputstream.c b/gio/glocalfileoutputstream.c
+index f34c3e439..f04180095 100644
+--- a/gio/glocalfileoutputstream.c
++++ b/gio/glocalfileoutputstream.c
+@@ -63,6 +63,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;
+@@ -850,6 +856,7 @@ handle_overwrite_open (const char *filename,
+ int res;
+ int mode;
+ int errsv;
++ gboolean replace_destination_set = (flags & G_FILE_CREATE_REPLACE_DESTINATION);
+
+ mode = mode_from_flags_or_info (flags, reference_info);
+
+@@ -960,7 +967,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 ||
+ (!(_g_stat_nlink (&original_stat) > 1) && !is_symlink))
+ {
+ char *dirname, *tmp_filename;
+@@ -979,7 +986,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, _g_stat_uid (&original_stat), _g_stat_gid (&original_stat)) == -1 ||
+@@ -1120,7 +1127,7 @@ handle_overwrite_open (const char *filename,
+ }
+ }
+
+- if (flags & G_FILE_CREATE_REPLACE_DESTINATION)
++ if (replace_destination_set)
+ {
+ g_close (fd, NULL);
+
+@@ -1205,7 +1212,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
+@@ -1235,7 +1242,10 @@ _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;
+diff --git a/gio/tests/file.c b/gio/tests/file.c
+index d8769656c..ddd1ffcba 100644
+--- a/gio/tests/file.c
++++ b/gio/tests/file.c
+@@ -686,7 +686,7 @@ test_replace_cancel (void)
+ guint count;
+ GError *error = NULL;
+
+- g_test_bug ("629301");
++ g_test_bug ("https://bugzilla.gnome.org/629301");
+
+ path = g_dir_make_tmp ("g_file_replace_cancel_XXXXXX", &error);
+ g_assert_no_error (error);
+@@ -805,6 +805,113 @@ test_replace_cancel (void)
+ g_object_unref (tmpdir);
+ }
+
++static void
++test_replace_symlink (void)
++{
++#ifdef G_OS_UNIX
++ gchar *tmpdir_path = NULL;
++ GFile *tmpdir = NULL, *source_file = NULL, *target_file = NULL;
++ GFileOutputStream *stream = NULL;
++ const gchar *new_contents = "this is a test message which should be written to source and not target";
++ gsize n_written;
++ GFileEnumerator *enumerator = NULL;
++ GFileInfo *info = NULL;
++ gchar *contents = NULL;
++ gsize length = 0;
++ GError *local_error = NULL;
++
++ g_test_bug ("https://gitlab.gnome.org/GNOME/glib/-/issues/2325");
++ g_test_summary ("Test that G_FILE_CREATE_REPLACE_DESTINATION doesn’t follow symlinks");
++
++ /* Create a fresh, empty working directory. */
++ tmpdir_path = g_dir_make_tmp ("g_file_replace_symlink_XXXXXX", &local_error);
++ g_assert_no_error (local_error);
++ tmpdir = g_file_new_for_path (tmpdir_path);
++
++ g_test_message ("Using temporary directory %s", tmpdir_path);
++ g_free (tmpdir_path);
++
++ /* Create symlink `source` which points to `target`. */
++ source_file = g_file_get_child (tmpdir, "source");
++ target_file = g_file_get_child (tmpdir, "target");
++ g_file_make_symbolic_link (source_file, "target", NULL, &local_error);
++ g_assert_no_error (local_error);
++
++ /* Ensure that `target` doesn’t exist */
++ g_assert_false (g_file_query_exists (target_file, NULL));
++
++ /* Replace the `source` symlink with a regular file using
++ * %G_FILE_CREATE_REPLACE_DESTINATION, which should replace it *without*
++ * following the symlink */
++ stream = g_file_replace (source_file, NULL, FALSE /* no backup */,
++ G_FILE_CREATE_REPLACE_DESTINATION, NULL, &local_error);
++ g_assert_no_error (local_error);
++
++ g_output_stream_write_all (G_OUTPUT_STREAM (stream), new_contents, strlen (new_contents),
++ &n_written, NULL, &local_error);
++ g_assert_no_error (local_error);
++ g_assert_cmpint (n_written, ==, strlen (new_contents));
++
++ g_output_stream_close (G_OUTPUT_STREAM (stream), NULL, &local_error);
++ g_assert_no_error (local_error);
++
++ g_clear_object (&stream);
++
++ /* At this point, there should still only be one file: `source`. It should
++ * now be a regular file. `target` should not exist. */
++ enumerator = g_file_enumerate_children (tmpdir,
++ G_FILE_ATTRIBUTE_STANDARD_NAME ","
++ G_FILE_ATTRIBUTE_STANDARD_TYPE,
++ G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, NULL, &local_error);
++ g_assert_no_error (local_error);
++
++ info = g_file_enumerator_next_file (enumerator, NULL, &local_error);
++ g_assert_no_error (local_error);
++ g_assert_nonnull (info);
++
++ g_assert_cmpstr (g_file_info_get_name (info), ==, "source");
++ g_assert_cmpint (g_file_info_get_file_type (info), ==, G_FILE_TYPE_REGULAR);
++
++ g_clear_object (&info);
++
++ info = g_file_enumerator_next_file (enumerator, NULL, &local_error);
++ g_assert_no_error (local_error);
++ g_assert_null (info);
++
++ g_file_enumerator_close (enumerator, NULL, &local_error);
++ g_assert_no_error (local_error);
++ g_clear_object (&enumerator);
++
++ /* Double-check that `target` doesn’t exist */
++ g_assert_false (g_file_query_exists (target_file, NULL));
++
++ /* Check the content of `source`. */
++ g_file_load_contents (source_file,
++ NULL,
++ &contents,
++ &length,
++ NULL,
++ &local_error);
++ g_assert_no_error (local_error);
++ g_assert_cmpstr (contents, ==, new_contents);
++ g_assert_cmpuint (length, ==, strlen (new_contents));
++ g_free (contents);
++
++ /* Tidy up. */
++ g_file_delete (source_file, NULL, &local_error);
++ g_assert_no_error (local_error);
++
++ g_file_delete (tmpdir, NULL, &local_error);
++ g_assert_no_error (local_error);
++
++ g_clear_object (&target_file);
++ g_clear_object (&source_file);
++ g_clear_object (&tmpdir);
++#else /* if !G_OS_UNIX */
++ g_test_skip ("Symlink replacement tests can only be run on Unix")
++#endif
++}
++
+ static void
+ on_file_deleted (GObject *object,
+ GAsyncResult *result,
+@@ -1785,8 +1892,6 @@ main (int argc, char *argv[])
+ {
+ g_test_init (&argc, &argv, NULL);
+
+- g_test_bug_base ("http://bugzilla.gnome.org/");
+-
+ g_test_add_func ("/file/basic", test_basic);
+ g_test_add_func ("/file/build-filename", test_build_filename);
+ g_test_add_func ("/file/parent", test_parent);
+@@ -1800,6 +1905,7 @@ main (int argc, char *argv[])
+ g_test_add_data_func ("/file/async-create-delete/4096", GINT_TO_POINTER (4096), test_create_delete);
+ g_test_add_func ("/file/replace-load", test_replace_load);
+ g_test_add_func ("/file/replace-cancel", test_replace_cancel);
++ g_test_add_func ("/file/replace-symlink", test_replace_symlink);
+ g_test_add_func ("/file/async-delete", test_async_delete);
+ g_test_add_func ("/file/copy-preserve-mode", test_copy_preserve_mode);
+ g_test_add_func ("/file/measure", test_measure);
+--
+2.33.0.464.g1972c5931b-goog
+
diff --git a/dev-libs/glib/glib-2.66.7-r1.ebuild b/dev-libs/glib/glib-2.66.7-r2.ebuild
similarity index 99%
rename from dev-libs/glib/glib-2.66.7-r1.ebuild
rename to dev-libs/glib/glib-2.66.7-r2.ebuild
index 82680f0..29e29cd 100644
--- a/dev-libs/glib/glib-2.66.7-r1.ebuild
+++ b/dev-libs/glib/glib-2.66.7-r2.ebuild
@@ -72,6 +72,10 @@
/usr/bin/gio-querymodules$(get_exeext)
)
+PATCHES=(
+ "${FILESDIR}"/glib-2.66.7-fix-CVE-2021-28153.patch
+)
+
pkg_setup() {
if use kernel_linux ; then
CONFIG_CHECK="~INOTIFY_USER"
diff --git a/dev-util/glib-utils/files/glib-2.66.7-fix-CVE-2021-28153.patch b/dev-util/glib-utils/files/glib-2.66.7-fix-CVE-2021-28153.patch
new file mode 100644
index 0000000..6732bff
--- /dev/null
+++ b/dev-util/glib-utils/files/glib-2.66.7-fix-CVE-2021-28153.patch
@@ -0,0 +1,232 @@
+From b0ee8d82e470a6365bf7201cfd093f7b953ec899 Mon Sep 17 00:00:00 2001
+From: Vaibhav Rustagi <vaibhavrustagi@google.com>
+Date: Tue, 21 Sep 2021 15:56:04 -0700
+Subject: [PATCH] Fixes for CVE-2021-28153.
+
+Cherry-pick from
+https://gitlab.gnome.org/GNOME/glib/-/merge_requests/1982/diffs?commit_id=317b3b587058a05dca95d56dac26568c5b098d33
+---
+ gio/glocalfileoutputstream.c | 20 +++++--
+ gio/tests/file.c | 112 ++++++++++++++++++++++++++++++++++-
+ 2 files changed, 124 insertions(+), 8 deletions(-)
+
+diff --git a/gio/glocalfileoutputstream.c b/gio/glocalfileoutputstream.c
+index f34c3e439..f04180095 100644
+--- a/gio/glocalfileoutputstream.c
++++ b/gio/glocalfileoutputstream.c
+@@ -63,6 +63,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;
+@@ -850,6 +856,7 @@ handle_overwrite_open (const char *filename,
+ int res;
+ int mode;
+ int errsv;
++ gboolean replace_destination_set = (flags & G_FILE_CREATE_REPLACE_DESTINATION);
+
+ mode = mode_from_flags_or_info (flags, reference_info);
+
+@@ -960,7 +967,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 ||
+ (!(_g_stat_nlink (&original_stat) > 1) && !is_symlink))
+ {
+ char *dirname, *tmp_filename;
+@@ -979,7 +986,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, _g_stat_uid (&original_stat), _g_stat_gid (&original_stat)) == -1 ||
+@@ -1120,7 +1127,7 @@ handle_overwrite_open (const char *filename,
+ }
+ }
+
+- if (flags & G_FILE_CREATE_REPLACE_DESTINATION)
++ if (replace_destination_set)
+ {
+ g_close (fd, NULL);
+
+@@ -1205,7 +1212,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
+@@ -1235,7 +1242,10 @@ _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;
+diff --git a/gio/tests/file.c b/gio/tests/file.c
+index d8769656c..ddd1ffcba 100644
+--- a/gio/tests/file.c
++++ b/gio/tests/file.c
+@@ -686,7 +686,7 @@ test_replace_cancel (void)
+ guint count;
+ GError *error = NULL;
+
+- g_test_bug ("629301");
++ g_test_bug ("https://bugzilla.gnome.org/629301");
+
+ path = g_dir_make_tmp ("g_file_replace_cancel_XXXXXX", &error);
+ g_assert_no_error (error);
+@@ -805,6 +805,113 @@ test_replace_cancel (void)
+ g_object_unref (tmpdir);
+ }
+
++static void
++test_replace_symlink (void)
++{
++#ifdef G_OS_UNIX
++ gchar *tmpdir_path = NULL;
++ GFile *tmpdir = NULL, *source_file = NULL, *target_file = NULL;
++ GFileOutputStream *stream = NULL;
++ const gchar *new_contents = "this is a test message which should be written to source and not target";
++ gsize n_written;
++ GFileEnumerator *enumerator = NULL;
++ GFileInfo *info = NULL;
++ gchar *contents = NULL;
++ gsize length = 0;
++ GError *local_error = NULL;
++
++ g_test_bug ("https://gitlab.gnome.org/GNOME/glib/-/issues/2325");
++ g_test_summary ("Test that G_FILE_CREATE_REPLACE_DESTINATION doesn’t follow symlinks");
++
++ /* Create a fresh, empty working directory. */
++ tmpdir_path = g_dir_make_tmp ("g_file_replace_symlink_XXXXXX", &local_error);
++ g_assert_no_error (local_error);
++ tmpdir = g_file_new_for_path (tmpdir_path);
++
++ g_test_message ("Using temporary directory %s", tmpdir_path);
++ g_free (tmpdir_path);
++
++ /* Create symlink `source` which points to `target`. */
++ source_file = g_file_get_child (tmpdir, "source");
++ target_file = g_file_get_child (tmpdir, "target");
++ g_file_make_symbolic_link (source_file, "target", NULL, &local_error);
++ g_assert_no_error (local_error);
++
++ /* Ensure that `target` doesn’t exist */
++ g_assert_false (g_file_query_exists (target_file, NULL));
++
++ /* Replace the `source` symlink with a regular file using
++ * %G_FILE_CREATE_REPLACE_DESTINATION, which should replace it *without*
++ * following the symlink */
++ stream = g_file_replace (source_file, NULL, FALSE /* no backup */,
++ G_FILE_CREATE_REPLACE_DESTINATION, NULL, &local_error);
++ g_assert_no_error (local_error);
++
++ g_output_stream_write_all (G_OUTPUT_STREAM (stream), new_contents, strlen (new_contents),
++ &n_written, NULL, &local_error);
++ g_assert_no_error (local_error);
++ g_assert_cmpint (n_written, ==, strlen (new_contents));
++
++ g_output_stream_close (G_OUTPUT_STREAM (stream), NULL, &local_error);
++ g_assert_no_error (local_error);
++
++ g_clear_object (&stream);
++
++ /* At this point, there should still only be one file: `source`. It should
++ * now be a regular file. `target` should not exist. */
++ enumerator = g_file_enumerate_children (tmpdir,
++ G_FILE_ATTRIBUTE_STANDARD_NAME ","
++ G_FILE_ATTRIBUTE_STANDARD_TYPE,
++ G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, NULL, &local_error);
++ g_assert_no_error (local_error);
++
++ info = g_file_enumerator_next_file (enumerator, NULL, &local_error);
++ g_assert_no_error (local_error);
++ g_assert_nonnull (info);
++
++ g_assert_cmpstr (g_file_info_get_name (info), ==, "source");
++ g_assert_cmpint (g_file_info_get_file_type (info), ==, G_FILE_TYPE_REGULAR);
++
++ g_clear_object (&info);
++
++ info = g_file_enumerator_next_file (enumerator, NULL, &local_error);
++ g_assert_no_error (local_error);
++ g_assert_null (info);
++
++ g_file_enumerator_close (enumerator, NULL, &local_error);
++ g_assert_no_error (local_error);
++ g_clear_object (&enumerator);
++
++ /* Double-check that `target` doesn’t exist */
++ g_assert_false (g_file_query_exists (target_file, NULL));
++
++ /* Check the content of `source`. */
++ g_file_load_contents (source_file,
++ NULL,
++ &contents,
++ &length,
++ NULL,
++ &local_error);
++ g_assert_no_error (local_error);
++ g_assert_cmpstr (contents, ==, new_contents);
++ g_assert_cmpuint (length, ==, strlen (new_contents));
++ g_free (contents);
++
++ /* Tidy up. */
++ g_file_delete (source_file, NULL, &local_error);
++ g_assert_no_error (local_error);
++
++ g_file_delete (tmpdir, NULL, &local_error);
++ g_assert_no_error (local_error);
++
++ g_clear_object (&target_file);
++ g_clear_object (&source_file);
++ g_clear_object (&tmpdir);
++#else /* if !G_OS_UNIX */
++ g_test_skip ("Symlink replacement tests can only be run on Unix")
++#endif
++}
++
+ static void
+ on_file_deleted (GObject *object,
+ GAsyncResult *result,
+@@ -1785,8 +1892,6 @@ main (int argc, char *argv[])
+ {
+ g_test_init (&argc, &argv, NULL);
+
+- g_test_bug_base ("http://bugzilla.gnome.org/");
+-
+ g_test_add_func ("/file/basic", test_basic);
+ g_test_add_func ("/file/build-filename", test_build_filename);
+ g_test_add_func ("/file/parent", test_parent);
+@@ -1800,6 +1905,7 @@ main (int argc, char *argv[])
+ g_test_add_data_func ("/file/async-create-delete/4096", GINT_TO_POINTER (4096), test_create_delete);
+ g_test_add_func ("/file/replace-load", test_replace_load);
+ g_test_add_func ("/file/replace-cancel", test_replace_cancel);
++ g_test_add_func ("/file/replace-symlink", test_replace_symlink);
+ g_test_add_func ("/file/async-delete", test_async_delete);
+ g_test_add_func ("/file/copy-preserve-mode", test_copy_preserve_mode);
+ g_test_add_func ("/file/measure", test_measure);
+--
+2.33.0.464.g1972c5931b-goog
+
diff --git a/dev-util/glib-utils/glib-utils-2.66.7-r1.ebuild b/dev-util/glib-utils/glib-utils-2.66.7-r2.ebuild
similarity index 96%
rename from dev-util/glib-utils/glib-utils-2.66.7-r1.ebuild
rename to dev-util/glib-utils/glib-utils-2.66.7-r2.ebuild
index c9f88eb..6c89264 100644
--- a/dev-util/glib-utils/glib-utils-2.66.7-r1.ebuild
+++ b/dev-util/glib-utils/glib-utils-2.66.7-r2.ebuild
@@ -28,6 +28,10 @@
cros-host? ( ${BDEPEND} )
"
+PATCHES=(
+ "${FILESDIR}"/glib-2.66.7-fix-CVE-2021-28153.patch
+)
+
src_configure() { :; }
do_xsltproc_command() {