src

Go monorepo.
git clone git://code.dwrz.net/src
Log | Files | Refs

sa1027.go (2534B)


      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 }