From 4c4cf568f0f710baf0bd04d52df715636bc6b971 Mon Sep 17 00:00:00 2001
From: Philip Withnall <pwithnall@endlessos.org>
Date: Thu, 17 Aug 2023 04:23:41 +0000
Subject: [PATCH] gvariant: Fix g_variant_byteswap() returning non-normal data

If `g_variant_byteswap()` was called on a non-normal variant of a type
which doesn’t need byteswapping, it would return a non-normal output.

That contradicts the documentation, which says that the return value is
always in normal form.

Fix the code so it matches the documentation.

Includes a unit test.

Signed-off-by: Philip Withnall <pwithnall@endlessos.org>

Helps: #2797

CVE: CVE-2023-32611

Upstream-Status: Backport [https://gitlab.gnome.org/GNOME/glib/-/commit/4c4cf568f0f710baf0bd04d52df715636bc6b971]

Signed-off-by: Soumya Sambu <soumya.sambu@windriver.com>
---
 glib/gvariant.c       |  8 +++++---
 glib/tests/gvariant.c | 24 ++++++++++++++++++++++++
 2 files changed, 29 insertions(+), 3 deletions(-)

diff --git a/glib/gvariant.c b/glib/gvariant.c
index 30a3280..7e568d1 100644
--- a/glib/gvariant.c
+++ b/glib/gvariant.c
@@ -6004,14 +6004,16 @@ g_variant_byteswap (GVariant *value)
       g_variant_serialised_byteswap (serialised);

       bytes = g_bytes_new_take (serialised.data, serialised.size);
-      new = g_variant_new_from_bytes (g_variant_get_type (value), bytes, TRUE);
+      new = g_variant_ref_sink (g_variant_new_from_bytes (g_variant_get_type (value), bytes, TRUE));
       g_bytes_unref (bytes);
     }
   else
     /* contains no multi-byte data */
-    new = value;
+    new = g_variant_get_normal_form (value);

-  return g_variant_ref_sink (new);
+  g_assert (g_variant_is_trusted (new));
+
+  return g_steal_pointer (&new);
 }

 /**
diff --git a/glib/tests/gvariant.c b/glib/tests/gvariant.c
index ad45043..36c86c2 100644
--- a/glib/tests/gvariant.c
+++ b/glib/tests/gvariant.c
@@ -3818,6 +3818,29 @@ test_gv_byteswap (void)
   g_free (string);
 }

+static void
+test_gv_byteswap_non_normal_non_aligned (void)
+{
+  const guint8 data[] = { 0x02 };
+  GVariant *v = NULL;
+  GVariant *v_byteswapped = NULL;
+
+  g_test_summary ("Test that calling g_variant_byteswap() on a variant which "
+                  "is in non-normal form and doesn’t need byteswapping returns "
+                  "the same variant in normal form.");
+
+  v = g_variant_new_from_data (G_VARIANT_TYPE_BOOLEAN, data, sizeof (data), FALSE, NULL, NULL);
+  g_assert_false (g_variant_is_normal_form (v));
+
+  v_byteswapped = g_variant_byteswap (v);
+  g_assert_true (g_variant_is_normal_form (v_byteswapped));
+
+  g_assert_cmpvariant (v, v_byteswapped);
+
+  g_variant_unref (v);
+  g_variant_unref (v_byteswapped);
+}
+
 static void
 test_parser (void)
 {
@@ -5553,6 +5576,7 @@ main (int argc, char **argv)
   g_test_add_func ("/gvariant/builder-memory", test_builder_memory);
   g_test_add_func ("/gvariant/hashing", test_hashing);
   g_test_add_func ("/gvariant/byteswap", test_gv_byteswap);
+  g_test_add_func ("/gvariant/byteswap/non-normal-non-aligned", test_gv_byteswap_non_normal_non_aligned);
   g_test_add_func ("/gvariant/parser", test_parses);
   g_test_add_func ("/gvariant/parser/integer-bounds", test_parser_integer_bounds);
   g_test_add_func ("/gvariant/parser/recursion", test_parser_recursion);
--
2.40.0
