Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

FrameDecoder panics on malformed input #29

Closed
pawanjay176 opened this issue May 6, 2020 · 6 comments · Fixed by #30
Closed

FrameDecoder panics on malformed input #29

pawanjay176 opened this issue May 6, 2020 · 6 comments · Fixed by #30
Labels

Comments

@pawanjay176
Copy link
Contributor

Encountered a panic thread 'main' panicked at 'attempt to subtract with overflow', while decoding malformed input bytes.

use snap::read::FrameDecoder;
use std::io::Read;

fn main() {
    let data = [255, 6, 0, 0, 115, 78, 97, 80, 112, 89, 0, 0, 0, 0, 38, 1, 255, 0].as_ref();
    let mut reader = FrameDecoder::new(data);
    let mut decoded = Vec::new();
    reader.read_to_end(&mut decoded).unwrap();
}

The panic occurred here

let sn = len - 4;

The malformed input after providing the correct stream identifier chunk has 4 zero bytes which leads to len becoming 0 and hence panics when we try to do unsafe subtraction in line 191.

The stack trace:

thread 'main' panicked at 'attempt to subtract with overflow', /Users/pawan/.cargo/registry/src/github.heygears.com-1ecc6299db9ec823/snap-1.0.0/src/read.rs:191:30
stack backtrace:
   0: backtrace::backtrace::libunwind::trace
             at /Users/runner/.cargo/registry/src/github.heygears.com-1ecc6299db9ec823/backtrace-0.3.44/src/backtrace/libunwind.rs:86
   1: backtrace::backtrace::trace_unsynchronized
             at /Users/runner/.cargo/registry/src/github.heygears.com-1ecc6299db9ec823/backtrace-0.3.44/src/backtrace/mod.rs:66
   2: std::sys_common::backtrace::_print_fmt
             at src/libstd/sys_common/backtrace.rs:78
   3: <std::sys_common::backtrace::_print::DisplayBacktrace as core::fmt::Display>::fmt
             at src/libstd/sys_common/backtrace.rs:59
   4: core::fmt::write
             at src/libcore/fmt/mod.rs:1063
   5: std::io::Write::write_fmt
             at src/libstd/io/mod.rs:1426
   6: std::sys_common::backtrace::_print
             at src/libstd/sys_common/backtrace.rs:62
   7: std::sys_common::backtrace::print
             at src/libstd/sys_common/backtrace.rs:49
   8: std::panicking::default_hook::{{closure}}
             at src/libstd/panicking.rs:204
   9: std::panicking::default_hook
             at src/libstd/panicking.rs:224
  10: std::panicking::rust_panic_with_hook
             at src/libstd/panicking.rs:470
  11: rust_begin_unwind
             at src/libstd/panicking.rs:378
  12: core::panicking::panic_fmt
             at src/libcore/panicking.rs:85
  13: core::panicking::panic
             at src/libcore/panicking.rs:52
  14: <snap::read::FrameDecoder<R> as std::io::Read>::read
             at /Users/pawan/.cargo/registry/src/github.heygears.com-1ecc6299db9ec823/snap-1.0.0/src/read.rs:191
  15: std::io::read_to_end_with_reservation
             at /rustc/4fb7144ed159f94491249e86d5bbd033b5d60550/src/libstd/io/mod.rs:394
  16: std::io::read_to_end
             at /rustc/4fb7144ed159f94491249e86d5bbd033b5d60550/src/libstd/io/mod.rs:361
  17: std::io::Read::read_to_end
             at /rustc/4fb7144ed159f94491249e86d5bbd033b5d60550/src/libstd/io/mod.rs:659
  18: upgrade_test::main
             at src/main.rs:8
  19: std::rt::lang_start::{{closure}}
             at /rustc/4fb7144ed159f94491249e86d5bbd033b5d60550/src/libstd/rt.rs:67
  20: std::rt::lang_start_internal::{{closure}}
             at src/libstd/rt.rs:52
  21: std::panicking::try::do_call
             at src/libstd/panicking.rs:303
  22: __rust_maybe_catch_panic
             at src/libpanic_unwind/lib.rs:86
  23: std::panicking::try
             at src/libstd/panicking.rs:281
  24: std::panic::catch_unwind
             at src/libstd/panic.rs:394
  25: std::rt::lang_start_internal
             at src/libstd/rt.rs:51
  26: std::rt::lang_start
             at /rustc/4fb7144ed159f94491249e86d5bbd033b5d60550/src/libstd/rt.rs:67
  27: upgrade_test::main

I think we can fix this by doing checked subtraction and returning an error if there's an underflow.
Will be happy to raise a PR if you can confirm it's an issue :)

@BurntSushi
Copy link
Owner

Definitely looks like a bug. Also looks like the bug is here too:

let n = len - 4;

Either checked subtraction or just adding an explicit if len < 4 { return error(...) } sounds great to me! Thanks for catching this!

@BurntSushi BurntSushi added the bug label May 6, 2020
@pawanjay176
Copy link
Contributor Author

Sure, no worries.
I'm trying to think what Error variant would be appropriate for this case. The closest fit seems to be UnsupportedChunkLength

rust-snappy/src/error.rs

Lines 156 to 165 in 1951d5d

/// This error occurs when trying to read a chunk with length greater than
/// that supported by this library when reading a Snappy frame formatted
/// stream.
/// This error only occurs when reading a Snappy frame formatted stream.
UnsupportedChunkLength {
/// The length of the chunk encountered.
len: u64,
/// True when this error occured while reading the stream header.
header: bool,
},

but the docs for this variant says trying to read a chunk with length greater than that supported. which isn't right.

Maybe this requires a new error variant along the lines of InvalidChunkLength?

@BurntSushi
Copy link
Owner

Ug. I meant to make the enum non-exhaustive when I did the 1.0 release. So that means adding a new variant is a breaking change.

UnsupportedChunkLength looks fine. The name is right anyway. I'd just tweak the description to be a bit more generic, i.e., "a chunk with an unexpected or incorrect length."

@pawanjay176
Copy link
Contributor Author

Ug. I meant to make the enum non-exhaustive when I did the 1.0 release. So that means adding a new variant is a breaking change.

Sorry, didn't think of this.

I'd just tweak the description to be a bit more generic, i.e., "a chunk with an unexpected or incorrect length."

This sounds great. Will do this.

@flipchan
Copy link

Got the same panic today:

use snap::read;
use std::io::Read;

fn main() {
         let data: &[u8] = &[0x10, 0x10, 0x0, 0x0];
         let mut buf = vec![];
         read::FrameDecoder::new(data).read_to_end(&mut buf).unwrap();
}
# ./target/release/testing
thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: Custom
 { kind: Other, error: StreamHeader { byte: 16 } }', src/libcore/result.rs:11$
8:5
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace$

found it with the help of https://github.com/rust-fuzz/cargo-fuzz

@BurntSushi
Copy link
Owner

@flipchan That's not the same panic. And it's not even clear that it's a bug. You're calling unwrap on a fallible operation, which will panic if an error occurs. The only way your code sample is a bug is if the input is a valid Snappy compressed frame. (It doesn't look like it is.)

BurntSushi added a commit that referenced this issue Jul 6, 2020
Previously, the code was assuming that we could always subtract
4 from the length, but the length is untrusted data. If an invalid
length is detected, we return an error instead.

Closes #29
doitian added a commit to nervosnetwork/ckb that referenced this issue Aug 10, 2020
# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants