From 3ebf0fc043b6c9b6c2381dab893b54ebcb8ac13d Mon Sep 17 00:00:00 2001 From: Armin Kuster Date: Tue, 2 Jul 2019 09:13:51 -0700 Subject: glib: Security fix for CVE-2019-9633 Source: gnome.org MR: 98802 Type: Security Fix Disposition: Backport from https://gitlab.gnome.org/GNOME/glib/commit/d553d92d6e9f53cbe5a34166fcb919ba652c6a8e ChangeID: b73c332f27f47ddc1b1cfd7424f24778acc0c318 Description: includes supporting patch. Fixes CVE-2019-9633 Signed-off-by: Armin Kuster Signed-off-by: Armin Kuster --- .../glib-2.0/glib-2.0/CVE-2019-9633_p1.patch | 316 +++++++++++++++++++++ .../glib-2.0/glib-2.0/CVE-2019-9633_p2.patch | 231 +++++++++++++++ meta/recipes-core/glib-2.0/glib-2.0_2.58.0.bb | 2 + 3 files changed, 549 insertions(+) create mode 100644 meta/recipes-core/glib-2.0/glib-2.0/CVE-2019-9633_p1.patch create mode 100644 meta/recipes-core/glib-2.0/glib-2.0/CVE-2019-9633_p2.patch (limited to 'meta') diff --git a/meta/recipes-core/glib-2.0/glib-2.0/CVE-2019-9633_p1.patch b/meta/recipes-core/glib-2.0/glib-2.0/CVE-2019-9633_p1.patch new file mode 100644 index 0000000000..f95716aecf --- /dev/null +++ b/meta/recipes-core/glib-2.0/glib-2.0/CVE-2019-9633_p1.patch @@ -0,0 +1,316 @@ +From c1e32b90576af11556c8a9178e43902f3394a4b0 Mon Sep 17 00:00:00 2001 +From: Patrick Griffis +Date: Mon, 29 Oct 2018 09:53:07 -0400 +Subject: [PATCH] gsocketclient: Improve handling of slow initial connections + +Currently a new connection will not be attempted until the previous +one has timed out and as the current API only exposes a single +timeout value in practice it often means that it will wait 30 seconds +(or forever with 0 (the default)) on each connection. + +This is unacceptable so we are now trying to follow the behavior +RFC 8305 recommends by making multiple connection attempts if +the connection takes longer than 250ms. The first connection +to make it to completion then wins. + +Upstream-Status: Backport +CVE: CVE-2019-9633 patch 1 +Affects: < 2.59.2 +Signed-off-by: Armin Kuster + +--- + gio/gsocketclient.c | 176 ++++++++++++++++++++++++++++++++++++++++++++-------- + 1 file changed, 151 insertions(+), 25 deletions(-) + +diff --git a/gio/gsocketclient.c b/gio/gsocketclient.c +index ddd1497..5c6513c 100644 +--- a/gio/gsocketclient.c ++++ b/gio/gsocketclient.c +@@ -2,6 +2,7 @@ + * + * Copyright © 2008, 2009 codethink + * Copyright © 2009 Red Hat, Inc ++ * Copyright © 2018 Igalia S.L. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public +@@ -49,6 +50,10 @@ + #include + #include "glibintl.h" + ++/* As recommended by RFC 8305 this is the time it waits ++ * on a connection before starting another concurrent attempt. ++ */ ++#define HAPPY_EYEBALLS_CONNECTION_ATTEMPT_TIMEOUT_MS 250 + + /** + * SECTION:gsocketclient +@@ -1328,28 +1333,82 @@ typedef struct + GSocketConnectable *connectable; + GSocketAddressEnumerator *enumerator; + GProxyAddress *proxy_addr; +- GSocketAddress *current_addr; +- GSocket *current_socket; ++ GSocket *socket; + GIOStream *connection; + ++ GSList *connection_attempts; + GError *last_error; + } GSocketClientAsyncConnectData; + ++static void connection_attempt_unref (gpointer attempt); ++ + static void + g_socket_client_async_connect_data_free (GSocketClientAsyncConnectData *data) + { + g_clear_object (&data->connectable); + g_clear_object (&data->enumerator); + g_clear_object (&data->proxy_addr); +- g_clear_object (&data->current_addr); +- g_clear_object (&data->current_socket); ++ g_clear_object (&data->socket); + g_clear_object (&data->connection); ++ g_slist_free_full (data->connection_attempts, connection_attempt_unref); + + g_clear_error (&data->last_error); + + g_slice_free (GSocketClientAsyncConnectData, data); + } + ++typedef struct ++{ ++ GSocketAddress *address; ++ GSocket *socket; ++ GIOStream *connection; ++ GSocketClientAsyncConnectData *data; /* unowned */ ++ GSource *timeout_source; ++ GCancellable *cancellable; ++ grefcount ref; ++} ConnectionAttempt; ++ ++static ConnectionAttempt * ++connection_attempt_new (void) ++{ ++ ConnectionAttempt *attempt = g_new0 (ConnectionAttempt, 1); ++ g_ref_count_init (&attempt->ref); ++ return attempt; ++} ++ ++static ConnectionAttempt * ++connection_attempt_ref (ConnectionAttempt *attempt) ++{ ++ g_ref_count_inc (&attempt->ref); ++ return attempt; ++} ++ ++static void ++connection_attempt_unref (gpointer pointer) ++{ ++ ConnectionAttempt *attempt = pointer; ++ if (g_ref_count_dec (&attempt->ref)) ++ { ++ g_clear_object (&attempt->address); ++ g_clear_object (&attempt->socket); ++ g_clear_object (&attempt->connection); ++ g_clear_object (&attempt->cancellable); ++ if (attempt->timeout_source) ++ { ++ g_source_destroy (attempt->timeout_source); ++ g_source_unref (attempt->timeout_source); ++ } ++ g_free (attempt); ++ } ++} ++ ++static void ++connection_attempt_remove (ConnectionAttempt *attempt) ++{ ++ attempt->data->connection_attempts = g_slist_remove (attempt->data->connection_attempts, attempt); ++ connection_attempt_unref (attempt); ++} ++ + static void + g_socket_client_async_connect_complete (GSocketClientAsyncConnectData *data) + { +@@ -1359,8 +1418,7 @@ g_socket_client_async_connect_complete (GSocketClientAsyncConnectData *data) + { + GSocketConnection *wrapper_connection; + +- wrapper_connection = g_tcp_wrapper_connection_new (data->connection, +- data->current_socket); ++ wrapper_connection = g_tcp_wrapper_connection_new (data->connection, data->socket); + g_object_unref (data->connection); + data->connection = (GIOStream *)wrapper_connection; + } +@@ -1389,8 +1447,7 @@ static void + enumerator_next_async (GSocketClientAsyncConnectData *data) + { + /* We need to cleanup the state */ +- g_clear_object (&data->current_socket); +- g_clear_object (&data->current_addr); ++ g_clear_object (&data->socket); + g_clear_object (&data->proxy_addr); + g_clear_object (&data->connection); + +@@ -1485,34 +1542,68 @@ g_socket_client_connected_callback (GObject *source, + GAsyncResult *result, + gpointer user_data) + { +- GSocketClientAsyncConnectData *data = user_data; ++ ConnectionAttempt *attempt = user_data; ++ GSocketClientAsyncConnectData *data = attempt->data; ++ GSList *l; + GError *error = NULL; + GProxy *proxy; + const gchar *protocol; + +- if (g_task_return_error_if_cancelled (data->task)) ++ /* data is NULL once the task is completed */ ++ if (data && g_task_return_error_if_cancelled (data->task)) + { + g_object_unref (data->task); ++ connection_attempt_unref (attempt); + return; + } + ++ if (attempt->timeout_source) ++ { ++ g_source_destroy (attempt->timeout_source); ++ g_clear_pointer (&attempt->timeout_source, g_source_unref); ++ } ++ + if (!g_socket_connection_connect_finish (G_SOCKET_CONNECTION (source), + result, &error)) + { +- clarify_connect_error (error, data->connectable, +- data->current_addr); +- set_last_error (data, error); ++ if (!g_cancellable_is_cancelled (attempt->cancellable)) ++ { ++ clarify_connect_error (error, data->connectable, attempt->address); ++ set_last_error (data, error); ++ } ++ else ++ g_clear_error (&error); ++ ++ if (data) ++ { ++ connection_attempt_remove (attempt); ++ enumerator_next_async (data); ++ } ++ else ++ connection_attempt_unref (attempt); + +- /* try next one */ +- enumerator_next_async (data); + return; + } + ++ data->socket = g_steal_pointer (&attempt->socket); ++ data->connection = g_steal_pointer (&attempt->connection); ++ ++ for (l = data->connection_attempts; l; l = g_slist_next (l)) ++ { ++ ConnectionAttempt *attempt_entry = l->data; ++ g_cancellable_cancel (attempt_entry->cancellable); ++ attempt_entry->data = NULL; ++ connection_attempt_unref (attempt_entry); ++ } ++ g_slist_free (data->connection_attempts); ++ data->connection_attempts = NULL; ++ connection_attempt_unref (attempt); ++ + g_socket_connection_set_cached_remote_address ((GSocketConnection*)data->connection, NULL); + g_socket_client_emit_event (data->client, G_SOCKET_CLIENT_CONNECTED, data->connectable, data->connection); + + /* wrong, but backward compatible */ +- g_socket_set_blocking (data->current_socket, TRUE); ++ g_socket_set_blocking (data->socket, TRUE); + + if (!data->proxy_addr) + { +@@ -1565,6 +1656,26 @@ g_socket_client_connected_callback (GObject *source, + } + } + ++static gboolean ++on_connection_attempt_timeout (gpointer data) ++{ ++ ConnectionAttempt *attempt = data; ++ ++ enumerator_next_async (attempt->data); ++ ++ g_clear_pointer (&attempt->timeout_source, g_source_unref); ++ return G_SOURCE_REMOVE; ++} ++ ++static void ++on_connection_cancelled (GCancellable *cancellable, ++ gpointer data) ++{ ++ GCancellable *attempt_cancellable = data; ++ ++ g_cancellable_cancel (attempt_cancellable); ++} ++ + static void + g_socket_client_enumerator_callback (GObject *object, + GAsyncResult *result, +@@ -1573,6 +1684,7 @@ g_socket_client_enumerator_callback (GObject *object, + GSocketClientAsyncConnectData *data = user_data; + GSocketAddress *address = NULL; + GSocket *socket; ++ ConnectionAttempt *attempt; + GError *error = NULL; + + if (g_task_return_error_if_cancelled (data->task)) +@@ -1585,6 +1697,9 @@ g_socket_client_enumerator_callback (GObject *object, + result, &error); + if (address == NULL) + { ++ if (data->connection_attempts) ++ return; ++ + g_socket_client_emit_event (data->client, G_SOCKET_CLIENT_COMPLETE, data->connectable, NULL); + if (!error) + { +@@ -1621,16 +1736,27 @@ g_socket_client_enumerator_callback (GObject *object, + return; + } + +- data->current_socket = socket; +- data->current_addr = address; +- data->connection = (GIOStream *) g_socket_connection_factory_create_connection (socket); +- +- g_socket_connection_set_cached_remote_address ((GSocketConnection*)data->connection, address); +- g_socket_client_emit_event (data->client, G_SOCKET_CLIENT_CONNECTING, data->connectable, data->connection); +- g_socket_connection_connect_async (G_SOCKET_CONNECTION (data->connection), ++ attempt = connection_attempt_new (); ++ attempt->data = data; ++ attempt->socket = socket; ++ attempt->address = address; ++ attempt->cancellable = g_cancellable_new (); ++ attempt->connection = (GIOStream *)g_socket_connection_factory_create_connection (socket); ++ attempt->timeout_source = g_timeout_source_new (HAPPY_EYEBALLS_CONNECTION_ATTEMPT_TIMEOUT_MS); ++ g_source_set_callback (attempt->timeout_source, on_connection_attempt_timeout, attempt, NULL); ++ g_source_attach (attempt->timeout_source, g_main_context_get_thread_default ()); ++ data->connection_attempts = g_slist_append (data->connection_attempts, attempt); ++ ++ if (g_task_get_cancellable (data->task)) ++ g_cancellable_connect (g_task_get_cancellable (data->task), G_CALLBACK (on_connection_cancelled), ++ g_object_ref (attempt->cancellable), g_object_unref); ++ ++ g_socket_connection_set_cached_remote_address ((GSocketConnection *)attempt->connection, address); ++ g_socket_client_emit_event (data->client, G_SOCKET_CLIENT_CONNECTING, data->connectable, attempt->connection); ++ g_socket_connection_connect_async (G_SOCKET_CONNECTION (attempt->connection), + address, +- g_task_get_cancellable (data->task), +- g_socket_client_connected_callback, data); ++ attempt->cancellable, ++ g_socket_client_connected_callback, connection_attempt_ref (attempt)); + } + + /** +-- +2.7.4 + diff --git a/meta/recipes-core/glib-2.0/glib-2.0/CVE-2019-9633_p2.patch b/meta/recipes-core/glib-2.0/glib-2.0/CVE-2019-9633_p2.patch new file mode 100644 index 0000000000..3bb2f5d917 --- /dev/null +++ b/meta/recipes-core/glib-2.0/glib-2.0/CVE-2019-9633_p2.patch @@ -0,0 +1,231 @@ +From d553d92d6e9f53cbe5a34166fcb919ba652c6a8e Mon Sep 17 00:00:00 2001 +From: Patrick Griffis +Date: Tue, 29 Jan 2019 10:07:06 -0500 +Subject: [PATCH] gsocketclient: Fix criticals + +This ensures the parent GTask is kept alive as long as an enumeration +is running and trying to connect. + +Closes #1646 +Closes #1649 + +Upstream-Status: Backport +CVE: CVE-2019-9633 patch 2 +Affects: < 2.59.2 +Signed-off-by: Armin Kuster + +--- + gio/gsocketclient.c | 74 +++++++++++++++++++++++++++++------------- + gio/tests/gsocketclient-slow.c | 55 ++++++++++++++++++++++++++++++- + 2 files changed, 106 insertions(+), 23 deletions(-) + +Index: glib-2.58.0/gio/gsocketclient.c +=================================================================== +--- glib-2.58.0.orig/gio/gsocketclient.c ++++ glib-2.58.0/gio/gsocketclient.c +@@ -1327,7 +1327,7 @@ g_socket_client_connect_to_uri (GSocketC + + typedef struct + { +- GTask *task; ++ GTask *task; /* unowned */ + GSocketClient *client; + + GSocketConnectable *connectable; +@@ -1345,6 +1345,7 @@ static void connection_attempt_unref (gp + static void + g_socket_client_async_connect_data_free (GSocketClientAsyncConnectData *data) + { ++ data->task = NULL; + g_clear_object (&data->connectable); + g_clear_object (&data->enumerator); + g_clear_object (&data->proxy_addr); +@@ -1444,13 +1445,19 @@ set_last_error (GSocketClientAsyncConnec + } + + static void +-enumerator_next_async (GSocketClientAsyncConnectData *data) ++enumerator_next_async (GSocketClientAsyncConnectData *data, ++ gboolean add_task_ref) + { + /* We need to cleanup the state */ + g_clear_object (&data->socket); + g_clear_object (&data->proxy_addr); + g_clear_object (&data->connection); + ++ /* Each enumeration takes a ref. This arg just avoids repeated unrefs when ++ an enumeration starts another enumeration */ ++ if (add_task_ref) ++ g_object_ref (data->task); ++ + g_socket_client_emit_event (data->client, G_SOCKET_CLIENT_RESOLVING, data->connectable, NULL); + g_socket_address_enumerator_next_async (data->enumerator, + g_task_get_cancellable (data->task), +@@ -1478,7 +1485,7 @@ g_socket_client_tls_handshake_callback ( + else + { + g_object_unref (object); +- enumerator_next_async (data); ++ enumerator_next_async (data, FALSE); + } + } + +@@ -1509,7 +1516,7 @@ g_socket_client_tls_handshake (GSocketCl + } + else + { +- enumerator_next_async (data); ++ enumerator_next_async (data, FALSE); + } + } + +@@ -1530,13 +1537,24 @@ g_socket_client_proxy_connect_callback ( + } + else + { +- enumerator_next_async (data); ++ enumerator_next_async (data, FALSE); + return; + } + + g_socket_client_tls_handshake (data); + } + ++static gboolean ++task_completed_or_cancelled (GTask *task) ++{ ++ if (g_task_get_completed (task)) ++ return TRUE; ++ else if (g_task_return_error_if_cancelled (task)) ++ return TRUE; ++ else ++ return FALSE; ++} ++ + static void + g_socket_client_connected_callback (GObject *source, + GAsyncResult *result, +@@ -1549,8 +1567,7 @@ g_socket_client_connected_callback (GObj + GProxy *proxy; + const gchar *protocol; + +- /* data is NULL once the task is completed */ +- if (data && g_task_return_error_if_cancelled (data->task)) ++ if (g_cancellable_is_cancelled (attempt->cancellable) || task_completed_or_cancelled (data->task)) + { + g_object_unref (data->task); + connection_attempt_unref (attempt); +@@ -1570,17 +1587,15 @@ g_socket_client_connected_callback (GObj + { + clarify_connect_error (error, data->connectable, attempt->address); + set_last_error (data, error); ++ connection_attempt_remove (attempt); ++ enumerator_next_async (data, FALSE); + } + else +- g_clear_error (&error); +- +- if (data) + { +- connection_attempt_remove (attempt); +- enumerator_next_async (data); ++ g_clear_error (&error); ++ g_object_unref (data->task); ++ connection_attempt_unref (attempt); + } +- else +- connection_attempt_unref (attempt); + + return; + } +@@ -1592,7 +1607,6 @@ g_socket_client_connected_callback (GObj + { + ConnectionAttempt *attempt_entry = l->data; + g_cancellable_cancel (attempt_entry->cancellable); +- attempt_entry->data = NULL; + connection_attempt_unref (attempt_entry); + } + g_slist_free (data->connection_attempts); +@@ -1625,7 +1639,7 @@ g_socket_client_connected_callback (GObj + G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, + _("Proxying over a non-TCP connection is not supported.")); + +- enumerator_next_async (data); ++ enumerator_next_async (data, FALSE); + } + else if (g_hash_table_contains (data->client->priv->app_proxies, protocol)) + { +@@ -1652,7 +1666,7 @@ g_socket_client_connected_callback (GObj + _("Proxy protocol “%s” is not supported."), + protocol); + +- enumerator_next_async (data); ++ enumerator_next_async (data, FALSE); + } + } + +@@ -1661,7 +1675,7 @@ on_connection_attempt_timeout (gpointer + { + ConnectionAttempt *attempt = data; + +- enumerator_next_async (attempt->data); ++ enumerator_next_async (attempt->data, TRUE); + + g_clear_pointer (&attempt->timeout_source, g_source_unref); + return G_SOURCE_REMOVE; +@@ -1687,7 +1701,7 @@ g_socket_client_enumerator_callback (GOb + ConnectionAttempt *attempt; + GError *error = NULL; + +- if (g_task_return_error_if_cancelled (data->task)) ++ if (task_completed_or_cancelled (data->task)) + { + g_object_unref (data->task); + return; +@@ -1698,7 +1712,10 @@ g_socket_client_enumerator_callback (GOb + if (address == NULL) + { + if (data->connection_attempts) +- return; ++ { ++ g_object_unref (data->task); ++ return; ++ } + + g_socket_client_emit_event (data->client, G_SOCKET_CLIENT_COMPLETE, data->connectable, NULL); + if (!error) +@@ -1732,7 +1749,7 @@ g_socket_client_enumerator_callback (GOb + if (socket == NULL) + { + g_object_unref (address); +- enumerator_next_async (data); ++ enumerator_next_async (data, FALSE); + return; + } + +@@ -1804,11 +1821,24 @@ g_socket_client_connect_async (GSocketCl + else + data->enumerator = g_socket_connectable_enumerate (connectable); + ++ /* The flow and ownership here isn't quite obvious: ++ - The task starts an async attempt to connect. ++ - Each attempt holds a single ref on task. ++ - Each attempt may create new attempts by timing out (not a failure) so ++ there are multiple attempts happening in parallel. ++ - Upon failure an attempt will start a new attempt that steals its ref ++ until there are no more attempts left and it drops its ref. ++ - Upon success it will cancel all other attempts and continue on ++ to the rest of the connection (tls, proxies, etc) which do not ++ happen in parallel and at the very end drop its ref. ++ - Upon cancellation an attempt drops its ref. ++ */ ++ + data->task = g_task_new (client, cancellable, callback, user_data); + g_task_set_source_tag (data->task, g_socket_client_connect_async); + g_task_set_task_data (data->task, data, (GDestroyNotify)g_socket_client_async_connect_data_free); + +- enumerator_next_async (data); ++ enumerator_next_async (data, FALSE); + } + + /** diff --git a/meta/recipes-core/glib-2.0/glib-2.0_2.58.0.bb b/meta/recipes-core/glib-2.0/glib-2.0_2.58.0.bb index 879bc48aef..f007596968 100644 --- a/meta/recipes-core/glib-2.0/glib-2.0_2.58.0.bb +++ b/meta/recipes-core/glib-2.0/glib-2.0_2.58.0.bb @@ -15,6 +15,8 @@ SRC_URI = "${GNOME_MIRROR}/glib/${SHRT_VER}/glib-${PV}.tar.xz \ file://0010-Do-not-hardcode-python-path-into-various-tools.patch \ file://date-lt.patch \ file://CVE-2019-12450.patch \ + file://CVE-2019-9633_p1.patch \ + file://CVE-2019-9633_p2.patch \ " SRC_URI_append_class-native = " file://relocate-modules.patch" -- cgit 1.2.3-korg