Hello community, here is the log from the commit of package glib2 for openSUSE:Factory checked in at 2018-11-08 09:43:40 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/glib2 (Old) and /work/SRC/openSUSE:Factory/.glib2.new (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "glib2" Thu Nov 8 09:43:40 2018 rev:201 rq:644294 version:2.58.1 Changes: -------- --- /work/SRC/openSUSE:Factory/glib2/glib2.changes 2018-10-17 08:17:56.858953230 +0200 +++ /work/SRC/openSUSE:Factory/.glib2.new/glib2.changes 2018-11-08 09:43:43.601434682 +0100 @@ -1,0 +2,20 @@ +Mon Oct 15 22:57:17 UTC 2018 - Scott Reeves <sree...@suse.com> + +- Add patchset to fix gvariant parsing issues. (bsc#1111499). + 0001-gvariant-Fix-checking-arithmetic-for-tuple-element-e.patch + 0002-gvarianttype-Impose-a-recursion-limit-of-64-on-varia.patch + 0003-gvariant-Check-array-offsets-against-serialised-data.patch + 0004-gvariant-Check-tuple-offsets-against-serialised-data.patch + 0005-gvariant-Limit-GVariant-strings-to-G_MAXSSIZE.patch + 0006-gdbusmessage-Validate-type-of-message-header-signatu.patch + 0007-gdbusmessage-Improve-documentation-for-g_dbus_messag.patch + 0008-gdbusmessage-Clarify-error-returns-for-g_dbus_messag.patch + 0009-gdbusmessage-Fix-a-typo-in-a-documentation-comment.patch + 0010-gdbusmessage-Check-for-valid-GVariantType-when-parsi.patch + 0011-gvariant-Clarify-internal-documentation-about-GVaria.patch + 0012-tests-Tidy-up-GError-handling-in-gdbus-serialization.patch + 0013-tests-Use-g_assert_null-in-gdbus-serialization-test.patch + 0014-gutf8-Add-a-g_utf8_validate_len-function.patch + 0015-glib-Port-various-callers-to-use-g_utf8_validate_len.patch + +------------------------------------------------------------------- New: ---- 0001-gvariant-Fix-checking-arithmetic-for-tuple-element-e.patch 0002-gvarianttype-Impose-a-recursion-limit-of-64-on-varia.patch 0003-gvariant-Check-array-offsets-against-serialised-data.patch 0004-gvariant-Check-tuple-offsets-against-serialised-data.patch 0005-gvariant-Limit-GVariant-strings-to-G_MAXSSIZE.patch 0006-gdbusmessage-Validate-type-of-message-header-signatu.patch 0007-gdbusmessage-Improve-documentation-for-g_dbus_messag.patch 0008-gdbusmessage-Clarify-error-returns-for-g_dbus_messag.patch 0009-gdbusmessage-Fix-a-typo-in-a-documentation-comment.patch 0010-gdbusmessage-Check-for-valid-GVariantType-when-parsi.patch 0011-gvariant-Clarify-internal-documentation-about-GVaria.patch 0012-tests-Tidy-up-GError-handling-in-gdbus-serialization.patch 0013-tests-Use-g_assert_null-in-gdbus-serialization-test.patch 0014-gutf8-Add-a-g_utf8_validate_len-function.patch 0015-glib-Port-various-callers-to-use-g_utf8_validate_len.patch ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ glib2.spec ++++++ --- /var/tmp/diff_new_pack.IXXgMx/_old 2018-11-08 09:43:44.241433936 +0100 +++ /var/tmp/diff_new_pack.IXXgMx/_new 2018-11-08 09:43:44.245433931 +0100 @@ -48,6 +48,23 @@ Patch3: glib2-dbus-socket-path.patch # PATCH-FIX-OPENSUSE glib2-gdbus-codegen-version.patch o...@aepfle.de -- Remove version string from files generated by gdbus-codegen Patch4: glib2-gdbus-codegen-version.patch +# PATCH-FIX-UPSTREAM 00[01-15]*.patch sree...@suse.com -- set of 15 patches fixing bsc#1111499 +Patch101: 0001-gvariant-Fix-checking-arithmetic-for-tuple-element-e.patch +Patch102: 0002-gvarianttype-Impose-a-recursion-limit-of-64-on-varia.patch +Patch103: 0003-gvariant-Check-array-offsets-against-serialised-data.patch +Patch104: 0004-gvariant-Check-tuple-offsets-against-serialised-data.patch +Patch105: 0005-gvariant-Limit-GVariant-strings-to-G_MAXSSIZE.patch +Patch106: 0006-gdbusmessage-Validate-type-of-message-header-signatu.patch +Patch107: 0007-gdbusmessage-Improve-documentation-for-g_dbus_messag.patch +Patch108: 0008-gdbusmessage-Clarify-error-returns-for-g_dbus_messag.patch +Patch109: 0009-gdbusmessage-Fix-a-typo-in-a-documentation-comment.patch +Patch110: 0010-gdbusmessage-Check-for-valid-GVariantType-when-parsi.patch +Patch111: 0011-gvariant-Clarify-internal-documentation-about-GVaria.patch +Patch112: 0012-tests-Tidy-up-GError-handling-in-gdbus-serialization.patch +Patch113: 0013-tests-Use-g_assert_null-in-gdbus-serialization-test.patch +Patch114: 0014-gutf8-Add-a-g_utf8_validate_len-function.patch +Patch115: 0015-glib-Port-various-callers-to-use-g_utf8_validate_len.patch + BuildRequires: docbook-xsl-stylesheets BuildRequires: fdupes BuildRequires: gamin-devel @@ -246,6 +263,21 @@ %patch2 -p1 %patch3 -p1 %patch4 -p1 +%patch101 -p1 +%patch102 -p1 +%patch103 -p1 +%patch104 -p1 +%patch105 -p1 +%patch106 -p1 +%patch107 -p1 +%patch108 -p1 +%patch109 -p1 +%patch110 -p1 +%patch111 -p1 +%patch112 -p1 +%patch113 -p1 +%patch114 -p1 +%patch115 -p1 cp -a %{SOURCE1} %{SOURCE2} %{SOURCE5} . cp -a %{SOURCE4} gnome_defaults.conf %if !%{with meson} ++++++ 0001-gvariant-Fix-checking-arithmetic-for-tuple-element-e.patch ++++++ >From 7ec1e7e7c8e085454927bb102faaad0c74cf5966 Mon Sep 17 00:00:00 2001 From: Philip Withnall <withn...@endlessm.com> Date: Thu, 16 Aug 2018 20:12:02 +0100 Subject: [PATCH 01/15] gvariant: Fix checking arithmetic for tuple element ends MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When checking whether a serialised GVariant tuple is in normal form, it’s possible for `offset_ptr -= offset_size` to underflow and wrap around, resulting in gvs_read_unaligned_le() reading memory outside the serialised GVariant bounds. See §(Tuples) in gvariant-serialiser.c for the documentation on how tuples are serialised. Briefly, all variable-length elements in the tuple have an offset to their end stored in an array of offsets at the end of the tuple. The width of each offset is in offset_size. offset_ptr is added to the start of the serialised tuple to get the offset which is currently being examined. The offset array is in reverse order compared to the tuple elements, hence the subtraction. The bug can be triggered if a tuple contains a load of variable-length elements, each of whose length is actually zero (i.e. empty arrays). Includes a unit test. oss-fuzz#9801 Signed-off-by: Philip Withnall <withn...@endlessm.com> --- glib/gvariant-serialiser.c | 3 +++ glib/tests/gvariant.c | 28 ++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/glib/gvariant-serialiser.c b/glib/gvariant-serialiser.c index 69f183121..96df54e23 100644 --- a/glib/gvariant-serialiser.c +++ b/glib/gvariant-serialiser.c @@ -1065,6 +1065,9 @@ gvs_tuple_is_normal (GVariantSerialised value) break; case G_VARIANT_MEMBER_ENDING_OFFSET: + if (offset_ptr < offset_size) + return FALSE; + offset_ptr -= offset_size; if (offset_ptr < offset) diff --git a/glib/tests/gvariant.c b/glib/tests/gvariant.c index de8e42d0b..a5095a380 100644 --- a/glib/tests/gvariant.c +++ b/glib/tests/gvariant.c @@ -4631,6 +4631,30 @@ test_stack_dict_init (void) g_variant_unref (variant); } +/* Test checking arbitrary binary data for normal form. This time, it’s a tuple + * with invalid element ends. */ +static void +test_normal_checking_tuples (void) +{ + const guint8 data[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 'a', '(', 'a', 'o', 'a', 'o', 'a', 'a', 'o', 'a', 'a', 'o', ')' + }; + gsize size = sizeof (data); + GVariant *variant = NULL; + GVariant *normal_variant = NULL; + + variant = g_variant_new_from_data (G_VARIANT_TYPE_VARIANT, data, size, + FALSE, NULL, NULL); + g_assert_nonnull (variant); + + normal_variant = g_variant_get_normal_form (variant); + g_assert_nonnull (normal_variant); + + g_variant_unref (normal_variant); + g_variant_unref (variant); +} + int main (int argc, char **argv) { @@ -4692,5 +4716,9 @@ main (int argc, char **argv) g_test_add_func ("/gvariant/stack-builder-init", test_stack_builder_init); g_test_add_func ("/gvariant/stack-dict-init", test_stack_dict_init); + + g_test_add_func ("/gvariant/normal-checking/tuples", + test_normal_checking_tuples); + return g_test_run (); } -- 2.14.4 ++++++ 0002-gvarianttype-Impose-a-recursion-limit-of-64-on-varia.patch ++++++ ++++ 839 lines (skipped) ++++++ 0003-gvariant-Check-array-offsets-against-serialised-data.patch ++++++ >From 3691b749fffc1f97a9e3c974321da3be49b2f017 Mon Sep 17 00:00:00 2001 From: Philip Withnall <withn...@endlessm.com> Date: Fri, 7 Sep 2018 22:26:05 +0100 Subject: [PATCH 03/15] gvariant: Check array offsets against serialised data length MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When getting a child from a serialised variable array, check its offset against the length of the serialised data of the array (excluding the length of the offset table). The offset was already checked against the length of the entire serialised array (including the offset table) — but a child should not be able to start inside the offset table. A test is included. oss-fuzz#9803 Signed-off-by: Philip Withnall <withn...@endlessm.com> --- glib/gvariant-serialiser.c | 2 +- glib/tests/gvariant.c | 26 ++++++++++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/glib/gvariant-serialiser.c b/glib/gvariant-serialiser.c index fe0bcf0aa..aa71d3c1c 100644 --- a/glib/gvariant-serialiser.c +++ b/glib/gvariant-serialiser.c @@ -694,7 +694,7 @@ gvs_variable_sized_array_get_child (GVariantSerialised value, (offset_size * index_), offset_size); - if (start < end && end <= value.size) + if (start < end && end <= value.size && end <= last_end) { child.data = value.data + start; child.size = end - start; diff --git a/glib/tests/gvariant.c b/glib/tests/gvariant.c index 51fa0f02b..671fdd94c 100644 --- a/glib/tests/gvariant.c +++ b/glib/tests/gvariant.c @@ -4715,6 +4715,30 @@ test_normal_checking_tuples (void) g_variant_unref (variant); } +/* Test that an array with invalidly large values in its offset table is + * normalised successfully without looping infinitely. */ +static void +test_normal_checking_array_offsets (void) +{ + const guint8 data[] = { + 0x07, 0xe5, 0x00, 0x07, 0x00, 0x07, 0x00, 0x00, + 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'g', + }; + gsize size = sizeof (data); + GVariant *variant = NULL; + GVariant *normal_variant = NULL; + + variant = g_variant_new_from_data (G_VARIANT_TYPE_VARIANT, data, size, + FALSE, NULL, NULL); + g_assert_nonnull (variant); + + normal_variant = g_variant_get_normal_form (variant); + g_assert_nonnull (normal_variant); + + g_variant_unref (normal_variant); + g_variant_unref (variant); +} + int main (int argc, char **argv) { @@ -4783,6 +4807,8 @@ main (int argc, char **argv) g_test_add_func ("/gvariant/normal-checking/tuples", test_normal_checking_tuples); + g_test_add_func ("/gvariant/normal-checking/array-offsets", + test_normal_checking_array_offsets); g_test_add_func ("/gvariant/recursion-limits/variant-in-variant", test_recursion_limits_variant_in_variant); -- 2.14.4 ++++++ 0004-gvariant-Check-tuple-offsets-against-serialised-data.patch ++++++ >From 183eed2b38b1d2fc3e6b149d7ac4cc062a619b48 Mon Sep 17 00:00:00 2001 From: Philip Withnall <withn...@endlessm.com> Date: Fri, 7 Sep 2018 22:28:37 +0100 Subject: [PATCH 04/15] gvariant: Check tuple offsets against serialised data length MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As with the previous commit, when getting a child from a serialised tuple, check its offset against the length of the serialised data of the tuple (excluding the length of the offset table). The offset was already checked against the length of the entire serialised tuple (including the offset table) — but a child should not be able to start inside the offset table. A test is included. oss-fuzz#9803 Signed-off-by: Philip Withnall <withn...@endlessm.com> --- glib/gvariant-serialiser.c | 16 ++++++++++++++-- glib/tests/gvariant.c | 26 ++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/glib/gvariant-serialiser.c b/glib/gvariant-serialiser.c index aa71d3c1c..643894919 100644 --- a/glib/gvariant-serialiser.c +++ b/glib/gvariant-serialiser.c @@ -870,7 +870,7 @@ gvs_tuple_get_child (GVariantSerialised value, const GVariantMemberInfo *member_info; GVariantSerialised child = { 0, }; gsize offset_size; - gsize start, end; + gsize start, end, last_end; member_info = g_variant_type_info_member_info (value.type_info, index_); child.type_info = g_variant_type_info_ref (member_info->type_info); @@ -940,7 +940,19 @@ gvs_tuple_get_child (GVariantSerialised value, offset_size * (member_info->i + 2), offset_size); - if (start < end && end <= value.size) + /* The child should not extend into the offset table. */ + if (index_ != g_variant_type_info_n_members (value.type_info) - 1) + { + GVariantSerialised last_child; + last_child = gvs_tuple_get_child (value, + g_variant_type_info_n_members (value.type_info) - 1); + last_end = last_child.data + last_child.size - value.data; + g_variant_type_info_unref (last_child.type_info); + } + else + last_end = end; + + if (start < end && end <= value.size && end <= last_end) { child.data = value.data + start; child.size = end - start; diff --git a/glib/tests/gvariant.c b/glib/tests/gvariant.c index 671fdd94c..1af1466cc 100644 --- a/glib/tests/gvariant.c +++ b/glib/tests/gvariant.c @@ -4739,6 +4739,30 @@ test_normal_checking_array_offsets (void) g_variant_unref (variant); } +/* Test that a tuple with invalidly large values in its offset table is + * normalised successfully without looping infinitely. */ +static void +test_normal_checking_tuple_offsets (void) +{ + const guint8 data[] = { + 0x07, 0xe5, 0x00, 0x07, 0x00, 0x07, + '(', 'a', 's', 'a', 's', 'a', 's', 'a', 's', 'a', 's', 'a', 's', ')', + }; + gsize size = sizeof (data); + GVariant *variant = NULL; + GVariant *normal_variant = NULL; + + variant = g_variant_new_from_data (G_VARIANT_TYPE_VARIANT, data, size, + FALSE, NULL, NULL); + g_assert_nonnull (variant); + + normal_variant = g_variant_get_normal_form (variant); + g_assert_nonnull (normal_variant); + + g_variant_unref (normal_variant); + g_variant_unref (variant); +} + int main (int argc, char **argv) { @@ -4809,6 +4833,8 @@ main (int argc, char **argv) test_normal_checking_tuples); g_test_add_func ("/gvariant/normal-checking/array-offsets", test_normal_checking_array_offsets); + g_test_add_func ("/gvariant/normal-checking/tuple-offsets", + test_normal_checking_tuple_offsets); g_test_add_func ("/gvariant/recursion-limits/variant-in-variant", test_recursion_limits_variant_in_variant); -- 2.14.4 ++++++ 0005-gvariant-Limit-GVariant-strings-to-G_MAXSSIZE.patch ++++++ >From ee54f72bc190fd5b95688c0d8270adee90f8117b Mon Sep 17 00:00:00 2001 From: Philip Withnall <withn...@endlessm.com> Date: Tue, 18 Sep 2018 13:29:18 +0100 Subject: [PATCH 05/15] gvariant: Limit GVariant strings to G_MAXSSIZE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When validating a string to see if it’s valid UTF-8, we pass a gsize to g_utf8_validate(), which only takes a gssize. For large gsize values, this will result in the gssize actually being negative, which will change g_utf8_validate()’s behaviour to stop at the first nul byte. That would allow subsequent nul bytes through the string validator, against its documented behaviour. Add a test case. oss-fuzz#10319 Signed-off-by: Philip Withnall <withn...@endlessm.com> --- glib/gvariant-serialiser.c | 3 ++- glib/tests/gvariant.c | 26 ++++++++++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/glib/gvariant-serialiser.c b/glib/gvariant-serialiser.c index 643894919..bbdcc7a0c 100644 --- a/glib/gvariant-serialiser.c +++ b/glib/gvariant-serialiser.c @@ -1643,6 +1643,7 @@ g_variant_serialiser_is_string (gconstpointer data, const gchar *expected_end; const gchar *end; + /* Strings must end with a nul terminator. */ if (size == 0) return FALSE; @@ -1651,7 +1652,7 @@ g_variant_serialiser_is_string (gconstpointer data, if (*expected_end != '\0') return FALSE; - g_utf8_validate (data, size, &end); + g_utf8_validate_len (data, size, &end); return end == expected_end; } diff --git a/glib/tests/gvariant.c b/glib/tests/gvariant.c index 1af1466cc..e575c8013 100644 --- a/glib/tests/gvariant.c +++ b/glib/tests/gvariant.c @@ -4763,6 +4763,30 @@ test_normal_checking_tuple_offsets (void) g_variant_unref (variant); } +/* Test that an empty object path is normalised successfully to the base object + * path, ‘/’. */ +static void +test_normal_checking_empty_object_path (void) +{ + const guint8 data[] = { + 0x20, 0x20, 0x00, 0x00, 0x00, 0x00, + '(', 'h', '(', 'a', 'i', 'a', 'b', 'i', 'o', ')', ')', + }; + gsize size = sizeof (data); + GVariant *variant = NULL; + GVariant *normal_variant = NULL; + + variant = g_variant_new_from_data (G_VARIANT_TYPE_VARIANT, data, size, + FALSE, NULL, NULL); + g_assert_nonnull (variant); + + normal_variant = g_variant_get_normal_form (variant); + g_assert_nonnull (normal_variant); + + g_variant_unref (normal_variant); + g_variant_unref (variant); +} + int main (int argc, char **argv) { @@ -4835,6 +4859,8 @@ main (int argc, char **argv) test_normal_checking_array_offsets); g_test_add_func ("/gvariant/normal-checking/tuple-offsets", test_normal_checking_tuple_offsets); + g_test_add_func ("/gvariant/normal-checking/empty-object-path", + test_normal_checking_empty_object_path); g_test_add_func ("/gvariant/recursion-limits/variant-in-variant", test_recursion_limits_variant_in_variant); -- 2.14.4 ++++++ 0006-gdbusmessage-Validate-type-of-message-header-signatu.patch ++++++ >From bef07b9c22e2f2127ae2c7cb5ac9a6568be39509 Mon Sep 17 00:00:00 2001 From: Philip Withnall <withn...@endlessm.com> Date: Thu, 16 Aug 2018 21:33:52 +0100 Subject: [PATCH 06/15] gdbusmessage: Validate type of message header signature field MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Parsing a D-Bus message with the signature field in the message header of type other than ‘g’ (GVariant type signature) would cause a critical warning. Instead, we should return a runtime error. Includes a test. oss-fuzz#9825 Signed-off-by: Philip Withnall <withn...@endlessm.com> --- gio/gdbusmessage.c | 19 ++++++++++++++ gio/tests/gdbus-serialization.c | 56 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 75 insertions(+) diff --git a/gio/gdbusmessage.c b/gio/gdbusmessage.c index 8de836bf6..79bc11c16 100644 --- a/gio/gdbusmessage.c +++ b/gio/gdbusmessage.c @@ -2115,6 +2115,15 @@ g_dbus_message_new_from_blob (guchar *blob, const gchar *signature_str; gsize signature_str_len; + if (!g_variant_is_of_type (signature, G_VARIANT_TYPE_SIGNATURE)) + { + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_ARGUMENT, + _("Signature header found but is not of type signature")); + goto out; + } + signature_str = g_variant_get_string (signature, &signature_str_len); /* signature but no body */ @@ -2695,6 +2704,16 @@ g_dbus_message_to_blob (GDBusMessage *message, body_start_offset = mbuf.valid_len; signature = g_dbus_message_get_header (message, G_DBUS_MESSAGE_HEADER_FIELD_SIGNATURE); + + if (signature != NULL && !g_variant_is_of_type (signature, G_VARIANT_TYPE_SIGNATURE)) + { + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_ARGUMENT, + _("Signature header found but is not of type signature")); + goto out; + } + signature_str = NULL; if (signature != NULL) signature_str = g_variant_get_string (signature, NULL); diff --git a/gio/tests/gdbus-serialization.c b/gio/tests/gdbus-serialization.c index 2ab856c48..3cdade6d7 100644 --- a/gio/tests/gdbus-serialization.c +++ b/gio/tests/gdbus-serialization.c @@ -873,6 +873,20 @@ message_serialize_header_checks (void) g_assert (blob == NULL); g_object_unref (message); + /* + * check that we can't serialize messages with SIGNATURE set to a non-signature-typed value + */ + message = g_dbus_message_new_signal ("/the/path", "The.Interface", "TheMember"); + g_dbus_message_set_header (message, G_DBUS_MESSAGE_HEADER_FIELD_SIGNATURE, g_variant_new_boolean (FALSE)); + blob = g_dbus_message_to_blob (message, &blob_size, G_DBUS_CAPABILITY_FLAGS_NONE, &error); + + g_assert_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT); + g_assert_cmpstr (error->message, ==, "Signature header found but is not of type signature"); + g_assert_null (blob); + + g_clear_error (&error); + g_clear_object (&message); + /* * check we can't serialize signal messages with INTERFACE, PATH or MEMBER unset / set to reserved value */ @@ -1083,6 +1097,46 @@ test_double_array (void) /* ---------------------------------------------------------------------------------------------------- */ +/* Test that an invalid header in a D-Bus message (specifically, with a type + * which doesn’t match what’s expected for the given header) is gracefully + * handled with an error rather than a crash. + * The set of bytes here come directly from fuzzer output. */ +static void +test_message_parse_non_signature_header (void) +{ + const guint8 data[] = { + 0x6c, /* byte order */ + 0x04, /* message type */ + 0x0f, /* message flags */ + 0x01, /* major protocol version */ + 0x00, 0x00, 0x00, 0x00, /* body length */ + 0x00, 0x00, 0x00, 0xbc, /* message serial */ + /* a{yv} of header fields: + * (things start to be invalid below here) */ + 0x02, 0x00, 0x00, 0x00, /* array length (in bytes) */ + G_DBUS_MESSAGE_HEADER_FIELD_SIGNATURE, /* array key */ + /* GVariant array value: */ + 0x04, /* signature length */ + 'd', 0x00, 0x00, 'F', /* signature (invalid) */ + 0x00, /* nul terminator */ + /* (GVariant array value payload missing) */ + /* (message body length missing) */ + }; + gsize size = sizeof (data); + GDBusMessage *message = NULL; + GError *local_error = NULL; + + message = g_dbus_message_new_from_blob ((guchar *) data, size, + G_DBUS_CAPABILITY_FLAGS_NONE, + &local_error); + g_assert_error (local_error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT); + g_assert_null (message); + + g_clear_error (&local_error); +} + +/* ---------------------------------------------------------------------------------------------------- */ + int main (int argc, char *argv[]) @@ -1102,6 +1156,8 @@ main (int argc, message_parse_empty_arrays_of_arrays); g_test_add_func ("/gdbus/message-serialize/double-array", test_double_array); + g_test_add_func ("/gdbus/message-parse/non-signature-header", + test_message_parse_non_signature_header); return g_test_run(); } -- 2.14.4 ++++++ 0007-gdbusmessage-Improve-documentation-for-g_dbus_messag.patch ++++++ >From f6fffa31a480e63787cbb7ee440da562400b0702 Mon Sep 17 00:00:00 2001 From: Philip Withnall <withn...@endlessm.com> Date: Thu, 16 Aug 2018 21:35:31 +0100 Subject: [PATCH 07/15] gdbusmessage: Improve documentation for g_dbus_message_get_header() The caller is responsible for checking the type of the returned GVariant. Signed-off-by: Philip Withnall <withn...@endlessm.com> --- gio/gdbusmessage.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/gio/gdbusmessage.c b/gio/gdbusmessage.c index 79bc11c16..22fcb2025 100644 --- a/gio/gdbusmessage.c +++ b/gio/gdbusmessage.c @@ -985,7 +985,10 @@ g_dbus_message_set_serial (GDBusMessage *message, * * Gets a header field on @message. * - * Returns: A #GVariant with the value if the header was found, %NULL + * The caller is responsible for checking the type of the returned #GVariant + * matches what is expected. + * + * Returns: (transfer none) (nullable): A #GVariant with the value if the header was found, %NULL * otherwise. Do not free, it is owned by @message. * * Since: 2.26 -- 2.14.4 ++++++ 0008-gdbusmessage-Clarify-error-returns-for-g_dbus_messag.patch ++++++ >From 95dc427c394f059c2caec91fe3ceda6e13d2f375 Mon Sep 17 00:00:00 2001 From: Philip Withnall <withn...@endlessm.com> Date: Tue, 18 Sep 2018 14:48:43 +0100 Subject: [PATCH 08/15] gdbusmessage: Clarify error returns for g_dbus_message_new_from_blob() Signed-off-by: Philip Withnall <withn...@endlessm.com> --- gio/gdbusmessage.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/gio/gdbusmessage.c b/gio/gdbusmessage.c index 22fcb2025..97888ec57 100644 --- a/gio/gdbusmessage.c +++ b/gio/gdbusmessage.c @@ -2006,6 +2006,9 @@ g_dbus_message_bytes_needed (guchar *blob, * order that the message was in can be retrieved using * g_dbus_message_get_byte_order(). * + * If the @blob cannot be parsed, contains invalid fields, or contains invalid + * headers, %G_IO_ERROR_INVALID_ARGUMENT will be returned. + * * Returns: A new #GDBusMessage or %NULL if @error is set. Free with * g_object_unref(). * -- 2.14.4 ++++++ 0009-gdbusmessage-Fix-a-typo-in-a-documentation-comment.patch ++++++ >From 3c12ef3961b95ffacf2797cdbbf52f8e0374cbe5 Mon Sep 17 00:00:00 2001 From: Philip Withnall <withn...@endlessm.com> Date: Tue, 18 Sep 2018 14:57:51 +0100 Subject: [PATCH 09/15] gdbusmessage: Fix a typo in a documentation comment Signed-off-by: Philip Withnall <withn...@endlessm.com> --- gio/gdbusmessage.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gio/gdbusmessage.c b/gio/gdbusmessage.c index 97888ec57..b1d73fad5 100644 --- a/gio/gdbusmessage.c +++ b/gio/gdbusmessage.c @@ -1929,7 +1929,7 @@ parse_value_from_blob (GMemoryBuffer *buf, /** * g_dbus_message_bytes_needed: - * @blob: (array length=blob_len) (element-type guint8): A blob represent a binary D-Bus message. + * @blob: (array length=blob_len) (element-type guint8): A blob representing a binary D-Bus message. * @blob_len: The length of @blob (must be at least 16). * @error: Return location for error or %NULL. * -- 2.14.4 ++++++ 0010-gdbusmessage-Check-for-valid-GVariantType-when-parsi.patch ++++++ >From 07997766cdc251fd1b3e90807f9259070a2a0bdd Mon Sep 17 00:00:00 2001 From: Philip Withnall <withn...@endlessm.com> Date: Tue, 18 Sep 2018 15:17:44 +0100 Subject: [PATCH 10/15] gdbusmessage: Check for valid GVariantType when parsing a variant blob MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The code was checking whether the signature provided by the blob was a valid D-Bus signature — but that’s a superset of a valid GVariant type string, since a D-Bus signature is zero or more complete types. A GVariant type string is exactly one complete type. This meant that a D-Bus message with a header field containing a variant with an empty type signature (for example) could cause a critical warning in the code parsing it. Fix that by checking whether the string is a valid type string too. Unit test included. oss-fuzz#9810 Signed-off-by: Philip Withnall <withn...@endlessm.com> --- gio/gdbusmessage.c | 5 ++- gio/tests/gdbus-serialization.c | 89 +++++++++++++++++++++++++++++++++++++++-- 2 files changed, 90 insertions(+), 4 deletions(-) diff --git a/gio/gdbusmessage.c b/gio/gdbusmessage.c index b1d73fad5..169e6fd15 100644 --- a/gio/gdbusmessage.c +++ b/gio/gdbusmessage.c @@ -1846,8 +1846,11 @@ parse_value_from_blob (GMemoryBuffer *buf, sig = read_string (buf, (gsize) siglen, &local_error); if (sig == NULL) goto fail; - if (!g_variant_is_signature (sig)) + if (!g_variant_is_signature (sig) || + !g_variant_type_string_is_valid (sig)) { + /* A D-Bus signature can contain zero or more complete types, + * but a GVariant has to be exactly one complete type. */ g_set_error (&local_error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, diff --git a/gio/tests/gdbus-serialization.c b/gio/tests/gdbus-serialization.c index 3cdade6d7..5848442e0 100644 --- a/gio/tests/gdbus-serialization.c +++ b/gio/tests/gdbus-serialization.c @@ -1105,7 +1105,7 @@ static void test_message_parse_non_signature_header (void) { const guint8 data[] = { - 0x6c, /* byte order */ + 'l', /* little-endian byte order */ 0x04, /* message type */ 0x0f, /* message flags */ 0x01, /* major protocol version */ @@ -1115,11 +1115,90 @@ test_message_parse_non_signature_header (void) * (things start to be invalid below here) */ 0x02, 0x00, 0x00, 0x00, /* array length (in bytes) */ G_DBUS_MESSAGE_HEADER_FIELD_SIGNATURE, /* array key */ - /* GVariant array value: */ + /* Variant array value: */ 0x04, /* signature length */ 'd', 0x00, 0x00, 'F', /* signature (invalid) */ 0x00, /* nul terminator */ - /* (GVariant array value payload missing) */ + /* (Variant array value payload missing) */ + /* (message body length missing) */ + }; + gsize size = sizeof (data); + GDBusMessage *message = NULL; + GError *local_error = NULL; + + message = g_dbus_message_new_from_blob ((guchar *) data, size, + G_DBUS_CAPABILITY_FLAGS_NONE, + &local_error); + g_assert_error (local_error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT); + g_assert_null (message); + + g_clear_error (&local_error); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +/* Test that an invalid header in a D-Bus message (specifically, containing a + * variant with an empty type signature) is gracefully handled with an error + * rather than a crash. The set of bytes here come directly from fuzzer + * output. */ +static void +test_message_parse_empty_signature_header (void) +{ + const guint8 data[] = { + 'l', /* little-endian byte order */ + 0x20, /* message type */ + 0x20, /* message flags */ + 0x01, /* major protocol version */ + 0x20, 0x20, 0x20, 0x00, /* body length (invalid) */ + 0x20, 0x20, 0x20, 0x20, /* message serial */ + /* a{yv} of header fields: + * (things start to be even more invalid below here) */ + 0x20, 0x20, 0x20, 0x00, /* array length (in bytes) */ + 0x20, /* array key */ + /* Variant array value: */ + 0x00, /* signature length */ + 0x00, /* nul terminator */ + /* (Variant array value payload missing) */ + /* (message body length missing) */ + }; + gsize size = sizeof (data); + GDBusMessage *message = NULL; + GError *local_error = NULL; + + message = g_dbus_message_new_from_blob ((guchar *) data, size, + G_DBUS_CAPABILITY_FLAGS_NONE, + &local_error); + g_assert_error (local_error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT); + g_assert_null (message); + + g_clear_error (&local_error); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +/* Test that an invalid header in a D-Bus message (specifically, containing a + * variant with a type signature containing multiple complete types) is + * gracefully handled with an error rather than a crash. The set of bytes here + * come directly from fuzzer output. */ +static void +test_message_parse_multiple_signature_header (void) +{ + const guint8 data[] = { + 'l', /* little-endian byte order */ + 0x20, /* message type */ + 0x20, /* message flags */ + 0x01, /* major protocol version */ + 0x20, 0x20, 0x20, 0x00, /* body length (invalid) */ + 0x20, 0x20, 0x20, 0x20, /* message serial */ + /* a{yv} of header fields: + * (things start to be even more invalid below here) */ + 0x20, 0x20, 0x20, 0x00, /* array length (in bytes) */ + 0x20, /* array key */ + /* Variant array value: */ + 0x02, /* signature length */ + 'b', 'b', /* two complete types */ + 0x00, /* nul terminator */ + /* (Variant array value payload missing) */ /* (message body length missing) */ }; gsize size = sizeof (data); @@ -1158,6 +1237,10 @@ main (int argc, g_test_add_func ("/gdbus/message-serialize/double-array", test_double_array); g_test_add_func ("/gdbus/message-parse/non-signature-header", test_message_parse_non_signature_header); + g_test_add_func ("/gdbus/message-parse/empty-signature-header", + test_message_parse_empty_signature_header); + g_test_add_func ("/gdbus/message-parse/multiple-signature-header", + test_message_parse_multiple_signature_header); return g_test_run(); } -- 2.14.4 ++++++ 0011-gvariant-Clarify-internal-documentation-about-GVaria.patch ++++++ >From 483311c861b5d4669e6630bd37de5e4a1873f257 Mon Sep 17 00:00:00 2001 From: Philip Withnall <withn...@endlessm.com> Date: Tue, 18 Sep 2018 15:21:51 +0100 Subject: [PATCH 11/15] gvariant: Clarify internal documentation about GVariant type strings Signed-off-by: Philip Withnall <withn...@endlessm.com> --- glib/gvariant-serialiser.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/glib/gvariant-serialiser.c b/glib/gvariant-serialiser.c index bbdcc7a0c..ccf96b4aa 100644 --- a/glib/gvariant-serialiser.c +++ b/glib/gvariant-serialiser.c @@ -1717,7 +1717,9 @@ g_variant_serialiser_is_object_path (gconstpointer data, * Performs the checks for being a valid string. * * Also, ensures that @data is a valid D-Bus type signature, as per the - * D-Bus specification. + * D-Bus specification. Note that this means the empty string is valid, as the + * D-Bus specification defines a signature as “zero or more single complete + * types”. */ gboolean g_variant_serialiser_is_signature (gconstpointer data, -- 2.14.4 ++++++ 0012-tests-Tidy-up-GError-handling-in-gdbus-serialization.patch ++++++ >From 18b41369d361aa38dbdd5a712e78b1bf9568a7d6 Mon Sep 17 00:00:00 2001 From: Philip Withnall <withn...@endlessm.com> Date: Thu, 20 Sep 2018 17:41:10 +0100 Subject: [PATCH 12/15] tests: Tidy up GError handling in gdbus-serialization test This introduces no functional changes; just a bit of code tidying. Signed-off-by: Philip Withnall <withn...@endlessm.com> --- gio/tests/gdbus-serialization.c | 35 ++++++++++++----------------------- 1 file changed, 12 insertions(+), 23 deletions(-) diff --git a/gio/tests/gdbus-serialization.c b/gio/tests/gdbus-serialization.c index 5848442e0..ba16bf3c8 100644 --- a/gio/tests/gdbus-serialization.c +++ b/gio/tests/gdbus-serialization.c @@ -857,7 +857,7 @@ message_serialize_header_checks (void) { GDBusMessage *message; GDBusMessage *reply; - GError *error; + GError *error = NULL; guchar *blob; gsize blob_size; @@ -865,11 +865,10 @@ message_serialize_header_checks (void) * check we can't serialize messages with INVALID type */ message = g_dbus_message_new (); - error = NULL; blob = g_dbus_message_to_blob (message, &blob_size, G_DBUS_CAPABILITY_FLAGS_NONE, &error); g_assert_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT); g_assert_cmpstr (error->message, ==, "Cannot serialize message: type is INVALID"); - g_error_free (error); + g_clear_error (&error); g_assert (blob == NULL); g_object_unref (message); @@ -894,49 +893,44 @@ message_serialize_header_checks (void) /* ----- */ /* interface NULL => error */ g_dbus_message_set_interface (message, NULL); - error = NULL; blob = g_dbus_message_to_blob (message, &blob_size, G_DBUS_CAPABILITY_FLAGS_NONE, &error); g_assert_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT); g_assert_cmpstr (error->message, ==, "Cannot serialize message: SIGNAL message: PATH, INTERFACE or MEMBER header field is missing"); - g_error_free (error); + g_clear_error (&error); g_assert (blob == NULL); /* interface reserved value => error */ g_dbus_message_set_interface (message, "org.freedesktop.DBus.Local"); - error = NULL; blob = g_dbus_message_to_blob (message, &blob_size, G_DBUS_CAPABILITY_FLAGS_NONE, &error); g_assert_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT); g_assert_cmpstr (error->message, ==, "Cannot serialize message: SIGNAL message: The INTERFACE header field is using the reserved value org.freedesktop.DBus.Local"); - g_error_free (error); + g_clear_error (&error); g_assert (blob == NULL); /* reset interface */ g_dbus_message_set_interface (message, "The.Interface"); /* ----- */ /* path NULL => error */ g_dbus_message_set_path (message, NULL); - error = NULL; blob = g_dbus_message_to_blob (message, &blob_size, G_DBUS_CAPABILITY_FLAGS_NONE, &error); g_assert_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT); g_assert_cmpstr (error->message, ==, "Cannot serialize message: SIGNAL message: PATH, INTERFACE or MEMBER header field is missing"); - g_error_free (error); + g_clear_error (&error); g_assert (blob == NULL); /* path reserved value => error */ g_dbus_message_set_path (message, "/org/freedesktop/DBus/Local"); - error = NULL; blob = g_dbus_message_to_blob (message, &blob_size, G_DBUS_CAPABILITY_FLAGS_NONE, &error); g_assert_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT); g_assert_cmpstr (error->message, ==, "Cannot serialize message: SIGNAL message: The PATH header field is using the reserved value /org/freedesktop/DBus/Local"); - g_error_free (error); + g_clear_error (&error); g_assert (blob == NULL); /* reset path */ g_dbus_message_set_path (message, "/the/path"); /* ----- */ /* member NULL => error */ g_dbus_message_set_member (message, NULL); - error = NULL; blob = g_dbus_message_to_blob (message, &blob_size, G_DBUS_CAPABILITY_FLAGS_NONE, &error); g_assert_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT); g_assert_cmpstr (error->message, ==, "Cannot serialize message: SIGNAL message: PATH, INTERFACE or MEMBER header field is missing"); - g_error_free (error); + g_clear_error (&error); g_assert (blob == NULL); /* reset member */ g_dbus_message_set_member (message, "TheMember"); @@ -951,22 +945,20 @@ message_serialize_header_checks (void) /* ----- */ /* path NULL => error */ g_dbus_message_set_path (message, NULL); - error = NULL; blob = g_dbus_message_to_blob (message, &blob_size, G_DBUS_CAPABILITY_FLAGS_NONE, &error); g_assert_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT); g_assert_cmpstr (error->message, ==, "Cannot serialize message: METHOD_CALL message: PATH or MEMBER header field is missing"); - g_error_free (error); + g_clear_error (&error); g_assert (blob == NULL); /* reset path */ g_dbus_message_set_path (message, "/the/path"); /* ----- */ /* member NULL => error */ g_dbus_message_set_member (message, NULL); - error = NULL; blob = g_dbus_message_to_blob (message, &blob_size, G_DBUS_CAPABILITY_FLAGS_NONE, &error); g_assert_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT); g_assert_cmpstr (error->message, ==, "Cannot serialize message: METHOD_CALL message: PATH or MEMBER header field is missing"); - g_error_free (error); + g_clear_error (&error); g_assert (blob == NULL); /* reset member */ g_dbus_message_set_member (message, "TheMember"); @@ -983,11 +975,10 @@ message_serialize_header_checks (void) reply = g_dbus_message_new_method_reply (message); g_assert_cmpint (g_dbus_message_get_reply_serial (reply), ==, 42); g_dbus_message_set_header (reply, G_DBUS_MESSAGE_HEADER_FIELD_REPLY_SERIAL, NULL); - error = NULL; blob = g_dbus_message_to_blob (reply, &blob_size, G_DBUS_CAPABILITY_FLAGS_NONE, &error); g_assert_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT); g_assert_cmpstr (error->message, ==, "Cannot serialize message: METHOD_RETURN message: REPLY_SERIAL header field is missing"); - g_error_free (error); + g_clear_error (&error); g_assert (blob == NULL); g_object_unref (reply); /* method error - first nuke ERROR_NAME, then REPLY_SERIAL */ @@ -995,21 +986,19 @@ message_serialize_header_checks (void) g_assert_cmpint (g_dbus_message_get_reply_serial (reply), ==, 42); /* nuke ERROR_NAME */ g_dbus_message_set_error_name (reply, NULL); - error = NULL; blob = g_dbus_message_to_blob (reply, &blob_size, G_DBUS_CAPABILITY_FLAGS_NONE, &error); g_assert_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT); g_assert_cmpstr (error->message, ==, "Cannot serialize message: ERROR message: REPLY_SERIAL or ERROR_NAME header field is missing"); - g_error_free (error); + g_clear_error (&error); g_assert (blob == NULL); /* reset ERROR_NAME */ g_dbus_message_set_error_name (reply, "Some.Error.Name"); /* nuke REPLY_SERIAL */ g_dbus_message_set_header (reply, G_DBUS_MESSAGE_HEADER_FIELD_REPLY_SERIAL, NULL); - error = NULL; blob = g_dbus_message_to_blob (reply, &blob_size, G_DBUS_CAPABILITY_FLAGS_NONE, &error); g_assert_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT); g_assert_cmpstr (error->message, ==, "Cannot serialize message: ERROR message: REPLY_SERIAL or ERROR_NAME header field is missing"); - g_error_free (error); + g_clear_error (&error); g_assert (blob == NULL); g_object_unref (reply); g_object_unref (message); -- 2.14.4 ++++++ 0013-tests-Use-g_assert_null-in-gdbus-serialization-test.patch ++++++ >From ba962c3f21a38e44cbd87e8e3cb1922b5f588889 Mon Sep 17 00:00:00 2001 From: Philip Withnall <withn...@endlessm.com> Date: Thu, 20 Sep 2018 17:42:05 +0100 Subject: [PATCH 13/15] tests: Use g_assert_null() in gdbus-serialization test This introduces no real functional changes (except when compiling with G_DISABLE_ASSERT, in which case it fixes the test). Mostly just a code cleanup. Signed-off-by: Philip Withnall <withn...@endlessm.com> --- gio/tests/gdbus-serialization.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/gio/tests/gdbus-serialization.c b/gio/tests/gdbus-serialization.c index ba16bf3c8..e7c402e70 100644 --- a/gio/tests/gdbus-serialization.c +++ b/gio/tests/gdbus-serialization.c @@ -869,7 +869,7 @@ message_serialize_header_checks (void) g_assert_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT); g_assert_cmpstr (error->message, ==, "Cannot serialize message: type is INVALID"); g_clear_error (&error); - g_assert (blob == NULL); + g_assert_null (blob); g_object_unref (message); /* @@ -897,14 +897,14 @@ message_serialize_header_checks (void) g_assert_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT); g_assert_cmpstr (error->message, ==, "Cannot serialize message: SIGNAL message: PATH, INTERFACE or MEMBER header field is missing"); g_clear_error (&error); - g_assert (blob == NULL); + g_assert_null (blob); /* interface reserved value => error */ g_dbus_message_set_interface (message, "org.freedesktop.DBus.Local"); blob = g_dbus_message_to_blob (message, &blob_size, G_DBUS_CAPABILITY_FLAGS_NONE, &error); g_assert_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT); g_assert_cmpstr (error->message, ==, "Cannot serialize message: SIGNAL message: The INTERFACE header field is using the reserved value org.freedesktop.DBus.Local"); g_clear_error (&error); - g_assert (blob == NULL); + g_assert_null (blob); /* reset interface */ g_dbus_message_set_interface (message, "The.Interface"); /* ----- */ @@ -914,14 +914,14 @@ message_serialize_header_checks (void) g_assert_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT); g_assert_cmpstr (error->message, ==, "Cannot serialize message: SIGNAL message: PATH, INTERFACE or MEMBER header field is missing"); g_clear_error (&error); - g_assert (blob == NULL); + g_assert_null (blob); /* path reserved value => error */ g_dbus_message_set_path (message, "/org/freedesktop/DBus/Local"); blob = g_dbus_message_to_blob (message, &blob_size, G_DBUS_CAPABILITY_FLAGS_NONE, &error); g_assert_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT); g_assert_cmpstr (error->message, ==, "Cannot serialize message: SIGNAL message: The PATH header field is using the reserved value /org/freedesktop/DBus/Local"); g_clear_error (&error); - g_assert (blob == NULL); + g_assert_null (blob); /* reset path */ g_dbus_message_set_path (message, "/the/path"); /* ----- */ @@ -931,7 +931,7 @@ message_serialize_header_checks (void) g_assert_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT); g_assert_cmpstr (error->message, ==, "Cannot serialize message: SIGNAL message: PATH, INTERFACE or MEMBER header field is missing"); g_clear_error (&error); - g_assert (blob == NULL); + g_assert_null (blob); /* reset member */ g_dbus_message_set_member (message, "TheMember"); /* ----- */ @@ -949,7 +949,7 @@ message_serialize_header_checks (void) g_assert_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT); g_assert_cmpstr (error->message, ==, "Cannot serialize message: METHOD_CALL message: PATH or MEMBER header field is missing"); g_clear_error (&error); - g_assert (blob == NULL); + g_assert_null (blob); /* reset path */ g_dbus_message_set_path (message, "/the/path"); /* ----- */ @@ -959,7 +959,7 @@ message_serialize_header_checks (void) g_assert_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT); g_assert_cmpstr (error->message, ==, "Cannot serialize message: METHOD_CALL message: PATH or MEMBER header field is missing"); g_clear_error (&error); - g_assert (blob == NULL); + g_assert_null (blob); /* reset member */ g_dbus_message_set_member (message, "TheMember"); /* ----- */ @@ -979,7 +979,7 @@ message_serialize_header_checks (void) g_assert_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT); g_assert_cmpstr (error->message, ==, "Cannot serialize message: METHOD_RETURN message: REPLY_SERIAL header field is missing"); g_clear_error (&error); - g_assert (blob == NULL); + g_assert_null (blob); g_object_unref (reply); /* method error - first nuke ERROR_NAME, then REPLY_SERIAL */ reply = g_dbus_message_new_method_error (message, "Some.Error.Name", "the message"); @@ -990,7 +990,7 @@ message_serialize_header_checks (void) g_assert_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT); g_assert_cmpstr (error->message, ==, "Cannot serialize message: ERROR message: REPLY_SERIAL or ERROR_NAME header field is missing"); g_clear_error (&error); - g_assert (blob == NULL); + g_assert_null (blob); /* reset ERROR_NAME */ g_dbus_message_set_error_name (reply, "Some.Error.Name"); /* nuke REPLY_SERIAL */ @@ -999,7 +999,7 @@ message_serialize_header_checks (void) g_assert_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT); g_assert_cmpstr (error->message, ==, "Cannot serialize message: ERROR message: REPLY_SERIAL or ERROR_NAME header field is missing"); g_clear_error (&error); - g_assert (blob == NULL); + g_assert_null (blob); g_object_unref (reply); g_object_unref (message); } -- 2.14.4 ++++++ 0014-gutf8-Add-a-g_utf8_validate_len-function.patch ++++++ >From e10e7d012e0c80b1c8e97a92b9153fb8c1341d47 Mon Sep 17 00:00:00 2001 From: Philip Withnall <withn...@endlessm.com> Date: Mon, 1 Oct 2018 19:52:14 +0100 Subject: [PATCH 14/15] gutf8: Add a g_utf8_validate_len() function This is a variant of g_utf8_validate() which requires the length to be specified, thereby allowing string lengths up to G_MAXSIZE rather than just G_MAXSSIZE. Signed-off-by: Philip Withnall <withn...@endlessm.com> --- docs/reference/glib/glib-sections.txt | 1 + glib/gunicode.h | 4 ++++ glib/gutf8.c | 42 ++++++++++++++++++++++++++++++----- glib/tests/utf8-validate.c | 15 +++++++++---- 4 files changed, 53 insertions(+), 9 deletions(-) diff --git a/docs/reference/glib/glib-sections.txt b/docs/reference/glib/glib-sections.txt index f88d6b53b..d875f9a3a 100644 --- a/docs/reference/glib/glib-sections.txt +++ b/docs/reference/glib/glib-sections.txt @@ -2964,6 +2964,7 @@ g_utf8_strrchr g_utf8_strreverse g_utf8_substring g_utf8_validate +g_utf8_validate_len g_utf8_make_valid <SUBSECTION> diff --git a/glib/gunicode.h b/glib/gunicode.h index 481bc5212..36f841b9d 100644 --- a/glib/gunicode.h +++ b/glib/gunicode.h @@ -847,6 +847,10 @@ GLIB_AVAILABLE_IN_ALL gboolean g_utf8_validate (const gchar *str, gssize max_len, const gchar **end); +GLIB_AVAILABLE_IN_2_58 +gboolean g_utf8_validate_len (const gchar *str, + gsize max_len, + const gchar **end); GLIB_AVAILABLE_IN_ALL gchar *g_utf8_strup (const gchar *str, diff --git a/glib/gutf8.c b/glib/gutf8.c index a0fb16370..291534fc8 100644 --- a/glib/gutf8.c +++ b/glib/gutf8.c @@ -1669,16 +1669,48 @@ g_utf8_validate (const char *str, { const gchar *p; - if (max_len < 0) - p = fast_validate (str); + if (max_len >= 0) + return g_utf8_validate_len (str, max_len, end); + + p = fast_validate (str); + + if (end) + *end = p; + + if (*p != '\0') + return FALSE; else - p = fast_validate_len (str, max_len); + return TRUE; +} + +/** + * g_utf8_validate_len: + * @str: (array length=max_len) (element-type guint8): a pointer to character data + * @max_len: max bytes to validate + * @end: (out) (optional) (transfer none): return location for end of valid data + * + * Validates UTF-8 encoded text. + * + * As with g_utf8_validate(), but @max_len must be set, and hence this function + * will always return %FALSE if any of the bytes of @str are nul. + * + * Returns: %TRUE if the text was valid UTF-8 + * Since: 2.60 + */ +gboolean +g_utf8_validate_len (const char *str, + gsize max_len, + const gchar **end) + +{ + const gchar *p; + + p = fast_validate_len (str, max_len); if (end) *end = p; - if ((max_len >= 0 && p != str + max_len) || - (max_len < 0 && *p != '\0')) + if (p != str + max_len) return FALSE; else return TRUE; diff --git a/glib/tests/utf8-validate.c b/glib/tests/utf8-validate.c index 1609bde34..5806b29a0 100644 --- a/glib/tests/utf8-validate.c +++ b/glib/tests/utf8-validate.c @@ -280,15 +280,22 @@ do_test (gconstpointer d) result = g_utf8_validate (test->text, test->max_len, &end); - g_assert (result == test->valid); - g_assert (end - test->text == test->offset); + g_assert_true (result == test->valid); + g_assert_cmpint (end - test->text, ==, test->offset); if (test->max_len < 0) { result = g_utf8_validate (test->text, strlen (test->text), &end); - g_assert (result == test->valid); - g_assert (end - test->text == test->offset); + g_assert_true (result == test->valid); + g_assert_cmpint (end - test->text, ==, test->offset); + } + else + { + result = g_utf8_validate_len (test->text, test->max_len, &end); + + g_assert_true (result == test->valid); + g_assert_cmpint (end - test->text, ==, test->offset); } } -- 2.14.4 ++++++ 0015-glib-Port-various-callers-to-use-g_utf8_validate_len.patch ++++++ >From c23efe320561d99edc4cd066317b5a5b131c7004 Mon Sep 17 00:00:00 2001 From: Philip Withnall <withn...@endlessm.com> Date: Thu, 4 Oct 2018 13:22:13 +0100 Subject: [PATCH 15/15] glib: Port various callers to use g_utf8_validate_len() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These were callers which explicitly specified the string length to g_utf8_validate(), when it couldn’t be negative, and hence should be able to unconditionally benefit from the increased string handling length. At least one call site would have previously silently changed behaviour if called with strings longer than G_MAXSSIZE in length. Another call site was passing strlen(string) to g_utf8_validate(), which seems pointless: just pass -1 instead, and let g_utf8_validate() calculate the string length. Its behaviour on embedded nul bytes wouldn’t change, as strlen() stops at the first one. Signed-off-by: Philip Withnall <withn...@endlessm.com> --- gio/glocalfileinfo.c | 4 ++-- glib/giochannel.c | 2 +- glib/gmarkup.c | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/gio/glocalfileinfo.c b/gio/glocalfileinfo.c index ed7e99400..59cfb9ba9 100644 --- a/gio/glocalfileinfo.c +++ b/gio/glocalfileinfo.c @@ -1063,7 +1063,7 @@ make_valid_utf8 (const char *name) { GString *string; const gchar *remainder, *invalid; - gint remaining_bytes, valid_bytes; + gsize remaining_bytes, valid_bytes; string = NULL; remainder = name; @@ -1071,7 +1071,7 @@ make_valid_utf8 (const char *name) while (remaining_bytes != 0) { - if (g_utf8_validate (remainder, remaining_bytes, &invalid)) + if (g_utf8_validate_len (remainder, remaining_bytes, &invalid)) break; valid_bytes = invalid - remainder; diff --git a/glib/giochannel.c b/glib/giochannel.c index d8c3b0b09..88fd8c81d 100644 --- a/glib/giochannel.c +++ b/glib/giochannel.c @@ -2323,7 +2323,7 @@ reconvert: /* UTF-8, just validate, emulate g_iconv */ - if (!g_utf8_validate (from_buf, try_len, &badchar)) + if (!g_utf8_validate_len (from_buf, try_len, &badchar)) { gunichar try_char; gsize incomplete_len = from_buf + try_len - badchar; diff --git a/glib/gmarkup.c b/glib/gmarkup.c index 43bb0c7f8..9b15b1281 100644 --- a/glib/gmarkup.c +++ b/glib/gmarkup.c @@ -455,7 +455,7 @@ slow_name_validate (GMarkupParseContext *context, { const gchar *p = name; - if (!g_utf8_validate (name, strlen (name), NULL)) + if (!g_utf8_validate (name, -1, NULL)) { set_error (context, error, G_MARKUP_ERROR_BAD_UTF8, _("Invalid UTF-8 encoded text in name — not valid “%s”"), name); @@ -538,7 +538,7 @@ text_validate (GMarkupParseContext *context, gint len, GError **error) { - if (!g_utf8_validate (p, len, NULL)) + if (!g_utf8_validate_len (p, len, NULL)) { set_error (context, error, G_MARKUP_ERROR_BAD_UTF8, _("Invalid UTF-8 encoded text in name — not valid “%s”"), p); -- 2.14.4