Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package libsoup for openSUSE:Factory checked in at 2026-05-30 22:54:44 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/libsoup (Old) and /work/SRC/openSUSE:Factory/.libsoup.new.1937 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "libsoup" Sat May 30 22:54:44 2026 rev:169 rq:1355867 version:3.6.6 Changes: -------- --- /work/SRC/openSUSE:Factory/libsoup/libsoup.changes 2026-03-27 16:49:50.044256985 +0100 +++ /work/SRC/openSUSE:Factory/.libsoup.new.1937/libsoup.changes 2026-05-30 22:55:16.961249002 +0200 @@ -1,0 +2,6 @@ +Wed May 27 00:41:20 UTC 2026 - Xiaoguang Wang <[email protected]> + +- Add libsoup-CVE-2026-4271.patch: Protect message io while reading + and writing (bsc#1259767, CVE-2026-4271, glgo#GNOME/libsoup#496). + +------------------------------------------------------------------- New: ---- libsoup-CVE-2026-4271.patch ----------(New B)---------- New: - Add libsoup-CVE-2026-4271.patch: Protect message io while reading and writing (bsc#1259767, CVE-2026-4271, glgo#GNOME/libsoup#496). ----------(New E)---------- ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ libsoup.spec ++++++ --- /var/tmp/diff_new_pack.3oDhez/_old 2026-05-30 22:55:17.861285979 +0200 +++ /var/tmp/diff_new_pack.3oDhez/_new 2026-05-30 22:55:17.865286144 +0200 @@ -39,6 +39,8 @@ Patch18: libsoup-CVE-2026-1539.patch # PATCH-FIX-UPSTREAM libsoup-CVE-2026-0716.patch bsc#1256418 [email protected] -- websocket: Fix out-of-bounds read when reading unmasked frame Patch19: libsoup-CVE-2026-0716.patch +# PATCH-FIX-UPSTREAM libsoup-CVE-2026-4271.patch bsc#1259767 [email protected] -- protect message io while reading and writing +Patch20: libsoup-CVE-2026-4271.patch BuildRequires: glib-networking BuildRequires: meson >= 0.53 ++++++ _scmsync.obsinfo ++++++ --- /var/tmp/diff_new_pack.3oDhez/_old 2026-05-30 22:55:17.909287951 +0200 +++ /var/tmp/diff_new_pack.3oDhez/_new 2026-05-30 22:55:17.917288280 +0200 @@ -1,6 +1,6 @@ -mtime: 1774253489 -commit: baa7ff7022ffd3e536ca7209f324de055624508ce16aeb0caea56d29d81ec53b +mtime: 1779851962 +commit: 23e8e6291088c302da6310fc6217d04ffd26fee1c8dc650123d4c4de13312a3b url: https://src.opensuse.org/GNOME/libsoup -revision: baa7ff7022ffd3e536ca7209f324de055624508ce16aeb0caea56d29d81ec53b +revision: 23e8e6291088c302da6310fc6217d04ffd26fee1c8dc650123d4c4de13312a3b projectscmsync: https://src.opensuse.org/GNOME/_ObsPrj ++++++ build.specials.obscpio ++++++ ++++++ build.specials.obscpio ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/.gitignore new/.gitignore --- old/.gitignore 1970-01-01 01:00:00.000000000 +0100 +++ new/.gitignore 2026-05-27 05:19:22.000000000 +0200 @@ -0,0 +1,5 @@ +*.obscpio +*.osc +_build.* +.pbuild +osc-collab.* ++++++ libsoup-CVE-2026-4271.patch ++++++ >From 489affa74c8a229b8a4dd541710d4a5debedb7b4 Mon Sep 17 00:00:00 2001 From: Carlos Garcia Campos <[email protected]> Date: Mon, 16 Feb 2026 12:09:08 +0100 Subject: [PATCH] server: protect message io while reading and writing Ensure the nghttp2 session is not destroyed while being used. Closes #496 --- .../http2/soup-server-message-io-http2.c | 117 +++++++++++++----- tests/http2-test.c | 54 ++++++++ 2 files changed, 141 insertions(+), 30 deletions(-) diff --git a/libsoup/server/http2/soup-server-message-io-http2.c b/libsoup/server/http2/soup-server-message-io-http2.c index 913afb46..6f8d1bb6 100644 --- a/libsoup/server/http2/soup-server-message-io-http2.c +++ b/libsoup/server/http2/soup-server-message-io-http2.c @@ -69,6 +69,8 @@ typedef struct { GHashTable *messages; guint in_callback; + guint protected; + gboolean destroyed; } SoupServerMessageIOHTTP2; static void soup_server_message_io_http2_send_response (SoupServerMessageIOHTTP2 *io, @@ -146,6 +148,8 @@ soup_server_message_io_http2_destroy (SoupServerMessageIO *iface) { SoupServerMessageIOHTTP2 *io = (SoupServerMessageIOHTTP2 *)iface; + io->destroyed = TRUE; + if (io->read_source) { g_source_destroy (io->read_source); g_source_unref (io->read_source); @@ -160,10 +164,14 @@ soup_server_message_io_http2_destroy (SoupServerMessageIO *iface) } g_clear_object (&io->iostream); - g_clear_pointer (&io->session, nghttp2_session_del); - g_clear_pointer (&io->messages, g_hash_table_unref); + io->istream = NULL; + io->ostream = NULL; - g_free (io); + if (io->protected == 0) { + g_clear_pointer (&io->session, nghttp2_session_del); + g_clear_pointer (&io->messages, g_hash_table_unref); + g_free (io); + } } static void @@ -321,7 +329,33 @@ static const SoupServerMessageIOFuncs io_funcs = { soup_server_message_io_http2_is_paused }; +static void +soup_server_message_io_http2_protect (SoupServerMessageIOHTTP2 *io) +{ + io->protected++; + g_object_ref (io->conn); +} + static gboolean +soup_server_message_io_http2_unprotect (SoupServerMessageIOHTTP2 *io) +{ + g_object_unref (io->conn); + + if (--io->protected > 0) + return FALSE; + + if (io->destroyed) { + g_clear_pointer (&io->session, nghttp2_session_del); + g_clear_pointer (&io->messages, g_hash_table_unref); + g_free (io); + + return TRUE; + } + + return FALSE; +} + +static void io_write (SoupServerMessageIOHTTP2 *io, GError **error) { @@ -336,51 +370,57 @@ io_write (SoupServerMessageIOHTTP2 *io, if (io->write_buffer_size == 0) { /* Done */ io->write_buffer = NULL; - return TRUE; + return; } } + if (!io->ostream) + return; + gssize ret = g_pollable_stream_write (io->ostream, io->write_buffer + io->written_bytes, io->write_buffer_size - io->written_bytes, FALSE, NULL, error); - if (ret < 0) - return FALSE; - - io->written_bytes += ret; - return TRUE; + if (ret > 0) + io->written_bytes += ret; } static gboolean io_write_ready (GObject *stream, SoupServerMessageIOHTTP2 *io) { - SoupServerConnection *conn = io->conn; GError *error = NULL; - g_object_ref (conn); + soup_server_message_io_http2_protect (io); + + while (!error) { + if (io->destroyed) + break; + + if (!nghttp2_session_want_write (io->session)) + break; - while (!error && soup_server_connection_get_io_data (conn) == (SoupServerMessageIO *)io && nghttp2_session_want_write (io->session)) io_write (io, &error); + } if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) { g_error_free (error); - g_object_unref (conn); + soup_server_message_io_http2_unprotect (io); return G_SOURCE_CONTINUE; } - if (soup_server_connection_get_io_data (conn) == (SoupServerMessageIO *)io) { + if (!io->destroyed) { if (error) h2_debug (io, NULL, "[SESSION] IO error: %s", error->message); g_clear_pointer (&io->write_source, g_source_unref); if (error || (!nghttp2_session_want_read (io->session) && !nghttp2_session_want_write (io->session))) - soup_server_connection_disconnect (conn); + soup_server_connection_disconnect (io->conn); } g_clear_error (&error); - g_object_unref (conn); + soup_server_message_io_http2_unprotect (io); return G_SOURCE_REMOVE; } @@ -390,13 +430,12 @@ static gboolean io_write_idle_cb (SoupServerMessageIOHTTP2* io); static void io_try_write (SoupServerMessageIOHTTP2 *io) { - SoupServerConnection *conn = io->conn; GError *error = NULL; if (io->write_source) return; - if (io->in_callback && soup_server_connection_get_io_data (conn) == (SoupServerMessageIO *)io) { + if (io->in_callback && !io->destroyed) { if (!nghttp2_session_want_write (io->session)) return; @@ -416,12 +455,19 @@ io_try_write (SoupServerMessageIOHTTP2 *io) g_clear_pointer (&io->write_idle_source, g_source_unref); } - g_object_ref (conn); + soup_server_message_io_http2_protect (io); + + while (!error) { + if (io->destroyed) + break; + + if (!nghttp2_session_want_write (io->session)) + break; - while (!error && soup_server_connection_get_io_data (conn) == (SoupServerMessageIO *)io && !io->in_callback && nghttp2_session_want_write (io->session)) io_write (io, &error); + } - if (soup_server_connection_get_io_data (conn) == (SoupServerMessageIO *)io) { + if (!io->destroyed) { if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) { g_clear_error (&error); io->write_source = g_pollable_output_stream_create_source (G_POLLABLE_OUTPUT_STREAM (io->ostream), NULL); @@ -434,11 +480,11 @@ io_try_write (SoupServerMessageIOHTTP2 *io) h2_debug (io, NULL, "[SESSION] IO error: %s", error->message); if (error || (!nghttp2_session_want_read (io->session) && !nghttp2_session_want_write (io->session))) - soup_server_connection_disconnect (conn); + soup_server_connection_disconnect (io->conn); } g_clear_error (&error); - g_object_unref (conn); + soup_server_message_io_http2_unprotect (io); } static gboolean @@ -481,31 +527,37 @@ static gboolean io_read_ready (GObject *stream, SoupServerMessageIOHTTP2 *io) { - SoupServerConnection *conn = io->conn; gboolean progress = TRUE; GError *error = NULL; - g_object_ref (conn); + soup_server_message_io_http2_protect (io); + + while (progress) { + if (io->destroyed) + break; + + if (!nghttp2_session_want_read (io->session)) + break; - while (progress && soup_server_connection_get_io_data (conn) == (SoupServerMessageIO *)io && nghttp2_session_want_read (io->session)) progress = io_read (io, &error); + } if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) { g_error_free (error); - g_object_unref (conn); + soup_server_message_io_http2_unprotect (io); return G_SOURCE_CONTINUE; } - if (soup_server_connection_get_io_data (conn) == (SoupServerMessageIO *)io) { + if (!io->destroyed) { if (error) h2_debug (io, NULL, "[SESSION] IO error: %s", error->message); if (error || (!nghttp2_session_want_read (io->session) && !nghttp2_session_want_write (io->session))) - soup_server_connection_disconnect (conn); + soup_server_connection_disconnect (io->conn); } g_clear_error (&error); - g_object_unref (conn); + soup_server_message_io_http2_unprotect (io); return G_SOURCE_REMOVE; } @@ -931,5 +983,10 @@ soup_server_message_io_http2_new (SoupServerConnection *conn, nghttp2_submit_settings (io->session, NGHTTP2_FLAG_NONE, settings, G_N_ELEMENTS (settings)); io_try_write (io); +#ifdef __clang_analyzer__ + // Suppress false positive about io being destroyed here, since at this point we have only + // send the initial settings and not callback is called. + [[clang::suppress]] +#endif return (SoupServerMessageIO *)io; } diff --git a/tests/http2-test.c b/tests/http2-test.c index 0846a0a6..d12b4bf7 100644 --- a/tests/http2-test.c +++ b/tests/http2-test.c @@ -1413,6 +1413,40 @@ do_broken_pseudo_header_test (Test *test, gconstpointer data) g_uri_unref (uri); } +static void +disconnect_on_got_headers (SoupServerMessage *msg, gpointer user_data) +{ + GUri *uri; + SoupServerConnection *conn; + + uri = soup_server_message_get_uri (msg); + if (!g_str_equal (g_uri_get_path (uri), "/close-on-got-headers")) + return; + + conn = soup_server_message_get_connection (msg); + soup_server_connection_disconnect (conn); +} + +static void +do_server_disconnect_on_got_headers_test (Test *test, gconstpointer data) +{ + SoupMessage *msg; + GUri *uri; + GBytes *response; + GError *error = NULL; + + uri = g_uri_parse_relative (base_uri, "/close-on-got-headers", SOUP_HTTP_URI_FLAGS, NULL); + msg = soup_message_new_from_uri (SOUP_METHOD_GET, uri); + + response = soup_test_session_async_send (test->session, msg, NULL, &error); + g_assert_error (error, G_IO_ERROR, G_IO_ERROR_PARTIAL_INPUT); + + g_clear_error (&error); + g_bytes_unref (response); + g_object_unref (msg); + g_uri_unref (uri); +} + static gboolean unpause_message (SoupServerMessage *msg) { @@ -1550,12 +1584,26 @@ server_handler (SoupServer *server, shutdown (fd, SHUT_WR); #endif + soup_server_message_set_response (msg, "text/plain", + SOUP_MEMORY_STATIC, + "Success!", 8); + } else if (strcmp (path, "/close-on-got-headers") == 0) { soup_server_message_set_response (msg, "text/plain", SOUP_MEMORY_STATIC, "Success!", 8); } } +static void +server_request_started (SoupServer *server, + SoupServerMessage *msg, + SoupServerConnection *conn, + gpointer user_data) +{ + g_signal_connect (msg, "got-headers", + G_CALLBACK (disconnect_on_got_headers), NULL); +} + static gboolean server_basic_auth_callback (SoupAuthDomain *auth_domain, SoupServerMessage *msg, @@ -1582,6 +1630,8 @@ main (int argc, char **argv) return 0; server = soup_test_server_new (SOUP_TEST_SERVER_IN_THREAD | SOUP_TEST_SERVER_HTTP2); + g_signal_connect (server, "request-started", + G_CALLBACK (server_request_started), NULL); auth = soup_auth_domain_basic_new ("realm", "http2-test", "auth-callback", server_basic_auth_callback, NULL); @@ -1750,6 +1800,10 @@ main (int argc, char **argv) setup_session, do_broken_pseudo_header_test, teardown_session); + g_test_add ("/http2/server-disconnect-on-got-headers", Test, NULL, + setup_session, + do_server_disconnect_on_got_headers_test, + teardown_session); ret = g_test_run (); -- 2.53.0
