Skip to content

Commit

Permalink
internal/buildinfo: add support for ancient Go binaries
Browse files Browse the repository at this point in the history
As a result, govulncheck will report only stdlib vulnerabilities.

Change-Id: Ib9dd2445de41690b3e3122ad3789871b5d632441
Reviewed-on: https://go-review.googlesource.com/c/vuln/+/595615
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Ian Cottrell <iancottrell@google.com>
  • Loading branch information
zpavlinovic committed Jul 1, 2024
1 parent cfe93e3 commit 675d16b
Show file tree
Hide file tree
Showing 7 changed files with 51 additions and 54 deletions.
25 changes: 21 additions & 4 deletions internal/buildinfo/additions_scan.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,14 @@ import (
"debug/buildinfo"
"errors"
"fmt"
"io"
"net/url"
"os"
"runtime/debug"
"strings"

"golang.org/x/tools/go/packages"
"golang.org/x/vuln/internal/gosym"
"golang.org/x/vuln/internal/goversion"
)

func debugModulesToPackagesModules(debugModules []*debug.Module) []*packages.Module {
Expand All @@ -46,15 +47,31 @@ type Symbol struct {
}

// ExtractPackagesAndSymbols extracts symbols, packages, modules from
// bin as well as bin's metadata.
// Go binary file as well as bin's metadata.
//
// If the symbol table is not available, such as in the case of stripped
// binaries, returns module and binary info but without the symbol info.
func ExtractPackagesAndSymbols(bin io.ReaderAt) ([]*packages.Module, []Symbol, *debug.BuildInfo, error) {
bi, err := buildinfo.Read(bin)
func ExtractPackagesAndSymbols(file string) ([]*packages.Module, []Symbol, *debug.BuildInfo, error) {
bin, err := os.Open(file)
if err != nil {
return nil, nil, nil, err
}
defer bin.Close()

bi, err := buildinfo.Read(bin)
if err != nil {
// It could be that bin is an ancient Go binary.
v, err := goversion.ReadExe(file)
if err != nil {
return nil, nil, nil, err
}
bi := &debug.BuildInfo{
GoVersion: v.Release,
Main: debug.Module{Path: v.ModuleInfo},
}
// We cannot analyze symbol tables of ancient binaries.
return nil, nil, bi, nil
}

funcSymName := gosym.FuncSymName(bi.GoVersion)
if funcSymName == "" {
Expand Down
28 changes: 13 additions & 15 deletions internal/buildinfo/additions_scan_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ package buildinfo

import (
"fmt"
"os"
"os/exec"
"path/filepath"
"sort"
Expand Down Expand Up @@ -50,15 +49,10 @@ func testAll(t *testing.T, gooss, goarchs []string, ft func(*testing.T, string,
func TestExtractPackagesAndSymbols(t *testing.T) {
testAll(t, []string{"linux", "darwin", "windows", "freebsd"}, []string{"amd64", "386", "arm", "arm64"},
func(t *testing.T, goos, goarch string) {
binary, done := test.GoBuild(t, "testdata", "", false, "GOOS", goos, "GOARCH", goarch)
binary, done := test.GoBuild(t, "testdata/src", "", false, "GOOS", goos, "GOARCH", goarch)
defer done()

f, err := os.Open(binary)
if err != nil {
t.Fatal(err)
}
defer f.Close()
_, syms, _, err := ExtractPackagesAndSymbols(f)
_, syms, _, err := ExtractPackagesAndSymbols(binary)
if err != nil {
t.Fatal(err)
}
Expand All @@ -76,6 +70,16 @@ func TestExtractPackagesAndSymbols(t *testing.T) {
})
}

func TestAncientGoBinaries(t *testing.T) {
_, _, bi, err := ExtractPackagesAndSymbols("testdata/bin/hello-world")
if err != nil {
t.Fatal(err)
}
if bi.GoVersion != "go1.6.4" {
t.Errorf("want go1.6.4 Go binary version; got %s", bi.GoVersion)
}
}

// sortedSymbols gets symbols for pkg and
// sorts them for testing purposes.
func sortedSymbols(pkg string, syms []Symbol) []Symbol {
Expand Down Expand Up @@ -149,13 +153,7 @@ func Vuln() {
t.Fatalf("failed to build the binary %v %v", err, string(out))
}

exe, err := os.Open(filepath.Join(e.Config.Dir, "entry"))
if err != nil {
t.Fatal(err)
}
defer exe.Close()

_, syms, _, err := ExtractPackagesAndSymbols(exe)
_, syms, _, err := ExtractPackagesAndSymbols(filepath.Join(e.Config.Dir, "entry"))
if err != nil {
t.Fatal(err)
}
Expand Down
10 changes: 2 additions & 8 deletions internal/buildinfo/additions_stripped_122_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
package buildinfo

import (
"os"
"testing"

"golang.org/x/vuln/internal/test"
Expand All @@ -19,15 +18,10 @@ import (
func TestStrippedBinary(t *testing.T) {
testAll(t, []string{"linux", "windows", "freebsd", "darwin"}, []string{"amd64", "386", "arm", "arm64"},
func(t *testing.T, goos, goarch string) {
binary, done := test.GoBuild(t, "testdata", "", true, "GOOS", goos, "GOARCH", goarch)
binary, done := test.GoBuild(t, "testdata/src", "", true, "GOOS", goos, "GOARCH", goarch)
defer done()

f, err := os.Open(binary)
if err != nil {
t.Fatal(err)
}
defer f.Close()
_, syms, _, err := ExtractPackagesAndSymbols(f)
_, syms, _, err := ExtractPackagesAndSymbols(binary)
if err != nil {
t.Fatal(err)
}
Expand Down
19 changes: 4 additions & 15 deletions internal/buildinfo/additions_stripped_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
package buildinfo

import (
"os"
"testing"

"github.com/google/go-cmp/cmp"
Expand All @@ -23,15 +22,10 @@ func TestStrippedBinary(t *testing.T) {
// preserve the symbol table. See TestStrippedDarwin.
testAll(t, []string{"linux", "windows", "freebsd"}, []string{"amd64", "386", "arm", "arm64"},
func(t *testing.T, goos, goarch string) {
binary, done := test.GoBuild(t, "testdata", "", true, "GOOS", goos, "GOARCH", goarch)
binary, done := test.GoBuild(t, "testdata/src", "", true, "GOOS", goos, "GOARCH", goarch)
defer done()

f, err := os.Open(binary)
if err != nil {
t.Fatal(err)
}
defer f.Close()
_, syms, _, err := ExtractPackagesAndSymbols(f)
_, syms, _, err := ExtractPackagesAndSymbols(binary)
if err != nil {
t.Fatal(err)
}
Expand All @@ -47,15 +41,10 @@ func TestStrippedBinary(t *testing.T) {
func TestStrippedDarwin(t *testing.T) {
testAll(t, []string{"darwin"}, []string{"amd64", "386"},
func(t *testing.T, goos, goarch string) {
binary, done := test.GoBuild(t, "testdata", "", true, "GOOS", goos, "GOARCH", goarch)
binary, done := test.GoBuild(t, "testdata/src", "", true, "GOOS", goos, "GOARCH", goarch)
defer done()

f, err := os.Open(binary)
if err != nil {
t.Fatal(err)
}
defer f.Close()
_, syms, _, err := ExtractPackagesAndSymbols(f)
_, syms, _, err := ExtractPackagesAndSymbols(binary)
if err != nil {
t.Fatal(err)
}
Expand Down
Binary file added internal/buildinfo/testdata/bin/hello-world
Binary file not shown.
File renamed without changes.
23 changes: 11 additions & 12 deletions internal/scan/binary.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import (
"context"
"encoding/json"
"errors"
"io"
"os"
"runtime/debug"

Expand Down Expand Up @@ -39,17 +38,11 @@ func runBinary(ctx context.Context, handler govulncheck.Handler, cfg *config, cl
}

func createBin(path string) (*vulncheck.Bin, error) {
f, err := os.Open(path)
if err != nil {
return nil, err
}
defer f.Close()

// First check if the path points to a Go binary. Otherwise, blob
// parsing might json decode a Go binary which takes time.
//
// TODO(#64716): use fingerprinting to make this precise, clean, and fast.
mods, packageSymbols, bi, err := buildinfo.ExtractPackagesAndSymbols(f)
mods, packageSymbols, bi, err := buildinfo.ExtractPackagesAndSymbols(path)
if err == nil {
return &vulncheck.Bin{
Modules: mods,
Expand All @@ -61,17 +54,23 @@ func createBin(path string) (*vulncheck.Bin, error) {
}

// Otherwise, see if the path points to a valid blob.
bin := parseBlob(f)
bin := parseBlob(path)
if bin != nil {
return bin, nil
}

return nil, errors.New("unrecognized binary format")
}

// parseBlob extracts vulncheck.Bin from a valid blob. If it
// cannot recognize a valid blob, returns nil.
func parseBlob(from io.Reader) *vulncheck.Bin {
// parseBlob extracts vulncheck.Bin from a valid blob at path.
// If it cannot recognize a valid blob, returns nil.
func parseBlob(path string) *vulncheck.Bin {
from, err := os.Open(path)
if err != nil {
return nil
}
defer from.Close()

dec := json.NewDecoder(from)

var h header
Expand Down

0 comments on commit 675d16b

Please sign in to comment.