Skip to content
Closed
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 88 additions & 0 deletions callgraphutil/graph.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package callgraphutil
import (
"bytes"
"fmt"
"go/token"
"go/types"

"golang.org/x/tools/go/callgraph"
Expand Down Expand Up @@ -186,6 +187,25 @@ func checkBlockInstruction(root *ssa.Function, allFns map[*ssa.Function]bool, g
}
instrCall = fn
}
case *ssa.UnOp:
// Handle calls through struct field function pointers.
// This occurs when a function is stored in a struct field and called
// through that field, like: cmd.run(args)
//
// In SSA, this is represented as a field access followed by a dereference,
// where the field contains a function pointer.
if callt.Op == token.MUL {
// This is a dereference operation, check if it's dereferencing a field access
switch fieldAccess := callt.X.(type) {
case *ssa.FieldAddr:
// This is a field address access, we need to track what function
// might be stored in this field by looking at assignments to this field
instrCall = findFunctionInField(g, fieldAccess, allFns)
case *ssa.Field:
// This is a field value access
instrCall = findFunctionInFieldValue(g, fieldAccess, allFns)
}
}
default:
// case *ssa.TypeAssert: ??
// fmt.Printf("unknown call type: %v: %[1]T\n", callt)
Expand Down Expand Up @@ -306,3 +326,71 @@ func AddFunction(cg *callgraph.Graph, target *ssa.Function, allFns map[*ssa.Func

return nil
}
// findFunctionInField attempts to find what function is stored in a struct field
// by analyzing field assignments throughout the program.
func findFunctionInField(g *callgraph.Graph, fieldAddr *ssa.FieldAddr, allFns map[*ssa.Function]bool) *ssa.Function {
// Get the field index and struct type
fieldIndex := fieldAddr.Field
structType := fieldAddr.X.Type()

// Look through all functions to find assignments to this field
for fn := range allFns {
for _, block := range fn.Blocks {
for _, instr := range block.Instrs {
// Look for store instructions that assign to this field
if store, ok := instr.(*ssa.Store); ok {
if fieldAddr, ok := store.Addr.(*ssa.FieldAddr); ok {
// Check if this is the same field we're looking for
if fieldAddr.Field == fieldIndex &&
types.Identical(fieldAddr.X.Type(), structType) {
// Found an assignment to this field, check what's being assigned
switch val := store.Val.(type) {
case *ssa.Function:
return val
case *ssa.MakeClosure:
if closureFn, ok := val.Fn.(*ssa.Function); ok {
return closureFn
}
}
}
}
}
}
}
}
return nil
}

// findFunctionInFieldValue attempts to find what function is stored in a struct field value.
func findFunctionInFieldValue(g *callgraph.Graph, field *ssa.Field, allFns map[*ssa.Function]bool) *ssa.Function {
// For field values, we need to trace back to where the struct was created
// and what function was assigned to this field.
fieldIndex := field.Field
structType := field.X.Type()

// Look through all functions to find struct literal creations or field assignments
for fn := range allFns {
for _, block := range fn.Blocks {
for _, instr := range block.Instrs {
// Look for struct literal creations (alloc + store instructions)
// or direct field assignments
if store, ok := instr.(*ssa.Store); ok {
if fieldAddr, ok := store.Addr.(*ssa.FieldAddr); ok {
if fieldAddr.Field == fieldIndex &&
types.Identical(fieldAddr.X.Type(), structType) {
switch val := store.Val.(type) {
case *ssa.Function:
return val
case *ssa.MakeClosure:
if closureFn, ok := val.Fn.(*ssa.Function); ok {
return closureFn
}
}
}
}
}
}
}
}
return nil
}
189 changes: 189 additions & 0 deletions callgraphutil/taint-metadata.csv
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot this seems like it was unintentionally included in the PR?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're absolutely right - it was unintentionally included. I've removed callgraphutil/taint-metadata.csv and updated .gitignore to prevent CSV test artifacts from being committed in the future. Fixed in a37739b.

Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
id,pkg,func
64,go/types,(*go/types.Signature).String
82,github.com/picatz/taint/xss,github.com/picatz/taint/xss.init
142,github.com/picatz/taint/cmd/taint,github.com/picatz/taint/cmd/taint.terminalWriteFn$1
150,github.com/picatz/taint/cmd/taint,(github.com/picatz/taint/cmd/taint.commands).eval
19,github.com/picatz/taint/callgraphutil,github.com/picatz/taint/callgraphutil.NewVulncheckCallGraph
33,golang.org/x/tools/go/callgraph,(*golang.org/x/tools/go/callgraph.Graph).CreateNode
34,golang.org/x/tools/go/ssa/ssautil,golang.org/x/tools/go/ssa/ssautil.AllFunctions
47,golang.org/x/tools/go/callgraph,golang.org/x/tools/go/callgraph.AddEdge
73,shared,(context.Context).Err
177,database/sql,(*database/sql.DB).Query
35,github.com/picatz/taint/callgraphutil,github.com/picatz/taint/callgraphutil.AddFunction
128,github.com/picatz/taint/cmd/ssadump,github.com/picatz/taint/cmd/ssadump.main$1
13,github.com/picatz/taint/callgraphutil,github.com/picatz/taint/callgraphutil.PathSearch$1
18,github.com/picatz/taint/callgraphutil,github.com/picatz/taint/callgraphutil.forwardSlice
37,github.com/picatz/taint/callgraphutil,github.com/picatz/taint/callgraphutil.checkBlockInstruction
84,github.com/picatz/taint,github.com/picatz/taint.checkSSAInstruction
112,golang.org/x/tools/go/analysis,(*golang.org/x/tools/go/analysis.Pass).Reportf
153,os,os.Stat
12,github.com/picatz/taint/callgraphutil,github.com/picatz/taint/callgraphutil.PathSearchCallTo
83,github.com/picatz/taint,github.com/picatz/taint.checkSSAValue
108,github.com/picatz/taint,github.com/picatz/taint.NewSources
111,strings,strings.HasSuffix
135,golang.org/x/term,(*golang.org/x/term.Terminal).SetSize
137,golang.org/x/term,golang.org/x/term.Restore
9,fmt,fmt.Fprintf
27,strings,strings.Replace
31,github.com/picatz/taint/xss,github.com/picatz/taint/xss.run
38,golang.org/x/tools/go/ssa,(*golang.org/x/tools/go/ssa.register).Type
55,github.com/picatz/taint/callgraphutil,github.com/picatz/taint/callgraphutil.PathsSearch
87,github.com/picatz/taint,(github.com/picatz/taint.valueSet).add
114,github.com/picatz/taint/sql/injection,github.com/picatz/taint/sql/injection.imports
62,runtime,runtime.Version
74,golang.org/x/tools/go/callgraph/vta,golang.org/x/tools/go/callgraph/vta.CallGraph
102,golang.org/x/tools/go/ssa,(*golang.org/x/tools/go/ssa.Call).Common
178,github.com/picatz/taint/cmd/taint/example,github.com/picatz/taint/cmd/taint/example.main
8,github.com/picatz/taint/cmd/taint,github.com/picatz/taint/cmd/taint.init$4
26,github.com/picatz/taint/callgraphutil,github.com/picatz/taint/callgraphutil.WriteDOT$1
56,github.com/picatz/taint/callgraphutil,github.com/picatz/taint/callgraphutil.InstructionsFor
68,go/types,(*go/types.Signature).Recv
101,github.com/picatz/taint,github.com/picatz/taint.WalkSSA
129,go/parser,go/parser.ParseFile
168,net/url,net/url.Parse
169,os,os.TempDir
5,github.com/picatz/taint/callgraphutil,github.com/picatz/taint/callgraphutil.getNodeInfo
72,golang.org/x/tools/go/callgraph/cha,golang.org/x/tools/go/callgraph/cha.CallGraph
78,github.com/picatz/taint,github.com/picatz/taint.NewSinks
106,github.com/picatz/taint/callgraphutil,(github.com/picatz/taint/callgraphutil.Path).Last
115,strings,strings.HasPrefix
162,github.com/picatz/taint/cmd/taint,github.com/picatz/taint/cmd/taint.main
164,github.com/picatz/taint/cmd/taint,github.com/picatz/taint/cmd/taint.init$6
171,github.com/go-git/go-git/v5,(*github.com/go-git/go-git/v5.Repository).Head
14,github.com/picatz/taint/callgraphutil,github.com/picatz/taint/callgraphutil.WriteCosmograph
21,github.com/picatz/taint/callgraphutil,github.com/picatz/taint/callgraphutil.pruneSet
58,github.com/picatz/taint/callgraphutil,github.com/picatz/taint/callgraphutil.PathsSearch$1
75,shared,(context.Context).Err
86,github.com/picatz/taint,(github.com/picatz/taint.valueSet).includes
98,golang.org/x/tools/go/ssa,(*golang.org/x/tools/go/ssa.register).Referrers
147,github.com/charmbracelet/lipgloss,(github.com/charmbracelet/lipgloss.Style).Render
185,github.com/picatz/taint/xss,github.com/picatz/taint/xss.imports
20,github.com/picatz/taint/callgraphutil,github.com/picatz/taint/callgraphutil.forwardSlice$1
96,golang.org/x/tools/go/ssa,(*golang.org/x/tools/go/ssa.Function).Parent
179,database/sql,database/sql.Open
187,github.com/picatz/taint/cmd/xss,github.com/picatz/taint/cmd/xss.main
16,fmt,fmt.Sprintf
44,golang.org/x/tools/go/ssa,(*golang.org/x/tools/go/ssa.Package).Func
60,github.com/picatz/taint/callgraphutil,github.com/picatz/taint/callgraphutil.InstructionsFor$1
66,strings,strings.Contains
113,golang.org/x/tools/go/analysis/singlechecker,golang.org/x/tools/go/analysis/singlechecker.Main
125,golang.org/x/tools/go/ssa/ssautil,golang.org/x/tools/go/ssa/ssautil.Packages
134,golang.org/x/term,golang.org/x/term.NewTerminal
29,github.com/picatz/taint/log/injection,github.com/picatz/taint/log/injection.run
45,go/types,(*go/types.object).Type
69,go/types,go/types.Identical
117,github.com/picatz/taint/cmd/ssadump,github.com/picatz/taint/cmd/ssadump.main
146,bufio,(*bufio.Writer).Flush
149,golang.org/x/term,(*golang.org/x/term.Terminal).ReadLine
184,net/url,(*net/url.URL).Query
65,go/types,(*go/types.Package).GoVersion
116,github.com/picatz/taint/cmd/sqli,github.com/picatz/taint/cmd/sqli.main
157,path/filepath,path/filepath.Base
25,bufio,(*bufio.Writer).WriteString
54,golang.org/x/tools/go/ssa,(*golang.org/x/tools/go/ssa.CallCommon).Signature
59,github.com/picatz/taint/callgraphutil,github.com/picatz/taint/callgraphutil.PathsSearchCallTo$1
61,github.com/picatz/taint/callgraphutil,github.com/picatz/taint/callgraphutil.CallersOf
93,golang.org/x/tools/go/ssa,(*golang.org/x/tools/go/ssa.Alloc).Referrers
107,golang.org/x/tools/go/callgraph,(*golang.org/x/tools/go/callgraph.Node).String
158,os,os.Open
43,go/types,(*go/types.object).Name
10,bytes,(*bytes.Buffer).String
28,github.com/picatz/taint/callgraphutil,github.com/picatz/taint/callgraphutil.NewGraph
42,golang.org/x/tools/go/ssa,(*golang.org/x/tools/go/ssa.Program).ImportedPackage
94,golang.org/x/tools/go/ssa,(*golang.org/x/tools/go/ssa.FreeVar).Referrers
0,github.com/picatz/taint/cmd/logi,github.com/picatz/taint/cmd/logi.main
22,github.com/picatz/taint/callgraphutil,github.com/picatz/taint/callgraphutil.PathSearchCallTo$1
120,os,os.Exit
121,os,os.Getwd
7,github.com/picatz/taint/callgraphutil,github.com/picatz/taint/callgraphutil.GraphString
39,go/types,(*go/types.Interface).NumMethods
71,shared,(context.Context).Err
95,golang.org/x/tools/go/ssa,(*golang.org/x/tools/go/ssa.FreeVar).Parent
99,golang.org/x/tools/go/ssa,(*golang.org/x/tools/go/ssa.Call).Operands
145,bufio,(*bufio.Writer).Write
161,path/filepath,path/filepath.Join
180,net/http,net/http.NewServeMux
36,golang.org/x/tools/go/ssa,(*golang.org/x/tools/go/ssa.Function).DomPreorder
57,github.com/picatz/taint/callgraphutil,github.com/picatz/taint/callgraphutil.PathsSearchCallTo
76,golang.org/x/tools/go/callgraph,(*golang.org/x/tools/go/callgraph.Graph).DeleteSyntheticNodes
85,github.com/picatz/taint,github.com/picatz/taint.checkPath
159,os,(*os.File).Readdir
48,go/types,(*go/types.Named).Obj
51,go/types,(*go/types.MethodSet).Lookup
79,github.com/picatz/taint/cmd/taint,github.com/picatz/taint/cmd/taint.init$8
4,fmt,fmt.Errorf
11,github.com/picatz/taint/callgraphutil,github.com/picatz/taint/callgraphutil.PathSearch
53,golang.org/x/tools/go/ssa,(*golang.org/x/tools/go/ssa.CallCommon).IsInvoke
67,strings,strings.Split
90,golang.org/x/tools/go/ssa,(*golang.org/x/tools/go/ssa.Parameter).Referrers
152,strings,strings.TrimPrefix
3,encoding/csv,(*encoding/csv.Writer).Write
89,github.com/picatz/taint,(github.com/picatz/taint.stringSet).includes
103,shared,(golang.org/x/tools/go/ssa.Value).Referrers
122,os,os.Environ
133,golang.org/x/term,golang.org/x/term.GetSize
140,github.com/picatz/taint/cmd/taint,github.com/picatz/taint/cmd/taint.errorCommandFn$1
155,strings,strings.TrimSuffix
166,strings,strings.Join
6,github.com/picatz/taint/callgraphutil,(*github.com/picatz/taint/callgraphutil.nodeInfo).CSV
17,golang.org/x/tools/go/ssa,(*golang.org/x/tools/go/ssa.Function).String
32,github.com/picatz/taint/cmd/taint,github.com/picatz/taint/cmd/taint.init$2
40,go/types,(*go/types.Interface).Method
105,github.com/picatz/taint/callgraphutil,(github.com/picatz/taint/callgraphutil.Path).Empty
127,golang.org/x/tools/go/ssa,(*golang.org/x/tools/go/ssa.Function).WriteTo
141,github.com/picatz/taint/cmd/taint,github.com/picatz/taint/cmd/taint.terminalWriteFn
170,github.com/go-git/go-git/v5,github.com/go-git/go-git/v5.PlainOpen
1,github.com/picatz/taint/callgraphutil,github.com/picatz/taint/callgraphutil.WriteCSV
2,encoding/csv,encoding/csv.NewWriter
24,bufio,bufio.NewWriter
119,os/signal,os/signal.NotifyContext
144,github.com/picatz/taint/cmd/taint,github.com/picatz/taint/cmd/taint.init$1
148,github.com/picatz/taint/cmd/taint,(github.com/picatz/taint/cmd/taint.commands).help
176,github.com/picatz/taint/cmd/taint/example,github.com/picatz/taint/cmd/taint/example.business
181,net/http,(*net/http.ServeMux).HandleFunc
15,go/types,(*go/types.Package).Path
63,golang.org/x/tools/go/ssa,(*golang.org/x/tools/go/ssa.Function).Name
100,github.com/picatz/taint,github.com/picatz/taint.walkSSA
156,path/filepath,path/filepath.Dir
173,github.com/go-git/go-git/v5/plumbing,(github.com/go-git/go-git/v5/plumbing.Hash).String
182,github.com/picatz/taint/cmd/taint/example,github.com/picatz/taint/cmd/taint/example.main$1
183,net/http,net/http.ListenAndServe
23,github.com/picatz/taint/callgraphutil,github.com/picatz/taint/callgraphutil.WriteDOT
30,github.com/picatz/taint/sql/injection,github.com/picatz/taint/sql/injection.run
109,github.com/picatz/taint/log/injection,github.com/picatz/taint/log/injection.imports
131,github.com/picatz/taint/cmd/taint,github.com/picatz/taint/cmd/taint.startShell
138,github.com/picatz/taint/cmd/taint,github.com/picatz/taint/cmd/taint.errorCommandFn
165,github.com/picatz/taint/cmd/taint,github.com/picatz/taint/cmd/taint.init$7
46,golang.org/x/tools/go/ssa,(*golang.org/x/tools/go/ssa.Program).NewFunction
52,go/types,(*go/types.Selection).Type
81,github.com/picatz/taint/sql/injection,github.com/picatz/taint/sql/injection.init
91,shared,(golang.org/x/tools/go/ssa.Value).Parent
151,github.com/picatz/taint/cmd/taint,github.com/picatz/taint/cmd/taint.startShell$1
172,github.com/go-git/go-git/v5/plumbing,(*github.com/go-git/go-git/v5/plumbing.Reference).Hash
174,github.com/go-git/go-git/v5,github.com/go-git/go-git/v5.PlainCloneContext
175,github.com/picatz/taint/cmd/taint/example,github.com/picatz/taint/cmd/taint/example.handle
77,github.com/picatz/taint/callgraphutil,github.com/picatz/taint/callgraphutil.CalleesOf
80,github.com/picatz/taint/log/injection,github.com/picatz/taint/log/injection.init
88,golang.org/x/tools/go/ssa,(*golang.org/x/tools/go/ssa.Parameter).Type
92,golang.org/x/tools/go/ssa,(*golang.org/x/tools/go/ssa.Function).Pos
104,github.com/picatz/taint,github.com/picatz/taint.Check
110,go/types,(*go/types.Package).Imports
97,golang.org/x/tools/go/ssa,(*golang.org/x/tools/go/ssa.FreeVar).Name
118,context,context.Background
126,golang.org/x/tools/go/ssa,(*golang.org/x/tools/go/ssa.Package).Build
130,github.com/picatz/taint/cmd/taint,github.com/picatz/taint/cmd/taint.makeRawTerminal
136,github.com/picatz/taint/cmd/taint,github.com/picatz/taint/cmd/taint.makeRawTerminal$1
139,github.com/picatz/taint/cmd/taint,github.com/picatz/taint/cmd/taint.init
143,github.com/picatz/taint/cmd/taint,github.com/picatz/taint/cmd/taint.clearScreen
49,go/types,(*go/types.object).Pkg
123,golang.org/x/tools/go/packages,golang.org/x/tools/go/packages.Load
124,golang.org/x/tools/go/packages,(golang.org/x/tools/go/packages.Error).Error
132,golang.org/x/term,golang.org/x/term.MakeRaw
154,os,os.IsNotExist
163,github.com/picatz/taint/cmd/taint,github.com/picatz/taint/cmd/taint.highlightNode
70,go/types,(*go/types.Pointer).Elem
160,os,(*os.File).Close
167,github.com/picatz/taint/cmd/taint,github.com/picatz/taint/cmd/taint.cloneRepository
186,github.com/picatz/taint/xss,github.com/picatz/taint/xss.run$1
41,go/types,(*go/types.Func).Pkg
50,golang.org/x/tools/go/types/typeutil,(*golang.org/x/tools/go/types/typeutil.MethodSetCache).MethodSet
Loading