Skip to content

Commit

Permalink
internal/symbols: compute proper db names for generic functions
Browse files Browse the repository at this point in the history
These should not include the type parameter/argument.

Fixes golang/go#63535

Change-Id: I08a5929825d569fb3104f084ea55766ba6f5542e
Reviewed-on: https://go-review.googlesource.com/c/vulndb/+/548195
Run-TryBot: Zvonimir Pavlinovic <[email protected]>
LUCI-TryBot-Result: Go LUCI <[email protected]>
TryBot-Result: Gopher Robot <[email protected]>
Reviewed-by: Tatiana Bradley <[email protected]>
  • Loading branch information
zpavlinovic committed Dec 14, 2023
1 parent 33a53d7 commit 8db0cac
Show file tree
Hide file tree
Showing 4 changed files with 45 additions and 32 deletions.
21 changes: 3 additions & 18 deletions internal/symbols/exported_functions.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,14 @@ import (

"golang.org/x/exp/slices"
"golang.org/x/tools/go/packages"
"golang.org/x/tools/go/ssa"
"golang.org/x/vulndb/internal/derrors"
"golang.org/x/vulndb/internal/osvutils"
"golang.org/x/vulndb/internal/report"
"golang.org/x/vulndb/internal/version"
)

// Exported returns a set of vulnerable symbols exported
// by a package p from the module m.
// Exported returns a set of vulnerable symbols, in the vuln
// db format, exported by a package p from the module m.
func Exported(m *report.Module, p *report.Package, errlog *log.Logger) (_ []string, err error) {
defer derrors.Wrap(&err, "Exported(%q, %q)", m.Module, p.Package)

Expand Down Expand Up @@ -175,22 +174,8 @@ func exportedFunctions(pkg *packages.Package, m *report.Module) (_ map[string]bo
names := map[string]bool{}
for _, e := range entries {
if pkgPath(e) == pkg.PkgPath {
names[ssaSymbolName(e)] = true
names[dbFuncName(e)] = true
}
}
return names, nil
}

func ssaSymbolName(fn *ssa.Function) string {
recv := fn.Signature.Recv()
if recv == nil {
return fn.Name()
}
recvType := recv.Type().String()
// Remove package path from type.
i := strings.LastIndexByte(recvType, '.')
if i < 0 {
return recvType + "." + fn.Name()
}
return recvType[i+1:] + "." + fn.Name()
}
26 changes: 18 additions & 8 deletions internal/symbols/patched_functions.go
Original file line number Diff line number Diff line change
Expand Up @@ -364,17 +364,27 @@ func astSymbolName(f *ast.FuncDecl) string {
return "" // sanity
}

// unpackIdent assumes e is of the form id or id[...]
// and then returns id. Otherwise, returns "".
unpackIdent := func(e ast.Expr) string {
switch xv := e.(type) {
case *ast.Ident:
return xv.Name
case *ast.IndexExpr:
if si, ok := xv.X.(*ast.Ident); ok {
return si.Name
}
}
return ""
}

// supported receiver type snames are id, *id, id[...], and *id[...].
t := ""
switch xv := field.Type.(type) {
case *ast.StarExpr:
if si, ok := xv.X.(*ast.Ident); ok {
t = si.Name
}
case *ast.Ident:
t = xv.Name
case *ast.IndexExpr:
// TODO(#63535): cover index instructions stemming from generics
return ""
t = unpackIdent(xv.X)
case *ast.Ident, *ast.IndexExpr:
t = unpackIdent(xv)
default:
panic(fmt.Sprintf("astSymbolName: unexpected receiver type: %v\n", reflect.TypeOf(field.Type)))
}
Expand Down
10 changes: 9 additions & 1 deletion internal/symbols/patched_functions_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,14 @@ func (a A) Do() {}
type B struct {}
func (b *B) Do() {}
type C[T any] struct {
t T
}
func (c C[T]) Do() {}
func (c *C[T]) Bar() {}
func Go[X any]() {}
`
fset := token.NewFileSet() // positions are relative to fset
f, err := parser.ParseFile(fset, "src.go", src, 0)
Expand All @@ -212,7 +220,7 @@ func (b *B) Do() {}
}
}
sort.Strings(got)
want := []string{"A.Do", "B.Do", "Foo"}
want := []string{"A.Do", "B.Do", "C.Bar", "C.Do", "Foo", "Go"}
if diff := cmp.Diff(want, got); diff != "" {
t.Errorf("(-got, want+):\n%s", diff)
}
Expand Down
20 changes: 15 additions & 5 deletions internal/symbols/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -179,15 +179,17 @@ func dbTypeFormat(t types.Type) string {

// dbFuncName computes a function name consistent with the namings used in vulnerability
// databases. Effectively, a qualified name of a function local to its enclosing package.
// If a receiver is a pointer, this information is not encoded in the resulting name. The
// name of anonymous functions is simply "". The function names are unique subject to the
// enclosing package, but not globally.
// If a receiver is a pointer, this information is not encoded in the resulting name. If
// a function has type argument/parameter, this information is omitted. The name of
// anonymous functions is simply "". The function names are unique subject to the enclosing
// package, but not globally.
//
// Examples:
//
// func (a A) foo (...) {...} -> A.foo
// func foo(...) {...} -> foo
// func (b *B) bar (...) {...} -> B.bar
// func (c C[T]) do(...) {...} -> C.do
func dbFuncName(f *ssa.Function) string {
selectBound := func(f *ssa.Function) types.Type {
// If f is a "bound" function introduced by ssa for a given type, return the type.
Expand Down Expand Up @@ -220,9 +222,17 @@ func dbFuncName(f *ssa.Function) string {
}

if qprefix == "" {
return f.Name()
return funcName(f)
}
return qprefix + "." + f.Name()
return qprefix + "." + funcName(f)
}

// funcName returns the name of the ssa function f.
// It is f.Name() without additional type argument
// information in case of generics.
func funcName(f *ssa.Function) string {
n, _, _ := strings.Cut(f.Name(), "[")
return n
}

// memberFuncs returns functions associated with the `member`:
Expand Down

0 comments on commit 8db0cac

Please sign in to comment.