Skip to content

Commit

Permalink
Find all methods that use unsafe pointers.
Browse files Browse the repository at this point in the history
Previously we went package-by-package and iterated through
(ssa.Package).Members, but this only contains functions, not methods.  Instead
we iterate through the result of ssautil.AllFunctions.

Add test cases for a package-level variable whose initialization expression
converts an unsafe pointer to *int, and a method which does the same.
  • Loading branch information
jcd authored and jessmcclintock committed Sep 25, 2023
1 parent 003c0ee commit 29c2da0
Show file tree
Hide file tree
Showing 3 changed files with 23 additions and 23 deletions.
35 changes: 12 additions & 23 deletions analyzer/analyzer.go
Original file line number Diff line number Diff line change
Expand Up @@ -359,7 +359,7 @@ func getPackageNodesWithCapability(pkgs []*packages.Package,
log.Fatal("Some packages had errors. Aborting analysis.")
}
graph, ssaProg, allFunctions := buildGraph(pkgs, true)
unsafePointerFunctions := findUnsafePointerConversions(pkgs, ssaProg)
unsafePointerFunctions := findUnsafePointerConversions(pkgs, ssaProg, allFunctions)
ssaProg = nil // possibly save memory; we don't use ssaProg again
safe, nodesByCapability = getNodeCapabilities(graph, classifier)
extraNodesByCapability = make(nodesetPerCapability)
Expand Down Expand Up @@ -437,7 +437,7 @@ func getPackageNodesWithCapability(pkgs []*packages.Package,

// findUnsafePointerConversions uses analysis of the syntax tree to find
// functions which convert unsafe.Pointer values to another type.
func findUnsafePointerConversions(pkgs []*packages.Package, ssaProg *ssa.Program) (unsafePointer map[*ssa.Function]struct{}) {
func findUnsafePointerConversions(pkgs []*packages.Package, ssaProg *ssa.Program, allFunctions map[*ssa.Function]bool) (unsafePointer map[*ssa.Function]struct{}) {
// AST nodes corresponding to functions which convert unsafe.Pointer values.
unsafeFunctionNodes := make(map[ast.Node]struct{})
// Packages which contain variables that are initialized using
Expand Down Expand Up @@ -468,32 +468,21 @@ func findUnsafePointerConversions(pkgs []*packages.Package, ssaProg *ssa.Program
// Find the *ssa.Function pointers corresponding to the syntax nodes found
// above.
unsafePointerFunctions := make(map[*ssa.Function]struct{})
var processFunction func(f *ssa.Function)
processFunction = func(f *ssa.Function) {
for f := range allFunctions {
if _, ok := unsafeFunctionNodes[f.Syntax()]; ok {
unsafePointerFunctions[f] = struct{}{}
}
// Process child functions, e.g. function literals contained inside f.
for _, fn := range f.AnonFuncs {
processFunction(fn)
}
}
for _, pkg := range ssaProg.AllPackages() {
_, initUsesUnsafePointer := packagesWithUnsafePointerUseInInitialization[pkg.Pkg]
// pkg.Members contains all "top-level" functions; other functions are
// reached recursively through those.
for _, m := range pkg.Members {
if f, ok := m.(*ssa.Function); ok {
if initUsesUnsafePointer && f.Name() == "init" {
// This package had an unsafe.Pointer conversion in the initialization
// expression for a package-scoped variable. f is the "init" function
// for the package, so we add it to unsafePointerFunctions.
// There will always be an init function for each package; if one
// didn't exist in the source, a synthetic one will have been
// created.
unsafePointerFunctions[f] = struct{}{}
}
processFunction(f)
if _, ok := packagesWithUnsafePointerUseInInitialization[pkg.Pkg]; ok {
// This package had an unsafe.Pointer conversion in the initialization
// expression for a package-scoped variable, so we add the package's
// "init" function to unsafePointerFunctions.
// There will always be an init function for each package; if one
// didn't exist in the source, a synthetic one will have been
// created.
if f := pkg.Func("init"); f != nil {
unsafePointerFunctions[f] = struct{}{}
}
}
}
Expand Down
2 changes: 2 additions & 0 deletions testing/analyzepackages_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,8 @@ func TestExpectedOutput(t *testing.T) {
{Fn: []string{`useunsafe.Indirect2`, `useunsafe.init\$1`}},
{Fn: []string{`useunsafe.NestedFunctions\$1\$1\$1`}, Cap: `CAPABILITY_UNSAFE_POINTER`},
{Fn: []string{`useunsafe.ReturnFunction\$1`}, Cap: `CAPABILITY_UNSAFE_POINTER`},
{Fn: []string{`useunsafe.T\).M`}, Cap: "CAPABILITY_UNSAFE_POINTER"},
{Fn: []string{`useunsafe.init$`}, Cap: `CAPABILITY_UNSAFE_POINTER`},
{Fn: []string{`useunsafe.init\$1`}, Cap: `CAPABILITY_UNSAFE_POINTER`},
}
for _, path := range expectedPaths {
Expand Down
9 changes: 9 additions & 0 deletions testpkgs/useunsafe/useunsafe.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ var (
I int
U up = up(&I)
IP *int
Y = *(*int)(U)
Z = func() int {
return *(*int)(U)
}
Expand Down Expand Up @@ -83,3 +84,11 @@ func Ok() uintptr {
var p unsafe.Pointer
return (uintptr)(p)
}

// T is a type with a method that uses an unsafe.Pointer.
type T struct{}

// M uses an unsafe pointer.
func (t T) M() int {
return *(*int)(U)
}

0 comments on commit 29c2da0

Please sign in to comment.