Skip to content

*_to_*_bits_toaltstack boundary guards: LE under-push for num_bits <= 1; BE over-push for num_bits = 0 #3

@WindOctober

Description

@WindOctober

Summary

  • limb_to_le_bits_toaltstack pushes to altstack only when num_bits ≥ 2. Callers that pop num_bits items underflow for num_bits = 1.
  • limb_to_be_bits_toaltstack unconditionally pushes even when num_bits = 0, leaving an unexpected extra item on altstack.

Both violate the contract “push exactly num_bits bits to altstack,” breaking stack discipline at the edges.

Proposed Fix

LE:

pub fn limb_to_le_bits_toaltstack(num_bits: usize) -> Script {
    if num_bits >= 2 {
        script! {
            { limb_to_le_bits_common(num_bits) }
            for _ in 0..num_bits { OP_TOALTSTACK }
        }
    } else if num_bits == 1 {
        script! {
            OP_TOALTSTACK
        }
    } else {
        script! {}
    }
}

BE:

pub fn limb_to_be_bits_toaltstack(num_bits: usize) -> Script {
    if num_bits >= 2 {
        script! {
            { limb_to_be_bits_common(num_bits) }
            OP_TOALTSTACK
            OP_TOALTSTACK
        }
    } else if num_bits == 1 {
        script! {
            OP_TOALTSTACK
        }
    } else {
        script! {}
    }
}

Acknowledgements: This issue was identified using Pomelo (https://eprint.iacr.org/2024/1768) by the UCSB/Nubit team. We also acknowledge BitVM#360 for first flagging potential problems in the limb_to_le_bits* family.

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