Skip to content
New issue

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

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

Already on GitHub? Sign in 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.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.com-1ecc6299db9ec823/backtrace-0.3.44/src/backtrace/libunwind.rs:86
   1: backtrace::backtrace::trace_unsynchronized
             at /Users/runner/.cargo/registry/src/github.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.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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants