We found a bug in the hyper HTTP library

(blog.cloudflare.com)

28 points | by Pop_- 4 days ago

5 comments

  • edelbitter 58 minutes ago
    Cloudflare does not notice (until a customer complains) that they are sending broken responses at scale? I would have thought they would notice this from sampling and linting a few replies.. just in case they did something like Cloudbleed again.
  • 100ms 19 minutes ago
    [flagged]
  • giammbo 31 minutes ago
    [flagged]
  • nopurpose 22 minutes ago
    [dead]
  • logicchains 56 minutes ago
    How does terrible code like this survive so long in such a key piece of infrastructure:

            let _ = self.poll_read(cx)?;
            let _ = self.poll_write(cx)?;
            let _ = self.poll_flush(cx)?;
    
    Surely at the very least a linter should have flagged that the return values aren't handled.
    • fwlr 3 minutes ago

          let _ = …?
      This is the Rust idiom for “I am intentionally ignoring this return value”. The linter would have caught

          self.poll_read()?;
      
      and in fact one of the options the linter itself suggests in this case is exactly this “let underscore equals” idiom. (Arguably, this code exists because of the linter, not due to its absence!)

      In any case, the return value is being “handled” - the question mark examines the result and breaks the loop if the result is not `Ok(…)`, ie if the call is not successful.

      Intentionally ignoring the successful return value isn’t necessarily terrible, either - you could be calling the function for its side effect, and you don’t care what the specific result of that effect is, just as long as there is some effect. E.g. maybe you have a state machine, and this is the code that repeatedly drives it.

      (Not coincidentally, polling is what you do to Futures, and Futures are state machines that you need to repeatedly drive…)

      In conclusion, I do not think this is prima facie terrible code, nor is it an obvious bug. Async rust is subtle and complicated, and not always fully understood by those who nevertheless have to use it.

    • lifthrasiir 30 minutes ago
      It is an explicit way to discard return values; `self.poll_read(cx)?` etc. alone would warn. Or in this case, `Poll<Result<(), Error>>` is unwrapped once and `Result<(), Error>` is being discarded. The decision to discard `Result<(), Error>` should have been intentional, albeit turned out to be not always the case.
      • watt 2 minutes ago
        If they're not going to handle the return values, they should change the function signature to reflect this aspirational contract, that that function "never fails".

        I see in the article they did change the poll_flush to run just-in-time at poll_shutdown. So they definitely can make a "best effort" poll_flush version that just does not return any errors for use in that loop.

        But all in all? Amateur hour.

    • QuantumNomad_ 28 minutes ago
      Assigning to _ in Rust specifically means that you intentionally want to discard the value, and the clippy linter and the Rust compiler both know that.
    • 3form 46 minutes ago
      Well, simple. You write a project without a linter, so as to be fast. Or on minimal settings. Then one day you turn it on and end up with 10k warnings at max checks. You ignore them and tune it down to a bearable level. Problem solved.

      That, or the linter sucks (or the idiom is too popular to be linted against, so then it's more of a language issue).