Open octobered opened 2 years ago
Go doesn't have macros, so it's not clear to me exactly what you are suggesting.
gcWriteBarrier
is run very frequently, so I agree that we should not add a run-time check for whether to check that the pointer is valid.
Perhaps we could do this with build tags, but I have a feeling that few people would such a feature in practice.
CC @golang/runtime
The first example seems like something that vet
should catch. It is rather easy to either forget that the API is actually func StorePointer(dst **T, t *T)
or to typo the unwieldily (*unsafe.Pointer)(...)
expression. I make this mistake basically every time I use the API. A vet
check would catch the problem at its source.
I think that it might be possible for vet
to catch the second case. It would a false positive if UserInfoFromARPC
were allocated off Go's heap (like C.malloc
or syscall.Mmap
), but that could be worked around by using //go:notinheap
. But I don't know.
If vet
could catch these then there wouldn't be a need for runtime checks.
FWIW, in the past for debugging I tried to set the write barrier buffer size to 1, so it will always go through the slow path, which has at least some check e.g. in findObject, and it was helpful. Perhaps we can make a GODEBUG mode that set the the buffer size to 1.
I think that you can actually get the effect of a buffer size of 1 today by using GODEBUG=cgocheck=2
. Though that's hardly intuitive.
A vet checker looks plausible: it can strip the unsafe.Pointer()
conversions and obtain the original types, and check whether the types of the two arguments:
atomic.StorePointer(T1, T2)
Valid cases include:
The check can easily go transitively over the types, e.g. when T2 and T3 are structs with fields of named types. So the checker may narrow down the patterns it will report, e.g. only report the case when T1 = T2, which covers the examples:
atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(s1)), unsafe.Pointer(&f1))
and
atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(¤t)), unsafe.Pointer(&rc))
The frequency of these patterns in real code is a question though.
The first example seems like something that
vet
should catch. It is rather easy to either forget that the API is actuallyfunc StorePointer(dst **T, t *T)
or to typo the unwieldily(*unsafe.Pointer)(...)
expression. I make this mistake basically every time I use the API. Avet
check would catch the problem at its source.I think that it might be possible for
vet
to catch the second case. It would a false positive ifUserInfoFromARPC
were allocated off Go's heap (likeC.malloc
orsyscall.Mmap
), but that could be worked around by using//go:notinheap
. But I don't know.If
vet
could catch these then there wouldn't be a need for runtime checks.
I get this point, vet
would be a less intrusive way. Actually I'm trying to developing a plugin under golangci-lint
so that if anyone meets this problem, they could use it to debug. ( I think these codes are not very common, should I add it go go vet
instead of golangci-lint
?
However the actual problem I concern it's that two examples I gave are just coming from my personal knowledge, I don't know other cases that could trigger this.
I think that you can actually get the effect of a buffer size of 1 today by using
GODEBUG=cgocheck=2
. Though that's hardly intuitive.
Well they are kinda different actually, I think you mean when cgocheck=2
, cgoCheckWriteBarrier
will be called. cgoCheckWriteBarrier
seems will not check it dst
and src
are both Go pointers, as this check depends on if this pointer smaller than span limit, which they are "in span" very likely. (These zombie pointers are smaller than span, but they are larger than freeindex
and their allocBits are unset. They could be in span and zombie.)
Go doesn't have macros, so it's not clear to me exactly what you are suggesting.
gcWriteBarrier
is run very frequently, so I agree that we should not add a run-time check for whether to check that the pointer is valid.Perhaps we could do this with build tags, but I have a feeling that few people would such a feature in practice.
CC @golang/runtime
I do find out go rarely use macros, however gcWriteBarrier
is assemble, I think I might could introduce a macro like GOAMD64_v2/3/4
and it's controlled by flags? (I haven't read related code, it's just my guess😂
and another thing I think maybe should be pointed out is invalidptr
can not fully disable zombie pointer check. invalidptr
will only affect if this pointer is collected by findObject
, if this pointer is marked by gcWriteBarrier
, this will just panic in reportZombies
, which is not controlled by invalidptr
.
I am not very sure but is it ok to make reportZombies
controlled by invalidptr
?
Another "fix" for this problem is to just wait for #50860 to be accepted and implemented. Then we can have people move to using the generic (*Pointer[T]).Store
which doesn't have the same confusion that the current StorePointer
has.