Skip to content

Panic "attempt to negate with overflow" parsing SMPTE division byte 0x80 #34

Description

@hey-jj

Smf::parse panics on a 14-byte file whose division high byte is 0x80 when the build enables overflow-checks. A consumer that parses untrusted .mid files aborts or unwinds instead of getting an Err.

Version: midly 0.5.3, default features.

Reproducer

fn main() {
    let f: [u8; 14] = [b'M', b'T', b'h', b'd', 0, 0, 0, 6, 0, 0, 0, 0, 0x80, 0x00];
    let _ = midly::Smf::parse(&f);
}

Build with overflow-checks = true. The dev profile turns it on by default.

Observed

thread 'main' panicked at src/primitive.rs:495:
attempt to negate with overflow

Expected

An Err from Smf::parse. The SMF spec says a division word with bit 15 set encodes a negative frame rate in bits 8-14. 0x80 decodes to -128, which is not one of the four legal rates (-24, -25, -29, -30), so the parser must reject it. Turning overflow-checks off produces that rejection: the same bytes return an error whose Display is invalid midi: invalid midi header.

Root cause

src/primitive.rs:495, in Timing::read:

-(bit_range!(raw, 8..16) as i8)

For a division high byte of 0x80 this evaluates -(-128i8), which overflows i8. With overflow checks off the negation wraps back to -128 and parsing returns an error, which is why the two settings behave differently.

Scope

Any input that reaches the header division field with high byte exactly 0x80. I reproduced it through Smf::parse, midly::parse, and SmfBytemap::parse. The overflow-checks setting, not the profile name, decides between the panic and the error.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions