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

ZlibDecoder accepts invalid content and does not error out #258

Open
robisonsantos opened this issue Dec 15, 2020 · 5 comments
Open

ZlibDecoder accepts invalid content and does not error out #258

robisonsantos opened this issue Dec 15, 2020 · 5 comments

Comments

@robisonsantos
Copy link

I am writing an application that needs to read a zlib stream of bytes and it should only succeed once the whole stream of valid bytes is received. Every time a new byte is read from the stream, I append to a vector of bytes an call something like this:

        let mut z = ZlibDecoder::new(&bytes[..]);
        let mut s = String::new();
        z.read_to_string(&mut s)?;

I was expecting the read_to_string call to keep failing until the whole valid stream is received, but ZlibDecoder seems to always succeed when the bytes are in some valid range.

Example:

    #[test]
    pub fn test_invalid_input() {
        let bytes:Vec<u8> = vec![77];
        let mut z = ZlibDecoder::new(&bytes[..]);
        let mut s = String::new();
        let result = z.read_to_string(&mut s);
        assert!(result.is_err());
    }
    #[test]
    pub fn test_invalid_input() {
        let bytes:Vec<u8> = vec![1,1,1,1];
        let mut z = ZlibDecoder::new(&bytes[..]);
        let mut s = String::new();
        let result = z.read_to_string(&mut s);
        assert!(result.is_err());
    }

The first test fails, while the second succeed. I was expecting both tests to fail since both inputs are not valid zlib data.

@alexcrichton
Copy link
Member

Can you clarify where ZlibDecoder is coming from and how you're depending on this crate? I added those tests to this repository and both passed.

@robisonsantos
Copy link
Author

I am using

[dependencies]
flate2 = { version = "1.0.17", features = ["zlib"], default-features = false }

@robisonsantos
Copy link
Author

Sorry, GitHub on mobile is sh**

I am using this

[dependencies]
flate2 = { version = "1.0.17", features = ["zlib"], default-features = false }

As described in the docs.

@alexcrichton
Copy link
Member

Ah ok it looks like this is related to the zlib feature, the miniz feature or the rust_backend port to Rust doesn't fail these tests.

I believe this patch at least fixes the read half, but the write half is more complicated which I'd have to dig in more to fix. I'm also not entirely sure if this is the right fix, I never was certain about handling the "buf error" status.

diff --git a/src/zio.rs b/src/zio.rs
index 5028188..4a04565 100644
--- a/src/zio.rs
+++ b/src/zio.rs
@@ -144,7 +144,29 @@ where
             // return that 0 bytes of data have been read then it will
             // be interpreted as EOF.
             Ok(Status::Ok) | Ok(Status::BufError) if read == 0 && !eof && dst.len() > 0 => continue,
-            Ok(Status::Ok) | Ok(Status::BufError) | Ok(Status::StreamEnd) => return Ok(read),
+
+            // Otherwise if we got OK or we're at the stream end, then report
+            // how much was read.
+            Ok(Status::Ok) | Ok(Status::StreamEnd) => return Ok(read),
+
+            // If a buffer error is flagged then this could be either normal or
+            // abnormal. If any data was read it simply means we need some more
+            // space to make progress, so report that data was read so the
+            // application can free up some space.
+            //
+            // If nothing was read, however, and we'll never be getting any
+            // more data into the input buffer, then that means this is a
+            // corrupst stream.
+            Ok(Status::BufError) => {
+                return if read == 0 && eof && dst.len() > 0 {
+                    Err(io::Error::new(
+                        io::ErrorKind::InvalidInput,
+                        "corrupt deflate stream",
+                    ))
+                } else {
+                    Ok(read)
+                };
+            }
 
             Err(..) => {
                 return Err(io::Error::new(
diff --git a/src/zlib/mod.rs b/src/zlib/mod.rs
index 1813a4e..83dc203 100644
--- a/src/zlib/mod.rs
+++ b/src/zlib/mod.rs
@@ -156,4 +156,20 @@ mod tests {
             v == w.finish().unwrap().finish().unwrap()
         }
     }
+
+    #[test]
+    fn invalid_is_invalid() {
+        let mut s = Vec::new();
+        let result = read::ZlibDecoder::new(&[77][..]).read_to_end(&mut s);
+        assert!(result.is_err());
+
+        let mut s = Vec::new();
+        let result = read::ZlibDecoder::new(&[1, 1, 1][..]).read_to_end(&mut s);
+        assert!(result.is_err());
+
+        let mut z = write::ZlibDecoder::new(Vec::new());
+        assert!(z.write_all(&[77]).is_err() || z.finish().is_err());
+        let mut z = write::ZlibDecoder::new(Vec::new());
+        assert!(z.write_all(&[1, 1, 1]).is_err() || z.finish().is_err());
+    }
 }

@robisonsantos
Copy link
Author

robisonsantos commented Dec 16, 2020 via email

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants