From 7a138d5d8a239104ba1134bd083500f87a42bbde Mon Sep 17 00:00:00 2001 From: "Victor M. Alvarez" Date: Sat, 14 Sep 2024 20:34:29 +0200 Subject: [PATCH] refactor(go): avoid using `unsafe.Pointer` for passing handles (#195) Functions that receive a `void*` in the C world must be called with an `unsafe.Pointer` from the Go world. However, we are not really passing a pointer, but a "handle", and coercing a handle into an `unsafe.Pointer` is problematic (`go vet` complains with "possible misuse of unsafe.Pointer", and it could cause a panic as described in https://i.hsfzxjy.site/invalid-pointer-will-bite-you/). The solution is introducing small C stub functions that receive an `uintptr_t` instead of a `void*`, and the call the original function, casting the `uintptr_t` into `void*`. --- go/compiler.go | 10 +++++----- go/example_test.go | 2 +- go/main.go | 45 ++++++++++++++++++++++++++++++--------------- go/scanner.go | 22 ++++++++++++++-------- 4 files changed, 50 insertions(+), 29 deletions(-) diff --git a/go/compiler.go b/go/compiler.go index c6ad5ea2..ed509872 100644 --- a/go/compiler.go +++ b/go/compiler.go @@ -176,17 +176,17 @@ type Compiler struct { cCompiler *C.YRX_COMPILER relaxedReSyntax bool errorOnSlowPattern bool - ignoredModules map[string]bool - vars map[string]interface{} - features []string + ignoredModules map[string]bool + vars map[string]interface{} + features []string } // NewCompiler creates a new compiler. func NewCompiler(opts ...CompileOption) (*Compiler, error) { c := &Compiler{ ignoredModules: make(map[string]bool), - vars: make(map[string]interface{}), - features: make([]string, 0), + vars: make(map[string]interface{}), + features: make([]string, 0), } for _, opt := range opts { diff --git a/go/example_test.go b/go/example_test.go index 4c58525d..735d6583 100644 --- a/go/example_test.go +++ b/go/example_test.go @@ -72,4 +72,4 @@ func Example_compilerAndScanner() { // Output: // rule foo matched // rule bar matched -} \ No newline at end of file +} diff --git a/go/main.go b/go/main.go index dd3267f9..59700eef 100644 --- a/go/main.go +++ b/go/main.go @@ -5,27 +5,42 @@ package yara_x // #cgo static_link pkg-config: --static yara_x_capi // #include // -// static uint64_t meta_i64(void* value) { +// static inline uint64_t meta_i64(void* value) { // return ((YRX_METADATA_VALUE*) value)->i64; // } // -// static double meta_f64(void* value) { +// static inline double meta_f64(void* value) { // return ((YRX_METADATA_VALUE*) value)->f64; // } // -// static bool meta_bool(void* value) { +// static inline bool meta_bool(void* value) { // return ((YRX_METADATA_VALUE*) value)->boolean; // } // -// static char* meta_str(void* value) { +// static inline char* meta_str(void* value) { // return ((YRX_METADATA_VALUE*) value)->string; // } // -// static YRX_METADATA_BYTES* meta_bytes(void* value) { +// static inline YRX_METADATA_BYTES* meta_bytes(void* value) { // return &(((YRX_METADATA_VALUE*) value)->bytes); // } // -// void onRule(YRX_RULE*, void*); +// enum YRX_RESULT static inline _yrx_rules_iterate( +// struct YRX_RULES *rules, +// YRX_RULE_CALLBACK callback, +// uintptr_t user_data) { +// return yrx_rules_iterate(rules, callback, (void*) user_data); +// } +// +// enum YRX_RESULT static inline _yrx_rules_iterate_imports( +// struct YRX_RULES *rules, +// YRX_IMPORT_CALLBACK callback, +// uintptr_t user_data) { +// return yrx_rules_iterate_imports(rules, callback, (void*) user_data); +// } +// +// +// void onRule(YRX_RULE*, uintptr_t); // void onImport(char*, void*); import "C" @@ -158,8 +173,8 @@ func (r *Rules) Destroy() { // called. // //export onRule -func onRule(rule *C.YRX_RULE, handle unsafe.Pointer) { - h := (cgo.Handle)(handle) +func onRule(rule *C.YRX_RULE, handle C.uintptr_t) { + h := cgo.Handle(handle) rules, ok := h.Value().(*[]*Rule) if !ok { panic("onRule didn't receive a *[]Rule") @@ -174,10 +189,10 @@ func (r *Rules) Slice() []*Rule { handle := cgo.NewHandle(&rules) defer handle.Delete() - C.yrx_rules_iterate( + C._yrx_rules_iterate( r.cRules, C.YRX_RULE_CALLBACK(C.onRule), - unsafe.Pointer(handle)) + C.uintptr_t(handle)) runtime.KeepAlive(r) return rules @@ -197,7 +212,7 @@ func (r *Rules) Count() int { // //export onImport func onImport(module_name *C.char, handle unsafe.Pointer) { - h := (cgo.Handle)(handle) + h := cgo.Handle(handle) imports, ok := h.Value().(*[]string) if !ok { panic("onImport didn't receive a *[]string") @@ -208,13 +223,13 @@ func onImport(module_name *C.char, handle unsafe.Pointer) { // Count returns the total number of rules. func (r *Rules) Imports() []string { imports := make([]string, 0) - handle := cgo.NewHandle(&imports) - defer handle.Delete() + handle := cgo.NewHandle(&imports) + defer handle.Delete() - C.yrx_rules_iterate_imports( + C._yrx_rules_iterate_imports( r.cRules, C.YRX_RULE_CALLBACK(C.onImport), - unsafe.Pointer(handle)) + C.uintptr_t(handle)) runtime.KeepAlive(r) return imports diff --git a/go/scanner.go b/go/scanner.go index 5b1de4bd..00bfe05a 100644 --- a/go/scanner.go +++ b/go/scanner.go @@ -2,7 +2,14 @@ package yara_x // #include // -// void onMatchingRule(YRX_RULE*, void*); +// enum YRX_RESULT static inline _yrx_scanner_on_matching_rule( +// struct YRX_SCANNER *scanner, +// YRX_RULE_CALLBACK callback, +// uintptr_t user_data) { +// return yrx_scanner_on_matching_rule(scanner, callback, (void*) user_data); +// } +// +// void onMatchingRule(YRX_RULE*, uintptr_t); import "C" import ( @@ -19,8 +26,6 @@ import ( "google.golang.org/protobuf/proto" ) - - // Scanner scans data with a set of compiled YARA rules. type Scanner struct { // Pointer to C-side scanner. @@ -55,9 +60,10 @@ func (s ScanResults) MatchingRules() []*Rule { } // This is the callback function called every time a YARA rule matches. +// //export onMatchingRule -func onMatchingRule(rule *C.YRX_RULE, handle unsafe.Pointer) { - h := (cgo.Handle)(handle) +func onMatchingRule(rule *C.YRX_RULE, handle C.uintptr_t) { + h := cgo.Handle(handle) scanner, ok := h.Value().(*Scanner) if !ok { panic("onMatchingRule didn't receive a Scanner") @@ -76,13 +82,12 @@ func NewScanner(r *Rules) *Scanner { panic("yrx_scanner_create failed") } - // Create a new handle that points to the scanner. s.handle = cgo.NewHandle(s) - C.yrx_scanner_on_matching_rule( + C._yrx_scanner_on_matching_rule( s.cScanner, C.YRX_RULE_CALLBACK(C.onMatchingRule), - unsafe.Pointer(s.handle)) + C.uintptr_t(s.handle)) runtime.SetFinalizer(s, (*Scanner).Destroy) return s @@ -242,5 +247,6 @@ func (s *Scanner) Destroy() { s.handle.Delete() s.cScanner = nil } + s.rules = nil runtime.SetFinalizer(s, nil) }