From 6e92ecd48e5d70d2fadece2cc49ba1d61d3f9d8a Mon Sep 17 00:00:00 2001 From: Alessio Greggi Date: Sun, 4 Aug 2024 00:20:28 +0200 Subject: [PATCH 1/4] fix: move saving logic outside captore package Signed-off-by: Alessio Greggi --- cmd/capture.go | 29 ++++++++++++-- cmd/hunt.go | 21 ++++++---- internal/captor/capture.go | 82 +++++++++----------------------------- internal/writer/write.go | 45 +++++++++++++++++++++ 4 files changed, 102 insertions(+), 75 deletions(-) create mode 100644 internal/writer/write.go diff --git a/cmd/capture.go b/cmd/capture.go index 59f9aa9..db946c1 100644 --- a/cmd/capture.go +++ b/cmd/capture.go @@ -16,7 +16,12 @@ limitations under the License. package cmd import ( + "fmt" + "os" + "strings" + "github.com/alegrey91/harpoon/internal/captor" + "github.com/alegrey91/harpoon/internal/writer" "github.com/spf13/cobra" ) @@ -35,13 +40,29 @@ by passing the function name symbol and the binary args. `, Example: " harpoon -f main.doSomething ./command arg1 arg2 ...", Run: func(cmd *cobra.Command, args []string) { - opts := captor.CaptureOptions{ + + functionSymbolList := strings.Split(functionSymbols, ",") + + captureOpts := captor.CaptureOptions{ CommandOutput: commandOutput, LibbpfOutput: libbpfOutput, - Save: save, - Directory: directory, } - captor.Capture(functionSymbols, args, opts) + for _, functionSymbol := range functionSymbolList { + syscalls, err := captor.Capture(functionSymbol, args, captureOpts) + if err != nil { + fmt.Printf("error capturing syscall: %v", err) + os.Exit(1) + } + + saveOpts := writer.WriteOptions{ + Save: save, + Directory: directory, + } + if err := writer.Write(syscalls, functionSymbols, saveOpts); err != nil { + fmt.Printf("error writing syscalls for symbol %s", functionSymbol) + os.Exit(1) + } + } }, } diff --git a/cmd/hunt.go b/cmd/hunt.go index 6cce4e8..fad515f 100644 --- a/cmd/hunt.go +++ b/cmd/hunt.go @@ -19,10 +19,10 @@ import ( "fmt" "io" "os" - "strings" "github.com/alegrey91/harpoon/internal/captor" meta "github.com/alegrey91/harpoon/internal/metadata" + "github.com/alegrey91/harpoon/internal/writer" "github.com/spf13/cobra" yaml "gopkg.in/yaml.v2" ) @@ -65,15 +65,24 @@ var huntCmd = &cobra.Command{ // command builder var captureArgs []string captureArgs = append(captureArgs, symbolsOrigins.TestBinaryPath) - functionSymbols := strings.Join(symbolsOrigins.Symbols, ",") opts := captor.CaptureOptions{ CommandOutput: commandOutput, LibbpfOutput: libbpfOutput, - Save: save, - Directory: directory, } - captor.Capture(functionSymbols, captureArgs, opts) + for _, functionSymbol := range symbolsOrigins.Symbols { + syscalls, err := captor.Capture(functionSymbol, captureArgs, opts) + if err != nil { + fmt.Printf("error capturing syscall: %v", err) + os.Exit(1) + } + + saveOpts := writer.WriteOptions{ + Save: save, + Directory: directory, + } + writer.Write(syscalls, functionSymbol, saveOpts) + } } }, } @@ -84,8 +93,6 @@ func init() { huntCmd.Flags().StringVarP(&harpoonFile, "file", "F", ".harpoon.yaml", "File with the result of analysis") huntCmd.MarkFlagRequired("file") - huntCmd.Flags().StringVarP(&functionSymbols, "functions", "f", "", "Name of the function symbols to be traced") - huntCmd.Flags().BoolVarP(&commandOutput, "include-cmd-output", "c", false, "Include the executed command output") huntCmd.Flags().BoolVarP(&libbpfOutput, "include-libbpf-output", "l", false, "Include the libbpf output") diff --git a/internal/captor/capture.go b/internal/captor/capture.go index 9ba2b72..0df28f6 100644 --- a/internal/captor/capture.go +++ b/internal/captor/capture.go @@ -5,17 +5,13 @@ import ( "encoding/binary" "fmt" "os" - "path" "path/filepath" - "strings" "sync" "unsafe" - "github.com/alegrey91/harpoon/internal/archiver" embedded "github.com/alegrey91/harpoon/internal/embeddable" "github.com/alegrey91/harpoon/internal/executor" probes "github.com/alegrey91/harpoon/internal/probesfacade" - syscallsw "github.com/alegrey91/harpoon/internal/syscallswriter" bpf "github.com/aquasecurity/libbpfgo" ) @@ -36,11 +32,9 @@ type event struct { type CaptureOptions struct { CommandOutput bool LibbpfOutput bool - Save bool - Directory string } -func Capture(functionSymbols string, cmdArgs []string, opts CaptureOptions) { +func Capture(functionSymbol string, cmdArgs []string, opts CaptureOptions) ([]uint32, error) { if !opts.LibbpfOutput { // suppress libbpf log ouput bpf.SetLoggerCbs( @@ -52,13 +46,10 @@ func Capture(functionSymbols string, cmdArgs []string, opts CaptureOptions) { ) } - functionSymbolList := strings.Split(functionSymbols, ",") - objectFile, err := embedded.BPFObject.ReadFile("output/ebpf.o") bpfModule, err := bpf.NewModuleFromBuffer(objectFile, "ebpf.o") if err != nil { - fmt.Printf("error loading BPF object file: %v\n", err) - os.Exit(-1) + return nil, fmt.Errorf("error loading BPF object file: %v", err) } defer bpfModule.Close() @@ -68,44 +59,35 @@ func Capture(functionSymbols string, cmdArgs []string, opts CaptureOptions) { */ config, err := bpfModule.GetMap(bpfConfigMap) if err != nil { - fmt.Printf("error retrieving map (%s) from BPF program: %v\n", bpfConfigMap, err) - os.Exit(-1) + return nil, fmt.Errorf("error retrieving map (%s) from BPF program: %v", bpfConfigMap, err) } enterFuncProbe, err := bpfModule.GetProgram(uprobeEnterFunc) if err != nil { - fmt.Printf("error loading program (%s): %v\n", uprobeEnterFunc, err) - os.Exit(-1) + return nil, fmt.Errorf("error loading program (%s): %v", uprobeEnterFunc, err) } exitFuncProbe, err := bpfModule.GetProgram(uprobeExitFunc) if err != nil { - fmt.Printf("error loading program (%s): %v\n", uprobeExitFunc, err) - os.Exit(-1) + return nil, fmt.Errorf("error loading program (%s): %v", uprobeExitFunc, err) } traceFunction, err := bpfModule.GetProgram(tracepointFunc) if err != nil { - fmt.Printf("error loading program (%s): %v\n", tracepointFunc, err) - os.Exit(-1) + return nil, fmt.Errorf("error loading program (%s): %v", tracepointFunc, err) } bpfModule.BPFLoadObject() - for _, functionSymbol := range functionSymbolList { - offset, err := probes.AttachUProbe(cmdArgs[0], functionSymbol, enterFuncProbe) - if err != nil { - fmt.Printf("error attaching uprobe to %s: %v", functionSymbol, err) - os.Exit(-1) - } + offset, err := probes.AttachUProbe(cmdArgs[0], functionSymbol, enterFuncProbe) + if err != nil { + return nil, fmt.Errorf("error attaching uprobe to %s: %v", functionSymbol, err) + } - err = probes.AttachURETProbe(cmdArgs[0], functionSymbol, exitFuncProbe, offset) - if err != nil { - fmt.Printf("error attaching uretprobe to %s: %v", functionSymbol, err) - os.Exit(-1) - } + err = probes.AttachURETProbe(cmdArgs[0], functionSymbol, exitFuncProbe, offset) + if err != nil { + return nil, fmt.Errorf("error attaching uretprobe to %s: %v", functionSymbol, err) } traceLink, err := traceFunction.AttachTracepoint(tracepointCategory, tracepointName) if err != nil { - fmt.Printf("error attaching tracepoint at event (%s:%s): %v\n", tracepointCategory, tracepointName, err) - os.Exit(-1) + return nil, fmt.Errorf("error attaching tracepoint at event (%s:%s): %v", tracepointCategory, tracepointName, err) } defer traceLink.Destroy() @@ -118,8 +100,7 @@ func Capture(functionSymbols string, cmdArgs []string, opts CaptureOptions) { baseCmd := append([]byte(baseargs), 0) err = config.Update(unsafe.Pointer(&config_key_args), unsafe.Pointer(&baseCmd[0])) if err != nil { - fmt.Printf("error updating map (%s) with values %d / %s: %v\n", bpfConfigMap, config_key_args, baseargs, err) - os.Exit(-1) + return nil, fmt.Errorf("error updating map (%s) with values %d / %s: %v", bpfConfigMap, config_key_args, baseargs, err) } // init perf buffer @@ -127,8 +108,7 @@ func Capture(functionSymbols string, cmdArgs []string, opts CaptureOptions) { lostChannel := make(chan uint64) rb, err := bpfModule.InitPerfBuf(bpfEventsMap, eventsChannel, lostChannel, 1) if err != nil { - fmt.Printf("error initializing map (%s) with PerfBuffer: %v\n", bpfEventsMap, err) - os.Exit(-1) + return nil, fmt.Errorf("error initializing map (%s) with PerfBuffer: %v", bpfEventsMap, err) } // run args that we want to trace @@ -144,6 +124,7 @@ func Capture(functionSymbols string, cmdArgs []string, opts CaptureOptions) { var e event err := binary.Read(bytes.NewBuffer(data), binary.LittleEndian, &e) if err != nil { + fmt.Fprintf(os.Stderr, "error reading event: %v\n", err) return } syscalls = append(syscalls, e.SyscallID) @@ -159,32 +140,5 @@ func Capture(functionSymbols string, cmdArgs []string, opts CaptureOptions) { wg.Wait() rb.Stop() - var errOut error - if opts.Save { - fileName := archiver.Convert(functionSymbolList[0]) - err := os.MkdirAll(opts.Directory, os.ModePerm) - if err != nil { - fmt.Printf("error creating directory: %v\n", err) - os.Exit(-1) - } - file, err := os.Create(path.Join(opts.Directory, fileName)) - if err != nil { - fmt.Printf("error creating file %s: %v\n", file.Name(), err) - os.Exit(-1) - } - defer file.Close() - - if err := file.Chmod(0744); err != nil { - fmt.Printf("error setting permissions to %s: %v\n", file.Name(), err) - } - // write to file - errOut = syscallsw.Print(file, syscalls) - } else { - // write to stdout - errOut = syscallsw.Print(os.Stdout, syscalls) - } - if errOut != nil { - fmt.Printf("error printing out system calls: %v\n", errOut) - os.Exit(-1) - } + return syscalls, nil } diff --git a/internal/writer/write.go b/internal/writer/write.go new file mode 100644 index 0000000..8058903 --- /dev/null +++ b/internal/writer/write.go @@ -0,0 +1,45 @@ +package writer + +import ( + "fmt" + "os" + "path" + + "github.com/alegrey91/harpoon/internal/archiver" + syscallsw "github.com/alegrey91/harpoon/internal/syscallswriter" +) + +type WriteOptions struct { + Save bool + Directory string +} + +func Write(syscalls []uint32, functionSymbol string, opts WriteOptions) error { + var errOut error + if opts.Save { + fileName := archiver.Convert(functionSymbol) + err := os.MkdirAll(opts.Directory, os.ModePerm) + if err != nil { + return fmt.Errorf("error creating directory: %v", err) + } + file, err := os.Create(path.Join(opts.Directory, fileName)) + if err != nil { + return fmt.Errorf("error creating file %s: %v", file.Name(), err) + } + defer file.Close() + + if err := file.Chmod(0744); err != nil { + return fmt.Errorf("error setting permissions to %s: %v", file.Name(), err) + } + // write to file + errOut = syscallsw.Print(file, syscalls) + } else { + // write to stdout + errOut = syscallsw.Print(os.Stdout, syscalls) + } + if errOut != nil { + return fmt.Errorf("error printing out system calls: %v", errOut) + } + + return nil +} From 180a6b2d98641ae7ef45397f6476ec76599a7c6e Mon Sep 17 00:00:00 2001 From: Alessio Greggi Date: Sun, 4 Aug 2024 00:21:19 +0200 Subject: [PATCH 2/4] fix: add missing return in build command Signed-off-by: Alessio Greggi --- cmd/build.go | 1 + 1 file changed, 1 insertion(+) diff --git a/cmd/build.go b/cmd/build.go index 2fe3d39..58c176f 100644 --- a/cmd/build.go +++ b/cmd/build.go @@ -84,6 +84,7 @@ var buildCmd = &cobra.Command{ if err := profileFile.Chmod(0644); err != nil { fmt.Printf("error setting permissions to %s: %v\n", profileFile.Name(), err) + return } // write to file fmt.Fprintln(profileFile, profile) From e4d685fb3bc367d7d58a2e4ac3d17289920751db Mon Sep 17 00:00:00 2001 From: Alessio Greggi Date: Sun, 4 Aug 2024 23:03:26 +0200 Subject: [PATCH 3/4] remove os.Exit in cmd/capture.go Signed-off-by: Alessio Greggi --- cmd/capture.go | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/cmd/capture.go b/cmd/capture.go index db946c1..6280f19 100644 --- a/cmd/capture.go +++ b/cmd/capture.go @@ -17,7 +17,6 @@ package cmd import ( "fmt" - "os" "strings" "github.com/alegrey91/harpoon/internal/captor" @@ -39,7 +38,7 @@ var captureCmd = &cobra.Command{ by passing the function name symbol and the binary args. `, Example: " harpoon -f main.doSomething ./command arg1 arg2 ...", - Run: func(cmd *cobra.Command, args []string) { + RunE: func(cmd *cobra.Command, args []string) error { functionSymbolList := strings.Split(functionSymbols, ",") @@ -50,8 +49,7 @@ by passing the function name symbol and the binary args. for _, functionSymbol := range functionSymbolList { syscalls, err := captor.Capture(functionSymbol, args, captureOpts) if err != nil { - fmt.Printf("error capturing syscall: %v", err) - os.Exit(1) + return fmt.Errorf("error capturing syscall: %v", err) } saveOpts := writer.WriteOptions{ @@ -59,10 +57,10 @@ by passing the function name symbol and the binary args. Directory: directory, } if err := writer.Write(syscalls, functionSymbols, saveOpts); err != nil { - fmt.Printf("error writing syscalls for symbol %s", functionSymbol) - os.Exit(1) + return fmt.Errorf("error writing syscalls for symbol %s", functionSymbol) } } + return nil }, } From a305baed3516cde8d9b2d206fb05ab8c365dbe9f Mon Sep 17 00:00:00 2001 From: Alessio Greggi Date: Sun, 4 Aug 2024 23:20:44 +0200 Subject: [PATCH 4/4] wrap errors with %w instead of using %v Signed-off-by: Alessio Greggi --- cmd/capture.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/capture.go b/cmd/capture.go index 6280f19..ad888bc 100644 --- a/cmd/capture.go +++ b/cmd/capture.go @@ -49,7 +49,7 @@ by passing the function name symbol and the binary args. for _, functionSymbol := range functionSymbolList { syscalls, err := captor.Capture(functionSymbol, args, captureOpts) if err != nil { - return fmt.Errorf("error capturing syscall: %v", err) + return fmt.Errorf("error capturing syscall: %w", err) } saveOpts := writer.WriteOptions{ @@ -57,7 +57,7 @@ by passing the function name symbol and the binary args. Directory: directory, } if err := writer.Write(syscalls, functionSymbols, saveOpts); err != nil { - return fmt.Errorf("error writing syscalls for symbol %s", functionSymbol) + return fmt.Errorf("error writing syscalls for symbol %s: %w", functionSymbol, err) } } return nil