diff --git a/analyzer/analyzer.go b/analyzer/analyzer.go index cc40ca8..a0f5a58 100644 --- a/analyzer/analyzer.go +++ b/analyzer/analyzer.go @@ -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. @@ -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, @@ -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 diff --git a/analyzer/opcode/opcode.go b/analyzer/opcode/opcode.go index c120659..ad07aaf 100644 --- a/analyzer/opcode/opcode.go +++ b/analyzer/opcode/opcode.go @@ -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()), }) @@ -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 diff --git a/analyzer/syscall/asm_syscall.go b/analyzer/syscall/asm_syscall.go index c4a1d4a..2c02912 100644 --- a/analyzer/syscall/asm_syscall.go +++ b/analyzer/syscall/asm_syscall.go @@ -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 @@ -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, }) } } @@ -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 diff --git a/analyzer/syscall/go_syscall.go b/analyzer/syscall/go_syscall.go index 83718f0..db44e0b 100644 --- a/analyzer/syscall/go_syscall.go +++ b/analyzer/syscall/go_syscall.go @@ -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 @@ -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(), @@ -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, @@ -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() { diff --git a/cmd/trace.go b/cmd/trace.go index 2562336..d901646 100644 --- a/cmd/trace.go +++ b/cmd/trace.go @@ -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, diff --git a/common/stack_tracer.go b/common/stack_tracer.go index 1e93f3d..19cfdc1 100644 --- a/common/stack_tracer.go +++ b/common/stack_tracer.go @@ -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 { @@ -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, diff --git a/renderer/text.go b/renderer/text.go index 00f0c3f..938c173 100644 --- a/renderer/text.go +++ b/renderer/text.go @@ -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++ } @@ -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(