Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
34 changes: 18 additions & 16 deletions analyzer/analyzer.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ type Analyzer interface {
Analyze(path string, withTrace bool) ([]*Issue, error)

// TraceStack generates callstack for a function to debug
TraceStack(path string, function string) (*IssueSource, error)
TraceStack(path string, function string) (*CallStack, error)
}

// IssueSeverity represents the severity level of an issue.
Expand All @@ -21,32 +21,34 @@ const (

// Issue represents a single issue found by the analyzer.
type Issue struct {
Sources *IssueSource `json:"sources"`
Message string `json:"message"` // A description of the issue.
Severity IssueSeverity `json:"severity"`
CallStack *CallStack `json:"callStack"`
Message string `json:"message"` // A description of the issue.
Severity IssueSeverity `json:"severity"`
Impact string `json:"impact,omitempty"`
Reference string `json:"reference,omitempty"`
}

// IssueSource represents a location in the code where the issue originates.
type IssueSource struct {
File string `json:"file"`
Line int `json:"line"` // The line number where the issue was found.
Function string `json:"function"` // The function where the issue was found.
AbsPath string `json:"absPath"` // The absolute file path.
CallStack *IssueSource `json:"callStack,omitempty"` // The trace of calls leading to this source.
// CallStack represents a location in the code where the issue originates.
type CallStack struct {
File string `json:"file"`
Line int `json:"line"` // The line number where the issue was found.
Function string `json:"function"` // The function where the issue was found.
AbsPath string `json:"absPath"` // The absolute file path.
CallStack *CallStack `json:"callStack,omitempty"` // The trace of calls leading to this source.
}

// Copy creates a deep copy of the IssueSource.
func (src *IssueSource) Copy() *IssueSource {
// Copy creates a deep copy of the CallStack.
func (src *CallStack) Copy() *CallStack {
if src == nil {
return nil
}
// Recursively copy the CallStack
var copiedCallStack *IssueSource
var copiedCallStack *CallStack
if src.CallStack != nil {
copiedCallStack = src.CallStack.Copy()
}

return &IssueSource{
return &CallStack{
File: src.File,
Line: src.Line,
Function: src.Function,
Expand All @@ -56,7 +58,7 @@ func (src *IssueSource) Copy() *IssueSource {
}

// AddCallStack add a call stack to the stack et end
func (src *IssueSource) AddCallStack(stack *IssueSource) {
func (src *CallStack) AddCallStack(stack *CallStack) {
// Recursively copy the CallStack
if src.CallStack == nil {
src.CallStack = stack
Expand Down
6 changes: 3 additions & 3 deletions analyzer/opcode/opcode.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,8 @@ func (op *opcode) Analyze(path string, withTrace bool) ([]*analyzer.Issue, error
source.CallStack = nil
}
issues = append(issues, &analyzer.Issue{
Severity: analyzer.IssueSeverityCritical,
Sources: source,
Severity: analyzer.IssueSeverityCritical,
CallStack: source,
Message: fmt.Sprintf("Incompatible Opcode Detected: Opcode: %s, Funct: %s",
instruction.OpcodeHex(), instruction.Funct()),
})
Expand Down Expand Up @@ -75,7 +75,7 @@ func (op *opcode) buildCallGraph(path string) (asmparser.CallGraph, error) {
}

// TraceStack generates callstack for a function to debug
func (op *opcode) TraceStack(path string, function string) (*analyzer.IssueSource, error) {
func (op *opcode) TraceStack(path string, function string) (*analyzer.CallStack, error) {
graph, err := op.buildCallGraph(path)
if err != nil {
return nil, err
Expand Down
16 changes: 12 additions & 4 deletions analyzer/syscall/asm_syscall.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@ import (
"github.com/ChainSafe/vm-compat/profile"
)

const (
analyzerWorkingPrincipalURL = "https://github.com/ChainSafe/vm-compat?tab=readme-ov-file#how-it-works"
potentialImpactMsg = `This syscall is present in the program, but its execution depends on the actual runtime behavior.
If the execution path does not reach this syscall, it may not affect execution.`
)

// asmSyscallAnalyser analyzes system calls in assembly files.
type asmSyscallAnalyser struct {
profile *profile.VMProfile
Expand Down Expand Up @@ -69,9 +75,11 @@ func (a *asmSyscallAnalyser) Analyze(path string, withTrace bool) ([]*analyzer.I
}

issues = append(issues, &analyzer.Issue{
Severity: severity,
Message: message,
Sources: source,
Severity: severity,
Message: message,
CallStack: source,
Impact: potentialImpactMsg,
Reference: analyzerWorkingPrincipalURL,
})
}
}
Expand Down Expand Up @@ -99,7 +107,7 @@ func (a *asmSyscallAnalyser) buildCallGraph(path string) (asmparser.CallGraph, e
}

// TraceStack generates callstack for a function to debug
func (a *asmSyscallAnalyser) TraceStack(path string, function string) (*analyzer.IssueSource, error) {
func (a *asmSyscallAnalyser) TraceStack(path string, function string) (*analyzer.CallStack, error) {
graph, err := a.buildCallGraph(path)
if err != nil {
return nil, err
Expand Down
26 changes: 13 additions & 13 deletions analyzer/syscall/go_syscall.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,16 +64,16 @@ func (a *goSyscallAnalyser) Analyze(path string, withTrace bool) ([]*analyzer.Is
}

issues = append(issues, &analyzer.Issue{
Severity: severity,
Sources: stackTrace,
Message: message,
Severity: severity,
CallStack: stackTrace,
Message: message,
})
}

return issues, nil
}

func (a *goSyscallAnalyser) TraceStack(path string, function string) (*analyzer.IssueSource, error) {
func (a *goSyscallAnalyser) TraceStack(path string, function string) (*analyzer.CallStack, error) {
cg, fset, err := a.buildCallGraph(path)
if err != nil {
return nil, err
Expand Down Expand Up @@ -131,15 +131,15 @@ func (a *goSyscallAnalyser) extractSyscalls(cg *callgraph.Graph) []*syscallSourc
return syscalls
}

func (a *goSyscallAnalyser) edgeToCallStack(stack *lifo.Stack[*callgraph.Edge], fset *token.FileSet, fullStack bool) *analyzer.IssueSource {
var issueSource *analyzer.IssueSource
func (a *goSyscallAnalyser) edgeToCallStack(stack *lifo.Stack[*callgraph.Edge], fset *token.FileSet, fullStack bool) *analyzer.CallStack {
var issueSource *analyzer.CallStack
for !stack.IsEmpty() {
edge, _ := stack.Pop()
if edge.Site == nil {
continue
}
position := fset.Position(edge.Site.Pos())
src := &analyzer.IssueSource{
src := &analyzer.CallStack{
File: position.Filename,
Line: position.Line,
Function: edge.Caller.Func.String(),
Expand All @@ -157,18 +157,18 @@ func (a *goSyscallAnalyser) edgeToCallStack(stack *lifo.Stack[*callgraph.Edge],
return issueSource
}

func (a *goSyscallAnalyser) buildCallStack(cg *callgraph.Graph, fset *token.FileSet, functions []string) map[string]*analyzer.IssueSource {
sources := make(map[string]*lifo.Stack[*analyzer.IssueSource])
currentStack := lifo.Stack[*analyzer.IssueSource]{}
func (a *goSyscallAnalyser) buildCallStack(cg *callgraph.Graph, fset *token.FileSet, functions []string) map[string]*analyzer.CallStack {
sources := make(map[string]*lifo.Stack[*analyzer.CallStack])
currentStack := lifo.Stack[*analyzer.CallStack]{}
seen := make(map[*callgraph.Node]bool)
var visit func(n *callgraph.Node, edge *callgraph.Edge)

visit = func(n *callgraph.Node, edge *callgraph.Edge) {
var src *analyzer.IssueSource
var src *analyzer.CallStack
if edge != nil && edge.Caller != nil && edge.Site != nil {
position := fset.Position(edge.Site.Pos())
fn := edge.Caller.Func.String()
src = &analyzer.IssueSource{
src = &analyzer.CallStack{
File: position.Filename,
Line: position.Line,
Function: fn,
Expand Down Expand Up @@ -200,7 +200,7 @@ func (a *goSyscallAnalyser) buildCallStack(cg *callgraph.Graph, fset *token.File
visit(n, nil)
}
}
issuesSources := make(map[string]*analyzer.IssueSource)
issuesSources := make(map[string]*analyzer.CallStack)
for fn, stack := range sources {
source, _ := stack.Pop()
for !stack.IsEmpty() {
Expand Down
2 changes: 1 addition & 1 deletion cmd/trace.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ func TraceCaller(ctx *cli.Context) error {
return nil
}

func printCallStack(source *analyzer.IssueSource, str string) string {
func printCallStack(source *analyzer.CallStack, str string) string {
fileInfo := fmt.Sprintf(
" \033[94m\033]8;;file://%s:%d\033\\%s:%d\033]8;;\033\\\033[0m",
source.AbsPath, source.Line, source.File, source.Line,
Expand Down
8 changes: 4 additions & 4 deletions common/stack_tracer.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ func TraceAsmCaller(
graph asmparser.CallGraph,
function string,
endCond func(string) bool,
) (*analyzer.IssueSource, error) {
) (*analyzer.CallStack, error) {
var segment asmparser.Segment
for _, seg := range graph.Segments() {
if seg.Label() == function {
Expand All @@ -26,15 +26,15 @@ func TraceAsmCaller(
return nil, fmt.Errorf("could not find %s in %s", function, filePath)
}
seen := make(map[asmparser.Segment]bool)
var visit func(graph asmparser.CallGraph, segment asmparser.Segment) *analyzer.IssueSource
var visit func(graph asmparser.CallGraph, segment asmparser.Segment) *analyzer.CallStack

visit = func(graph asmparser.CallGraph, segment asmparser.Segment) *analyzer.IssueSource {
visit = func(graph asmparser.CallGraph, segment asmparser.Segment) *analyzer.CallStack {
if seen[segment] {
return nil
}
seen[segment] = true

source := &analyzer.IssueSource{
source := &analyzer.CallStack{
File: filepath.Base(filePath),
Line: segment.Instructions()[0].Line() - 1, // function start line
AbsPath: filePath,
Expand Down
12 changes: 9 additions & 3 deletions renderer/text.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,10 +76,16 @@ func (r *TextRenderer) Render(issues []*analyzer.Issue, output io.Writer) error
for _, msg := range sortedMessages {
groupedIssue := groupedIssues[msg]
report.WriteString(fmt.Sprintf("%d. [%s] %s\n", issueCounter, groupedIssue[0].Severity, msg))
report.WriteString(" - Sources:")
if len(groupedIssue[0].Impact) > 0 {
report.WriteString(fmt.Sprintf(" - Impact: %s \n", groupedIssue[0].Impact))
}
if len(groupedIssue[0].Reference) > 0 {
report.WriteString(fmt.Sprintf(" - Referance: %s \n", groupedIssue[0].Reference))
}
report.WriteString(" - CallStack:")

for _, issue := range groupedIssue {
report.WriteString(fmt.Sprintf("%s\n", buildCallStack(output, issue.Sources, "")))
report.WriteString(fmt.Sprintf("%s\n", buildCallStack(output, issue.CallStack, "")))
}
issueCounter++
}
Expand All @@ -96,7 +102,7 @@ func (r *TextRenderer) Render(issues []*analyzer.Issue, output io.Writer) error
return err
}

func buildCallStack(output io.Writer, source *analyzer.IssueSource, str string) string {
func buildCallStack(output io.Writer, source *analyzer.CallStack, str string) string {
var fileInfo string
if output == os.Stdout {
fileInfo = fmt.Sprintf(
Expand Down
Loading