-
Notifications
You must be signed in to change notification settings - Fork 17.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
cmd/vet: detect non-64-bit-aligned arguments to sync/atomic funcs #11891
Comments
At my company we have been bitten multiple times by non-64bits-aligned pointers used with Each time it happened to us, the atomically accessed pointer was always a struct field and the struct was never embedded in another one, nor was it in a slice. I think this use case is the most common one, it is relatively rare to see a slice of structs having fields that are atomically accessed, but it might happen. I'd like to work on a CL adding a vet check to cover for that case, that is also probably the simplest to detect, that is: detecting non-64bits alignment of a 64bit variable, when the variable is a field of a non-embedded struct. This check would not cover all the cases, but I believe it would cover the most common one. Also it would be better than having no check at all, and could be a basis on which to improve upon. |
related: #599 @arl could you post a code snippet to show how the struct variable (edit: is declared)? There are some ways to guarantee 64-bit alignments for struct fileds. |
@go101 for example you can define your struct such as the atomically accessed field is the first struct field. In that case it's guaranteed it will be 64bits aligned, even on 32bits architectures. package main
import (
"sync/atomic"
)
type A struct {
b int64 // b is accessed via a sync/atomic function
c int32
d int64 // d should NOT be accessed via a sync/atomic function as it's not 64bits aligned.
}
func main() {
a := A{}
// Atomically increment a.b. This is valid, b is the first field of the struct, so it's
// guaranteed to be 64-bits aligned.
atomic.AddInt64(&a.b, 1)
} edit: updated the example to a complete one |
I briefly spoke with @arl this past week, and adding a simple version of this check sounds fine to me. It could be expanded to add support for the more complex cases later, such as slices of structs. As far as I can tell, the check would pass the correctness and precision criteria; the issue causes runtime panics, and it should be possible to have zero false positives. Frequency is a bit more subjective, as the check would simply save developer's time. Any piece of software that is properly tested shouldn't have this bug shipped out, as it should be caught by a panic at run time. Labelling as NeedsDecision, but this gets a thumbs up from me. /cc @alandonovan @robpike @josharian |
@go101 just saw your edit. |
The same code using The only way I know to avoid false positives:
Thoughts ? CC @ianlancetaylor @alandonovan |
I would be in favor of an opt-in check, enabled with an option or with |
We already have an arch-aware vet checks, asmdecl. It works by checking the code in play assuming GOOS/GOARCH. So if you only ever compile for amd64, you’ll never see the vet check fire, but arguably, you also don’t have a problem. As long as you run your tests at least once for any target architecture, vet will check that target architecture, and catch the problem. I haven’t thought much about this particular check, I just wanted to point out that we have a good arch-dependent vet mechanism. |
@josharian @ianlancetaylor It looks like using GOOS/GOARCH/GOARM could also be a good solution, with some false positives only in a very specific case (i.e the user is running vet on arm32 but never had the intention to run their code from arm32). Compared to hiding the vet check behind an experimental flag, this solution has the advantage of covering users that follow vet best practices (running it once per arch) |
Notice that, if they’re compiling on arm32, I think it’s safe to assume that they would try to run at least once on arm32, if only for development purposes. |
I hadn't realised we did have arch-dependent checks in vet; Josh's suggestion sounds great. I agree that if someone runs We might have false negatives if a package is compiled for 32-bit, but only tested on 64-bit. I presume said package will have bigger issues than atomic parameter alignment, though :) |
I have been working on a new The new check comes under the form of a new Basically the code does the following: The check currently does not recurse into embedded structs (embedded or not) to know their size, so when that case is encountered, the vet check issues no warning in order to avoid false positives. This could potentially be a useful addition, but I'd like somebody to have a look at my code before adding that. I added some tests, right now I'm not sure about how to make those tests platform-dependent, that is exclude them on non-targeted platforms? |
Change https://golang.org/cl/158277 mentions this issue: |
It's been four months since @arl suggested implementing this. All replies have been in favor so far, so I'll replace NeedsDecision with NeedsFix. If Alan has any reservations, he can always -2 the CL itself :) |
Looks like this was reverted in golang/tools@718ddee and a new CL with tests is required. |
The first CL is still merged. The second one, which is an improvement over the first, has been reverted due to falling tests on some platforms. |
@arl Could you elaborate on which cases are currently handled and which ones aren't? Should users simply be able to run Is the alignment check specific to the host architecture, and if so is there a way to check for alignment issues on a different architecture? |
How does one build and use The
|
@tmm1 Only cases handled are atomic variables used inside a global struct, however it fails to recognize when atomic functions are called from inside a struct method (when potentially affected struct is a pointer receiver). I pushed a CL to handle exactly this some time ago, see https://go-review.googlesource.com/c/tools/+/163997 , but tests fail on mips and x86, they pass on arm though, it didn't receive much love though :). Strange thing is there are cases where alignment is 64-bit on mips and x86, while, at least to my understanding of the bug note, they shouldn't. To try it yourself, you should run atomicalign on one of the affected architectures, as this check is a no-op on non-affected ones. |
Discussion at https://groups.google.com/d/topic/golang-nuts/CN_rTywuD2U/discussion.
Per sync/atomic "BUG" note:
It would be highly desirable if go/vet could detect when this rule is violated and warn about such cases. This note is easy to overlook, and non-trivial to satisfy. It's also not obvious how it applies in more complicated scenarios - e.g.:
etc. (see the discussion linked above)
The text was updated successfully, but these errors were encountered: