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