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

read_zero_byte_vec emitted when read is in the next block #9274

Closed
unrealhoang opened this issue Aug 1, 2022 · 15 comments · Fixed by #11766
Closed

read_zero_byte_vec emitted when read is in the next block #9274

unrealhoang opened this issue Aug 1, 2022 · 15 comments · Fixed by #11766
Assignees
Labels
C-bug Category: Clippy is not doing the correct thing I-false-positive Issue: The lint was triggered on code it shouldn't have

Comments

@unrealhoang
Copy link

unrealhoang commented Aug 1, 2022

Summary

As the heuristic is checking if the next statement of an empty vec includes read(&mut v), if the next statement/expression is a block with resize before read, the lint is still fired.

Lint Name

read_zero_byte_vec

Reproducer

I tried this code:

    let mut v = Vec::new();
    {
        v.resize(10, 0);
        r.read(&mut v).unwrap();
    }

I saw this happen:

error: reading zero byte data to `Vec`
   --> src/main.rs:10:5
    |
130 | /     {
131 | |         v.resize(10, 0);
132 | |         r.read(&mut v).unwrap();
133 | |     }
    | |_____^
    |
    = note: `#[deny(clippy::read_zero_byte_vec)]` on by default
    = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#read_zero_byte_vec

I expected to not seeing this lint fired off.

Version

rustc 1.64.0-nightly (0f4bcadb4 2022-07-30)
binary: rustc
commit-hash: 0f4bcadb46006bc484dad85616b484f93879ca4e
commit-date: 2022-07-30
host: x86_64-unknown-linux-gnu
release: 1.64.0-nightly
LLVM version: 14.0.6

Additional Labels

No response

@unrealhoang unrealhoang added C-bug Category: Clippy is not doing the correct thing I-false-positive Issue: The lint was triggered on code it shouldn't have labels Aug 1, 2022
@tamaroning
Copy link
Contributor

tamaroning commented Aug 11, 2022

I added this lint. (#8964)
For now, this lint checks reads immidiately after creating Vecs and dosen't catch other many cases.
For example, the code below doesn't trigger the lint.

let mut data = Vec::with_capacity(100);
();
f.read(&mut data).unwrap();

Linting all cases by just using AST is tough so I made a compromise.

nicholasbishop added a commit to nicholasbishop/writedisk that referenced this issue Aug 14, 2022
This lint is triggering incorrectly, maybe due to:
rust-lang/rust-clippy#9274
nicholasbishop added a commit to nicholasbishop/writedisk that referenced this issue Aug 14, 2022
This lint is triggering incorrectly, maybe due to:
rust-lang/rust-clippy#9274
@crepererum
Copy link

I've also encountered this and found it quite confusing under which conditions the lint is triggered:

// triggers read_zero_byte_vec
pub fn f1<T: std::io::Read>(x: &mut T) {
    let mut to_read = 100;
    let mut buf = vec![];

    while to_read > 0 {
        let step = to_read.min(10);
        buf.resize(step, 0);
        x.read_exact(&mut buf).unwrap();
        to_read -= step;
    }
}

// does NOT trigger read_zero_byte_vec
pub fn f2<T: std::io::Read>(x: &mut T) {
    let mut buf = vec![];
    let mut to_read = 100;

    while to_read > 0 {
        let step = to_read.min(10);
        buf.resize(step, 0);
        x.read_exact(&mut buf).unwrap();
        to_read -= step;
    }
}

// does NOT trigger read_zero_byte_vec
pub fn f3<T: std::io::Read>(x: &mut T) {
    let mut to_read = 100;

    while to_read > 0 {
        let mut buf = vec![];
        let step = to_read.min(10);
        buf.resize(step, 0);
        x.read_exact(&mut buf).unwrap();
        to_read -= step;
    }
}

@wildbook
Copy link

wildbook commented Oct 5, 2022

I just discovered this lint through false positives when running clippy against older code.

All of my so far hits can be simplified down to variants of this:

let mut data = vec![];
match () {
    () => {
        data.resize(10, 0);
        input.read_exact(&mut data)?;
    },
}

It'd be nice if there was a mention of the false positives or restrictions in the lint index, similar to most other lints with known failure cases. I'm also not sure that a lint with so easy-to-trigger false positives should be deny by default, but perhaps I'm just unlucky with it.

@joshtriplett
Copy link
Member

It's fine if this has false negatives, but it needs to not have false positives. This shouldn't be checking "the next statement" in a way that includes the whole next block.

@joshtriplett
Copy link
Member

I ran into this with code that reduces to the following:

pub fn func(mut stream: impl std::io::Read) {
    let mut size_buf = [0u8; 4];
    let mut buf = Vec::new();
    loop {
        stream.read_exact(&mut size_buf).unwrap();
        let size = u32::from_le_bytes(size_buf) as usize;
        buf.resize(size, 0);
        stream.read_exact(&mut buf).unwrap();
    }
}

This is literally reading the size and resizing the buffer to the appropriate size before reading.

Mark-Simulacrum added a commit to Mark-Simulacrum/rust that referenced this issue Nov 2, 2022
This avoids reporting false-positives; see rust-lang/rust-clippy#9274
for details.
bors added a commit to rust-lang-ci/rust that referenced this issue Nov 2, 2022
[stable] Lower lint level for READ_ZERO_BYTE_VEC

This avoids reporting false-positives; see rust-lang/rust-clippy#9274 for details.

cc `@rust-lang/clippy` -- do we want a direct change landed on stable here? If so, please r  this PR, otherwise we can just close it. Would appreciate confirmation this is the right change to make as well.

cc `@joshtriplett` -- filing due to https://rust-lang.zulipchat.com/#narrow/stream/301329-t-devtools/topic/clippy.20false.20positive
@joshtriplett
Copy link
Member

I'm still encountering this. I'd love to see this lint improved to have better dataflow handling.

@Stargateur
Copy link

Stargateur commented Apr 13, 2023

a lint that bug shouldn't be in deny by default, warn at best.

@joshtriplett
Copy link
Member

Yeah, if this can't be easily fixed (and I'm hoping it can be), it should be disabled due to false positives.

