Page MenuHomePhabricator

D9519.diff
No OneTemporary

D9519.diff

diff --git a/test/functional/p2p_invalid_messages.py b/test/functional/p2p_invalid_messages.py
--- a/test/functional/p2p_invalid_messages.py
+++ b/test/functional/p2p_invalid_messages.py
@@ -28,6 +28,9 @@
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import hex_str_to_bytes
+MSG_LIMIT = 2 * 1024 * 1024 # 2MB, per MAX_PROTOCOL_MESSAGE_LENGTH
+VALID_DATA_LIMIT = MSG_LIMIT - 5 # Account for the 5-byte length prefix
+
class msg_unrecognized:
"""Nonsensical message. Modeled after similar types in test_framework.messages."""
@@ -55,16 +58,6 @@
self.setup_clean_chain = True
def run_test(self):
- """
- . Test msg header
- 0. Send a bunch of large (2MB) messages of an unrecognized type. Check to see
- that it isn't an effective DoS against the node.
-
- 1. Send an oversized (2MB+) message and check that we're disconnected.
-
- 2. Send a few messages with an incorrect data size in the header, ensure the
- messages are ignored.
- """
self.test_magic_bytes()
self.test_checksum()
self.test_size()
@@ -75,101 +68,7 @@
self.test_addrv2_unrecognized_network()
self.test_large_inv()
self.test_unsolicited_ava_messages()
-
- node = self.nodes[0]
- self.node = node
- node.add_p2p_connection(P2PDataStore())
- conn2 = node.add_p2p_connection(P2PDataStore())
-
- # 2MB, per MAX_PROTOCOL_MESSAGE_LENGTH
- msg_limit = 2 * 1024 * 1024
- # Account for the 4-byte length prefix
- valid_data_limit = msg_limit - 5
-
- #
- # 0.
- #
- # Send as large a message as is valid, ensure we aren't disconnected but
- # also can't exhaust resources.
- #
- msg_at_size = msg_unrecognized(str_data="b" * valid_data_limit)
- assert len(msg_at_size.serialize()) == msg_limit
-
- self.log.info(
- "Sending a bunch of large, junk messages to test memory exhaustion. May take a bit...")
-
- # Run a bunch of times to test for memory exhaustion.
- for _ in range(80):
- node.p2p.send_message(msg_at_size)
-
- # Check that, even though the node is being hammered by nonsense from one
- # connection, it can still service other peers in a timely way.
- for _ in range(20):
- conn2.sync_with_ping(timeout=2)
-
- # Peer 1, despite serving up a bunch of nonsense, should still be
- # connected.
- self.log.info("Waiting for node to drop junk messages.")
- node.p2p.sync_with_ping(timeout=320)
- assert node.p2p.is_connected
-
- #
- # 1.
- #
- # Send an oversized message, ensure we're disconnected.
- #
- msg_over_size = msg_unrecognized(str_data="b" * (valid_data_limit + 1))
- assert len(msg_over_size.serialize()) == (msg_limit + 1)
-
- with node.assert_debug_log(["Oversized header detected"]):
- # An unknown message type (or *any* message type) over
- # MAX_PROTOCOL_MESSAGE_LENGTH should result in a disconnect.
- node.p2p.send_message(msg_over_size)
- node.p2p.wait_for_disconnect(timeout=4)
-
- node.disconnect_p2ps()
- conn = node.add_p2p_connection(P2PDataStore())
- conn.wait_for_verack()
-
- #
- # 2.
- #
- # Send messages with an incorrect data size in the header.
- #
- actual_size = 100
- msg = msg_unrecognized(str_data="b" * actual_size)
-
- # TODO: handle larger-than cases. I haven't been able to pin down what
- # behavior to expect.
- for wrong_size in (2, 77, 78, 79):
- self.log.info(
- "Sending a message with incorrect size of {}".format(wrong_size))
-
- # Unmodified message should submit okay.
- node.p2p.send_and_ping(msg)
-
- # A message lying about its data size results in a disconnect when the incorrect
- # data size is less than the actual size.
- #
- # TODO: why does behavior change at 78 bytes?
- #
- node.p2p.send_raw_message(
- self._tweak_msg_data_size(
- msg, wrong_size))
-
- # For some reason unknown to me, we sometimes have to push additional data to the
- # peer in order for it to realize a disconnect.
- try:
- node.p2p.send_message(msg_ping(nonce=123123))
- except IOError:
- pass
-
- node.p2p.wait_for_disconnect(timeout=10)
- node.disconnect_p2ps()
- node.add_p2p_connection(P2PDataStore())
-
- # Node is still up.
- conn = node.add_p2p_connection(P2PDataStore())
+ self.test_resource_exhaustion()
def test_magic_bytes(self):
conn = self.nodes[0].add_p2p_connection(P2PDataStore())
@@ -212,16 +111,9 @@
def test_size(self):
conn = self.nodes[0].add_p2p_connection(P2PDataStore())
with self.nodes[0].assert_debug_log(['']):
- msg = conn.build_message(msg_unrecognized(str_data="d"))
- cut_len = (
- # magic
- 4 +
- # command
- 12
- )
- # modify len to MAX_SIZE + 1
- msg = msg[:cut_len] + \
- struct.pack("<I", 0x02000000 + 1) + msg[cut_len + 4:]
+ # Create a message with oversized payload
+ msg = msg_unrecognized(str_data="d" * (VALID_DATA_LIMIT + 1))
+ msg = conn.build_message(msg)
self.nodes[0].p2p.send_raw_message(msg)
conn.wait_for_disconnect(timeout=1)
self.nodes[0].disconnect_p2ps()
@@ -374,25 +266,30 @@
conn.send_and_ping(msg)
self.nodes[0].disconnect_p2ps()
- def _tweak_msg_data_size(self, message, wrong_size):
- """
- Return a raw message based on another message but with an incorrect data size in
- the message header.
- """
- raw_msg = self.node.p2p.build_message(message)
+ def test_resource_exhaustion(self):
+ conn = self.nodes[0].add_p2p_connection(P2PDataStore())
+ conn2 = self.nodes[0].add_p2p_connection(P2PDataStore())
+ msg_at_size = msg_unrecognized(str_data="b" * VALID_DATA_LIMIT)
+ assert len(msg_at_size.serialize()) == MSG_LIMIT
+
+ self.log.info(
+ "Sending a bunch of large, junk messages to test memory exhaustion. May take a bit...")
- bad_size_bytes = struct.pack("<I", wrong_size)
- num_header_bytes_before_size = 4 + 12
+ # Run a bunch of times to test for memory exhaustion.
+ for _ in range(80):
+ conn.send_message(msg_at_size)
- # Replace the correct data size in the message with an incorrect one.
- raw_msg_with_wrong_size = (
- raw_msg[:num_header_bytes_before_size] +
- bad_size_bytes +
- raw_msg[(num_header_bytes_before_size + len(bad_size_bytes)):]
- )
- assert len(raw_msg) == len(raw_msg_with_wrong_size)
+ # Check that, even though the node is being hammered by nonsense from one
+ # connection, it can still service other peers in a timely way.
+ for _ in range(20):
+ conn2.sync_with_ping(timeout=2)
- return raw_msg_with_wrong_size
+ # Peer 1, despite being served up a bunch of nonsense, should still be
+ # connected.
+ self.log.info("Waiting for node to drop junk messages.")
+ conn.sync_with_ping(timeout=400)
+ assert conn.is_connected
+ self.nodes[0].disconnect_p2ps()
if __name__ == '__main__':

File Metadata

Mime Type
text/plain
Expires
Sat, Mar 1, 11:25 (5 h, 52 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
5187591
Default Alt Text
D9519.diff (7 KB)

Event Timeline