0

I want to pass a uintptr to unsafe.Pointer but govet is telling me possible misuse of unsafe.Pointer. I can't figure out how to satisfy govet.


func Example(base uintptr) byte {

    x := *(*byte)(unsafe.Add(base, 4))

    return x

}

If i pass &base govet does to complain but breaks the functionality because it is passing the address of uintptr.

  • I doubt you cannot have both. – Volker Dec 17 '22 at 12:36
  • 2
    While @paul-hanking seems to have nailed it, I sense there may exist a bigger problem: if you've implemented `Example` in an attempt to have "convenient pointer arithmetics in Go", this particular approach is doomed. The reason is than `uintptr` is not considered by the Go runtime as a live pointer, while `unsafe.Pointer` is. This means, that betwen the moment you have obtained an `uintptr` value off a pointer and `Example` starts accessing that value, the object reachable via the original pointer might have been legitimately collected by the GC, and you'll get a runtime panic. – kostix Dec 17 '22 at 15:01
  • 2
    Hence, `unitptr` values which _contain addresses_ must be confined to a single Go statement. In other words, such values must be used in the very statement in which they are materialized. To give an example, `syscall.RawSyscall(entryPoint, uintptr(someUnsafePointerVariable), 0, 0)` is OK because an `uintptr` value is completely confined within a statement: it's materialized and used only in it but not past it. – kostix Dec 17 '22 at 15:06
  • 2
    To say that in other words, you cannot safely store an `uintptr` value and _then_ use it some time later. This _may_ work if you still have other live references to the object whose address is contained in an `uintptr` variable, but making sure such references exist is a very non-obvious thing which would require extensive documentation in the code, and is fragile an approach after all. So, caveat emptor. – kostix Dec 17 '22 at 15:07
  • 2
    Even if you can assure that the object still lives when you cast it back to `unsafe.Pointer`, the object may have been relocated, and the outdated `uintptr` will not contain the address of the object anymore. – Quân Anh Mai Dec 17 '22 at 15:21
  • what about atomic.Storepointer() or atomic.Storeuintptr() can this store the unsafe.Pointer() or uintptr safely @kostix – user19323515 Dec 17 '22 at 15:41
  • 2
    I'm afraid what I said did not come across, unfortunately. "Safety" here means interplay with Go's garbage collector; while the GC does work in parallel with your Go code, the issue is not about atomicity of writing or reading `uintptr` values. What I wanted to explain, is that `unsafe.Pointer`s _do_ reference the memory blocks which addresses they contain—as far as the GC is concerned—wile `uintptr` values _do not._ This means, that if a variable of type `uintptr` is the sole variable containing an address of some object in your program, that object is eligible for garbage collection. – kostix Dec 17 '22 at 16:17
  • The Go compiler has an (undocumented, I think, or at least not easily accessible) quirk in that it guarantees that in any single program statement any `uintptr` values obtained from `unsafe.Pointers` and then used are counted as live references to the memory blocks they address. "Leaking" `unitptr`s past the statement they were produced in, will likely lead to runtime panics unless the liveness of the memoy blocks they address are guaranteed through other live references (I'm repeating myself here). – kostix Dec 17 '22 at 16:24
  • 1
    It’s important to remember that a `uintptr` is not a pointer, it is simply an integer value which just happens to be pointer sized. Storing a pointer value in a `uintptr` throws away all pointer semantics, just as it would if you stored the value in a `uint64` – JimB Dec 17 '22 at 16:25
  • 1
    Yep, simple and concise. +1 to JimB. – kostix Dec 17 '22 at 16:26
  • @QuânAnhMai, contemporary stock Go implementations do not move memory (except for values in maps, which are non-addressable for this reason) so it's impossible for an object to be reallocated by the runtime, but the language spec does not preclude such implementations which allows "3rd-party" Go implementation or any future "stock" Go versionto do so, hence good point. – kostix Dec 17 '22 at 16:31
  • 1
    @kostix While currently Go does not have a moving collector and the pointers to the heap are stable, the stack can be relocated, so the pointers pointing to objects on it may need to be updated in the process. – Quân Anh Mai Dec 17 '22 at 17:07
  • @QuânAnhMai, thanks, I stand corrected! – kostix Dec 17 '22 at 18:14

1 Answers1

1

unsafe.Add takes an unsafe.Pointer as its first argument, but you're passing it a uintptr. It's not govet that complains, it's the go compiler, and here is the error:

cannot use base (variable of type uintptr) as type unsafe.Pointer in argument to unsafe.Add

Instead:

x := *(*byte)unsafe.Pointer(base + 4)

As a complete program (although this program is probably unsound because the a array could in principle be garbage collected before Example is called).

package main

import (
    "fmt"
    "unsafe"
)

func Example(base uintptr) byte {
    return *(*byte)(unsafe.Pointer(base + 4))
}

func main() {
    a := [10]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
    fmt.Println(Example(uintptr(unsafe.Pointer(&a[0]))))
}
Paul Hankin
  • 54,811
  • 11
  • 92
  • 118