@nyurik
Copy link
Contributor

nyurik commented Aug 9, 2023

This example also generated this false-positive, and allowing it for any specific line did not work - I had to wrap it in a block:

#[allow(clippy::read_zero_byte_vec)]
{
  let mut buf = Vec::with_capacity(2048);
  for i in 0..10 {
      buf.resize(100, 0);
      reader.read_exact(&mut buf)?;
  }
}

@iczero
Copy link

iczero commented Aug 24, 2023

Hello, I've encountered a false positive with the following code:

let file = File::open(args.input_file)?;
let mut reader = BufReader::new(file);
let mut pkt_buf = Vec::new();
loop {
    let mut len_buf = [0u8; 4];
    reader.read_exact(&mut len_buf)?;
    let len = parse_pkt_line_length(&len_buf);
    println!("{}", len);
    if len <= 4 {
        continue;
    }
    pkt_buf.resize((len - 4) as usize, 0);
    reader.read_exact(&mut pkt_buf)?;
    dump_as_readable_ascii(&pkt_buf, true);
}

Swapping these two lines fixes it:

let mut pkt_buf = Vec::new();
let mut reader = BufReader::new(file);

@Ralith
Copy link

Ralith commented Oct 23, 2023

This is still happening:

    loop {
        let mut len = [0; 2];
        stream.read_exact(&mut len).await?;
        let len = u16::from_le_bytes(len);
        buf.resize(len.into(), 0);
        stream.read_exact(&mut buf).await?;
        // ...
    }

Can we just disable this broken lint until it's fixed?

@dswij
Copy link
Member

dswij commented Oct 27, 2023

@rustbot claim

bors added a commit that referenced this issue Oct 29, 2023
move `read_zero_byte_vec` to nursery

I think the concerns in #9274 are valid, and we should move this to nursery while we're reworking this.

changelog: [`read_zero_byte_vec`] moved to nursery
@pac-work
Copy link

We have encountered this bug yesterday with clippy 0.1.72 (d5c2e9c 2023-09-13). The interesting thing is that it does not fire when initializing using construct similar to this: let mut buffer = vec![0; BUFFER_SIZE];. See full attached example:

use std::fs::File;
use std::io::Read;

/// This is a very simplified example of strange Clippy behavior we see. The code logic may not make
/// sense to you that much, real world code is much more complex, involves some serial lines,
/// waiting for data to be available there, reading different amounts of data in each loop, etc.
fn main() {
    const BUFFER_SIZE: usize = 100;
    let mut file = File::open("/tmp/test.txt").unwrap();
    // Depending on the vector initialization below, one does or does not get the error of "reading
    // zero byte data to `Vec`". One is more performant then the other, true, but neither of them
    // ever reads 0 bytes.
    let mut buffer = Vec::new();
    //let mut buffer = vec![0; BUFFER_SIZE];
    loop {
        buffer.resize(BUFFER_SIZE, 0);
        let bytes_read = file.read(&mut buffer).unwrap();
        buffer.truncate(bytes_read);
        println!("{:?}", buffer);
        if bytes_read == 0 || buffer[0] == b'0' {
            break;
        }
    }
}

@dswij
Copy link
Member

dswij commented Nov 16, 2023

@pac-work the lint only fires for Vec::with_capacity, Vec::default, or Vec::new. It won't fire with vec![] inits.

@bonsairobo
Copy link

I just hit this. I echo the sentiment of other comments: don't lint this by default until false positives are fixed.

bors added a commit that referenced this issue Jan 17, 2024
`read_zero_byte_vec` refactor for better heuristics

Fixes #9274

Previously, the implementation of `read_zero_byte_vec` only checks for the next statement after the vec init. This fails when there is a block with statements that are expanded and walked by the old visitor.

This PR refactors so that:

1. It checks if there is a `resize`	on the vec
2. It works on blocks properly

e.g. This should properly lint now:

```
    let mut v = Vec::new();
    {
        f.read(&mut v)?;
        //~^ ERROR: reading zero byte data to `Vec`
    }
```

changelog: [`read_zero_byte_vec`] Refactored for better heuristics
bors added a commit that referenced this issue Jan 17, 2024
`read_zero_byte_vec` refactor for better heuristics

Fixes #9274

Previously, the implementation of `read_zero_byte_vec` only checks for the next statement after the vec init. This fails when there is a block with statements that are expanded and walked by the old visitor.

This PR refactors so that:

1. It checks if there is a `resize`	on the vec
2. It works on blocks properly

e.g. This should properly lint now:

```
    let mut v = Vec::new();
    {
        f.read(&mut v)?;
        //~^ ERROR: reading zero byte data to `Vec`
    }
```

changelog: [`read_zero_byte_vec`] Refactored for better heuristics
@bors bors closed this as completed in e27ebf2 Jan 17, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
C-bug Category: Clippy is not doing the correct thing I-false-positive Issue: The lint was triggered on code it shouldn't have
Projects
None yet
Development

Successfully merging a pull request may close this issue.