GoPLS Viewer

Home|gopls/go/analysis/passes/atomicalign/atomicalign.go
1// Copyright 2019 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5// Package atomicalign defines an Analyzer that checks for non-64-bit-aligned
6// arguments to sync/atomic functions. On non-32-bit platforms, those functions
7// panic if their argument variables are not 64-bit aligned. It is therefore
8// the caller's responsibility to arrange for 64-bit alignment of such variables.
9// See https://golang.org/pkg/sync/atomic/#pkg-note-BUG
10package atomicalign
11
12import (
13    "go/ast"
14    "go/token"
15    "go/types"
16
17    "golang.org/x/tools/go/analysis"
18    "golang.org/x/tools/go/analysis/passes/inspect"
19    "golang.org/x/tools/go/analysis/passes/internal/analysisutil"
20    "golang.org/x/tools/go/ast/inspector"
21)
22
23const Doc = "check for non-64-bits-aligned arguments to sync/atomic functions"
24
25var Analyzer = &analysis.Analyzer{
26    Name:     "atomicalign",
27    Doc:      Doc,
28    Requires: []*analysis.Analyzer{inspect.Analyzer},
29    Run:      run,
30}
31
32func run(pass *analysis.Pass) (interface{}, error) {
33    if 8*pass.TypesSizes.Sizeof(types.Typ[types.Uintptr]) == 64 {
34        return nilnil // 64-bit platform
35    }
36    if !analysisutil.Imports(pass.Pkg"sync/atomic") {
37        return nilnil // doesn't directly import sync/atomic
38    }
39
40    inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
41    nodeFilter := []ast.Node{
42        (*ast.CallExpr)(nil),
43    }
44
45    inspect.Preorder(nodeFilter, func(node ast.Node) {
46        call := node.(*ast.CallExpr)
47        selok := call.Fun.(*ast.SelectorExpr)
48        if !ok {
49            return
50        }
51        pkgIdentok := sel.X.(*ast.Ident)
52        if !ok {
53            return
54        }
55        pkgNameok := pass.TypesInfo.Uses[pkgIdent].(*types.PkgName)
56        if !ok || pkgName.Imported().Path() != "sync/atomic" {
57            return
58        }
59
60        switch sel.Sel.Name {
61        case "AddInt64""AddUint64",
62            "LoadInt64""LoadUint64",
63            "StoreInt64""StoreUint64",
64            "SwapInt64""SwapUint64",
65            "CompareAndSwapInt64""CompareAndSwapUint64":
66
67            // For all the listed functions, the expression to check is always the first function argument.
68            check64BitAlignment(passsel.Sel.Namecall.Args[0])
69        }
70    })
71
72    return nilnil
73}
74
75func check64BitAlignment(pass *analysis.PassfuncName stringarg ast.Expr) {
76    // Checks the argument is made of the address operator (&) applied to
77    // to a struct field (as opposed to a variable as the first word of
78    // uint64 and int64 variables can be relied upon to be 64-bit aligned.
79    unaryok := arg.(*ast.UnaryExpr)
80    if !ok || unary.Op != token.AND {
81        return
82    }
83
84    // Retrieve the types.Struct in order to get the offset of the
85    // atomically accessed field.
86    selok := unary.X.(*ast.SelectorExpr)
87    if !ok {
88        return
89    }
90    tvarok := pass.TypesInfo.Selections[sel].Obj().(*types.Var)
91    if !ok || !tvar.IsField() {
92        return
93    }
94
95    stypeok := pass.TypesInfo.Types[sel.X].Type.Underlying().(*types.Struct)
96    if !ok {
97        return
98    }
99
100    var offset int64
101    var fields []*types.Var
102    for i := 0i < stype.NumFields(); i++ {
103        f := stype.Field(i)
104        fields = append(fieldsf)
105        if f == tvar {
106            // We're done, this is the field we were looking for,
107            // no need to fill the fields slice further.
108            offset = pass.TypesSizes.Offsetsof(fields)[i]
109            break
110        }
111    }
112    if offset&7 == 0 {
113        return // 64-bit aligned
114    }
115
116    pass.ReportRangef(arg"address of non 64-bit aligned field .%s passed to atomic.%s"tvar.Name(), funcName)
117}
118
MembersX
inspect
token
types
Doc
check64BitAlignment.fields
check64BitAlignment.BlockStmt.f
ast
check64BitAlignment.funcName
check64BitAlignment.offset
check64BitAlignment.i
inspector
analysisutil
run
run.pass
run.nodeFilter
check64BitAlignment
check64BitAlignment.pass
check64BitAlignment.arg
analysis
Members
X