Skip to content

Commit

Permalink
refactor(go): avoid using unsafe.Pointer for passing handles (#195)
Browse files Browse the repository at this point in the history
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*`.
  • Loading branch information
plusvic authored Sep 14, 2024
1 parent 55ccf51 commit 7a138d5
Show file tree
Hide file tree
Showing 4 changed files with 50 additions and 29 deletions.
10 changes: 5 additions & 5 deletions go/compiler.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
2 changes: 1 addition & 1 deletion go/example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,4 +72,4 @@ func Example_compilerAndScanner() {
// Output:
// rule foo matched
// rule bar matched
}
}
45 changes: 30 additions & 15 deletions go/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,27 +5,42 @@ package yara_x
// #cgo static_link pkg-config: --static yara_x_capi
// #include <yara_x.h>
//
// 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"

Expand Down Expand Up @@ -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")
Expand All @@ -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
Expand All @@ -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")
Expand All @@ -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
Expand Down
22 changes: 14 additions & 8 deletions go/scanner.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,14 @@ package yara_x

// #include <yara_x.h>
//
// 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 (
Expand All @@ -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.
Expand Down Expand Up @@ -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")
Expand All @@ -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
Expand Down Expand Up @@ -242,5 +247,6 @@ func (s *Scanner) Destroy() {
s.handle.Delete()
s.cScanner = nil
}
s.rules = nil
runtime.SetFinalizer(s, nil)
}

0 comments on commit 7a138d5

Please sign in to comment.