github.com/amarpal/go-tools@v0.0.0-20240422043104-40142f59f616/staticcheck/sa1027/sa1027.go (about) 1 package sa1027 2 3 import ( 4 "fmt" 5 "go/types" 6 7 "github.com/amarpal/go-tools/analysis/callcheck" 8 "github.com/amarpal/go-tools/analysis/lint" 9 "github.com/amarpal/go-tools/go/ir" 10 "github.com/amarpal/go-tools/go/ir/irutil" 11 "github.com/amarpal/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.Documentation{ 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 }