-
Notifications
You must be signed in to change notification settings - Fork 945
fuzz-tests: Add a test for fundee_channel()
#8411
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
Changelog-None: `fundee_channel()` in `openingd/openingd.c` is responsible for handling incoming `open_channel` messages from a peer. Since it deals with external input, add a test for it.
Add a minimal input set as a seed corpus for the newly introduced test. This leads to discovery of interesting code paths faster.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the crash is probably happening because the initial state
we use may have certain developer flags enabled. We should make sure we're using flags that would be used in production.
@@ -72,5 +72,17 @@ FUZZ_COMMON_OBJS := \ | |||
$(FUZZ_TARGETS_OBJS): $(COMMON_HEADERS) $(WIRE_HEADERS) $(COMMON_SRC) | |||
$(FUZZ_TARGETS_BIN): $(LIBFUZZ_OBJS) $(FUZZ_COMMON_OBJS) $(BITCOIN_OBJS) | |||
|
|||
tests/fuzz/fuzz-open_channel: common/shutdown_scriptpubkey.o \ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: let's put this rule up by the other target-specific ones
{ | ||
struct state *state = talz(ctx, struct state); | ||
|
||
state->developer = fromwire_bool(cursor, max); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should always set developer = false
, since we're only interested in bugs that happen in production.
= state->upfront_shutdown_script[REMOTE] | ||
= NULL; | ||
|
||
fromwire_bitcoin_outpoint(cursor, max, &state->funding); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are we setting the funding outpoint here? Shouldn't this be set later by the funding_create
message?
&state->minimum_depth, | ||
&state->min_feerate, &state->max_feerate, | ||
&state->dev_force_tmp_channel_id, | ||
&state->allowdustreserve, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is probably one immediate cause of the crash -- this lets the fuzzer set allowdustreserve = true
, which bypasses the checks needed to sanitize dust limits and channel reserves.
allowdustreserve
is a developer option that we should probably always set to false for this test.
u8 remaining_len = fromwire_u8(cursor, max); | ||
if (remaining_len > *max) | ||
remaining_len = *max; | ||
towire_u8_array(&openingd_init_msg, *cursor, remaining_len); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we need to be more careful than to just populate state
with random values. At the very least there are several developer options we should probably disable. And there might be other constraints we need to satisfy.
} | ||
|
||
static u8 *create_open_channel_msg(const tal_t *ctx, const u8 **cursor, size_t *max, struct state *state) | ||
{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think we should be doing any of the validation in this function. The open_channel
message comes from the peer, which means it could contain anything. We should basically use raw fuzzer-generated data for the message.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right, that's what I did initially but that approach took too long for the fuzzer to reach deeper code paths in the target function. By doing it this way, the fuzzer can achieve new coverage quicker while still retaining the ability to tweak the message.
/* These have the same definitions as the original peer_reed and peer_write. | ||
* We reiterate these here because we want them to use test_sync_write and | ||
* test_sync_read. | ||
*/ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since we're overriding peer_read
and peer_write
, we can just call test_sync_read
and test_sync_write
directly here for clarity.
struct amount_sat reserve = amount_sat(100); | ||
u32 mindepth = 10; | ||
return towire_openingd_got_offer_reply(ctx, NULL, NULL, NULL, | ||
&reserve, mindepth); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we should just use reserve=NULL for now, which will use the default of 1% of the channel capacity.
assert(false && "Too many HSMD writes!"); | ||
} | ||
else if (fd == PEER_FD) | ||
{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks like we could also get in the case from negotiation_failed
, and in that scenario there wouldn't be an accept_channel
message to send.
return towire_funding_created(ctx, &state->channel_id, | ||
&state->funding.txid, state->funding.n, &sig.s); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
funding_created
is a message sent by the peer and could contain anything. Ideally we would use fuzzer-generated data for this message, so we can check if the peer can crash us from this message.
In fact it looks like the peer can send other non-funding-created
messages to fundee_channel
as well, which we should also try here.
Of course to fuzz deeper in the state machine we also want valid funding_created
messages to be created sometimes. So there's an opportunity for some optimization here.
fundee_channel()
inopeningd/openingd.c
is responsible for handling incomingopen_channel
messages from a peer. Since it deals with external input, add a test for it.Checklist
Before submitting the PR, ensure the following tasks are completed. If an item is not applicable to your PR, please mark it as checked: