diff --git a/src/net_processing.h b/src/net_processing.h
--- a/src/net_processing.h
+++ b/src/net_processing.h
@@ -128,4 +128,9 @@
 /** Relay transaction to every node */
 void RelayTransaction(const TxId &txid, const CConnman &connman);
 
+bool ProcessMessage(const Config &config, CNode *pfrom,
+                    const std::string &strCommand, CDataStream &vRecv,
+                    int64_t nTimeReceived, CConnman *connman, BanMan *banman,
+                    const std::atomic<bool> &interruptMsgProc);
+
 #endif // BITCOIN_NET_PROCESSING_H
diff --git a/src/net_processing.cpp b/src/net_processing.cpp
--- a/src/net_processing.cpp
+++ b/src/net_processing.cpp
@@ -2230,11 +2230,10 @@
     }
 }
 
-static bool ProcessMessage(const Config &config, CNode *pfrom,
-                           const std::string &strCommand, CDataStream &vRecv,
-                           int64_t nTimeReceived, CConnman *connman,
-                           BanMan *banman,
-                           const std::atomic<bool> &interruptMsgProc) {
+bool ProcessMessage(const Config &config, CNode *pfrom,
+                    const std::string &strCommand, CDataStream &vRecv,
+                    int64_t nTimeReceived, CConnman *connman, BanMan *banman,
+                    const std::atomic<bool> &interruptMsgProc) {
     const CChainParams &chainparams = config.GetChainParams();
     LogPrint(BCLog::NET, "received: %s (%u bytes) peer=%d\n",
              SanitizeString(strCommand), vRecv.size(), pfrom->GetId());
diff --git a/src/test/fuzz/CMakeLists.txt b/src/test/fuzz/CMakeLists.txt
--- a/src/test/fuzz/CMakeLists.txt
+++ b/src/test/fuzz/CMakeLists.txt
@@ -44,6 +44,21 @@
 	endforeach()
 endfunction()
 
+function(add_process_message_fuzz_targets)
+	foreach(_fuzz_test_name ${ARGN})
+		sanitize_target_name("fuzz-process_message_" ${_fuzz_test_name} _fuzz_target_name)
+		add_fuzz_target(
+			${_fuzz_target_name}
+			process_message_${_fuzz_test_name}
+
+			# Sources
+			process_message.cpp
+		)
+
+		target_compile_definitions(${_fuzz_target_name} PRIVATE MESSAGE_TYPE=${_fuzz_test_name})
+	endforeach()
+endfunction()
+
 add_regular_fuzz_targets(
 	addrdb
 	bloom_filter
@@ -53,6 +68,7 @@
 	eval_script
 	net_permissions
 	parse_iso8601
+	process_message
 	psbt
 	script
 	script_flags
@@ -101,3 +117,30 @@
 	txoutcompressor_deserialize
 	txundo_deserialize
 )
+
+add_process_message_fuzz_targets(
+	addr
+	block
+	blocktxn
+	cmpctblock
+	feefilter
+	filteradd
+	filterclear
+	filterload
+	getaddr
+	getblocks
+	getblocktxn
+	getdata
+	getheaders
+	headers
+	inv
+	mempool
+	notfound
+	ping
+	pong
+	sendcmpct
+	sendheaders
+	tx
+	verack
+	version
+)
diff --git a/src/test/fuzz/process_message.cpp b/src/test/fuzz/process_message.cpp
new file mode 100644
--- /dev/null
+++ b/src/test/fuzz/process_message.cpp
@@ -0,0 +1,129 @@
+// Copyright (c) 2020 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <banman.h>
+#include <chainparams.h>
+#include <config.h>
+#include <consensus/consensus.h>
+#include <net.h>
+#include <net_processing.h>
+#include <protocol.h>
+#include <scheduler.h>
+#include <script/script.h>
+#include <streams.h>
+#include <validationinterface.h>
+#include <version.h>
+
+#include <test/fuzz/FuzzedDataProvider.h>
+#include <test/fuzz/fuzz.h>
+#include <test/util/mining.h>
+#include <test/util/setup_common.h>
+
+#include <algorithm>
+#include <atomic>
+#include <cassert>
+#include <chrono>
+#include <cstdint>
+#include <iosfwd>
+#include <iostream>
+#include <map>
+#include <memory>
+#include <set>
+#include <string>
+#include <vector>
+
+namespace {
+
+#ifdef MESSAGE_TYPE
+#define TO_STRING_(s) #s
+#define TO_STRING(s) TO_STRING_(s)
+const std::string LIMIT_TO_MESSAGE_TYPE{TO_STRING(MESSAGE_TYPE)};
+#else
+const std::string LIMIT_TO_MESSAGE_TYPE;
+#endif
+
+const std::map<std::string, std::set<std::string>>
+    EXPECTED_DESERIALIZATION_EXCEPTIONS = {
+        {"CDataStream::read(): end of data: iostream error",
+         {"addr", "block", "blocktxn", "cmpctblock", "feefilter", "filteradd",
+          "filterload", "getblocks", "getblocktxn", "getdata", "getheaders",
+          "headers", "inv", "notfound", "ping", "sendcmpct", "tx"}},
+        {"CompactSize exceeds limit of type: iostream error", {"cmpctblock"}},
+        {"differential value overflow: iostream error", {"getblocktxn"}},
+        {"index overflowed 16 bits: iostream error", {"getblocktxn"}},
+        {"index overflowed 16-bits: iostream error", {"cmpctblock"}},
+        {"indexes overflowed 16 bits: iostream error", {"getblocktxn"}},
+        {"non-canonical ReadCompactSize(): iostream error",
+         {"addr", "block", "blocktxn", "cmpctblock", "filteradd", "filterload",
+          "getblocks", "getblocktxn", "getdata", "getheaders", "headers", "inv",
+          "notfound", "tx"}},
+        {"ReadCompactSize(): size too large: iostream error",
+         {"addr", "block", "blocktxn", "cmpctblock", "filteradd", "filterload",
+          "getblocks", "getblocktxn", "getdata", "getheaders", "headers", "inv",
+          "notfound", "tx"}},
+        {"Superfluous witness record: iostream error",
+         {"block", "blocktxn", "cmpctblock", "tx"}},
+        {"Unknown transaction optional data: iostream error",
+         {"block", "blocktxn", "cmpctblock", "tx"}},
+};
+
+const RegTestingSetup *g_setup;
+} // namespace
+
+void initialize() {
+    static RegTestingSetup setup{};
+    g_setup = &setup;
+
+    for (int i = 0; i < 2 * COINBASE_MATURITY; i++) {
+        MineBlock(GetConfig(), g_setup->m_node, CScript() << OP_TRUE);
+    }
+    SyncWithValidationInterfaceQueue();
+}
+
+void test_one_input(const std::vector<uint8_t> &buffer) {
+    const Config &config = GetConfig();
+    FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
+    const std::string random_message_type{
+        fuzzed_data_provider.ConsumeBytesAsString(CMessageHeader::COMMAND_SIZE)
+            .c_str()};
+    if (!LIMIT_TO_MESSAGE_TYPE.empty() &&
+        random_message_type != LIMIT_TO_MESSAGE_TYPE) {
+        return;
+    }
+    CDataStream random_bytes_data_stream{
+        fuzzed_data_provider.ConsumeRemainingBytes<uint8_t>(), SER_NETWORK,
+        PROTOCOL_VERSION};
+    CNode p2p_node{0,
+                   ServiceFlags(NODE_NETWORK | NODE_BLOOM),
+                   0,
+                   INVALID_SOCKET,
+                   CAddress{CService{in_addr{0x0100007f}, 7777}, NODE_NETWORK},
+                   0,
+                   0,
+                   CAddress{},
+                   std::string{},
+                   false};
+    p2p_node.fSuccessfullyConnected = true;
+    p2p_node.nVersion = PROTOCOL_VERSION;
+    p2p_node.SetSendVersion(PROTOCOL_VERSION);
+    g_setup->m_node.peer_logic->InitializeNode(config, &p2p_node);
+    try {
+        (void)ProcessMessage(
+            config, &p2p_node, random_message_type, random_bytes_data_stream,
+            GetTimeMillis(), g_setup->m_node.connman.get(),
+            g_setup->m_node.banman.get(), std::atomic<bool>{false});
+    } catch (const std::ios_base::failure &e) {
+        const std::string exception_message{e.what()};
+        const auto p =
+            EXPECTED_DESERIALIZATION_EXCEPTIONS.find(exception_message);
+        if (p == EXPECTED_DESERIALIZATION_EXCEPTIONS.cend() ||
+            p->second.count(random_message_type) == 0) {
+            std::cout << "Unexpected exception when processing message type \""
+                      << random_message_type << "\": " << exception_message
+                      << std::endl;
+            assert(false);
+        }
+    }
+    SyncWithValidationInterfaceQueue();
+}
diff --git a/src/test/util/setup_common.cpp b/src/test/util/setup_common.cpp
--- a/src/test/util/setup_common.cpp
+++ b/src/test/util/setup_common.cpp
@@ -15,6 +15,7 @@
 #include <logging.h>
 #include <miner.h>
 #include <net.h>
+#include <net_processing.h>
 #include <noui.h>
 #include <pow/pow.h>
 #include <rpc/blockchain.h>
@@ -85,7 +86,7 @@
 
 BasicTestingSetup::BasicTestingSetup(const std::string &chainName)
     : m_path_root{fs::temp_directory_path() / "test_common_" PACKAGE_NAME /
-                  std::to_string(g_insecure_rand_ctx_temp_path.rand32())} {
+                  g_insecure_rand_ctx_temp_path.rand256().ToString()} {
     SetMockTime(0);
     fs::create_directories(m_path_root);
     gArgs.ForceSetArg("-datadir", m_path_root.string());
@@ -176,6 +177,8 @@
                                  nullptr, DEFAULT_MISBEHAVING_BANTIME);
     // Deterministic randomness for tests.
     m_node.connman = std::make_unique<CConnman>(config, 0x1337, 0x1337);
+    m_node.peer_logic = std::make_unique<PeerLogicValidation>(
+        m_node.connman.get(), m_node.banman.get(), *m_node.scheduler);
 }
 
 TestingSetup::~TestingSetup() {