|
| 1 | +From 53218077378ef40edf3bd9e84d881f82c53bc749 Mon Sep 17 00:00:00 2001 |
| 2 | +From: Kevin Lockwood < [email protected]> |
| 3 | +Date: Fri, 13 Jun 2025 09:57:21 -0700 |
| 4 | +Subject: [PATCH 1/2] soup-message-headers: Correct merge of ranges |
| 5 | + |
| 6 | +It had been skipping every second range, which generated an array |
| 7 | +of a lot of insane ranges, causing large memory usage by the server. |
| 8 | + |
| 9 | +Closes #428 |
| 10 | + |
| 11 | +Part-of: <https://gitlab.gnome.org/GNOME/libsoup/-/merge_requests/452> |
| 12 | +Upstream Patch Reference: https://gitlab.gnome.org/GNOME/libsoup/-/merge_requests/452/diffs?commit_id=9bb92f7a685e31e10e9e8221d0342280432ce836 |
| 13 | +--- |
| 14 | + libsoup/soup-message-headers.c | 1 + |
| 15 | + tests/meson.build | 1 + |
| 16 | + tests/server-mem-limit-test.c | 144 +++++++++++++++++++++++++++++++++ |
| 17 | + 3 files changed, 146 insertions(+) |
| 18 | + create mode 100644 tests/server-mem-limit-test.c |
| 19 | + |
| 20 | +diff --git a/libsoup/soup-message-headers.c b/libsoup/soup-message-headers.c |
| 21 | +index c5168ea..9123dc1 100644 |
| 22 | +--- a/libsoup/soup-message-headers.c |
| 23 | ++++ b/libsoup/soup-message-headers.c |
| 24 | +@@ -1213,6 +1213,7 @@ soup_message_headers_get_ranges_internal (SoupMessageHeaders *hdrs, |
| 25 | + if (cur->start <= prev->end) { |
| 26 | + prev->end = MAX (prev->end, cur->end); |
| 27 | + g_array_remove_index (array, i); |
| 28 | ++ i--; |
| 29 | + } |
| 30 | + } |
| 31 | + } |
| 32 | +diff --git a/tests/meson.build b/tests/meson.build |
| 33 | +index ff94a4f..d78df11 100644 |
| 34 | +--- a/tests/meson.build |
| 35 | ++++ b/tests/meson.build |
| 36 | +@@ -89,6 +89,7 @@ tests = [ |
| 37 | + {'name': 'samesite'}, |
| 38 | + {'name': 'session'}, |
| 39 | + {'name': 'server-auth'}, |
| 40 | ++ {'name': 'server-mem-limit'}, |
| 41 | + {'name': 'server'}, |
| 42 | + {'name': 'sniffing'}, |
| 43 | + {'name': 'sniffing', |
| 44 | +diff --git a/tests/server-mem-limit-test.c b/tests/server-mem-limit-test.c |
| 45 | +new file mode 100644 |
| 46 | +index 0000000..98f1c40 |
| 47 | +--- /dev/null |
| 48 | ++++ b/tests/server-mem-limit-test.c |
| 49 | +@@ -0,0 +1,144 @@ |
| 50 | ++/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */ |
| 51 | ++/* |
| 52 | ++ * Copyright (C) 2025 Red Hat <www.redhat.com> |
| 53 | ++ */ |
| 54 | ++ |
| 55 | ++#include "test-utils.h" |
| 56 | ++ |
| 57 | ++#include <sys/resource.h> |
| 58 | ++ |
| 59 | ++/* |
| 60 | ++ This test limits memory usage to trigger too large buffer allocation crash. |
| 61 | ++ As restoring the limits back to what it was does not always work, it's split |
| 62 | ++ out of the server-test.c test with copied minimal server code. |
| 63 | ++ */ |
| 64 | ++ |
| 65 | ++typedef struct { |
| 66 | ++ SoupServer *server; |
| 67 | ++ GUri *base_uri, *ssl_base_uri; |
| 68 | ++ GSList *handlers; |
| 69 | ++} ServerData; |
| 70 | ++ |
| 71 | ++static void |
| 72 | ++server_setup_nohandler (ServerData *sd, gconstpointer test_data) |
| 73 | ++{ |
| 74 | ++ sd->server = soup_test_server_new (SOUP_TEST_SERVER_IN_THREAD); |
| 75 | ++ sd->base_uri = soup_test_server_get_uri (sd->server, "http", NULL); |
| 76 | ++ if (tls_available) |
| 77 | ++ sd->ssl_base_uri = soup_test_server_get_uri (sd->server, "https", NULL); |
| 78 | ++} |
| 79 | ++ |
| 80 | ++static void |
| 81 | ++server_add_handler (ServerData *sd, |
| 82 | ++ const char *path, |
| 83 | ++ SoupServerCallback callback, |
| 84 | ++ gpointer user_data, |
| 85 | ++ GDestroyNotify destroy) |
| 86 | ++{ |
| 87 | ++ soup_server_add_handler (sd->server, path, callback, user_data, destroy); |
| 88 | ++ sd->handlers = g_slist_prepend (sd->handlers, g_strdup (path)); |
| 89 | ++} |
| 90 | ++ |
| 91 | ++static void |
| 92 | ++server_setup (ServerData *sd, gconstpointer test_data) |
| 93 | ++{ |
| 94 | ++ server_setup_nohandler (sd, test_data); |
| 95 | ++} |
| 96 | ++ |
| 97 | ++static void |
| 98 | ++server_teardown (ServerData *sd, gconstpointer test_data) |
| 99 | ++{ |
| 100 | ++ GSList *iter; |
| 101 | ++ |
| 102 | ++ for (iter = sd->handlers; iter; iter = iter->next) |
| 103 | ++ soup_server_remove_handler (sd->server, iter->data); |
| 104 | ++ g_slist_free_full (sd->handlers, g_free); |
| 105 | ++ |
| 106 | ++ g_clear_pointer (&sd->server, soup_test_server_quit_unref); |
| 107 | ++ g_clear_pointer (&sd->base_uri, g_uri_unref); |
| 108 | ++ g_clear_pointer (&sd->ssl_base_uri, g_uri_unref); |
| 109 | ++} |
| 110 | ++ |
| 111 | ++static void |
| 112 | ++server_file_callback (SoupServer *server, |
| 113 | ++ SoupServerMessage *msg, |
| 114 | ++ const char *path, |
| 115 | ++ GHashTable *query, |
| 116 | ++ gpointer data) |
| 117 | ++{ |
| 118 | ++ void *mem; |
| 119 | ++ |
| 120 | ++ g_assert_cmpstr (path, ==, "/file"); |
| 121 | ++ g_assert_cmpstr (soup_server_message_get_method (msg), ==, SOUP_METHOD_GET); |
| 122 | ++ |
| 123 | ++ mem = g_malloc0 (sizeof (char) * 1024 * 1024); |
| 124 | ++ /* fedora-scan CI claims a warning about possibly leaked `mem` variable, thus use |
| 125 | ++ the copy and free it explicitly, to workaround the false positive; the g_steal_pointer() |
| 126 | ++ did not help for the malloc-ed memory */ |
| 127 | ++ soup_server_message_set_response (msg, "application/octet-stream", SOUP_MEMORY_COPY, mem, sizeof (char) * 1024 *1024); |
| 128 | ++ soup_server_message_set_status (msg, SOUP_STATUS_OK, NULL); |
| 129 | ++ g_free (mem); |
| 130 | ++} |
| 131 | ++ |
| 132 | ++static void |
| 133 | ++do_ranges_overlaps_test (ServerData *sd, gconstpointer test_data) |
| 134 | ++{ |
| 135 | ++ SoupSession *session; |
| 136 | ++ SoupMessage *msg; |
| 137 | ++ GString *range; |
| 138 | ++ GUri *uri; |
| 139 | ++ const char *chunk = ",0,0,0,0,0,0,0,0,0,0,0"; |
| 140 | ++ |
| 141 | ++ g_test_bug ("428"); |
| 142 | ++ |
| 143 | ++ #ifdef G_OS_WIN32 |
| 144 | ++ g_test_skip ("Cannot run under windows"); |
| 145 | ++ return; |
| 146 | ++ #endif |
| 147 | ++ |
| 148 | ++ range = g_string_sized_new (99 * 1024); |
| 149 | ++ g_string_append (range, "bytes=1024"); |
| 150 | ++ while (range->len < 99 * 1024) |
| 151 | ++ g_string_append (range, chunk); |
| 152 | ++ |
| 153 | ++ session = soup_test_session_new (NULL); |
| 154 | ++ server_add_handler (sd, "/file", server_file_callback, NULL, NULL); |
| 155 | ++ |
| 156 | ++ uri = g_uri_parse_relative (sd->base_uri, "/file", SOUP_HTTP_URI_FLAGS, NULL); |
| 157 | ++ |
| 158 | ++ msg = soup_message_new_from_uri ("GET", uri); |
| 159 | ++ soup_message_headers_append (soup_message_get_request_headers (msg), "Range", range->str); |
| 160 | ++ |
| 161 | ++ soup_test_session_send_message (session, msg); |
| 162 | ++ |
| 163 | ++ soup_test_assert_message_status (msg, SOUP_STATUS_PARTIAL_CONTENT); |
| 164 | ++ |
| 165 | ++ g_object_unref (msg); |
| 166 | ++ |
| 167 | ++ g_string_free (range, TRUE); |
| 168 | ++ g_uri_unref (uri); |
| 169 | ++ |
| 170 | ++ soup_test_session_abort_unref (session); |
| 171 | ++} |
| 172 | ++ |
| 173 | ++int |
| 174 | ++main (int argc, char **argv) |
| 175 | ++{ |
| 176 | ++ int ret; |
| 177 | ++ |
| 178 | ++ test_init (argc, argv, NULL); |
| 179 | ++ |
| 180 | ++ #ifndef G_OS_WIN32 |
| 181 | ++ struct rlimit new_rlimit = { 1024 * 1024 * 64, 1024 * 1024 * 64 }; |
| 182 | ++ /* limit memory usage, to trigger too large memory allocation abort */ |
| 183 | ++ g_assert_cmpint (setrlimit (RLIMIT_DATA, &new_rlimit), ==, 0); |
| 184 | ++ #endif |
| 185 | ++ |
| 186 | ++ g_test_add ("/server-mem/range-overlaps", ServerData, NULL, |
| 187 | ++ server_setup, do_ranges_overlaps_test, server_teardown); |
| 188 | ++ |
| 189 | ++ ret = g_test_run (); |
| 190 | ++ |
| 191 | ++ test_cleanup (); |
| 192 | ++ return ret; |
| 193 | ++} |
| 194 | +-- |
| 195 | +2.34.1 |
| 196 | + |
| 197 | + |
| 198 | +From abedf4259b478b2ad5c33af36fb163b5ace821c0 Mon Sep 17 00:00:00 2001 |
| 199 | +From: Kevin Lockwood < [email protected]> |
| 200 | +Date: Fri, 13 Jun 2025 09:57:54 -0700 |
| 201 | +Subject: [PATCH 2/2] server-mem-limit-test: Limit memory usage only when not |
| 202 | + built witha sanitizer |
| 203 | + |
| 204 | +A build with -Db_sanitize=address crashes with failed mmap(), which is done |
| 205 | +inside libasan. The test requires 20.0TB of virtual memory when running with |
| 206 | +the sanitizer, which is beyond unsigned integer limits and may not trigger |
| 207 | +the bug anyway. |
| 208 | + |
| 209 | +Part-of: <https://gitlab.gnome.org/GNOME/libsoup/-/merge_requests/452> |
| 210 | +Upstream Patch Reference: https://gitlab.gnome.org/GNOME/libsoup/-/merge_requests/452/diffs?commit_id=eeace39ec686094ff6a05a43e5fce06e9c37f376 |
| 211 | +--- |
| 212 | + meson.build | 4 ++++ |
| 213 | + tests/server-mem-limit-test.c | 13 +++++++++---- |
| 214 | + 2 files changed, 13 insertions(+), 4 deletions(-) |
| 215 | + |
| 216 | +diff --git a/meson.build b/meson.build |
| 217 | +index ff5dc95..5ed4125 100644 |
| 218 | +--- a/meson.build |
| 219 | ++++ b/meson.build |
| 220 | +@@ -389,6 +389,10 @@ configinc = include_directories('.') |
| 221 | + |
| 222 | + prefix = get_option('prefix') |
| 223 | + |
| 224 | ++if get_option('b_sanitize') != 'none' |
| 225 | ++ cdata.set_quoted('B_SANITIZE_OPTION', get_option('b_sanitize')) |
| 226 | ++endif |
| 227 | ++ |
| 228 | + cdata.set_quoted('PACKAGE_VERSION', soup_version) |
| 229 | + cdata.set_quoted('LOCALEDIR', join_paths(prefix, get_option('localedir'))) |
| 230 | + cdata.set_quoted('GETTEXT_PACKAGE', libsoup_api_name) |
| 231 | +diff --git a/tests/server-mem-limit-test.c b/tests/server-mem-limit-test.c |
| 232 | +index 98f1c40..65dc875 100644 |
| 233 | +--- a/tests/server-mem-limit-test.c |
| 234 | ++++ b/tests/server-mem-limit-test.c |
| 235 | +@@ -126,14 +126,19 @@ main (int argc, char **argv) |
| 236 | + { |
| 237 | + int ret; |
| 238 | + |
| 239 | +- test_init (argc, argv, NULL); |
| 240 | +- |
| 241 | +- #ifndef G_OS_WIN32 |
| 242 | +- struct rlimit new_rlimit = { 1024 * 1024 * 64, 1024 * 1024 * 64 }; |
| 243 | ++ /* a build with an address sanitizer may crash on mmap() with the limit, |
| 244 | ++ thus skip the limit set in such case, even it may not necessarily |
| 245 | ++ trigger the bug if it regresses */ |
| 246 | ++ #if !defined(G_OS_WIN32) && !defined(B_SANITIZE_OPTION) |
| 247 | ++ struct rlimit new_rlimit = { 1024UL * 1024UL * 1024UL * 2UL, 1024UL * 1024UL * 1024UL * 2UL }; |
| 248 | + /* limit memory usage, to trigger too large memory allocation abort */ |
| 249 | + g_assert_cmpint (setrlimit (RLIMIT_DATA, &new_rlimit), ==, 0); |
| 250 | ++ #else |
| 251 | ++ g_message ("server-mem-limit-test: Running without memory limit"); |
| 252 | + #endif |
| 253 | + |
| 254 | ++ test_init (argc, argv, NULL); |
| 255 | ++ |
| 256 | + g_test_add ("/server-mem/range-overlaps", ServerData, NULL, |
| 257 | + server_setup, do_ranges_overlaps_test, server_teardown); |
| 258 | + |
| 259 | +-- |
| 260 | +2.34.1 |
| 261 | + |
0 commit comments