diff --git a/framework/deproxy/deproxy_base.py b/framework/deproxy/deproxy_base.py index 8b3b0b874..6cf66df18 100755 --- a/framework/deproxy/deproxy_base.py +++ b/framework/deproxy/deproxy_base.py @@ -59,6 +59,12 @@ def set_rst_tcp_to_closing_connection(self) -> None: def set_lock(self, polling_lock: threading.Lock) -> None: self.__polling_lock = polling_lock + def set_size_of_receiving_buffer(self, new_buffer_size: int) -> None: + """Set the size of the receiving buffer.""" + self.__acquire() + self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, new_buffer_size) + self.__release() + def _bind(self, address: tuple) -> None: """ Wrapper for `bind` method to add some log details. diff --git a/framework/test_suite/tester.py b/framework/test_suite/tester.py index 55acde763..c619921a9 100644 --- a/framework/test_suite/tester.py +++ b/framework/test_suite/tester.py @@ -14,21 +14,21 @@ import run_config from framework.deproxy import deproxy_client, deproxy_manager from framework.deproxy.deproxy_auto_parser import DeproxyAutoParser -from framework.deproxy.deproxy_server import deproxy_srv_factory +from framework.deproxy.deproxy_server import StaticDeproxyServer, deproxy_srv_factory from framework.helpers import clickhouse, dmesg, error, remote, tf_cfg, util from framework.helpers.memworker import MemoryChecker from framework.helpers.networker import NetWorker from framework.helpers.tf_cfg import test_logger from framework.helpers.util import fill_template -from framework.services import base_server, curl_client, external_client +from framework.services import curl_client, external_client from framework.services import tempesta as tfw from framework.services import wrk_client -from framework.services.docker_server import docker_srv_factory -from framework.services.nginx_server import nginx_srv_factory +from framework.services.docker_server import DockerServer, docker_srv_factory +from framework.services.nginx_server import Nginx, nginx_srv_factory from framework.services.stateful import Stateful __author__ = "Tempesta Technologies, Inc." -__copyright__ = "Copyright (C) 2018-2025 Tempesta Technologies, Inc." +__copyright__ = "Copyright (C) 2018-2026 Tempesta Technologies, Inc." __license__ = "GPL2" @@ -282,7 +282,7 @@ def __create_servers(self): # Copy description to keep it clean between several tests. self.__create_backend(server.copy()) - def get_server(self, sid: str | int) -> base_server.BaseServer: + def get_server(self, sid: str | int) -> StaticDeproxyServer | DockerServer | Nginx: """Return client with specified id""" server = self.__servers.get(sid, None) if server is None: diff --git a/tests/cve/__init__.py b/tests/cve/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/cve/test_cve.py b/tests/cve/test_cve.py new file mode 100644 index 000000000..aa05589f3 --- /dev/null +++ b/tests/cve/test_cve.py @@ -0,0 +1,554 @@ +__author__ = "Tempesta Technologies, Inc." +__copyright__ = "Copyright (C) 2026 Tempesta Technologies, Inc." +__license__ = "GPL2" + +import random +import string +from pathlib import Path + +from hyperframe.frame import HeadersFrame, PriorityFrame + +from framework.deproxy.deproxy_message import HttpMessage +from framework.helpers import dmesg, memworker, remote, tf_cfg +from framework.test_suite import marks, tester + + +class TestSlowRead(tester.TempestaTest): + clients = [ + { + "id": f"deproxy-{i}", + "type": "deproxy_h2", + "addr": "${tempesta_ip}", + "port": "443", + "ssl": True, + "ssl_hostname": "tempesta-tech.com", + } + for i in range(20) + ] + + backends = [ + { + "id": "nginx", + "type": "nginx", + "status_uri": "http://${server_ip}:8000/nginx_status", + "config": """ +pid ${pid}; +worker_processes auto; + +events { + worker_connections 1024; + use epoll; +} + +http { + keepalive_timeout ${server_keepalive_timeout}; + keepalive_requests 10; + sendfile on; + tcp_nopush on; + tcp_nodelay on; + + open_file_cache max=1000; + open_file_cache_valid 30s; + open_file_cache_min_uses 2; + open_file_cache_errors off; + + # [ debug | info | notice | warn | error | crit | alert | emerg ] + # Fully disable log errors. + error_log /dev/null emerg; + + # Disable access log altogether. + access_log off; + + server { + listen ${server_ip}:8000; + + location / { + root ${server_resources}; + } + location /nginx_status { + stub_status on; + } + } +} +""", + } + ] + + tempesta = { + "config": """ + cache 0; + keepalive_timeout 10; + listen 443 proto=h2; + + tls_match_any_server_name; + + srv_group default { + server ${server_ip}:8000; + } + + vhost tempesta-tech.com { + tls_certificate ${tempesta_workdir}/tempesta.crt; + tls_certificate_key ${tempesta_workdir}/tempesta.key; + proxy_pass default; + } + + """ + } + + response_file_name = "large.txt" + response_file_path = str(Path(tf_cfg.cfg.get("Server", "resources")) / response_file_name) + + @classmethod + def setUpClass(cls): + super().setUpClass() + remote.server.run_cmd( + f"fallocate -l {1024**2 * int(tf_cfg.cfg.get("General", "long_body_size"))} {cls.response_file_path}" + ) + + @classmethod + def tearDownClass(cls): + super().tearDownClass() + remote.server.remove_file(cls.response_file_path) + + async def test_cve_2019_9511(self): + """ + CVE-2019-9511 - “Data Dribble” + Some HTTP/2 implementations are vulnerable to window size manipulation + and stream prioritization manipulation, potentially leading to a denial of service. + The attacker requests a large amount of data from a specified resource over multiple streams. + They manipulate window size and stream priority to force the server to queue the data in 1-byte chunks. + Depending on how efficiently this data is queued, this can consume excess CPU, memory, or both. + """ + await self.start_all_services() + + request = self.get_clients()[0].create_request( + method="GET", + headers=[], + authority="tempesta-tech.com", + uri=f"/{self.response_file_name}", + ) + + for client in self.get_clients(): + client.update_initial_settings(initial_window_size=1) + client.send_bytes(client.h2_connection.data_to_send()) + self.assertTrue(await client.wait_for_ack_settings()) + client.make_requests([request] * 100) + + for client in self.get_clients(): + await client.wait_for_connection_close(strict=True) + + async def test_cve_2019_9517(self): + """ + CVE-2019-9517 - “Internal Data Buffering” + Some HTTP/2 implementations are vulnerable to unconstrained internal data buffering, + potentially leading to a denial of service. The attacker opens the HTTP/2 window + so the peer can send without constraint; however, they leave the TCP window closed + so the peer cannot actually write (many of) the bytes on the wire. + The attacker sends a stream of requests for a large response object. + Depending on how the servers queue the responses, this can consume excess memory, CPU, or both. + """ + await self.start_all_services() + + request = self.get_clients()[0].create_request( + method="GET", + headers=[], + authority="tempesta-tech.com", + uri=f"/{self.response_file_name}", + ) + + for client in self.get_clients(): + client.update_initial_settings() + client.send_bytes(client.h2_connection.data_to_send()) + self.assertTrue(await client.wait_for_ack_settings()) + client.set_size_of_receiving_buffer(new_buffer_size=1) + client.make_requests([request] * 100) + + for client in self.get_clients(): + await client.wait_for_connection_close(timeout=20, strict=True) + + +class TestHttp2FrameFlood(tester.TempestaTest): + """ + Test ability to handle requests from the client + under control frames flood. + Also check that there is no kernel BUGS and WARNINGs + under flood. + """ + + backends = [ + { + "id": "deproxy", + "type": "deproxy", + "port": "8000", + "response": "static", + "response_content": "HTTP/1.1 200 OK\r\nServer: Debian\r\nContent-Length: 0\r\n\r\n", + } + ] + + tempesta = { + "config": """ + listen 443 proto=h2; + + server ${server_ip}:8000; + + tls_certificate ${tempesta_workdir}/tempesta.crt; + tls_certificate_key ${tempesta_workdir}/tempesta.key; + tls_match_any_server_name; + cache 0; + """ + } + + clients = [ + { + "id": "ctrl_frames_flood", + "type": "external", + "binary": "ctrl_frames_flood", + "ssl": True, + "cmd_args": ( + "-address ${tempesta_ip}:443 -threads 4 -connections 100 -frame_count 100000" + ), + }, + { + "id": "gflood", + "type": "external", + "binary": "gflood", + "ssl": True, + "cmd_args": ( + "-address ${tempesta_ip}:443 -host tempesta-tech.com " + "-threads 4 -connections 10000 -streams 100 -headers_cnt 7" + ), + }, + { + "id": "deproxy", + "type": "deproxy_h2", + "addr": "${tempesta_ip}", + "port": "443", + "ssl": True, + }, + ] + + def random_depends_on(self, stream_id: int) -> int: + depends_on = random.randint(1, 199) + if depends_on % 2 == 0: + depends_on += 1 + if depends_on == stream_id: + return self.random_depends_on(stream_id) + return depends_on + + @staticmethod + def random_stream_id(max_id: int) -> int: + stream_id = random.randint(1, max_id) + if stream_id % 2 == 0: + stream_id = stream_id - 1 if stream_id == max_id else stream_id + 1 + return stream_id + + async def test_cve_2019_9513(self): + """ + CVE-2019-9513 “Resource Loop” + Some HTTP/2 implementations are vulnerable to resource loops, potentially leading to a denial of service. + The attacker creates multiple request streams and continually shuffles the priority of the streams in a way + that causes substantial churn to the priority tree. This can consume excess CPU. + + Tempesta FW blocks a lot of priority frames. + """ + server = self.get_server("deproxy") + client = self.get_client("deproxy") + + server.set_response("") + + await self.start_all_services(client=False) + + request = client.create_request(uri="/", method="GET", headers=[]) + + client.start() + client.make_request(request) + for stream_id in range(3, 200, 2): + client.make_request( + request, + priority_weight=random.randint(1, 255), + priority_depends_on=self.random_depends_on(stream_id), + priority_exclusive=bool(random.randint(0, 1)), + ) + + for _ in range(1000): + client.send_bytes( + PriorityFrame( + stream_id=self.random_stream_id(199), + depends_on=self.random_depends_on(stream_id), + stream_weight=random.randint(1, 255), + exclusive=bool(random.randint(0, 1)), + ).serialize() + ) + await client.wait_for_connection_close(strict=True) + + @dmesg.limited_rate_on_tempesta_node + async def test_cve_2024_2758(self): + """ + CVE-2024-2758 "Continuation flood" + Many HTTP/2 implementations do not properly limit or sanitize the amount of CONTINUATION frames sent within + a single stream. An attacker that can send packets to a target server can send a stream of CONTINUATION + frames that will not be appended to the header list in memory but will still be processed and decoded + by the server or will be appended to the header list, causing an out of memory (OOM) crash. + """ + await self.start_all_services(client=False) + + client = self.get_client("gflood") + + client.start() + await self.wait_while_busy(client) + client.stop() + + self.assertEqual(0, client.returncode) + + @marks.Parameterize.expand( + [ + marks.Param( + name="2019_9512", + cmd_args=f"-ctrl_frame_type ping_frame", + stat_name="cl_ping_frame_exceeded", + ), + marks.Param( + name="2019_9515", + cmd_args=f"-ctrl_frame_type settings_frame", + stat_name="cl_settings_frame_exceeded", + ), + marks.Param( + name="2023-44487", + cmd_args=f"-ctrl_frame_type rapid_reset -rapid_reset_type rst", + stat_name="cl_rst_frame_exceeded", + ), + marks.Param( + name="2025-8671_by_window_update", + cmd_args=f"-ctrl_frame_type window_update", + stat_name="cl_wnd_update_frame_exceeded", + ), + marks.Param( + name="2025-8671_by_window_update", + cmd_args=f"-ctrl_frame_type rapid_reset -rapid_reset_type window_update", + stat_name="cl_rst_frame_exceeded", + ), + marks.Param( + name="2025-8671_by_priority", + cmd_args=f"-ctrl_frame_type rapid_reset -rapid_reset_type priority", + stat_name="cl_rst_frame_exceeded", + ), + marks.Param( + name="2025-8671_by_flood_rst_batch", + cmd_args=f"-ctrl_frame_type rapid_reset -rapid_reset_type batch", + stat_name="cl_rst_frame_exceeded", + ), + marks.Param( + name="2025-8671_by_headers_max_concurrent_streams_exceeded", + cmd_args=f"-ctrl_frame_type rapid_reset -rapid_reset_type headers_by_max_streams_exceeded", + stat_name="cl_rst_frame_exceeded", + ), + marks.Param( + name="2025-8671_by_headers_invalid_dependency", + cmd_args=f"-ctrl_frame_type rapid_reset -rapid_reset_type headers_by_invalid_dependency", + stat_name="cl_rst_frame_exceeded", + ), + marks.Param( + name="2025-8671_by_incorrect_frame_type", + cmd_args=f"-ctrl_frame_type rapid_reset -rapid_reset_type incorrect_frame_type", + stat_name="cl_rst_frame_exceeded", + ), + marks.Param( + name="2025-8671_by_incorrect_header", + cmd_args=f"-ctrl_frame_type rapid_reset -rapid_reset_type incorrect_header", + stat_name="cl_rst_frame_exceeded", + ), + ] + ) + @dmesg.unlimited_rate_on_tempesta_node + async def test_cve(self, name, cmd_args, stat_name): + """ + CVE-2019-9512 “Ping Flood” + Some HTTP/2 implementations are vulnerable to ping floods, potentially leading to a denial of service. + The attacker sends continual pings to an HTTP/2 peer, causing the peer to build an internal queue of responses. + Depending on how efficiently this data is queued, this can consume excess CPU, memory, or both. + + CVE-2019-9514 "Reset Flood" + Some HTTP/2 implementations are vulnerable to a reset flood, potentially leading to a denial of service. + Servers that accept direct connections from untrusted clients could be remotely made to allocate an + unlimited amount of memory, until the program crashes. + The attacker opens a number of streams and sends an invalid request over each stream that should solicit + a stream of RST_STREAM frames from the peer. Depending on how the peer queues the RST_STREAM frames, + this can consume excess memory, CPU, or both. + + CVE-2019-9515 "Settings Flood" + Some HTTP/2 implementations are vulnerable to a settings flood, potentially leading to a denial of service. + The attacker sends a stream of SETTINGS frames to the peer. Since the RFC requires that the peer reply with + one acknowledgement per SETTINGS frame, an empty SETTINGS frame is almost equivalent in behavior to a ping. + Depending on how efficiently this data is queued, this can consume excess CPU, memory, or both. + + CVE-2023-44487 "Rapid Reset" + The HTTP/2 protocol allows clients to indicate to the server that a previous stream should be canceled + by sending RST_STREAM frame. The protocol does not require the client and server to coordinate + the cancellation in any way, the client may do it unilaterally. The client may also assume that + the cancellation will take effect immediately when the server receives the RST_STREAM frame, + before any other data from that TCP connection is processed. + + CVE-2025-8671 "Made You Reset" + By opening streams and then rapidly triggering the server to reset them using malformed frames or flow + control errors, an attacker can exploit a discrepancy created between HTTP/2 streams accounting and the + servers active HTTP requests. Streams reset by the server are considered closed, even though backend + processing continues. This allows a client to cause the server to handle an unbounded number + of concurrent HTTP/2 requests on a single connection. + This is very similar to CVE-2019-9514 HTTP/2 Reset Flood + """ + server = self.get_server("deproxy") + flood_client = self.get_client("ctrl_frames_flood") + tempesta = self.get_tempesta() + + server.set_response( + "HTTP/1.1 200 OK\r\n" + + "Server: Debian\r\n" + + f"Date: {HttpMessage.date_time_string()}\r\n" + + "Content-Length: 2000\r\n\r\n" + + (2000 * "a") + ) + + await self.start_all_services(client=False) + + flood_client.options = flood_client.options + [cmd_args] + flood_client.start() + await self.wait_while_busy(flood_client) + flood_client.stop() + + self.assertEqual(0, flood_client.returncode) + tempesta.get_stats() + self.assertEqual(tempesta.stats.__dict__[stat_name], 100) + + +class TestH2Headers(tester.TempestaTest): + + clients = [ + { + "id": "deproxy", + "type": "deproxy_h2", + "addr": "${tempesta_ip}", + "port": "443", + "ssl": True, + "ssl_hostname": "tempesta-tech.com", + }, + ] + + backends = [ + { + "id": "nginx", + "type": "nginx", + "status_uri": "http://${server_ip}:8000/nginx_status", + "config": """ +pid ${pid}; +worker_processes auto; +events { + worker_connections 1024; + use epoll; +} +http { + keepalive_timeout ${server_keepalive_timeout}; + keepalive_requests 10; + tcp_nopush on; + tcp_nodelay on; + error_log /dev/null emerg; + access_log off; + server { + listen ${server_ip}:8000; + location / { + return 200 'foo'; + } + location /nginx_status { + stub_status on; + } + } +} +""", + } + ] + + tempesta = { + "config": """ + cache 0; + + keepalive_timeout 1000; + + listen 443 proto=h2; + + tls_match_any_server_name; + max_concurrent_streams 10000; + + srv_group default { + server ${server_ip}:8000; + } + frang_limits { + http_strict_host_checking false; + http_header_cnt 1000; + } + + ctrl_frame_rate_multiplier 65536; + + vhost tempesta-tech.com { + tls_certificate ${tempesta_workdir}/tempesta.crt; + tls_certificate_key ${tempesta_workdir}/tempesta.key; + proxy_pass default; + } + """ + } + + @staticmethod + def randomword(length): + letters = string.ascii_lowercase + return "".join(random.choice(letters) for i in range(length)) + + @dmesg.limited_rate_on_tempesta_node + async def test_2019_9516(self): + """ + CVE-2019-9516 “0-Length Headers Leak” + Some HTTP/2 implementations are vulnerable to a header leak, potentially leading to a denial of service. + The attacker sends a stream of headers with a 0-length header name and 0-length header value, + optionally Huffman encoded into 1-byte or greater headers. Some implementations allocate memory + for these headers and keep the allocation alive until the session dies. This can consume excess memory. + + 1. For 0-length header name, tempesta returns 400 and GOAWAY and + closes the connection, so no further effect happens after. + 2. If we send a request followed by RST_STREAM, temepesta will close + the connection when the response arrives from the backend, + so no further effect too. + """ + await self.start_all_services() + client = self.get_client("deproxy") + + request = client.create_request( + uri="/", + method="GET", + headers=[(self.randomword(100), self.randomword(100)) for _ in range(50)], + ) + + # create http2 connection and stream 1. The stream and connection are open. + # It is necessary for check of a memory consumption + client.make_request(client.create_request(method="GET", headers=[]), end_stream=False) + + with memworker.check_memory_consumptions(self): + for stream_id in range(3, 20000, 2): + client.stream_id = stream_id + client.make_request(request, end_stream=False) + + # send trailer headers with invalid `x-forwarded-for` header + # it is necessary for calling the RST_STREAM + client.send_bytes( + HeadersFrame( + stream_id=stream_id, + data=client.h2_connection.encoder.encode( + [("x-forwarded-for", "1.1.1.1.1.1")] + ), + flags=["END_STREAM", "END_HEADERS"], + ).serialize(), + expect_response=True, + ) + + self.assertTrue(await client.wait_for_response(120)) + + # close first stream and http2 connection and finish test + client.stream_id = 1 + client.make_request("data", end_stream=True) + self.assertTrue(await client.wait_for_response()) diff --git a/tests/stress/test_header_leak.py b/tests/stress/test_header_leak.py index 8fbdeb5e6..e69de29bb 100644 --- a/tests/stress/test_header_leak.py +++ b/tests/stress/test_header_leak.py @@ -1,157 +0,0 @@ -__author__ = "Tempesta Technologies, Inc." -__copyright__ = "Copyright (C) 2024 Tempesta Technologies, Inc." -__license__ = "GPL2" - -import random -import re -import string - -import psutil -from hyperframe.frame import HeadersFrame - -from framework.helpers import dmesg, remote -from framework.test_suite import tester - - -def randomword(length): - letters = string.ascii_lowercase - return "".join(random.choice(letters) for i in range(length)) - - -def get_memory_lines(*names): - """Get values from /proc/meminfo""" - [stdout, stderr] = remote.tempesta.run_cmd("cat /proc/meminfo") - lines = [] - for name in names: - line = re.search("%s:[ ]+([0-9]+)" % name, str(stdout)) - if line: - lines.append(int(line.group(1))) - else: - raise Exception("Can not get %s from /proc/meminfo" % name) - return lines - - -class TestH2HeaderLeak(tester.TempestaTest): - """ - 1. For 0-length header name, tempesta returns 400 and GOAWAY and - closes the connection, so no further effect happens after. - 2. If we send a request followed by RST_STREAM, temepesta will close - the connection when the response arrives from the backend, - so no further effect too. - """ - - clients = [ - { - "id": "deproxy", - "type": "deproxy_h2", - "addr": "${tempesta_ip}", - "port": "443", - "ssl": True, - "ssl_hostname": "tempesta-tech.com", - }, - ] - - backends = [ - { - "id": "nginx", - "type": "nginx", - "status_uri": "http://${server_ip}:8000/nginx_status", - "config": """ -pid ${pid}; -worker_processes auto; -events { - worker_connections 1024; - use epoll; -} -http { - keepalive_timeout ${server_keepalive_timeout}; - keepalive_requests 10; - tcp_nopush on; - tcp_nodelay on; - error_log /dev/null emerg; - access_log off; - server { - listen ${server_ip}:8000; - location / { - return 200 'foo'; - } - location /nginx_status { - stub_status on; - } - } -} -""", - } - ] - - tempesta = { - "config": """ - cache 0; - - keepalive_timeout 1000; - - listen 443 proto=h2; - - tls_match_any_server_name; - max_concurrent_streams 10000; - - srv_group default { - server ${server_ip}:8000; - } - frang_limits { - http_strict_host_checking false; - http_header_cnt 1000; - } - - ctrl_frame_rate_multiplier 65536; - - vhost tempesta-tech.com { - tls_certificate ${tempesta_workdir}/tempesta.crt; - tls_certificate_key ${tempesta_workdir}/tempesta.key; - proxy_pass default; - } - """ - } - - @dmesg.limited_rate_on_tempesta_node - async def test(self): - await self.start_all_services() - client = self.get_client("deproxy") - - request = client.create_request( - uri="/", method="GET", headers=[(randomword(100), randomword(100)) for _ in range(50)] - ) - - # create http2 connection and stream 1. The stream and connection are open. - # It is necessary for check of a memory consumption - client.make_request(client.create_request(method="GET", headers=[]), end_stream=False) - - # save a memory consumption and make a lot of requests with different headers - (mem1,) = get_memory_lines("MemAvailable") - mem1 = mem1 + psutil.Process().memory_info().rss // 1024 # python memory - for stream_id in range(3, 20000, 2): - client.stream_id = stream_id - client.make_request(request, end_stream=False) - - # send trailer headers with invalid `x-forwarded-for` header - # it is necessary for calling the RST_STREAM - client.send_bytes( - HeadersFrame( - stream_id=stream_id, - data=client.h2_connection.encoder.encode([("x-forwarded-for", "1.1.1.1.1.1")]), - flags=["END_STREAM", "END_HEADERS"], - ).serialize(), - expect_response=True, - ) - - self.assertTrue(await client.wait_for_response(120)) - # check a memory consumption (http2 connection is still open) - (mem2,) = get_memory_lines("MemAvailable") - mem2 = mem2 + psutil.Process().memory_info().rss // 1024 - memdiff = abs(mem2 - mem1) / mem1 - self.assertLess(memdiff, 0.05) - - # close first stream and http2 connection and finish test - client.stream_id = 1 - client.make_request("data", end_stream=True) - self.assertTrue(await client.wait_for_response()) diff --git a/tests/stress/test_stress.py b/tests/stress/test_stress.py index ae61290cb..37fcb482d 100644 --- a/tests/stress/test_stress.py +++ b/tests/stress/test_stress.py @@ -6,11 +6,9 @@ import time from pathlib import Path -from framework.deproxy.deproxy_message import HttpMessage from framework.helpers import dmesg, networker, remote, tf_cfg from framework.services import tempesta from framework.test_suite import marks, tester -from tests.frang.frang_test_case import FrangTestCase __author__ = "Tempesta Technologies, Inc." __copyright__ = "Copyright (C) 2022-2026 Tempesta Technologies, Inc." @@ -796,207 +794,3 @@ async def test_h2_post_request(self): async def test_h2_put_request(self): await self._test_h2load(method="PUT") - - -class TestContinuationFlood(tester.TempestaTest): - """ - Test stability against CONTINUATION frame flood. - """ - - clients = [ - { - "id": "gflood", - "type": "external", - "binary": "gflood", - "ssl": True, - "cmd_args": "-address ${tempesta_ip}:443 -host tempesta-tech.com -threads 4 -connections 10000 -streams 100 -headers_cnt 7", - }, - ] - - backends = [ - { - "id": "nginx", - "type": "nginx", - "port": "8000", - "status_uri": "http://${server_ip}:8000/nginx_status", - "config": NGINX_CONFIG, - } - ] - - tempesta = { - "config": """ - listen 443 proto=h2; - - server ${server_ip}:8000; - - tls_certificate ${tempesta_workdir}/tempesta.crt; - tls_certificate_key ${tempesta_workdir}/tempesta.key; - tls_match_any_server_name; - cache 0; - """ - } - - @dmesg.limited_rate_on_tempesta_node - async def test(self): - client = self.get_client("gflood") - - await self.start_all_services(client=True) - await self.wait_while_busy(client) - client.stop() - self.assertEqual(0, client.returncode) - - -class TestRequestsUnderCtrlFrameFlood(FrangTestCase): - """ - Test ability to handle requests from the client - under control frames frame flood. - Also check that there is no kernel BUGS and WARNINGs - under flood. - """ - - backends = [ - { - "id": "deproxy", - "type": "deproxy", - "port": "8000", - "response": "static", - "response_content": ( - "HTTP/1.1 200 OK\r\n" - + f"Date: {HttpMessage.date_time_string()}\r\n" - + "Server: debian\r\n" - + "Content-Length: 0\r\n\r\n" - ), - } - ] - - tempesta = { - "config": """ - listen 443 proto=h2; - - server ${server_ip}:8000; - - tls_certificate ${tempesta_workdir}/tempesta.crt; - tls_certificate_key ${tempesta_workdir}/tempesta.key; - tls_match_any_server_name; - cache 0; - """ - } - - clients = [ - { - "id": "ctrl_frames_flood", - "type": "external", - "binary": "ctrl_frames_flood", - "ssl": True, - "cmd_args": "", - }, - ] - - async def _test(self, cmd_args): - await self.start_all_services(client=False) - flood_client = self.get_client("ctrl_frames_flood") - flood_client.options = [cmd_args % tf_cfg.cfg.get("Tempesta", "ip")] - flood_client.start() - await self.wait_while_busy(flood_client) - flood_client.stop() - - def _check_ping_frame_exceeded(self): - tempesta = self.get_tempesta() - tempesta.get_stats() - self.assertEqual(tempesta.stats.cl_ping_frame_exceeded, 100) - - def _check_prio_frame_exceeded(self): - tempesta = self.get_tempesta() - tempesta.get_stats() - self.assertEqual(tempesta.stats.cl_priority_frame_exceeded, 100) - - def _check_settings_frame_exceeded(self): - tempesta = self.get_tempesta() - stats = tempesta.get_stats() - self.assertEqual(tempesta.stats.cl_settings_frame_exceeded, 100) - - def _check_wnd_update_frame_exceeded(self): - tempesta = self.get_tempesta() - stats = tempesta.get_stats() - self.assertEqual(tempesta.stats.cl_wnd_update_frame_exceeded, 100) - - def _check_rst_frame_exceeded(self): - tempesta = self.get_tempesta() - stats = tempesta.get_stats() - self.assertEqual(tempesta.stats.cl_rst_frame_exceeded, 100) - - @marks.Parameterize.expand( - [ - marks.Param( - name="PingFlood", - cmd_args=f"-address %s:443 -threads 4 -connections 100 -ctrl_frame_type ping_frame -frame_count 100000", - check_func=_check_ping_frame_exceeded, - ), - marks.Param( - name="SettingsFlood", - cmd_args=f"-address %s:443 -threads 4 -connections 100 -ctrl_frame_type settings_frame -frame_count 100000", - check_func=_check_settings_frame_exceeded, - ), - marks.Param( - name="WndUpdateFlood", - cmd_args=f"-address %s:443 -threads 4 -connections 100 -ctrl_frame_type window_update -frame_count 100000", - check_func=_check_wnd_update_frame_exceeded, - ), - marks.Param( - name="RstFloodByWndUpdate", - cmd_args=f"-address %s:443 -threads 4 -connections 100 -ctrl_frame_type rapid_reset -rapid_reset_type window_update -frame_count 100000", - check_func=_check_rst_frame_exceeded, - ), - marks.Param( - name="RstFloodByPriority", - cmd_args=f"-address %s:443 -threads 4 -connections 100 -ctrl_frame_type rapid_reset -rapid_reset_type priority -frame_count 100000", - check_func=_check_rst_frame_exceeded, - ), - marks.Param( - name="RstFloodByRst", - cmd_args=f"-address %s:443 -threads 4 -connections 100 -ctrl_frame_type rapid_reset -rapid_reset_type rst -frame_count 100000", - check_func=_check_rst_frame_exceeded, - ), - marks.Param( - name="RstFloodByRstBatch", - cmd_args=f"-address %s:443 -threads 4 -connections 100 -ctrl_frame_type rapid_reset -rapid_reset_type batch -frame_count 100000", - check_func=_check_rst_frame_exceeded, - ), - marks.Param( - name="RstByHeadersMaxConcurrentStreamsExceeded", - cmd_args=f"-address %s:443 -threads 4 -connections 100 -ctrl_frame_type rapid_reset -rapid_reset_type headers_by_max_streams_exceeded -frame_count 100000", - check_func=_check_rst_frame_exceeded, - ), - marks.Param( - name="RstByHeadersInvalidDependency", - cmd_args=f"-address %s:443 -threads 4 -connections 100 -ctrl_frame_type rapid_reset -rapid_reset_type headers_by_invalid_dependency -frame_count 100000", - check_func=_check_rst_frame_exceeded, - ), - marks.Param( - name="RstByIncorrectFrameType", - cmd_args=f"-address %s:443 -threads 4 -connections 100 -ctrl_frame_type rapid_reset -rapid_reset_type incorrect_frame_type -frame_count 100000", - check_func=_check_rst_frame_exceeded, - ), - marks.Param( - name="RstByIncorrectHeader", - cmd_args=f"-address %s:443 -threads 4 -connections 100 -ctrl_frame_type rapid_reset -rapid_reset_type incorrect_header -frame_count 100000", - check_func=_check_rst_frame_exceeded, - ), - ] - ) - @dmesg.unlimited_rate_on_tempesta_node - async def test(self, name, cmd_args, check_func): - server = self.get_server("deproxy") - response_body = 2000 * "a" - server.set_response( - "HTTP/1.1 200 OK\r\n" - + "Server: Debian\r\n" - + f"Date: {HttpMessage.date_time_string()}\r\n" - + f"Content-Length: {len(response_body)}\r\n\r\n" - + response_body - ) - await self._test(cmd_args) - check_func(self) - - -# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 diff --git a/tests/tests_disabled.json b/tests/tests_disabled.json index c1eb2b6af..b3d70b842 100644 --- a/tests/tests_disabled.json +++ b/tests/tests_disabled.json @@ -189,21 +189,25 @@ "name": "tests.clickhouse.test_clickhouse_logs.TestClickHouseLogsDelay", "reason": "The response time varies each time, and it is not possible to predict it exactly." }, - { - "name": "tests.stress.test_stress.TestContinuationFlood", - "reason": "Disabled by test issue #817" - }, { "name": "tests.stress.test_stress.TestStressNoCacheMTU80", - "reason": "Disabled by test issue #817" + "reason": "Disabled by test issue #" }, { "name": "tests.http2_general.test_h2_frame.TestPostponedFrames", "reason": "Disabled by test issue #862" }, - { + { "name": "tests.server_connections.test_close_dead_connections.TestFinishH2StreamsByClient.test_drop_server_connection_for_goaway", "reason": "Disabled by issue #2626" + }, + { + "name": "tests.cve.test_cve.TestSlowRead.test_cve_2019_9511", + "reason": "Disabled by issue #2627" + }, + { + "name": "tests.cve.test_cve.TestSlowRead.test_cve_2019_9517", + "reason": "Disabled by issue #1715" } ] } diff --git a/tests/tests_disabled_dbgkernel.json b/tests/tests_disabled_dbgkernel.json index ed81bc142..f1e16c030 100644 --- a/tests/tests_disabled_dbgkernel.json +++ b/tests/tests_disabled_dbgkernel.json @@ -18,11 +18,11 @@ "reason" : "the tests did not work on the first run" }, { - "name": "tests.stress.test_header_leak.TestH2HeaderLeak", + "name": "tests.cve.test_cve.TestH2HeaderLeak", "reason" : "the tests did not work on the first run" }, { - "name": "tests.stress.test_stress.TestContinuationFlood", + "name": "tests.cve.test_cve.TestHttp2FrameFlood.test_cve_2024_2758", "reason" : "the tests did not work on the first run" }, { diff --git a/tests/tests_disabled_remote.json b/tests/tests_disabled_remote.json index 2430ed5b1..00698e0d1 100644 --- a/tests/tests_disabled_remote.json +++ b/tests/tests_disabled_remote.json @@ -104,22 +104,18 @@ { "name": "tests.frang.test_concurrent_connections.TestConcurrentConnectionsNonTempesta", "reason": "Is not intended to run on remote setup. Local only." - }, - { - "name": "tests.stress.test_stress.TestContinuationFlood", - "reason": "Disabled by test issue #817" }, { "name": "tests.stress.test_stress.H2LoadStressMTU80", - "reason": "Disabled by test issue #817" + "reason": "Disabled by test issue #816" }, { "name": "tests.stress.test_stress.TlsWrkStressMTU80", - "reason": "Disabled by test issue #817" + "reason": "Disabled by test issue #816" }, { "name": "tests.stress.test_stress.WrkStressMTU80", - "reason": "Disabled by test issue #817" + "reason": "Disabled by test issue #816" } ] } diff --git a/tests/tests_disabled_tcpseg.json b/tests/tests_disabled_tcpseg.json index 3cb925dda..fd5d57d7e 100644 --- a/tests/tests_disabled_tcpseg.json +++ b/tests/tests_disabled_tcpseg.json @@ -1,6 +1,10 @@ { "disable" : true, "disabled" : [ + { + "name": "tests.cve", + "reason": "These tests should not be run with TCP segmentation." + }, { "name": "tests.nonidempotent.test_nonidempotent.RetryNonIdempotentPostH1Test", "reason": "These tests should not be run with TCP segmentation. It is difficult to ensure stability in this test." @@ -161,10 +165,6 @@ "name": "tests.http2_general.test_h2_hpack.TestHpackBomb", "reason": "These tests should not be run with TCP segmentation. We have separate tests for `http_max_header_list_size`." }, - { - "name": "tests.stress.test_ddos", - "reason": "These tests should not be run with TCP segmentation. These tests does not use deproxy." - }, { "name": "tests.frang.test_config", "reason": "These tests should not be run with TCP segmentation." @@ -256,22 +256,6 @@ { "name": "tests.frang.test_concurrent_connections.TestConcurrentConnectionsNonTempesta", "reason": "Is not intended to run with segmentation." - }, - { - "name": "tests.stress.test_stress.TestContinuationFlood", - "reason": "Disabled by test issue #817" - }, - { - "name": "tests.stress.test_stress.H2LoadStressMTU80", - "reason": "Disabled by test issue #817" - }, - { - "name": "tests.stress.test_stress.TlsWrkStressMTU80", - "reason": "Disabled by test issue #817" - }, - { - "name": "tests.stress.test_stress.WrkStressMTU80", - "reason": "Disabled by test issue #817" } ] } diff --git a/tests/tests_retry b/tests/tests_retry index 44d48b1a3..ba8a5061c 100644 --- a/tests/tests_retry +++ b/tests/tests_retry @@ -1,3 +1,2 @@ tests.frang.test_request_rate_burst tests.frang.test_http_resp_code_block -tests.leaks