Files
libsoup/libsoup-CVE-2026-2443.patch
Bjørn Lie 745a3951bf - Add more CVE fixes:
+ libsoup-CVE-2025-32049.patch (bsc#1240751 CVE-2025-32049
    glgo#GNOME/libsoup#390)
  + libsoup-CVE-2026-2443.patch (bsc#1258170 CVE-2026-2443
    glgo#GNOME/libsoup#487)
  + libsoup-CVE-2026-2369.patch (bsc#1258120 CVE-2026-2369
    glgo#GNOME/libsoup!508)

OBS-URL: https://build.opensuse.org/package/show/GNOME:Factory/libsoup?expand=0&rev=327
2026-02-14 17:10:05 +00:00

345 lines
12 KiB
Diff
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
diff -urp libsoup-3.6.5.orig/libsoup/soup-message-headers.c libsoup-3.6.5/libsoup/soup-message-headers.c
--- libsoup-3.6.5.orig/libsoup/soup-message-headers.c 2026-02-14 04:14:09.575357979 -0600
+++ libsoup-3.6.5/libsoup/soup-message-headers.c 2026-02-14 04:17:20.278921604 -0600
@@ -1176,10 +1176,16 @@ sort_ranges (gconstpointer a, gconstpoin
}
/* like soup_message_headers_get_ranges(), except it returns:
- * SOUP_STATUS_OK if there is no Range or it should be ignored.
- * SOUP_STATUS_PARTIAL_CONTENT if there is at least one satisfiable range.
- * SOUP_STATUS_REQUESTED_RANGE_NOT_SATISFIABLE if @check_satisfiable
- * is %TRUE and the request is not satisfiable given @total_length.
+ * - SOUP_STATUS_OK if there is no Range or it should be ignored due to being
+ * entirely invalid.
+ * - SOUP_STATUS_PARTIAL_CONTENT if there is at least one satisfiable range.
+ * - SOUP_STATUS_REQUESTED_RANGE_NOT_SATISFIABLE if @check_satisfiable
+ * is %TRUE, the Range is valid, but no part of the request is satisfiable
+ * given @total_length.
+ *
+ * @ranges and @length are only set if SOUP_STATUS_PARTIAL_CONTENT is returned.
+ *
+ * See https://httpwg.org/specs/rfc9110.html#field.range
*/
guint
soup_message_headers_get_ranges_internal (SoupMessageHeaders *hdrs,
@@ -1193,22 +1199,28 @@ soup_message_headers_get_ranges_internal
GArray *array;
char *spec, *end;
guint status = SOUP_STATUS_OK;
+ gboolean is_all_valid = TRUE;
if (!range || strncmp (range, "bytes", 5) != 0)
- return status;
+ return SOUP_STATUS_OK; /* invalid header or unknown range unit */
range += 5;
while (g_ascii_isspace (*range))
range++;
if (*range++ != '=')
- return status;
+ return SOUP_STATUS_OK; /* invalid header */
while (g_ascii_isspace (*range))
range++;
range_list = soup_header_parse_list (range);
if (!range_list)
- return status;
+ return SOUP_STATUS_OK; /* invalid list */
+ /* Loop through the ranges and modify the status accordingly. Default to
+ * status 200 (OK, ignoring the ranges). Switch to status 206 (Partial
+ * Content) if there is at least one partially valid range. Switch to
+ * status 416 (Range Not Satisfiable) if there are no partially valid
+ * ranges at all. */
array = g_array_new (FALSE, FALSE, sizeof (SoupRange));
for (r = range_list; r; r = r->next) {
SoupRange cur;
@@ -1221,30 +1233,44 @@ soup_message_headers_get_ranges_internal
cur.start = g_ascii_strtoull (spec, &end, 10);
if (*end == '-')
end++;
- if (*end) {
+ if (*end)
cur.end = g_ascii_strtoull (end, &end, 10);
- if (cur.end < cur.start) {
- status = SOUP_STATUS_OK;
- break;
- }
- } else
+ else
cur.end = total_length - 1;
}
+
if (*end) {
- status = SOUP_STATUS_OK;
- break;
- } else if (check_satisfiable && cur.start >= total_length) {
- if (status == SOUP_STATUS_OK)
- status = SOUP_STATUS_REQUESTED_RANGE_NOT_SATISFIABLE;
+ /* Junk after the range */
+ is_all_valid = FALSE;
+ continue;
+ }
+
+ if (cur.end < cur.start) {
+ is_all_valid = FALSE;
continue;
}
+ g_assert (cur.start >= 0);
+ if (cur.end >= total_length)
+ cur.end = total_length - 1;
+
+ if (cur.start >= total_length) {
+ /* Range is valid, but unsatisfiable */
+ continue;
+ }
+
+ /* We have at least one (at least partially) satisfiable range */
g_array_append_val (array, cur);
status = SOUP_STATUS_PARTIAL_CONTENT;
}
soup_header_free_list (range_list);
if (status != SOUP_STATUS_PARTIAL_CONTENT) {
+ g_assert (status == SOUP_STATUS_OK);
+
+ if (is_all_valid && check_satisfiable)
+ status = SOUP_STATUS_REQUESTED_RANGE_NOT_SATISFIABLE;
+
g_array_free (array, TRUE);
return status;
}
diff -urp libsoup-3.6.5.orig/tests/range-test.c libsoup-3.6.5/tests/range-test.c
--- libsoup-3.6.5.orig/tests/range-test.c 2026-02-14 03:51:18.021654823 -0600
+++ libsoup-3.6.5/tests/range-test.c 2026-02-14 04:17:20.279517774 -0600
@@ -61,7 +61,8 @@ check_part (SoupMessageHeaders *headers,
static void
do_single_range (SoupSession *session, SoupMessage *msg,
- int start, int end, gboolean succeed)
+ int start, int end, SoupStatus expected_status,
+ int expected_start, int expected_end)
{
const char *content_type;
GBytes *body;
@@ -71,7 +72,7 @@ do_single_range (SoupSession *session, S
body = soup_test_session_async_send (session, msg, NULL, NULL);
- if (!succeed) {
+ if (expected_status == SOUP_STATUS_REQUESTED_RANGE_NOT_SATISFIABLE) {
soup_test_assert_message_status (msg, SOUP_STATUS_REQUESTED_RANGE_NOT_SATISFIABLE);
if (soup_message_get_status (msg) != SOUP_STATUS_REQUESTED_RANGE_NOT_SATISFIABLE) {
const char *content_range;
@@ -81,31 +82,81 @@ do_single_range (SoupSession *session, S
if (content_range)
debug_printf (1, " Content-Range: %s\n", content_range);
}
+ } else if (expected_status == SOUP_STATUS_OK) {
+ soup_test_assert_message_status (msg, SOUP_STATUS_OK);
- g_object_unref (msg);
- return;
- }
-
- soup_test_assert_message_status (msg, SOUP_STATUS_PARTIAL_CONTENT);
+ content_type = soup_message_headers_get_content_type (
+ soup_message_get_response_headers (msg), NULL);
+ g_assert_cmpstr (content_type, !=, "multipart/byteranges");
+
+ g_assert_false (soup_message_headers_get_content_range (
+ soup_message_get_response_headers (msg), NULL, NULL, NULL));
+ g_assert_cmpint (soup_message_headers_get_content_length (
+ soup_message_get_response_headers (msg)), ==, g_bytes_get_size (full_response));
+ } else {
+ soup_test_assert_message_status (msg, SOUP_STATUS_PARTIAL_CONTENT);
+
+ content_type = soup_message_headers_get_content_type (
+ soup_message_get_response_headers (msg), NULL);
+ g_assert_cmpstr (content_type, !=, "multipart/byteranges");
- content_type = soup_message_headers_get_content_type (
- soup_message_get_response_headers (msg), NULL);
- g_assert_cmpstr (content_type, !=, "multipart/byteranges");
+ check_part (soup_message_get_response_headers (msg), body, TRUE, expected_start, expected_end);
+ }
- check_part (soup_message_get_response_headers (msg), body, TRUE, start, end);
- g_bytes_unref (body);
+ g_clear_pointer (&body, g_bytes_unref);
g_object_unref (msg);
}
static void
request_single_range (SoupSession *session, const char *uri,
- int start, int end, gboolean succeed)
+ int start, int end, SoupStatus expected_status,
+ int expected_start, int expected_end)
{
SoupMessage *msg;
msg = soup_message_new ("GET", uri);
soup_message_headers_set_range (soup_message_get_request_headers (msg), start, end);
- do_single_range (session, msg, start, end, succeed);
+ do_single_range (session, msg, start, end, expected_status, expected_start, expected_end);
+}
+
+/* This always asserts failure (either 406 or 200 with no Content-Range); its
+ * intended to be used for passing invalid
+ * Range header formats which cant be built by calling
+ * soup_message_headers_set_range(). */
+static void
+request_single_range_by_string (SoupSession *session, const char *uri,
+ const char *range, SoupStatus expected_status)
+{
+ SoupMessage *msg;
+ GBytes *body;
+
+ msg = soup_message_new ("GET", uri);
+ soup_message_headers_replace (soup_message_get_request_headers (msg), "Range", range);
+
+ debug_printf (1, " Range: %s\n",
+ soup_message_headers_get_one (soup_message_get_request_headers (msg), "Range"));
+
+ body = soup_test_session_async_send (session, msg, NULL, NULL);
+
+ if (expected_status == SOUP_STATUS_REQUESTED_RANGE_NOT_SATISFIABLE) {
+ soup_test_assert_message_status (msg, SOUP_STATUS_REQUESTED_RANGE_NOT_SATISFIABLE);
+ } else {
+ const char *content_type;
+
+ soup_test_assert_message_status (msg, SOUP_STATUS_OK);
+
+ content_type = soup_message_headers_get_content_type (
+ soup_message_get_response_headers (msg), NULL);
+ g_assert_cmpstr (content_type, !=, "multipart/byteranges");
+
+ g_assert_false (soup_message_headers_get_content_range (
+ soup_message_get_response_headers (msg), NULL, NULL, NULL));
+ g_assert_cmpint (soup_message_headers_get_content_length (
+ soup_message_get_response_headers (msg)), ==, g_bytes_get_size (full_response));
+ }
+
+ g_clear_pointer (&body, g_bytes_unref);
+ g_object_unref (msg);
}
static void
@@ -172,7 +223,9 @@ request_double_range (SoupSession *sessi
do_single_range (session, msg,
MIN (first_start, second_start),
MAX (first_end, second_end),
- TRUE);
+ SOUP_STATUS_PARTIAL_CONTENT,
+ MIN (first_start, second_start),
+ MAX (first_end, second_end));
} else
do_multi_range (session, msg, expected_return_ranges);
}
@@ -200,7 +253,9 @@ request_triple_range (SoupSession *sessi
do_single_range (session, msg,
MIN (first_start, MIN (second_start, third_start)),
MAX (first_end, MAX (second_end, third_end)),
- TRUE);
+ SOUP_STATUS_PARTIAL_CONTENT,
+ MIN (first_start, MIN (second_start, third_start)),
+ MAX (first_end, MAX (second_end, third_end)));
} else
do_multi_range (session, msg, expected_return_ranges);
}
@@ -256,7 +311,8 @@ do_range_test (SoupSession *session, con
debug_printf (1, "Requesting %d-%d\n", 0 * twelfths, 1 * twelfths);
request_single_range (session, uri,
0 * twelfths, 1 * twelfths,
- TRUE);
+ SOUP_STATUS_PARTIAL_CONTENT,
+ 0 * twelfths, 1 * twelfths);
/* B: 11, end-relative request. These two are mostly redundant
* in terms of data coverage, but they may still catch
@@ -265,11 +321,13 @@ do_range_test (SoupSession *session, con
debug_printf (1, "Requesting %d-\n", 11 * twelfths);
request_single_range (session, uri,
11 * twelfths, -1,
- TRUE);
+ SOUP_STATUS_PARTIAL_CONTENT,
+ 11 * twelfths, -1);
debug_printf (1, "Requesting -%d\n", 1 * twelfths);
request_single_range (session, uri,
-1 * twelfths, -1,
- TRUE);
+ SOUP_STATUS_PARTIAL_CONTENT,
+ -1 * twelfths, -1);
/* C: 2 and 5 */
debug_printf (1, "Requesting %d-%d,%d-%d\n",
@@ -322,7 +380,8 @@ do_range_test (SoupSession *session, con
(int) full_response_length + 100);
request_single_range (session, uri,
full_response_length + 1, full_response_length + 100,
- FALSE);
+ SOUP_STATUS_REQUESTED_RANGE_NOT_SATISFIABLE,
+ 0, 0);
debug_printf (1, "Requesting (semi-invalid) 1-10,%d-%d,20-30\n",
(int) full_response_length + 1,
@@ -331,6 +390,57 @@ do_range_test (SoupSession *session, con
1, 10,
full_response_length + 1, full_response_length + 100,
20, 30);
+
+ debug_printf (1, "Requesting (invalid end) %d-%d\n",
+ 1,
+ (int) full_response_length + 1000);
+ request_single_range (session, uri,
+ 1, full_response_length + 1000,
+ SOUP_STATUS_PARTIAL_CONTENT,
+ 1, full_response_length - 1);
+
+ debug_printf (1, "Requesting (end before start) %d-%d\n",
+ 10,
+ 1);
+ request_single_range (session, uri,
+ 10, 1,
+ SOUP_STATUS_OK,
+ 1, full_response_length);
+
+ debug_printf (1, "Requesting (malformed suffix length) -0\n");
+ request_single_range_by_string (session, uri,
+ "bytes=-0",
+ SOUP_STATUS_OK);
+
+ debug_printf (1, "Requesting (extra content after valid header value) 0-10\n");
+ request_single_range_by_string (session, uri,
+ "bytes=0-10 but with weird trailing content",
+ SOUP_STATUS_OK);
+
+ debug_printf (1, "Requesting (invalid range dash) 0a10\n");
+ request_single_range_by_string (session, uri,
+ "bytes=0a10",
+ SOUP_STATUS_OK);
+
+ debug_printf (1, "Requesting (invalid range unit) 0-10\n");
+ request_single_range_by_string (session, uri,
+ "horses=0-10",
+ SOUP_STATUS_OK);
+
+ debug_printf (1, "Requesting (missing equals) 0-10\n");
+ request_single_range_by_string (session, uri,
+ "bytes 0-10",
+ SOUP_STATUS_OK);
+
+ debug_printf (1, "Requesting (end before start but with whitespace) 10-1\n");
+ request_single_range_by_string (session, uri,
+ "bytes \t = \t 10-1",
+ SOUP_STATUS_OK);
+
+ debug_printf (1, "Requesting (delimiters but no ranges)\n");
+ request_single_range_by_string (session, uri,
+ "bytes=, ,,\t, ",
+ SOUP_STATUS_OK);
}
static void