Merge branch '2528-dbus-message-truncation' into 'main'

gdbusmessage: Add more bounds checking when parsing D-Bus messages

Closes #2528

See merge request GNOME/glib!2355
This commit is contained in:
Philip Withnall 2021-11-23 13:18:53 +00:00
commit e6062056bc
2 changed files with 215 additions and 76 deletions

View File

@ -83,21 +83,36 @@ g_memory_buffer_is_byteswapped (GMemoryBuffer *mbuf)
} }
static guchar static guchar
g_memory_buffer_read_byte (GMemoryBuffer *mbuf) g_memory_buffer_read_byte (GMemoryBuffer *mbuf,
GError **error)
{ {
g_return_val_if_fail (error == NULL || *error == NULL, 0);
if (mbuf->pos >= mbuf->valid_len) if (mbuf->pos >= mbuf->valid_len)
return 0; {
g_set_error (error,
G_IO_ERROR,
G_IO_ERROR_INVALID_ARGUMENT,
"Unexpected end of message while reading byte.");
return 0;
}
return mbuf->data [mbuf->pos++]; return mbuf->data [mbuf->pos++];
} }
static gint16 static gint16
g_memory_buffer_read_int16 (GMemoryBuffer *mbuf) g_memory_buffer_read_int16 (GMemoryBuffer *mbuf,
GError **error)
{ {
gint16 v; gint16 v;
g_return_val_if_fail (error == NULL || *error == NULL, -1);
if (mbuf->pos > mbuf->valid_len - 2) if (mbuf->pos > mbuf->valid_len - 2)
{ {
mbuf->pos = mbuf->valid_len; g_set_error (error,
G_IO_ERROR,
G_IO_ERROR_INVALID_ARGUMENT,
"Unexpected end of message while reading int16.");
return 0; return 0;
} }
@ -111,13 +126,19 @@ g_memory_buffer_read_int16 (GMemoryBuffer *mbuf)
} }
static guint16 static guint16
g_memory_buffer_read_uint16 (GMemoryBuffer *mbuf) g_memory_buffer_read_uint16 (GMemoryBuffer *mbuf,
GError **error)
{ {
guint16 v; guint16 v;
g_return_val_if_fail (error == NULL || *error == NULL, 0);
if (mbuf->pos > mbuf->valid_len - 2) if (mbuf->pos > mbuf->valid_len - 2)
{ {
mbuf->pos = mbuf->valid_len; g_set_error (error,
G_IO_ERROR,
G_IO_ERROR_INVALID_ARGUMENT,
"Unexpected end of message while reading uint16.");
return 0; return 0;
} }
@ -131,13 +152,19 @@ g_memory_buffer_read_uint16 (GMemoryBuffer *mbuf)
} }
static gint32 static gint32
g_memory_buffer_read_int32 (GMemoryBuffer *mbuf) g_memory_buffer_read_int32 (GMemoryBuffer *mbuf,
GError **error)
{ {
gint32 v; gint32 v;
g_return_val_if_fail (error == NULL || *error == NULL, -1);
if (mbuf->pos > mbuf->valid_len - 4) if (mbuf->pos > mbuf->valid_len - 4)
{ {
mbuf->pos = mbuf->valid_len; g_set_error (error,
G_IO_ERROR,
G_IO_ERROR_INVALID_ARGUMENT,
"Unexpected end of message while reading int32.");
return 0; return 0;
} }
@ -151,13 +178,19 @@ g_memory_buffer_read_int32 (GMemoryBuffer *mbuf)
} }
static guint32 static guint32
g_memory_buffer_read_uint32 (GMemoryBuffer *mbuf) g_memory_buffer_read_uint32 (GMemoryBuffer *mbuf,
GError **error)
{ {
guint32 v; guint32 v;
g_return_val_if_fail (error == NULL || *error == NULL, 0);
if (mbuf->pos > mbuf->valid_len - 4) if (mbuf->pos > mbuf->valid_len - 4)
{ {
mbuf->pos = mbuf->valid_len; g_set_error (error,
G_IO_ERROR,
G_IO_ERROR_INVALID_ARGUMENT,
"Unexpected end of message while reading uint32.");
return 0; return 0;
} }
@ -171,13 +204,19 @@ g_memory_buffer_read_uint32 (GMemoryBuffer *mbuf)
} }
static gint64 static gint64
g_memory_buffer_read_int64 (GMemoryBuffer *mbuf) g_memory_buffer_read_int64 (GMemoryBuffer *mbuf,
GError **error)
{ {
gint64 v; gint64 v;
g_return_val_if_fail (error == NULL || *error == NULL, -1);
if (mbuf->pos > mbuf->valid_len - 8) if (mbuf->pos > mbuf->valid_len - 8)
{ {
mbuf->pos = mbuf->valid_len; g_set_error (error,
G_IO_ERROR,
G_IO_ERROR_INVALID_ARGUMENT,
"Unexpected end of message while reading int64.");
return 0; return 0;
} }
@ -191,13 +230,19 @@ g_memory_buffer_read_int64 (GMemoryBuffer *mbuf)
} }
static guint64 static guint64
g_memory_buffer_read_uint64 (GMemoryBuffer *mbuf) g_memory_buffer_read_uint64 (GMemoryBuffer *mbuf,
GError **error)
{ {
guint64 v; guint64 v;
g_return_val_if_fail (error == NULL || *error == NULL, 0);
if (mbuf->pos > mbuf->valid_len - 8) if (mbuf->pos > mbuf->valid_len - 8)
{ {
mbuf->pos = mbuf->valid_len; g_set_error (error,
G_IO_ERROR,
G_IO_ERROR_INVALID_ARGUMENT,
"Unexpected end of message while reading uint64.");
return 0; return 0;
} }
@ -1500,7 +1545,9 @@ parse_value_from_blob (GMemoryBuffer *buf,
if (!just_align) if (!just_align)
{ {
gboolean v; gboolean v;
v = g_memory_buffer_read_uint32 (buf); v = g_memory_buffer_read_uint32 (buf, &local_error);
if (local_error)
goto fail;
ret = g_variant_new_boolean (v); ret = g_variant_new_boolean (v);
} }
break; break;
@ -1509,7 +1556,9 @@ parse_value_from_blob (GMemoryBuffer *buf,
if (!just_align) if (!just_align)
{ {
guchar v; guchar v;
v = g_memory_buffer_read_byte (buf); v = g_memory_buffer_read_byte (buf, &local_error);
if (local_error)
goto fail;
ret = g_variant_new_byte (v); ret = g_variant_new_byte (v);
} }
break; break;
@ -1519,7 +1568,9 @@ parse_value_from_blob (GMemoryBuffer *buf,
if (!just_align) if (!just_align)
{ {
gint16 v; gint16 v;
v = g_memory_buffer_read_int16 (buf); v = g_memory_buffer_read_int16 (buf, &local_error);
if (local_error)
goto fail;
ret = g_variant_new_int16 (v); ret = g_variant_new_int16 (v);
} }
break; break;
@ -1529,7 +1580,9 @@ parse_value_from_blob (GMemoryBuffer *buf,
if (!just_align) if (!just_align)
{ {
guint16 v; guint16 v;
v = g_memory_buffer_read_uint16 (buf); v = g_memory_buffer_read_uint16 (buf, &local_error);
if (local_error)
goto fail;
ret = g_variant_new_uint16 (v); ret = g_variant_new_uint16 (v);
} }
break; break;
@ -1539,7 +1592,9 @@ parse_value_from_blob (GMemoryBuffer *buf,
if (!just_align) if (!just_align)
{ {
gint32 v; gint32 v;
v = g_memory_buffer_read_int32 (buf); v = g_memory_buffer_read_int32 (buf, &local_error);
if (local_error)
goto fail;
ret = g_variant_new_int32 (v); ret = g_variant_new_int32 (v);
} }
break; break;
@ -1549,7 +1604,9 @@ parse_value_from_blob (GMemoryBuffer *buf,
if (!just_align) if (!just_align)
{ {
guint32 v; guint32 v;
v = g_memory_buffer_read_uint32 (buf); v = g_memory_buffer_read_uint32 (buf, &local_error);
if (local_error)
goto fail;
ret = g_variant_new_uint32 (v); ret = g_variant_new_uint32 (v);
} }
break; break;
@ -1559,7 +1616,9 @@ parse_value_from_blob (GMemoryBuffer *buf,
if (!just_align) if (!just_align)
{ {
gint64 v; gint64 v;
v = g_memory_buffer_read_int64 (buf); v = g_memory_buffer_read_int64 (buf, &local_error);
if (local_error)
goto fail;
ret = g_variant_new_int64 (v); ret = g_variant_new_int64 (v);
} }
break; break;
@ -1569,7 +1628,9 @@ parse_value_from_blob (GMemoryBuffer *buf,
if (!just_align) if (!just_align)
{ {
guint64 v; guint64 v;
v = g_memory_buffer_read_uint64 (buf); v = g_memory_buffer_read_uint64 (buf, &local_error);
if (local_error)
goto fail;
ret = g_variant_new_uint64 (v); ret = g_variant_new_uint64 (v);
} }
break; break;
@ -1583,7 +1644,9 @@ parse_value_from_blob (GMemoryBuffer *buf,
gdouble v_double; gdouble v_double;
} u; } u;
G_STATIC_ASSERT (sizeof (gdouble) == sizeof (guint64)); G_STATIC_ASSERT (sizeof (gdouble) == sizeof (guint64));
u.v_uint64 = g_memory_buffer_read_uint64 (buf); u.v_uint64 = g_memory_buffer_read_uint64 (buf, &local_error);
if (local_error)
goto fail;
ret = g_variant_new_double (u.v_double); ret = g_variant_new_double (u.v_double);
} }
break; break;
@ -1594,7 +1657,9 @@ parse_value_from_blob (GMemoryBuffer *buf,
{ {
guint32 len; guint32 len;
const gchar *v; const gchar *v;
len = g_memory_buffer_read_uint32 (buf); len = g_memory_buffer_read_uint32 (buf, &local_error);
if (local_error)
goto fail;
v = read_string (buf, (gsize) len, &local_error); v = read_string (buf, (gsize) len, &local_error);
if (v == NULL) if (v == NULL)
goto fail; goto fail;
@ -1608,7 +1673,9 @@ parse_value_from_blob (GMemoryBuffer *buf,
{ {
guint32 len; guint32 len;
const gchar *v; const gchar *v;
len = g_memory_buffer_read_uint32 (buf); len = g_memory_buffer_read_uint32 (buf, &local_error);
if (local_error)
goto fail;
v = read_string (buf, (gsize) len, &local_error); v = read_string (buf, (gsize) len, &local_error);
if (v == NULL) if (v == NULL)
goto fail; goto fail;
@ -1630,7 +1697,9 @@ parse_value_from_blob (GMemoryBuffer *buf,
{ {
guchar len; guchar len;
const gchar *v; const gchar *v;
len = g_memory_buffer_read_byte (buf); len = g_memory_buffer_read_byte (buf, &local_error);
if (local_error)
goto fail;
v = read_string (buf, (gsize) len, &local_error); v = read_string (buf, (gsize) len, &local_error);
if (v == NULL) if (v == NULL)
goto fail; goto fail;
@ -1652,7 +1721,9 @@ parse_value_from_blob (GMemoryBuffer *buf,
if (!just_align) if (!just_align)
{ {
gint32 v; gint32 v;
v = g_memory_buffer_read_int32 (buf); v = g_memory_buffer_read_int32 (buf, &local_error);
if (local_error)
goto fail;
ret = g_variant_new_handle (v); ret = g_variant_new_handle (v);
} }
break; break;
@ -1672,7 +1743,9 @@ parse_value_from_blob (GMemoryBuffer *buf,
const GVariantType *element_type; const GVariantType *element_type;
guint fixed_size; guint fixed_size;
array_len = g_memory_buffer_read_uint32 (buf); array_len = g_memory_buffer_read_uint32 (buf, &local_error);
if (local_error)
goto fail;
#ifdef DEBUG_SERIALIZER #ifdef DEBUG_SERIALIZER
is_leaf = FALSE; is_leaf = FALSE;
@ -1880,7 +1953,9 @@ parse_value_from_blob (GMemoryBuffer *buf,
GVariantType *variant_type; GVariantType *variant_type;
GVariant *value; GVariant *value;
siglen = g_memory_buffer_read_byte (buf); siglen = g_memory_buffer_read_byte (buf, &local_error);
if (local_error)
goto fail;
sig = read_string (buf, (gsize) siglen, &local_error); sig = read_string (buf, (gsize) siglen, &local_error);
if (sig == NULL) if (sig == NULL)
goto fail; goto fail;
@ -2078,7 +2153,7 @@ g_dbus_message_new_from_blob (guchar *blob,
GDBusCapabilityFlags capabilities, GDBusCapabilityFlags capabilities,
GError **error) GError **error)
{ {
gboolean ret; GError *local_error = NULL;
GMemoryBuffer mbuf; GMemoryBuffer mbuf;
GDBusMessage *message; GDBusMessage *message;
guchar endianness; guchar endianness;
@ -2091,11 +2166,8 @@ g_dbus_message_new_from_blob (guchar *blob,
/* TODO: check against @capabilities */ /* TODO: check against @capabilities */
ret = FALSE;
g_return_val_if_fail (blob != NULL, NULL); g_return_val_if_fail (blob != NULL, NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL); g_return_val_if_fail (error == NULL || *error == NULL, NULL);
g_return_val_if_fail (blob_len >= 12, NULL);
message = g_dbus_message_new (); message = g_dbus_message_new ();
@ -2103,7 +2175,10 @@ g_dbus_message_new_from_blob (guchar *blob,
mbuf.data = (gchar *)blob; mbuf.data = (gchar *)blob;
mbuf.len = mbuf.valid_len = blob_len; mbuf.len = mbuf.valid_len = blob_len;
endianness = g_memory_buffer_read_byte (&mbuf); endianness = g_memory_buffer_read_byte (&mbuf, &local_error);
if (local_error)
goto fail;
switch (endianness) switch (endianness)
{ {
case 'l': case 'l':
@ -2115,28 +2190,38 @@ g_dbus_message_new_from_blob (guchar *blob,
message->byte_order = G_DBUS_MESSAGE_BYTE_ORDER_BIG_ENDIAN; message->byte_order = G_DBUS_MESSAGE_BYTE_ORDER_BIG_ENDIAN;
break; break;
default: default:
g_set_error (error, g_set_error (&local_error,
G_IO_ERROR, G_IO_ERROR,
G_IO_ERROR_INVALID_ARGUMENT, G_IO_ERROR_INVALID_ARGUMENT,
_("Invalid endianness value. Expected 0x6c (“l”) or 0x42 (“B”) but found value 0x%02x"), _("Invalid endianness value. Expected 0x6c (“l”) or 0x42 (“B”) but found value 0x%02x"),
endianness); endianness);
goto out; goto fail;
} }
message->type = g_memory_buffer_read_byte (&mbuf); message->type = g_memory_buffer_read_byte (&mbuf, &local_error);
message->flags = g_memory_buffer_read_byte (&mbuf); if (local_error)
major_protocol_version = g_memory_buffer_read_byte (&mbuf); goto fail;
message->flags = g_memory_buffer_read_byte (&mbuf, &local_error);
if (local_error)
goto fail;
major_protocol_version = g_memory_buffer_read_byte (&mbuf, &local_error);
if (local_error)
goto fail;
if (major_protocol_version != 1) if (major_protocol_version != 1)
{ {
g_set_error (error, g_set_error (&local_error,
G_IO_ERROR, G_IO_ERROR,
G_IO_ERROR_INVALID_ARGUMENT, G_IO_ERROR_INVALID_ARGUMENT,
_("Invalid major protocol version. Expected 1 but found %d"), _("Invalid major protocol version. Expected 1 but found %d"),
major_protocol_version); major_protocol_version);
goto out; goto fail;
} }
message_body_len = g_memory_buffer_read_uint32 (&mbuf); message_body_len = g_memory_buffer_read_uint32 (&mbuf, &local_error);
message->serial = g_memory_buffer_read_uint32 (&mbuf); if (local_error)
goto fail;
message->serial = g_memory_buffer_read_uint32 (&mbuf, &local_error);
if (local_error)
goto fail;
#ifdef DEBUG_SERIALIZER #ifdef DEBUG_SERIALIZER
g_print ("Parsing blob (blob_len = 0x%04x bytes)\n", (gint) blob_len); g_print ("Parsing blob (blob_len = 0x%04x bytes)\n", (gint) blob_len);
@ -2156,9 +2241,9 @@ g_dbus_message_new_from_blob (guchar *blob,
G_DBUS_MAX_TYPE_DEPTH + 2 /* for the a{yv} */, G_DBUS_MAX_TYPE_DEPTH + 2 /* for the a{yv} */,
FALSE, FALSE,
2, 2,
error); &local_error);
if (headers == NULL) if (headers == NULL)
goto out; goto fail;
g_variant_iter_init (&iter, headers); g_variant_iter_init (&iter, headers);
while ((item = g_variant_iter_next_value (&iter)) != NULL) while ((item = g_variant_iter_next_value (&iter)) != NULL)
{ {
@ -2182,11 +2267,11 @@ g_dbus_message_new_from_blob (guchar *blob,
if (!g_variant_is_of_type (signature, G_VARIANT_TYPE_SIGNATURE)) if (!g_variant_is_of_type (signature, G_VARIANT_TYPE_SIGNATURE))
{ {
g_set_error_literal (error, g_set_error_literal (&local_error,
G_IO_ERROR, G_IO_ERROR,
G_IO_ERROR_INVALID_ARGUMENT, G_IO_ERROR_INVALID_ARGUMENT,
_("Signature header found but is not of type signature")); _("Signature header found but is not of type signature"));
goto out; goto fail;
} }
signature_str = g_variant_get_string (signature, &signature_str_len); signature_str = g_variant_get_string (signature, &signature_str_len);
@ -2194,12 +2279,12 @@ g_dbus_message_new_from_blob (guchar *blob,
/* signature but no body */ /* signature but no body */
if (message_body_len == 0 && signature_str_len > 0) if (message_body_len == 0 && signature_str_len > 0)
{ {
g_set_error (error, g_set_error (&local_error,
G_IO_ERROR, G_IO_ERROR,
G_IO_ERROR_INVALID_ARGUMENT, G_IO_ERROR_INVALID_ARGUMENT,
_("Signature header with signature “%s” found but message body is empty"), _("Signature header with signature “%s” found but message body is empty"),
signature_str); signature_str);
goto out; goto fail;
} }
else if (signature_str_len > 0) else if (signature_str_len > 0)
{ {
@ -2209,13 +2294,13 @@ g_dbus_message_new_from_blob (guchar *blob,
if (!g_variant_is_signature (signature_str) || if (!g_variant_is_signature (signature_str) ||
!g_variant_type_string_is_valid (tupled_signature_str)) !g_variant_type_string_is_valid (tupled_signature_str))
{ {
g_set_error (error, g_set_error (&local_error,
G_IO_ERROR, G_IO_ERROR,
G_IO_ERROR_INVALID_ARGUMENT, G_IO_ERROR_INVALID_ARGUMENT,
_("Parsed value “%s” is not a valid D-Bus signature (for body)"), _("Parsed value “%s” is not a valid D-Bus signature (for body)"),
signature_str); signature_str);
g_free (tupled_signature_str); g_free (tupled_signature_str);
goto out; goto fail;
} }
variant_type = g_variant_type_new (tupled_signature_str); variant_type = g_variant_type_new (tupled_signature_str);
@ -2228,10 +2313,10 @@ g_dbus_message_new_from_blob (guchar *blob,
G_DBUS_MAX_TYPE_DEPTH + 1 /* for the surrounding tuple */, G_DBUS_MAX_TYPE_DEPTH + 1 /* for the surrounding tuple */,
FALSE, FALSE,
2, 2,
error); &local_error);
g_variant_type_free (variant_type); g_variant_type_free (variant_type);
if (message->body == NULL) if (message->body == NULL)
goto out; goto fail;
} }
} }
else else
@ -2240,7 +2325,7 @@ g_dbus_message_new_from_blob (guchar *blob,
if (message_body_len != 0) if (message_body_len != 0)
{ {
/* G_GUINT32_FORMAT doesn't work with gettext, just use %u */ /* G_GUINT32_FORMAT doesn't work with gettext, just use %u */
g_set_error (error, g_set_error (&local_error,
G_IO_ERROR, G_IO_ERROR,
G_IO_ERROR_INVALID_ARGUMENT, G_IO_ERROR_INVALID_ARGUMENT,
g_dngettext (GETTEXT_PACKAGE, g_dngettext (GETTEXT_PACKAGE,
@ -2248,29 +2333,22 @@ g_dbus_message_new_from_blob (guchar *blob,
"No signature header in message but the message body is %u bytes", "No signature header in message but the message body is %u bytes",
message_body_len), message_body_len),
message_body_len); message_body_len);
goto out; goto fail;
} }
} }
if (!validate_headers (message, error)) if (!validate_headers (message, &local_error))
{ {
g_prefix_error (error, _("Cannot deserialize message: ")); g_prefix_error (&local_error, _("Cannot deserialize message: "));
goto out; goto fail;
} }
ret = TRUE; return message;
out: fail:
if (ret) g_clear_object (&message);
{ g_propagate_error (error, local_error);
return message; return NULL;
}
else
{
if (message != NULL)
g_object_unref (message);
return NULL;
}
} }
/* ---------------------------------------------------------------------------------------------------- */ /* ---------------------------------------------------------------------------------------------------- */

View File

@ -1472,6 +1472,65 @@ test_message_parse_deep_body_nesting (void)
/* ---------------------------------------------------------------------------------------------------- */ /* ---------------------------------------------------------------------------------------------------- */
static void
test_message_parse_truncated (void)
{
GDBusMessage *message = NULL;
GDBusMessage *message2 = NULL;
GVariantBuilder builder;
guchar *blob = NULL;
gsize size = 0;
GError *error = NULL;
g_test_summary ("Test that truncated messages are properly rejected.");
g_test_bug ("https://gitlab.gnome.org/GNOME/glib/-/issues/2528");
message = g_dbus_message_new ();
g_variant_builder_init (&builder, G_VARIANT_TYPE ("(asbynqiuxtd)"));
g_variant_builder_open (&builder, G_VARIANT_TYPE ("as"));
g_variant_builder_add (&builder, "s", "fourtytwo");
g_variant_builder_close (&builder);
g_variant_builder_add (&builder, "b", TRUE);
g_variant_builder_add (&builder, "y", 42);
g_variant_builder_add (&builder, "n", 42);
g_variant_builder_add (&builder, "q", 42);
g_variant_builder_add (&builder, "i", 42);
g_variant_builder_add (&builder, "u", 42);
g_variant_builder_add (&builder, "x", 42);
g_variant_builder_add (&builder, "t", 42);
g_variant_builder_add (&builder, "d", (gdouble) 42);
g_dbus_message_set_message_type (message, G_DBUS_MESSAGE_TYPE_METHOD_CALL);
g_dbus_message_set_header (message, G_DBUS_MESSAGE_HEADER_FIELD_PATH,
g_variant_new_object_path ("/foo/bar"));
g_dbus_message_set_header (message, G_DBUS_MESSAGE_HEADER_FIELD_MEMBER,
g_variant_new_string ("Member"));
g_dbus_message_set_body (message, g_variant_builder_end (&builder));
blob = g_dbus_message_to_blob (message, &size, G_DBUS_CAPABILITY_FLAGS_NONE, &error);
g_assert_no_error (error);
g_clear_object (&message);
/* Try parsing all possible prefixes of the full @blob. */
for (gsize i = 0; i < size; i++)
{
message2 = g_dbus_message_new_from_blob (blob, i, G_DBUS_CAPABILITY_FLAGS_NONE, &error);
g_assert_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT);
g_assert_null (message2);
g_clear_error (&error);
}
message2 = g_dbus_message_new_from_blob (blob, size, G_DBUS_CAPABILITY_FLAGS_NONE, &error);
g_assert_no_error (error);
g_assert_true (G_IS_DBUS_MESSAGE (message2));
g_clear_object (&message2);
g_free (blob);
}
/* ---------------------------------------------------------------------------------------------------- */
int int
main (int argc, main (int argc,
char *argv[]) char *argv[])
@ -1506,6 +1565,8 @@ main (int argc,
test_message_parse_deep_header_nesting); test_message_parse_deep_header_nesting);
g_test_add_func ("/gdbus/message-parse/deep-body-nesting", g_test_add_func ("/gdbus/message-parse/deep-body-nesting",
test_message_parse_deep_body_nesting); test_message_parse_deep_body_nesting);
g_test_add_func ("/gdbus/message-parse/truncated",
test_message_parse_truncated);
return g_test_run(); return g_test_run();
} }