Problem
Currently there are several message fields that the IR can only produce (mostly) valid values for:
| Message |
Field |
Why it's always valid |
funding_created |
funding_txid |
derived from the constructed funding outpoint |
funding_created |
funding_output_index |
derived from the constructed funding outpoint (always 0) |
funding_created |
signature |
computed |
channel_announcement |
all 4 signatures |
computed |
node_announcement |
signature |
computed |
announcement_signatures |
both signatures |
computed |
For these fields, smite is never able to produce invalid values such as all zeroes, all 0xff bytes, or random data.
This currently prevents smite from finding bugs like the CLN funding_txid==0 assertion crash.
Solution
We can add the ability to selectively malform the bytes of messages before we send them to the target.
Design
New module smite-ir/src/malform.rs.
/// Overwrites one field of an encoded message with the given bytes.
pub struct Malformation {
/// Byte offset into the encoded message, including the 2-byte type prefix.
pub offset: u16,
/// Replacement bytes.
pub bytes: Vec<u8>,
}
impl Malformation {
/// Applies the edit in place. Returns `true` if the bytes changed.
pub fn apply(&self, msg: &mut Vec<u8>) -> bool;
}
The relevant Send* operations (i.e. SendFundingCreated, SendMessage) get a new Option<Malformation> parameter, and the executor applies the malformation if present before sending the corresponding message.
Choosing Malformations
Malformations are limited to the fields listed above since other fields can already be freely modified by mutators. In theory, generators could apply malformations themselves, but in general I think it's better to leave it up to OperationParamMutator.
OperationParamMutator can randomly pick an allowlisted field for the current message type and then generate a sequence of bytes to replace that field with -- either random bytes or a repeated byte biased toward the existing INTERESTING_U8 values. Smaller field replacements could also draw directly from the INTERESTING_U* lists (encoding them in big endian).
Handling Oracles
Currently the executor maintains various oracle state to validate target responses against. If we malform the messages we send to the target, our oracle state can drift from the target's state and lead to false positive Violations.
I think in general the right solution is to update our oracle state based on the messages we actually send rather than the messages we originally constructed. And as long as the malformed messages are still decodable, we should be able to extract the needed values to do this. If the messages do not decode, or if a round trip produces a different byte sequence, then we can set a state_desynced flag in the executor that tells it to make all future Violations log-only and non-fatal. Crashes and hangs remain detectable.
Problem
Currently there are several message fields that the IR can only produce (mostly) valid values for:
funding_createdfunding_txidfunding_createdfunding_output_indexfunding_createdsignaturechannel_announcementnode_announcementsignatureannouncement_signaturesFor these fields, smite is never able to produce invalid values such as all zeroes, all
0xffbytes, or random data.This currently prevents smite from finding bugs like the CLN
funding_txid==0assertion crash.Solution
We can add the ability to selectively malform the bytes of messages before we send them to the target.
Design
New module
smite-ir/src/malform.rs.The relevant
Send*operations (i.e.SendFundingCreated,SendMessage) get a newOption<Malformation>parameter, and the executor applies the malformation if present before sending the corresponding message.Choosing Malformations
Malformations are limited to the fields listed above since other fields can already be freely modified by mutators. In theory, generators could apply malformations themselves, but in general I think it's better to leave it up to
OperationParamMutator.OperationParamMutatorcan randomly pick an allowlisted field for the current message type and then generate a sequence of bytes to replace that field with -- either random bytes or a repeated byte biased toward the existingINTERESTING_U8values. Smaller field replacements could also draw directly from theINTERESTING_U*lists (encoding them in big endian).Handling Oracles
Currently the executor maintains various oracle state to validate target responses against. If we malform the messages we send to the target, our oracle state can drift from the target's state and lead to false positive
Violations.I think in general the right solution is to update our oracle state based on the messages we actually send rather than the messages we originally constructed. And as long as the malformed messages are still decodable, we should be able to extract the needed values to do this. If the messages do not decode, or if a round trip produces a different byte sequence, then we can set a
state_desyncedflag in the executor that tells it to make all futureViolations log-only and non-fatal. Crashes and hangs remain detectable.