Description
Hi there, we (Rust group @sslab-gatech) are scanning crates on crates.io for potential soundness bugs. We noticed that DeflateOutput
's implementation of poll_read
does the following:
rust-libp2p/protocols/deflate/src/lib.rs
Lines 136 to 141 in 6400719
This sets up uninitialized bytes in read_interm
and then passes it to the user provided poll_read
method. This allows invoking undefined behavior from safe Rust code, reading uninitialized memory.
This issue is described a bit in the documentation for Read
:
It is your responsibility to make sure that
buf
is initialized before calling read. Calling read with an uninitializedbuf
(of the kind one obtains viaMaybeUninit<T>
) is not safe, and can lead to undefined behavior.
Here's an example that outputs uninitialized memory using this:
Click to expand
#![forbid(unsafe_code)]
use libp2p_core::upgrade::{InboundUpgrade, ProtocolName};
use libp2p_deflate::DeflateConfig;
use futures::executor;
use futures::io::{AsyncRead, AsyncReadExt, AsyncWrite, Error};
use futures::task::{Context, Poll};
use std::pin::Pin;
struct BrokenReader();
impl AsyncRead for BrokenReader {
fn poll_read(
self: Pin<&mut Self>,
cx: &mut Context,
buf: &mut [u8],
) -> Poll<Result<usize, Error>> {
// Dump out uninitialized memory
println!("{:?}", buf);
assert!(buf[0] == 0);
return Poll::Ready(Ok(buf.len()));
}
}
impl AsyncWrite for BrokenReader {
fn poll_write(
self: Pin<&mut Self>,
cx: &mut Context,
buf: &[u8],
) -> Poll<Result<usize, Error>> {
return Poll::Ready(Ok(buf.len()));
}
fn poll_flush(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Result<(), Error>> {
return Poll::Ready(Ok(()));
}
fn poll_close(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Result<(), Error>> {
return Poll::Ready(Ok(()));
}
}
fn main() {
executor::block_on(async {
let broken_reader = BrokenReader();
let deflate_config = DeflateConfig::default();
let mut deflate_output = deflate_config
.upgrade_inbound(broken_reader, "/test/1".as_bytes())
.await
.unwrap();
let mut buf = [1u8; 256];
deflate_output.read_exact(&mut buf).await.unwrap();
});
}