Skip to content

Commit

Permalink
Merge pull request #58 from hax0kartik/more-harnesses
Browse files Browse the repository at this point in the history
Add targets for RawNetworkMessage and BlockTransactionRequests
  • Loading branch information
brunoerg authored Jul 18, 2024
2 parents 632860d + e382d08 commit 020ac4b
Show file tree
Hide file tree
Showing 8 changed files with 271 additions and 2 deletions.
14 changes: 14 additions & 0 deletions btcd_lib/wrapper.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,20 @@ func go_btcd_addrv2(cinput *C.uchar, datalen C.int, count *C.ulonglong) bool {
return err == nil
}

//export go_btcd_rawmessage
func go_btcd_rawmessage(cinput *C.uchar, datalen C.int) C.int {
data := C.GoBytes(unsafe.Pointer(cinput), datalen)
r := bytes.NewReader(data)

_, _, err := wire.ReadMessage(r, wire.ProtocolVersion, wire.MainNet)

if err == nil {
return 0
}

return -1
}

func main() {

}
6 changes: 6 additions & 0 deletions patches/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
This directory contains the patches that need to be applied to make certain targets work.

You can use the following commands to apply the patches:
```bash
cd .. && git apply patches/*
```
52 changes: 52 additions & 0 deletions patches/btcd_raw_network_message.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
diff --git a/dependencies/btcd/wire/message.go b/dependencies/btcd/wire/message.go
index 1f412fa6..5043b607 100644
--- a/dependencies/btcd/wire/message.go
+++ b/dependencies/btcd/wire/message.go
@@ -398,14 +398,16 @@ func ReadMessageWithEncodingN(r io.Reader, pver uint32, btcnet BitcoinNet,
// Check for maximum length based on the message type as a malicious client
// could otherwise create a well-formed header and set the length to max
// numbers in order to exhaust the machine's memory.
- mpl := msg.MaxPayloadLength(pver)
- if hdr.length > mpl {
- discardInput(r, hdr.length)
- str := fmt.Sprintf("payload exceeds max length - header "+
- "indicates %v bytes, but max payload size for "+
- "messages of type [%v] is %v.", hdr.length, command, mpl)
- return totalBytes, nil, nil, messageError("ReadMessage", str)
- }
+ /*
+ mpl := msg.MaxPayloadLength(pver)
+ if hdr.length > mpl {
+ discardInput(r, hdr.length)
+ str := fmt.Sprintf("payload exceeds max length - header "+
+ "indicates %v bytes, but max payload size for "+
+ "messages of type [%v] is %v.", hdr.length, command, mpl)
+ return totalBytes, nil, nil, messageError("ReadMessage", str)
+ }
+ */

// Read payload.
payload := make([]byte, hdr.length)
@@ -416,13 +418,15 @@ func ReadMessageWithEncodingN(r io.Reader, pver uint32, btcnet BitcoinNet,
}

// Test checksum.
- checksum := chainhash.DoubleHashB(payload)[0:4]
- if !bytes.Equal(checksum, hdr.checksum[:]) {
- str := fmt.Sprintf("payload checksum failed - header "+
- "indicates %v, but actual checksum is %v.",
- hdr.checksum, checksum)
- return totalBytes, nil, nil, messageError("ReadMessage", str)
- }
+ /*
+ checksum := chainhash.DoubleHashB(payload)[0:4]
+ if !bytes.Equal(checksum, hdr.checksum[:]) {
+ str := fmt.Sprintf("payload checksum failed - header "+
+ "indicates %v, but actual checksum is %v.",
+ hdr.checksum, checksum)
+ return totalBytes, nil, nil, messageError("ReadMessage", str)
+ }
+ */

// Unmarshal message. NOTE: This must be a *bytes.Buffer since the
// MsgVersion BtcDecode function requires it.
14 changes: 14 additions & 0 deletions patches/rust-bitcoin_raw_network_message.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
Submodule dependencies/rust-bitcoin contains modified content
diff --git a/dependencies/rust-bitcoin/bitcoin/src/consensus/encode.rs b/dependencies/rust-bitcoin/bitcoin/src/consensus/encode.rs
index 63b0cc87..c594dabc 100644
--- a/dependencies/rust-bitcoin/bitcoin/src/consensus/encode.rs
+++ b/dependencies/rust-bitcoin/bitcoin/src/consensus/encode.rs
@@ -793,7 +793,7 @@ impl Decodable for CheckedData {
let opts = ReadBytesFromFiniteReaderOpts { len, chunk_size: MAX_VEC_SIZE };
let data = read_bytes_from_finite_reader(r, opts)?;
let expected_checksum = sha2_checksum(&data);
- if expected_checksum != checksum {
+ if false && expected_checksum != checksum {
Err(self::Error::InvalidChecksum { expected: expected_checksum, actual: checksum })
} else {
Ok(CheckedData { data, checksum })
3 changes: 2 additions & 1 deletion rust_bitcoin_lib/rust_bitcoin_lib.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ extern char* rust_miniscript_from_str_check_key(const char* miniscript_str);
extern char* rust_bitcoin_des_block(const uint8_t *data, size_t len);
extern char* rust_bitcoin_prefilledtransaction(const uint8_t *data, size_t len);
extern bool rust_bitcoin_addrv2(uint8_t *data, size_t len, uint64_t *count);
extern char* rust_bitcoin_cmpctblocks(uint8_t *data, size_t len);
extern char* rust_bitcoin_cmpctblocks(uint8_t *data, size_t len);
extern int rust_bitcoin_blocktransactionrequests(const uint8_t *data, size_t len);
36 changes: 35 additions & 1 deletion rust_bitcoin_lib/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ use std::ffi::{CStr, CString};
use std::str::{FromStr, Utf8Error};

use bitcoin::Block;
use bitcoin::bip152::{PrefilledTransaction,HeaderAndShortIds};
use bitcoin::p2p::message::RawNetworkMessage;
use bitcoin::bip152::{PrefilledTransaction,HeaderAndShortIds,BlockTransactionsRequest};
use bitcoin::consensus::{deserialize_partial, encode};

use miniscript::{Miniscript, Segwitv0, Tap};
Expand Down Expand Up @@ -158,6 +159,21 @@ pub unsafe extern "C" fn rust_bitcoin_headerandshortids(data: *const u8, len: us
}
}

#[no_mangle]
pub unsafe extern "C" fn rust_bitcoin_blocktransactionrequests(data: *const u8, len: usize) -> i32 {
// Safety: Ensure that the data pointer is valid for the given length
let data_slice = slice::from_raw_parts(data, len);

let res = deserialize_partial::<BlockTransactionsRequest>(data_slice);

match res {
Ok(block) => return block.0.indexes.len().try_into().unwrap(),
Err(_) => {
return -1;
}
}
}

#[no_mangle]
pub unsafe extern "C" fn rust_bitcoin_cmpctblocks(data: *const u8, len: usize) -> i32 {
// Safety: Ensure that the data pointer is valid for the given length
Expand All @@ -177,6 +193,24 @@ pub unsafe extern "C" fn rust_bitcoin_cmpctblocks(data: *const u8, len: usize) -
}
}

#[no_mangle]
pub unsafe extern "C" fn rust_bitcoin_rawmessage(data: *const u8, len: usize) -> i32 {
// Safety: Ensure that the data pointer is valid for the given length
let data_slice = slice::from_raw_parts(data, len);

let res = deserialize_partial::<RawNetworkMessage>(data_slice);

match res {
Ok(_) => 0,
Err(err) => {
if err.to_string().starts_with("unsupported segwit version") {
return -2;
}
return -1;
}
}
}

#[no_mangle]
pub unsafe extern "C" fn rust_miniscript_from_str_check_key(input: *const c_char) -> *mut c_char {
let Ok(desc) = c_str_to_str(input) else {
Expand Down
36 changes: 36 additions & 0 deletions targets/blocktransactionrequest.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#include <fuzzer/FuzzedDataProvider.h>
#include <string>
#include <iostream>

#include "bitcoin/src/test/fuzz/fuzz.h"
#include "bitcoin/src/blockencodings.h"
#include "bitcoin/src/streams.h"

extern "C" int rust_bitcoin_blocktransactionrequests(const uint8_t *data, size_t len);

int BlockTransactionRequestCore(Span<const uint8_t> buffer)
{
DataStream ds{buffer};
BlockTransactionsRequest request;
int res = 0;
try {
ds >> request;
res = request.indexes.size();
} catch (const std::ios_base::failure& e) {
if (std::string(e.what()).find("ReadCompactSize(): size too large") != std::string::npos)
return -2;
return -1;
}
return res;
}

FUZZ_TARGET(block_transaction_request)
{
int core{BlockTransactionRequestCore(buffer)};
int rust_bitcoin{rust_bitcoin_blocktransactionrequests(buffer.data(), buffer.size())};

if (core == -2)
return;

assert(core == rust_bitcoin);
}
112 changes: 112 additions & 0 deletions targets/raw_message_des.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
#include <fuzzer/FuzzedDataProvider.h>
#include <string_view>
#include <iostream>

#include "bitcoin/src/test/fuzz/fuzz.h"
#include "bitcoin/src/protocol.h"
#include "bitcoin/src/hash.h"
#include "bitcoin/src/net.h"

using namespace std::string_view_literals;

extern "C" int rust_bitcoin_rawmessage(const uint8_t *data, size_t len);
extern "C" int go_btcd_rawmessage(const uint8_t *data, size_t len);

/**
* This is a list of all the supported message types in btcd
* and we will only fuzz these message types.
* Note: This is missing message types related to compact blocks
* as compact blocks is not supported by btcd.
*/
static constexpr std::array LUT {
// "version"sv, // unstable because btcd supports older versions of the protocol while rust-bitcoin does not
"verack"sv,
"getaddr"sv,
"addr"sv,
// "addrv2"sv, // Fuzz using the addrv2 target
"getblocks"sv,
"inv"sv,
"getdata"sv,
"notfound"sv,
// "block"sv, // Fuzz using the block_des target
// "tx"sv, // // Fuzz using the tx_des target
"getheaders"sv,
"headers"sv,
"ping"sv,
"pong"sv,
"alert"sv,
"mempool"sv,
"filteradd"sv,
"filterclear"sv,
"filterload"sv,
"merkleblock"sv,
"reject"sv,
"sendheaders"sv,
"feefilter"sv,
"getcfilters"sv,
"getcfheaders"sv,
"getcfcheckpt"sv,
"cfilter"sv,
"cfheaders"sv,
"cfcheckpt"sv,
"sendaddrv2"sv
};

namespace {
int8_t g_type {-1}; // -1 = All
} // namespace

void initializeAndPerformChecks()
{
if (const auto val{std::getenv("LIMIT_TO_MESSAGE_TYPE")}) {
for (int i = 0; i < LUT.size(); i++) {
if (LUT[i] == val) {
g_type = i;
}
}
}

/**
* Manual patches are needed to be applied to btcd and rust bitcoin
* to remove checking of checksum and the max payload size of a message.
* Refer to the patches/ directory for additional info.
* We'll check if these patches have been applied and fail incase they have not.
*/

// Check whether patches have been applied
CMessageHeader header({0xf9, 0xbe, 0xb4, 0xd9}, "verack", 4);
header.pchChecksum[0] = 0; // should pass with a checksum value of 0

DataStream ds;
ds << header;
ds << 0xdfdfdfdf;

auto btcd{go_btcd_rawmessage((uint8_t*)ds.data(), ds.size())};
auto rust_bitcoin{rust_bitcoin_rawmessage((uint8_t*)ds.data(), ds.size())};

assert(rust_bitcoin == 0 || ("rust-bitcoin has not been patched." == nullptr));
assert(btcd == 0 || ("btcd has not been patched." == nullptr));
}

FUZZ_TARGET(raw_message_des, .init = initializeAndPerformChecks)
{
FuzzedDataProvider provider(buffer.data(), buffer.size());

if (provider.remaining_bytes() < 2) return;

const auto msgtype = g_type != -1 ? LUT[g_type] : LUT[provider.ConsumeIntegral<uint8_t>() % LUT.size()];
CMessageHeader header({0xf9, 0xbe, 0xb4, 0xd9}, msgtype.data(), provider.remaining_bytes());

DataStream ds;
ds << header;
ds << provider.ConsumeRemainingBytes<uint8_t>();

if (ds.size() > MAX_PROTOCOL_MESSAGE_LENGTH) return; // ignore messages larger than the max protocol size (32 MB)

auto btcd{go_btcd_rawmessage((uint8_t*)ds.data(), ds.size())};
auto rust_bitcoin{rust_bitcoin_rawmessage((uint8_t*)ds.data(), ds.size())};

if (rust_bitcoin == -2) return;

assert(btcd == rust_bitcoin);
}

0 comments on commit 020ac4b

Please sign in to comment.