Comment by atomic128

Comment by atomic128 9 days ago

24 replies

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".

      • tialaramex 9 days ago

        The fact Go has UB under data races has practical implications for sufficiently complex concurrent software. If you can induce a race on a non-trivial object, that's UB instantly - you can probably blow up the Go runtime and all bets are off.

        I would not characterise this fact, which is a design choice in Go, as similar to say a Rust soundness bug, which will sooner or later just get fixed. They aren't going to somehow magically fix this problem in Go, it's part of the design.

      • Yoric 9 days ago

        Well, time will tell. As Go usage increases, it becomes a more tempting target, which means that more malicious third-parties will start poring over the code of the std library and the frameworks looking exactly for this kind of vulnerability.

        The same goes for Rust, Swift or Zig, of course.

      • kaba0 9 days ago

        How is it not material? You only need to accidentally write and read a map at the same time in language that is supposedly for concurrency (which is why not the same as parallelism, in its case it does largely correlate).

        This is a ridiculous design issue with big ramifications.