Skip to content

Semantic field malformation #178

Description

@morehouse

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions