77 lines
2.5 KiB
Go
77 lines
2.5 KiB
Go
package sa1027
|
|
|
|
import (
|
|
"fmt"
|
|
"go/types"
|
|
|
|
"honnef.co/go/tools/analysis/callcheck"
|
|
"honnef.co/go/tools/analysis/lint"
|
|
"honnef.co/go/tools/go/ir"
|
|
"honnef.co/go/tools/go/ir/irutil"
|
|
"honnef.co/go/tools/internal/passes/buildir"
|
|
|
|
"golang.org/x/tools/go/analysis"
|
|
)
|
|
|
|
var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{
|
|
Analyzer: &analysis.Analyzer{
|
|
Name: "SA1027",
|
|
Requires: []*analysis.Analyzer{buildir.Analyzer},
|
|
Run: callcheck.Analyzer(checkAtomicAlignment),
|
|
},
|
|
Doc: &lint.RawDocumentation{
|
|
Title: `Atomic access to 64-bit variable must be 64-bit aligned`,
|
|
Text: `On ARM, x86-32, and 32-bit MIPS, it is the caller's responsibility to
|
|
arrange for 64-bit alignment of 64-bit words accessed atomically. The
|
|
first word in a variable or in an allocated struct, array, or slice
|
|
can be relied upon to be 64-bit aligned.
|
|
|
|
You can use the structlayout tool to inspect the alignment of fields
|
|
in a struct.`,
|
|
Since: "2019.2",
|
|
Severity: lint.SeverityWarning,
|
|
MergeIf: lint.MergeIfAny,
|
|
},
|
|
})
|
|
|
|
var Analyzer = SCAnalyzer.Analyzer
|
|
|
|
var checkAtomicAlignment = map[string]callcheck.Check{
|
|
"sync/atomic.AddInt64": checkAtomicAlignmentImpl,
|
|
"sync/atomic.AddUint64": checkAtomicAlignmentImpl,
|
|
"sync/atomic.CompareAndSwapInt64": checkAtomicAlignmentImpl,
|
|
"sync/atomic.CompareAndSwapUint64": checkAtomicAlignmentImpl,
|
|
"sync/atomic.LoadInt64": checkAtomicAlignmentImpl,
|
|
"sync/atomic.LoadUint64": checkAtomicAlignmentImpl,
|
|
"sync/atomic.StoreInt64": checkAtomicAlignmentImpl,
|
|
"sync/atomic.StoreUint64": checkAtomicAlignmentImpl,
|
|
"sync/atomic.SwapInt64": checkAtomicAlignmentImpl,
|
|
"sync/atomic.SwapUint64": checkAtomicAlignmentImpl,
|
|
}
|
|
|
|
func checkAtomicAlignmentImpl(call *callcheck.Call) {
|
|
sizes := call.Pass.TypesSizes
|
|
if sizes.Sizeof(types.Typ[types.Uintptr]) != 4 {
|
|
// Not running on a 32-bit platform
|
|
return
|
|
}
|
|
v, ok := irutil.Flatten(call.Args[0].Value.Value).(*ir.FieldAddr)
|
|
if !ok {
|
|
// TODO(dh): also check indexing into arrays and slices
|
|
return
|
|
}
|
|
T := v.X.Type().Underlying().(*types.Pointer).Elem().Underlying().(*types.Struct)
|
|
fields := make([]*types.Var, 0, T.NumFields())
|
|
for i := 0; i < T.NumFields() && i <= v.Field; i++ {
|
|
fields = append(fields, T.Field(i))
|
|
}
|
|
|
|
off := sizes.Offsetsof(fields)[v.Field]
|
|
if off%8 != 0 {
|
|
msg := fmt.Sprintf("address of non 64-bit aligned field %s passed to %s",
|
|
T.Field(v.Field).Name(),
|
|
irutil.CallName(call.Instr.Common()))
|
|
call.Invalid(msg)
|
|
}
|
|
}
|