Skip to content

Commit 712bbae

Browse files
committed
internal/vulncheck/internal/buildinfo: support stripped darwin binaries
With go1.22 and its prereleases, binaries are also stripped on darwin. This cannot be observed by checking emptiness of the symbol table, yet by non-existence of program symbols, "runtime.main" being a symbol that every program should have. Fixes golang/go#61051 Change-Id: If39214df9531bee66931a4155a2a8fbfbf3823cb Reviewed-on: https://go-review.googlesource.com/c/vuln/+/522157 Run-TryBot: Zvonimir Pavlinovic <[email protected]> Reviewed-by: Cherry Mui <[email protected]> TryBot-Result: Gopher Robot <[email protected]>
1 parent 3881ca8 commit 712bbae

File tree

6 files changed

+125
-58
lines changed

6 files changed

+125
-58
lines changed

cmd/govulncheck/main_command_118_test.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -142,8 +142,7 @@ func TestCommand(t *testing.T) {
142142
}
143143
runTestSuite(t, filepath.Join(testDir, "testdata"), govulndbURI.String(), *update)
144144
if runtime.GOOS != "darwin" {
145-
// TODO(https://go.dev/issue/61051): binaries are not currently stripped on darwin.
146-
// This is expected to change in Go 1.22.
145+
// Binaries are not stripped on darwin with go1.21 and earlier. See #61051.
147146
runTestSuite(t, filepath.Join(testDir, "testdata/strip"), govulndbURI.String(), *update)
148147
}
149148
}

