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  }