atomic128 9 days ago

Here is code to circumvent Go's memory safety without importing unsafe.

get() reads a byte at an arbitrary address and set() writes a byte at an arbitrary address.

This is excerpted from BUGFIX 66 ("Hack This Site"):

  func racer() {
      var (
          ptr1 *uintptr
          ptr2 *byte
          race any
          done = make(chan struct{})
      )
      put := func(x any) {
          for {
              select {
              case <-done:
                  return
              default:
                  race = x
              }
          }
      }
      go put(ptr1)
      go put(&ptr2)
      for {
          var ok bool
          ptr1, ok = race.(*uintptr)
          if ok && ptr1 != nil {
              close(done)
              break
          }
      }
      get := func(addr uintptr) byte {
          *ptr1 = addr
          return *ptr2
      }
      set := func(addr uintptr, to byte) {
          *ptr1 = addr
          *ptr2 = to
      }
      if get(0xdeadbeef) == 111 {
          set(0xbaaaaaad, 222)
      }
  }
  • tptacek 9 days ago

    "Without importing unsafe" is doing a lot of work for examples like this.

    • atomic128 9 days ago

      This comes from a webpage where the challenge is to compromise the site, despite the fact that Go imports are disallowed (including unsafe). It's a puzzle game.

      To clarify, I think Go is magnificent and I use it for everything. The racer() code is just a curiosity.

      • tptacek 9 days ago

        Right, it's a cool trick. It's just not material to real threat models, which is what people imply when they say "Go isn't memory safe".

ronsor 9 days ago

You shouldn't be modifying any variable concurrently without a mutex. The only exception to this is if the variable is (1) less than or equal to the CPU word size; (2) is at a CPU word size aligned address; and (3) atomic memory access functions are used to read and write the variable.

  • kiitos 9 days ago

    Even when a value satisfies these architecture-dependent requirements, the language still does not guarantee atomicity of concurrent reads/writes, and programs which rely on that assumption are buggy.

    • kaba0 9 days ago

      Logic bugs != memory safety bugs.

      E.g. in java you can mess up your logic with data races, but the racing itself is safe and can never cause the VM to enter an invalid state.

  • saghm 9 days ago

    Memory safety as long as you don't violate certain rules is what C and C++ also have. The problem is that programmers make mistakes because we're all human.

    • tptacek 9 days ago

      No, the "mistakes" we talk about with C/C++ are so common that it's hard to think of a major C/C++ project not to have them, and the "mistakes" we're talking about with Go or "unsafe" Rust are contrivances built to demonstrate things an actively malicious programmer could do. Equating the two is, obviously, a sleight of hand.

      • klabb3 9 days ago

        To add to this: the built in go race detector is very good at catching data races. It’s a runtime, but I’ve never had a race that couldn’t be reproduced in the race detector trivially.

        But yes, in theory Go has a memory safety problem because of it. In practice though, it’s that people don’t use the race detector, which is ridiculously easy to do.

      • kaba0 9 days ago

        It's only on Go, leave Rust out of it. Rust's safe part is entirely memory safe. Unsafe is the escape hatch, which pretty much every language has in the form of FFI.

    • throwaway894345 9 days ago

      > Memory safety as long as you don't violate certain rules is what C and C++ also have

      There are numbers between 0% and 100%, thus it's possible that Go can be less than 100% memory safe and still far safer than C or C++.

      • tptacek 9 days ago

        "100% memory safe" is mostly not a thing; it's not a concept that gets quantified this way. The closest thing I think you get to a compromised notion of safety that's actually noteworthy is Zig's allocator behavior (which can in ordinary code theoretically still produce UAF bugs, and UAF bugs --- lifecycle bugs more generally --- are the most pernicious kind of memory safety vulnerability). Most practitioners would still call Zig "memory safe". You can see how much bigger a deal that behavior is than the one we're talking about.

        I think the basic takeaway here is not to tie yourself up in nots trying to quantify memory safety. There's a reason Prossimo calls Go memory safe (not "mostly memory safe"), along with Rust, C#, Java, Swift, Python, and JavaScript. Ordinary code written in any of these languages is just not going to have exploitable memory corruption vulnerabilities. Other vulnerabilities, yes!

        • ngrilly 9 days ago

          I'm curious to hear why most practitioners would call Zig's allocators memory safe? Do you mean the std.heap.GeneralPurposeAllocator which protects against use-after-free when building in debug and release_safe mode (not release_fast)?

tptacek 9 days ago

It's easy to demonstrate contrived abuses of Go concurrency that break memory safety, but despite the enormous popularity of the language, actual shipping vulnerabilities --- mistakes in concurrency, not deliberately-engineered pathological cases, that yield attacker-controlled control over memory --- are basically nonexistent (I can't think of a single one; there must be one somewhere!).

Basically this is about as credible an argument as claiming that Rust isn't memory safe because its libraries have so much `unsafe` code. And that claim: not super credible.

Basically, the takeaway in both cases is that it's not safe to allow an attacker to write code for you in the language. But everybody assumes that's the case anyways, because it's the case with virtually every other language (with one very notable, fraught, and well-understood exception), too.

  • ViewTrick1002 9 days ago

    Instead there’s a whole host of subtle footguns which while not leading to true memory unsafety will lead to complete junk data.

    https://www.uber.com/en-SE/blog/data-race-patterns-in-go/

    • tptacek 9 days ago

      I don't care to litigate program correctness and ergonomics. Those are extremely subjective, and I don't feel like I ever get anywhere useful in those kinds of conversations. The most popular backend programming language in the industry is almost certainly Python, and it barely even has types. I still wouldn't dunk on it.

      This thread is about a much narrower question, which is code security. There, I feel like I'm on much firmer ground drawing and defending conclusions, and my conclusion is that there isn't a mainstream general-purpose modern language that is meaningfully more secure than Go (or than Rust, or than Python, etc).

Thaxll 9 days ago

Go is memory safe by modern standard.

If I show you a UB in Rust without the use of unsafe does it means Rust is unsafe?

  • K0nserv 9 days ago

    I believe UB without unsafe is considered a bug by the Rust language team.

    I should’ve said in my original comment, but I don’t mean to dunk on Go. In practice the issues illustrated in the blog post I linked seem unlikely to cause problems in practice, they are interesting nevertheless.

  • Yoric 9 days ago

    What does that mean?

    If I follow correctly, assuming that there are no bugs in the compilers/interpreters, Go is less memory-safe than Java, C#, Python (with GIL), JavaScript or Rust. The only languages that are less memory safe would be C, C++ or Zig.

  • kaba0 9 days ago

    That would mean it, yes. And yeah there is a bug in rust's borrow checker which can trigger something like that for some very special, "no human will ever write code like that" case. But this is an implementation detail for a semantically memory safe language, while in go's case having UB is a language primitive here.

    • Thaxll 9 days ago

      The trigger for Go is exactly "no human will ever write code like that".

JyB 9 days ago

That's like... the first thing you learn about the language and the primitives the language was built upon. Yes mutex are a thing.

  • K0nserv 9 days ago

    Sure race conditions in general, but the subtlety that it can cause memory unsafety, not something I recall being mentioned.

[removed] 9 days ago
[deleted]