https://zenn.dev/tenntenn/articles/658e16ae6966b3

はじめに

2022年のセキュリティ・キャンプ全国大会講師として参加しました。その中で、Build Constraintを用いたソースコードを解析する際に注意することを解説しました。本記事ではその際に解説した点について記述します。

セキュリティ・キャンプで用いた資料はこちらから閲覧できます。

Build Constraintとは

Build Constraintとは、いわゆるビルドタグと呼ばれる機能で環境(OSやアーキテクチャ、Goのバージョンなど)によってコンパイルするファイルを書き分ける機能です。

次のように、//go:buildコメントディレクティブをつけることで特定の条件を満たす場合にコンパイルするように書けます。この場合はGoのバージョンが1.19以上の場合にのみコンパイルされるようにしています。

//go:build go1.19

package main

func init() {
	panic("Go1.19 🎉")
}

Go1.18以下で動かすとパニックは起きませんが、Go1.19以上で動かすとパニックが発生します。Go Playgroundであれば複数のバージョンで実行できるので、試しに動かしてみると良いでしょう。なお、Go Playgroundは現在のバージョン、前のバージョン、開発バージョンで動かせます。記事執筆時点ではGo1.19が最新版なので、Go1.18で動かすと良いでしょう。Go1.19よりバージョンが上がった場合には、go1.19を最新版に読み替えてください。

ファイル名でもOSやアーキテクチャを指定することで出し分けることができます。prog__windows_amd64.goと名前をつけると、GOOSwindowsGOARCHamd64の場合にだけ、コンパイル対象になります。

静的解析ツールが対象としているコード

go vetgo/analysisを使った静的解析ツールは、動作する環境のGOOSGOARCH、そしてGoのバージョンによって解析対象のコードが変わります。つまり、その環境のビルド対象になるようなコードが静的解析の解析対象にもなるということです。

たとえば、次のようなgopherという名前の静的解析ツールがあった場合を考えます。識別子を見つけると、gopher!!というメッセージを該当のソースコードの位置に対して出力します。

func run(pass *analysis.Pass) (any, error) {
        inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)

        nodeFilter := []ast.Node{
                (*ast.Ident)(nil),
        }

        inspect.Preorder(nodeFilter, func(n ast.Node) {
                switch n := n.(type) {
                case *ast.Ident:
                        if n.Name == "gopher" {
                                pass.Reportf(n.Pos(), "gopher!!")
                        }
                }
        })

        return nil, nil
}

これを次の2つのファイルを持つexampleパッケージに対して実行します。

//go:build go1.19

package example

func gopher() {} // want "gopher!!"

次のように、この静的解析ツールをGo1.19のコンパイラでビルドし、上記のexampleパッケージを対象に実行してみます。ここでgo1.19コマンドはGo1.19のgoコマンドとしています[1]go1.19 vetコマンドを使ってビルドした静的解析ツールを実行するとa.goの該当箇所でgopher!!というメッセージが表示されています。

$ go1.19 build ./cmd/mylinter
$ cd testdata/src/example
$ go1.19 vet -vettool=`pwd`/../../../mylinter example
# a
./a.go:5:6: gopher!!

一方、Go1.18でビルドして実行してみます。この場合はa.goが解析対象から外れるため、メッセージは表示されません。