go vetのスタイルのGo Linterは、 reflect.SliceHeaderとreflect.StringHeaderの誤った使用を見つけて、建築サイズのフィールドを持つ構造体間の安全でないキャストを見つけます。

go-safer次の使用パターンを報告します。
reflect.SliceHeaderまたはreflect.StringHeaderがあります。reflect.SliceHeaderまたはreflect.StringHeaderのタイプのインスタンスへの割り当てがありますstringint 、 uint 、またはuintptr備えた異なる数のフィールドが含まれています。パターン1は、次のようなコードを識別します。
func unsafeFunction ( s string ) [] byte {
sH := ( * reflect . StringHeader )( unsafe . Pointer ( & s ))
bH := & reflect. SliceHeader {
Data : sH . Data ,
Len : sH . Len ,
Cap : sH . Len ,
}
return * ( * [] byte )( unsafe . Pointer ( bH ))
}また、 type MysteryType reflect.SliceHeaderのように、 reflect.SliceHeaderが名前が変更されたケースもキャッチします。
パターン2は、次のようなコードを識別します。
func unsafeFunction ( s string ) [] byte {
strH := ( * reflect . StringHeader )( unsafe . Pointer ( & str ))
sH := ( * reflect . SliceHeader )( unsafe . Pointer ( nil ))
sH . Len = strH . Len
sH . Cap = strH . Len
sH . Data = strH . Data
return
} safer-goタイプのreflect.SliceHeaderのオブジェクトへの割り当てをキャッチします。関数のコントロールフローグラフを使用すると、 sH実際のスライスをキャストすることによって導出されていないことがわかります(ここでは、代わりにnilです)。
パターン3識別されたキャストは次のように識別されます。
type A struct {
x int
}
type B struct {
y int64
}
func unsafeFunction ( a A ) B {
return * ( * B )( unsafe . Pointer ( & a ))
} passes/*/testdata/srcディレクトリのテストケースの誤った(報告)および安全なコードに関するより多くの例があります。
reflect.SliceHeaderまたはreflect.StringHeaderは、実際のスライスまたはstringをキャストすることによって作成されません。GOランタイムは、これらのタイプ内のDataフィールドを基礎となるデータアレイへの参照として扱いません。したがって、Garbage Collectorがリテラルヘッダーインスタンスから実際のスライスまたはstringに最終キャストの直前に実行される場合、元のスライスまたはstringを収集する場合があります。これにより、情報リークの脆弱性につながる可能性があります。
これらの安全でないパターンの固定バージョンの提案などの詳細について
go-saferをインストールするには、次のコマンドを使用します。
go get github.com/jlauinger/go-safer
これにより、 go-safer $GOPATH/binにインストールされるため、 $PATH環境変数に含まれていることを確認してください。
このようなパッケージでゴーザファーを実行します:
$ go-safer example/cmd
または、スペースで区切られた複数のパッケージを供給します。
$ go-safer example/cmd example/util strings
パッケージをチェックし、再帰的にそのすべての輸入品を使用するには、使用してください./... :
$ go-safer example/cmd/...
最後に、現在のディレクトリのパッケージを確認するには、使用できます. :
$ go-safer .
go-safer go vetと同じ旗を受け入れます。
Flags:
-V print version and exit
-all
no effect (deprecated)
-c int
display offending line with this many lines of context (default -1)
-cpuprofile string
write CPU profile to this file
-debug string
debug flags, any subset of "fpstv"
-fix
apply all suggested fixes
-flags
print analyzer flags in JSON
-json
emit JSON output
-memprofile string
write memory profile to this file
-source
no effect (deprecated)
-tags string
no effect (deprecated)
-trace string
write trace log to this file
-v no effect (deprecated)
-help Flagを提供すると、 go-saferの使用情報が印刷されます。
$ go-safer -help
プロジェクトがgoモジュールとgo.modファイルを使用している場合、 go-safer分析する前にすべての依存関係を自動的に取得します。 go buildとまったく同じように動作します。
異なる形式の依存関係管理を使用する場合は、マニュアルgo get 、 go mod vendorなどを使用する場合は、分析前にすべての依存関係を最新の状態にするために、 go-saferを実行する前に依存関係管理を実行する必要があります。
ソースコードを取得してバイナリをコンパイルするには、これを実行します。
$ git clone https://github.com/jlauinger/go-safer
$ cd go-safer
$ go build
テストケースを実行するには、次のコマンドを使用します。
$ go test ./...
go-safer golang.org/x/tools/go/analysis/analysistest x/tools/go/analysis/analysistestのテストインフラストラクチャを使用しています。テストケースを追加するには、 passes/sliceheader/testdata/srcのbadまたはgood Directories内に新しいパッケージを作成します。必要に応じて、多くのGOファイルをパッケージに追加します。
次に、パッケージパスを指定して、新しいパッケージをsliceheader_test.goファイルに登録します。
テストケースソースファイルでは、報告する(またはそうでない)行に注釈のコメントを追加します。コメントは次のように見えなければなりません:
sH.Len = strH.Len // want "assigning to reflect header object" "assigning to reflect header object"
fmt.Println("hello world") // ok
報告すべき行を示す注釈は、 wantから始めて、目的のメッセージを2回持つ必要があります。何らかの理由で、テストインフラストラクチャにより、 go-saferは注釈を2回出力します。したがって、テストに合格するには2回も予想される必要があります。
structcastパスのテストケースも同様に追加できます。
go-safer Go Vet標準インフラストラクチャに基づいて構築されているため、Passesを独自のGo Vetベースのリナーにインポートできます。
MITライセンス(「ライセンス」)に基づいてライセンスされています。ライセンスに準拠している場合を除き、このプロジェクトを使用することはできません。ここでライセンスのコピーを入手できます。
著作権2020ヨハネス・ラウィンガー
このツールは、Tu Darmstadtのソフトウェアテクノロジーグループでの私の修士論文の一部として開発されました。