Comment by tptacek

Comment by tptacek 10 days ago

7 replies

My point has nothing to do with whether the language will achieve "soundness". It's that this is behavior that has not over the last 15 years produced exploitable vulnerabilities, despite extremely high incentives for those vulnerabilities to be unearthed.

pkolaczk 10 days ago

You don’t need to blow up the runtime to cause a vulnerability due to a data race in Go:

https://security.snyk.io/vuln/SNYK-DEBIAN13-GOLANGGITHUBGORE...

  • arp242 10 days ago

    That's a completely different type of vulnerability than the UB that's being talked about.

    > The call to sync.Pool.Get will then return a bytes.Buffer that hasn't had bytes.Buffer.Reset called on it. This dirty buffer will contain the HTTP request body from an unrelated request.

    This is just a good ol' logic error, that just so happens to also be a race.

    • unscaled 10 days ago

      These type of UB bugs in Go are a bit of a red herring, since most race conditions arise from improper use of shared mutability, and would still be a problem even in the presence of full memory safety, for instance:

      https://github.com/golang/go/issues/37669

      https://github.com/golang/go/issues/48340

      These types of race conditions cannot happen in Rust. Not because Rust does not have UB, but because Rust does not allow multiple writable pointers ("mutable borrows") to the same memory region. If you want shared AND mutable access to memory, you must use a thread-safe construct such as Mutex or Cell — or drop into unsafe code.

      Rust does not prevent all types of errors of course. Dirty buffer reuse (as in the GP example) is still possible in Rust. You could still have situations where a buffer is returned to a pool without resetting it. But this could only be a pure logic error where you've forgot to reset the buffer and it would occur consistently and thus would be easy to reproduce and debug. In addition, with idiomatic Rust, you could enforce proper buffer cleanup in Rust by wrapping the Buffer with a type that implements Drop.

      More specifically, the vulnerability mentioned in GP is not possible in Rust. The description is a bit misleading, but the issue was not that the buffer was returned to the pool without being reset, but rather that the same buffer was returned to the pool TWICE under certain conditions, due to a data race. This is not possible in Rust. You cannot put the same owned buffer twice in a pool, due to Rust's move semantics (affine types). And if we want to be completely honest, you'd probably won't need to pool buffers in Rust to begin with, since you don't need to avoid garbage collection (there is none). In most cases, malloc is going to work good enough as your "pool".

      We have a serious problem as an industry, where there is a popular conception of memory safety and type safety as a binary property: a language is either safe or unsafe, either sound or unsound. But it's more of a spectrum, and not even a contiguous one at that. This comments thread is split between people who say that large size atomicity UB is not a major issue in practice and people willing to completely rule off Go's memory safety based on that. But we could just say Go sits near the safe end of the spectrum of memory safety — it certainly does far better than C. My security concerns with Go, after nearly 9 years of using are mostly about race conditions, memory leaks and lack of better mechanisms to enforce type safety (such as sum types and affine types).

      • tptacek 10 days ago

        Given the total lack of empirical evidence (that is: language-specific vulnerabilities in the myriad large high-profile Go projects run all over the Internet; instances of bug classes not found in other mainstream memory-safe languages --- "memory-safe" here just to factor C/C++ out of that set) for those security concerns, why do you prioritize them?

        We of course continue to find SQLI, authz, SSRF, metacharacter parsing and cryptography vulnerabilities in Go codebases, the same way we do in literally every general-purpose programming language; what the the vulnerabilities we actually see, over 15 years of explosive growth in use, that are distinctive to Go? It's been 4 years since I was a full-time software security person, but I keep up, and did a lot of work in Go before then, and I'm not aware of anything beyond "if you skip the ,ok on a type conversion you might panic something".