sa1027.go raw
1 package sa1027
2
3 import (
4 "fmt"
5 "go/types"
6
7 "honnef.co/go/tools/analysis/callcheck"
8 "honnef.co/go/tools/analysis/lint"
9 "honnef.co/go/tools/go/ir"
10 "honnef.co/go/tools/go/ir/irutil"
11 "honnef.co/go/tools/internal/passes/buildir"
12
13 "golang.org/x/tools/go/analysis"
14 )
15
16 var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{
17 Analyzer: &analysis.Analyzer{
18 Name: "SA1027",
19 Requires: []*analysis.Analyzer{buildir.Analyzer},
20 Run: callcheck.Analyzer(checkAtomicAlignment),
21 },
22 Doc: &lint.RawDocumentation{
23 Title: `Atomic access to 64-bit variable must be 64-bit aligned`,
24 Text: `On ARM, x86-32, and 32-bit MIPS, it is the caller's responsibility to
25 arrange for 64-bit alignment of 64-bit words accessed atomically. The
26 first word in a variable or in an allocated struct, array, or slice
27 can be relied upon to be 64-bit aligned.
28
29 You can use the structlayout tool to inspect the alignment of fields
30 in a struct.`,
31 Since: "2019.2",
32 Severity: lint.SeverityWarning,
33 MergeIf: lint.MergeIfAny,
34 },
35 })
36
37 var Analyzer = SCAnalyzer.Analyzer
38
39 var checkAtomicAlignment = map[string]callcheck.Check{
40 "sync/atomic.AddInt64": checkAtomicAlignmentImpl,
41 "sync/atomic.AddUint64": checkAtomicAlignmentImpl,
42 "sync/atomic.CompareAndSwapInt64": checkAtomicAlignmentImpl,
43 "sync/atomic.CompareAndSwapUint64": checkAtomicAlignmentImpl,
44 "sync/atomic.LoadInt64": checkAtomicAlignmentImpl,
45 "sync/atomic.LoadUint64": checkAtomicAlignmentImpl,
46 "sync/atomic.StoreInt64": checkAtomicAlignmentImpl,
47 "sync/atomic.StoreUint64": checkAtomicAlignmentImpl,
48 "sync/atomic.SwapInt64": checkAtomicAlignmentImpl,
49 "sync/atomic.SwapUint64": checkAtomicAlignmentImpl,
50 }
51
52 func checkAtomicAlignmentImpl(call *callcheck.Call) {
53 sizes := call.Pass.TypesSizes
54 if sizes.Sizeof(types.Typ[types.Uintptr]) != 4 {
55 // Not running on a 32-bit platform
56 return
57 }
58 v, ok := irutil.Flatten(call.Args[0].Value.Value).(*ir.FieldAddr)
59 if !ok {
60 // TODO(dh): also check indexing into arrays and slices
61 return
62 }
63 T := v.X.Type().Underlying().(*types.Pointer).Elem().Underlying().(*types.Struct)
64 fields := make([]*types.Var, 0, T.NumFields())
65 for i := 0; i < T.NumFields() && i <= v.Field; i++ {
66 fields = append(fields, T.Field(i))
67 }
68
69 off := sizes.Offsetsof(fields)[v.Field]
70 if off%8 != 0 {
71 msg := fmt.Sprintf("address of non 64-bit aligned field %s passed to %s",
72 T.Field(v.Field).Name(),
73 irutil.CallName(call.Instr.Common()))
74 call.Invalid(msg)
75 }
76 }
77