internal/vulncheck/internal/buildinfo/additions_buildinfo.go

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,10 @@ func (x *peExe) PCLNTab() ([]byte, uint64) {
192192

193193
// SymbolInfo is derived from cmd/internal/objfile/macho.go:symbols.
194194
func (x *machoExe) SymbolInfo(name string) (uint64, uint64, io.ReaderAt, error) {
195-
sym := x.lookupSymbol(name)
195+
sym, err := x.lookupSymbol(name)
196+
if err != nil {
197+
return 0, 0, nil, err
198+
}
196199
if sym == nil {
197200
return 0, 0, nil, fmt.Errorf("no symbol %q", name)
198201
}
@@ -203,15 +206,26 @@ func (x *machoExe) SymbolInfo(name string) (uint64, uint64, io.ReaderAt, error)
203206
return sym.Value, seg.Addr, seg.ReaderAt, nil
204207
}
205208

206-
func (x *machoExe) lookupSymbol(name string) *macho.Symbol {
209+
func (x *machoExe) lookupSymbol(name string) (*macho.Symbol, error) {
210+
const mustExistSymbol = "runtime.main"
207211
x.symbolsOnce.Do(func() {
208212
x.symbols = make(map[string]*macho.Symbol, len(x.f.Symtab.Syms))
209213
for _, s := range x.f.Symtab.Syms {
210214
s := s // make a copy to prevent aliasing
211215
x.symbols[s.Name] = &s
212216
}
217+
// In the presence of stripping, the symbol table for darwin
218+
// binaries will not be empty, but the program symbols will
219+
// be missing.
220+
if _, ok := x.symbols[mustExistSymbol]; !ok {
221+
x.symbolsErr = ErrNoSymbols
222+
}
213223
})
214-
return x.symbols[name]
224+
225+
if x.symbolsErr != nil {
226+
return nil, x.symbolsErr
227+
}
228+
return x.symbols[name], nil
215229
}
216230

217231
func (x *machoExe) segmentContaining(addr uint64) *macho.Segment {

internal/vulncheck/internal/buildinfo/additions_scan_test.go

Lines changed: 0 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -63,56 +63,3 @@ func TestExtractPackagesAndSymbols(t *testing.T) {
6363
}
6464
})
6565
}
66-
67-
// TestStrippedBinary checks that there is no symbol table for
68-
// stripped binaries. This does not include darwin binaries.
69-
// For more info, see #61051.
70-
func TestStrippedBinary(t *testing.T) {
71-
// We exclude darwin as its stripped binaries seem to
72-
// preserve the symbol table. See TestStrippedDarwin.
73-
testAll(t, []string{"linux", "windows", "freebsd"}, []string{"amd64", "386", "arm", "arm64"},
74-
func(t *testing.T, goos, goarch string) {
75-
binary, done := test.GoBuild(t, "testdata", "", true, "GOOS", goos, "GOARCH", goarch)
76-
defer done()
77-
78-
f, err := os.Open(binary)
79-
if err != nil {
80-
t.Fatal(err)
81-
}
82-
defer f.Close()
83-
_, syms, _, err := ExtractPackagesAndSymbols(f)
84-
if err != nil {
85-
t.Fatal(err)
86-
}
87-
if syms != nil {
88-
t.Errorf("want empty symbol table; got %v symbols", len(syms))
89-
}
90-
})
91-
}
92-
93-
// TestStrippedDarwin checks that the symbol table exists and
94-
// is complete on darwin even in the presence of stripping.
95-
// This test will become obsolete once #61051 is addressed.
96-
func TestStrippedDarwin(t *testing.T) {
97-
t.Skip("to temporarily resolve #61511")
98-
testAll(t, []string{"darwin"}, []string{"amd64", "386", "arm", "arm64"},
99-
func(t *testing.T, goos, goarch string) {
100-
binary, done := test.GoBuild(t, "testdata", "", true, "GOOS", goos, "GOARCH", goarch)
101-
defer done()
102-
103-
f, err := os.Open(binary)
104-
if err != nil {
105-
t.Fatal(err)
106-
}
107-
defer f.Close()
108-
_, syms, _, err := ExtractPackagesAndSymbols(f)
109-
if err != nil {
110-
t.Fatal(err)
111-
}
112-
got := syms["main"]
113-
want := []string{"f", "g", "main"}
114-
if !cmp.Equal(got, want) {
115-
t.Errorf("\ngot %q\nwant %q", got, want)
116-
}
117-
})
118-
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// Copyright 2023 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+
//go:build go1.22
6+
// +build go1.22
7+
8+
package buildinfo
9+
10+
import (
11+
"os"
12+
"testing"
13+
14+
"golang.org/x/vuln/internal/test"
15+
)
16+
17+
// TestStrippedBinary checks that there is no symbol table for
18+
// stripped binaries.
19+
func TestStrippedBinary(t *testing.T) {
20+
testAll(t, []string{"linux", "windows", "freebsd", "darwin"}, []string{"amd64", "386", "arm", "arm64"},
21+
func(t *testing.T, goos, goarch string) {
22+
binary, done := test.GoBuild(t, "testdata", "", true, "GOOS", goos, "GOARCH", goarch)
23+
defer done()
24+
25+
f, err := os.Open(binary)
26+
if err != nil {
27+
t.Fatal(err)
28+
}
29+
defer f.Close()
30+
_, syms, _, err := ExtractPackagesAndSymbols(f)
31+
if err != nil {
32+
t.Fatal(err)
33+
}
34+
if syms != nil {
35+
t.Errorf("want empty symbol table; got %v symbols", len(syms))
36+
}
37+
})
38+
}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
// Copyright 2023 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+
//go:build go1.18 && !go1.22
6+
// +build go1.18,!go1.22
7+
8+
package buildinfo
9+
10+
import (
11+
"os"
12+
"testing"
13+
14+
"github.com/google/go-cmp/cmp"
15+
"golang.org/x/vuln/internal/test"
16+
)
17+
18+
// TestStrippedBinary checks that there is no symbol table for
19+
// stripped binaries. This does not include darwin binaries.
20+
// For more info, see #61051.
21+
func TestStrippedBinary(t *testing.T) {
22+
// We exclude darwin as its stripped binaries seem to
23+
// preserve the symbol table. See TestStrippedDarwin.
24+
testAll(t, []string{"linux", "windows", "freebsd"}, []string{"amd64", "386", "arm", "arm64"},
25+
func(t *testing.T, goos, goarch string) {
26+
binary, done := test.GoBuild(t, "testdata", "", true, "GOOS", goos, "GOARCH", goarch)
27+
defer done()
28+
29+
f, err := os.Open(binary)
30+
if err != nil {
31+
t.Fatal(err)
32+
}
33+
defer f.Close()
34+
_, syms, _, err := ExtractPackagesAndSymbols(f)
35+
if err != nil {
36+
t.Fatal(err)
37+
}
38+
if syms != nil {
39+
t.Errorf("want empty symbol table; got %v symbols", len(syms))
40+
}
41+
})
42+
}
43+
44+
// TestStrippedDarwin checks that the symbol table exists and
45+
// is complete on darwin even in the presence of stripping.
46+
// For more info, see #61051.
47+
func TestStrippedDarwin(t *testing.T) {
48+
testAll(t, []string{"darwin"}, []string{"amd64", "386"},
49+
func(t *testing.T, goos, goarch string) {
50+
binary, done := test.GoBuild(t, "testdata", "", true, "GOOS", goos, "GOARCH", goarch)
51+
defer done()
52+
53+
f, err := os.Open(binary)
54+
if err != nil {
55+
t.Fatal(err)
56+
}
57+
defer f.Close()
58+
_, syms, _, err := ExtractPackagesAndSymbols(f)
59+
if err != nil {
60+
t.Fatal(err)
61+
}
62+
got := syms["main"]
63+
want := []string{"f", "g", "main"}
64+
if !cmp.Equal(got, want) {
65+
t.Errorf("\ngot %q\nwant %q", got, want)
66+
}
67+
})
68+
}

internal/vulncheck/internal/buildinfo/buildinfo.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,7 @@ type machoExe struct {
174174

175175
symbols map[string]*macho.Symbol // Addition: symbols in the binary
176176
symbolsOnce sync.Once // Addition: for computing symbols
177+
symbolsErr error // Addition: error for computing symbols
177178
}
178179

179180
func (x *machoExe) ReadData(addr, size uint64) ([]byte, error) {

0 commit comments

Comments
 (0)