diff -Nru golang-github-hillu-go-yara-1.0.9/cbpool.go golang-github-hillu-go-yara-1.1.0/cbpool.go --- golang-github-hillu-go-yara-1.0.9/cbpool.go 1970-01-01 00:00:00.000000000 +0000 +++ golang-github-hillu-go-yara-1.1.0/cbpool.go 2018-11-07 09:10:02.000000000 +0000 @@ -0,0 +1,103 @@ +// Copyright © 2018 Hilko Bengen +// All rights reserved. +// +// Use of this source code is governed by the license that can be +// found in the LICENSE file. + +package yara + +import ( + "reflect" + "runtime" + "sync" + "unsafe" +) + +// #include +import "C" + +// cbPoolPool implements a key/value store for data that is safe +// to pass as callback data through CGO functions. +// +// The keys are pointers which do not directly reference the stored +// values, therefore any "Go pointer to Go pointer" errors are avoided. +type cbPool struct { + indices []int + objects []interface{} + m sync.RWMutex +} + +// MakePool creates a Pool that can hold n elements. +func makecbPool(n int) *cbPool { + p := &cbPool{ + indices: make([]int, 0), + objects: make([]interface{}, n), + } + hdr := (*reflect.SliceHeader)(unsafe.Pointer(&p.indices)) + hdr.Data = uintptr(C.calloc(C.size_t(n), C.size_t(unsafe.Sizeof(int(0))))) + hdr.Len = n + runtime.SetFinalizer(p, (*cbPool).Finalize) + return p +} + +// Put adds an element to the cbPool, returning a stable pointer +// suitable for passing through CGO. It panics if the pool is full. +func (p *cbPool) Put(obj interface{}) unsafe.Pointer { + p.m.Lock() + defer p.m.Unlock() + for id, val := range p.indices { + if val != 0 { + continue + } + p.indices[id] = id + 1 + p.objects[id] = obj + return unsafe.Pointer(&p.indices[id]) + } + panic("cbPool storage exhausted") +} + +func (p *cbPool) checkPointer(ptr unsafe.Pointer) { + if uintptr(ptr) < uintptr(unsafe.Pointer(&p.indices[0])) || + uintptr(unsafe.Pointer(&p.indices[len(p.indices)-1])) < uintptr(ptr) { + panic("Attempt to access pool using invalid pointer") + } +} + +// Put accesses an element stored in the cbPool, using a pointer +// previously returned by Put. It panics if the pointer is invalid or +// if it references an empty slot. +func (p *cbPool) Get(ptr unsafe.Pointer) interface{} { + p.m.RLock() + defer p.m.RUnlock() + p.checkPointer(ptr) + id := *(*int)(ptr) - 1 + if id == -1 { + panic("Attempt to get nonexistent value from pool") + } + return p.objects[id] +} + +// Delete removes an element from the cbPool, using a pointer previously +// returned by Put. It panics if the pointer is invalid or if it +// references an empty slot. +func (p *cbPool) Delete(ptr unsafe.Pointer) { + p.m.Lock() + defer p.m.Unlock() + p.checkPointer(ptr) + id := *(*int)(ptr) - 1 + if id == -1 { + panic("Attempt to delete nonexistent value from pool") + } + p.indices[id] = 0 + p.objects[id] = nil + return +} + +func (p *cbPool) Finalize() { + p.m.Lock() + defer p.m.Unlock() + if p.indices != nil { + C.free(unsafe.Pointer(&p.indices[0])) + p.indices = nil + } +} diff -Nru golang-github-hillu-go-yara-1.0.9/cbpool_test.go golang-github-hillu-go-yara-1.1.0/cbpool_test.go --- golang-github-hillu-go-yara-1.0.9/cbpool_test.go 1970-01-01 00:00:00.000000000 +0000 +++ golang-github-hillu-go-yara-1.1.0/cbpool_test.go 2018-11-07 09:10:02.000000000 +0000 @@ -0,0 +1,63 @@ +// Copyright © 2018 Hilko Bengen +// All rights reserved. +// +// Use of this source code is governed by the license that can be +// found in the LICENSE file. + +package yara + +import ( + "testing" +) + +func TestBasic(t *testing.T) { + pool := makecbPool(32) + p1 := pool.Put("asdf") + p2 := pool.Put("ghjk") + s1, ok := pool.Get(p1).(string) + if !ok || s1 != "asdf" { + t.Errorf("s1: expected 'asdf', got '%v'", s1) + } + pool.Delete(p1) + i := func() interface{} { + defer func() { + if x := recover(); x != nil { + t.Logf("Get: Got expected panic: %v", x) + } + }() + x := pool.Get(p1) + t.Error("Get: No panic was triggered.") + return x + }() + if s1, ok := i.(string); ok || s1 == "asdf" { + t.Errorf("s1: expected nil, got '%v'", s1) + } + s2, ok := pool.Get(p2).(string) + if !ok || s2 != "ghjk" { + t.Errorf("s1: expected 'hjkl', got '%v'", s1) + } + pool.Delete(p2) + func() { + defer func() { + if x := recover(); x != nil { + t.Logf("Delete: Got expected panic: %v", x) + } + }() + pool.Delete(p2) + t.Error("Delete: No panic was triggered.") + }() + + // Fill pool + for i := 0; i < 32; i++ { + pool.Put(i) + } + func() { + defer func() { + if x := recover(); x != nil { + t.Logf("full pool: Got expected panic: %v", x) + } + }() + pool.Put(100) + t.Error("full pool: No panic was triggered.") + }() +} diff -Nru golang-github-hillu-go-yara-1.0.9/compiler_addfile.go golang-github-hillu-go-yara-1.1.0/compiler_addfile.go --- golang-github-hillu-go-yara-1.0.9/compiler_addfile.go 1970-01-01 00:00:00.000000000 +0000 +++ golang-github-hillu-go-yara-1.1.0/compiler_addfile.go 2018-11-07 09:10:02.000000000 +0000 @@ -0,0 +1,66 @@ +// Copyright © 2018 Hilko Bengen +// All rights reserved. +// +// Use of this source code is governed by the license that can be +// found in the LICENSE file. + +// +build yara3.4 yara3.5 + +package yara + +/* +#include + +#include +#include +#include + +#ifdef _WIN32 +#define fdopen _fdopen +#define dup _dup +#endif + +void compilerCallback(int, char*, int, char*, void*); +*/ +import "C" +import ( + "errors" + "os" + "unsafe" +) + +// AddFile compiles rules from a file. Rules are added to the +// specified namespace. +// +// If this function returns an error, the Compiler object will become +// unusable. +func (c *Compiler) AddFile(file *os.File, namespace string) (err error) { + if c.cptr.errors != 0 { + return errors.New("Compiler cannot be used after parse error") + } + fd := C.dup(C.int(file.Fd())) + fh, err := C.fdopen(fd, C.CString("r")) + if err != nil { + return err + } + defer C.fclose(fh) + var ns *C.char + if namespace != "" { + ns = C.CString(namespace) + defer C.free(unsafe.Pointer(ns)) + } + filename := C.CString(file.Name()) + defer C.free(unsafe.Pointer(filename)) + id := callbackData.Put(c) + defer callbackData.Delete(id) + C.yr_compiler_set_callback(c.cptr, C.YR_COMPILER_CALLBACK_FUNC(C.compilerCallback), id) + numErrors := int(C.yr_compiler_add_file(c.cptr, fh, ns, filename)) + if numErrors > 0 { + var buf [1024]C.char + msg := C.GoString(C.yr_compiler_get_error_message( + c.cptr, (*C.char)(unsafe.Pointer(&buf[0])), 1024)) + err = errors.New(msg) + } + keepAlive(c) + return +} diff -Nru golang-github-hillu-go-yara-1.0.9/compiler_addfile_yara36.go golang-github-hillu-go-yara-1.1.0/compiler_addfile_yara36.go --- golang-github-hillu-go-yara-1.0.9/compiler_addfile_yara36.go 1970-01-01 00:00:00.000000000 +0000 +++ golang-github-hillu-go-yara-1.1.0/compiler_addfile_yara36.go 2018-11-07 09:10:02.000000000 +0000 @@ -0,0 +1,52 @@ +// Copyright © 2018 Hilko Bengen +// All rights reserved. +// +// Use of this source code is governed by the license that can be +// found in the LICENSE file. + +// +build !yara3.4,!yara3.5 + +package yara + +/* +#include +#include + +void compilerCallback(int, char*, int, char*, void*); +*/ +import "C" +import ( + "errors" + "os" + "unsafe" +) + +// AddFile compiles rules from a file. Rules are added to the +// specified namespace. +// +// If this function returns an error, the Compiler object will become +// unusable. +func (c *Compiler) AddFile(file *os.File, namespace string) (err error) { + if c.cptr.errors != 0 { + return errors.New("Compiler cannot be used after parse error") + } + var ns *C.char + if namespace != "" { + ns = C.CString(namespace) + defer C.free(unsafe.Pointer(ns)) + } + filename := C.CString(file.Name()) + defer C.free(unsafe.Pointer(filename)) + id := callbackData.Put(c) + defer callbackData.Delete(id) + C.yr_compiler_set_callback(c.cptr, C.YR_COMPILER_CALLBACK_FUNC(C.compilerCallback), id) + numErrors := int(C.yr_compiler_add_fd(c.cptr, (C.YR_FILE_DESCRIPTOR)(file.Fd()), ns, filename)) + if numErrors > 0 { + var buf [1024]C.char + msg := C.GoString(C.yr_compiler_get_error_message( + c.cptr, (*C.char)(unsafe.Pointer(&buf[0])), 1024)) + err = errors.New(msg) + } + keepAlive(c) + return +} diff -Nru golang-github-hillu-go-yara-1.0.9/compiler.go golang-github-hillu-go-yara-1.1.0/compiler.go --- golang-github-hillu-go-yara-1.0.9/compiler.go 2018-07-10 12:28:32.000000000 +0000 +++ golang-github-hillu-go-yara-1.1.0/compiler.go 2018-11-07 09:10:02.000000000 +0000 @@ -21,7 +21,6 @@ import "C" import ( "errors" - "os" "runtime" "unsafe" ) @@ -100,39 +99,15 @@ } } -// AddFile compiles rules from a file. Rules are added to the -// specified namespace. -func (c *Compiler) AddFile(file *os.File, namespace string) (err error) { - fd := C.dup(C.int(file.Fd())) - fh, err := C.fdopen(fd, C.CString("r")) - if err != nil { - return err - } - defer C.fclose(fh) - var ns *C.char - if namespace != "" { - ns = C.CString(namespace) - defer C.free(unsafe.Pointer(ns)) - } - filename := C.CString(file.Name()) - defer C.free(unsafe.Pointer(filename)) - id := callbackData.Put(c) - defer callbackData.Delete(id) - C.yr_compiler_set_callback(c.cptr, C.YR_COMPILER_CALLBACK_FUNC(C.compilerCallback), id) - numErrors := int(C.yr_compiler_add_file(c.cptr, fh, ns, filename)) - if numErrors > 0 { - var buf [1024]C.char - msg := C.GoString(C.yr_compiler_get_error_message( - c.cptr, (*C.char)(unsafe.Pointer(&buf[0])), 1024)) - err = errors.New(msg) - } - keepAlive(c) - return -} - // AddString compiles rules from a string. Rules are added to the // specified namespace. +// +// If this function returns an error, the Compiler object will become +// unusable. func (c *Compiler) AddString(rules string, namespace string) (err error) { + if c.cptr.errors != 0 { + return errors.New("Compiler cannot be used after parse error") + } var ns *C.char if namespace != "" { ns = C.CString(namespace) @@ -188,6 +163,9 @@ // GetRules returns the compiled ruleset. func (c *Compiler) GetRules() (*Rules, error) { + if c.cptr.errors != 0 { + return nil, errors.New("Compiler cannot be used after parse error") + } var yrRules *C.YR_RULES if err := newError(C.yr_compiler_get_rules(c.cptr, &yrRules)); err != nil { return nil, err @@ -205,6 +183,7 @@ if c, err = NewCompiler(); err != nil { return } + defer c.Destroy() for k, v := range variables { if err = c.DefineVariable(k, v); err != nil { return diff -Nru golang-github-hillu-go-yara-1.0.9/config.go golang-github-hillu-go-yara-1.1.0/config.go --- golang-github-hillu-go-yara-1.0.9/config.go 1970-01-01 00:00:00.000000000 +0000 +++ golang-github-hillu-go-yara-1.1.0/config.go 2018-11-07 09:10:02.000000000 +0000 @@ -0,0 +1,40 @@ +// Copyright © 2015-2017 Hilko Bengen . All rights reserved. +// Use of this source code is governed by the license that can be +// found in the LICENSE file. + +// +build !yara3.3,!yara3.4 + +package yara + +// #include +import "C" +import "unsafe" + +type ConfigName uint32 + +const ( + ConfigStackSize ConfigName = C.YR_CONFIG_STACK_SIZE + ConfigMaxStringsPerRule = C.YR_CONFIG_MAX_STRINGS_PER_RULE +) + +// SetCnofiguration sets a global YARA configuration option. +func SetConfiguration(name ConfigName, src interface{}) error { + i, ok := src.(int) + if !ok { + return newError(C.ERROR_INTERNAL_FATAL_ERROR) + } + u := C.uint32_t(i) + return newError( + C.yr_set_configuration(C.YR_CONFIG_NAME(name), unsafe.Pointer(&u))) +} + +// GetConfiguration gets a global YARA configuration option. +func GetConfiguration(name ConfigName) (interface{}, error) { + var u C.uint32_t + if err := newError(C.yr_get_configuration( + C.YR_CONFIG_NAME(name), unsafe.Pointer(&u)), + ); err != nil { + return nil, err + } + return int(u), nil +} diff -Nru golang-github-hillu-go-yara-1.0.9/config_yara38.go golang-github-hillu-go-yara-1.1.0/config_yara38.go --- golang-github-hillu-go-yara-1.0.9/config_yara38.go 1970-01-01 00:00:00.000000000 +0000 +++ golang-github-hillu-go-yara-1.1.0/config_yara38.go 2018-11-07 09:10:02.000000000 +0000 @@ -0,0 +1,12 @@ +// Copyright © 2015-2017 Hilko Bengen . All rights reserved. +// Use of this source code is governed by the license that can be +// found in the LICENSE file. + +// +build !yara3.3,!yara3.4,!yara3.5,!yara3.6,!yara3.7 + +package yara + +// #include +import "C" + +const ConfigMaxMatchData ConfigName = C.YR_CONFIG_MAX_MATCH_DATA diff -Nru golang-github-hillu-go-yara-1.0.9/debian/changelog golang-github-hillu-go-yara-1.1.0/debian/changelog --- golang-github-hillu-go-yara-1.0.9/debian/changelog 2018-07-14 16:02:56.000000000 +0000 +++ golang-github-hillu-go-yara-1.1.0/debian/changelog 2018-11-13 13:38:53.000000000 +0000 @@ -1,3 +1,10 @@ +golang-github-hillu-go-yara (1.1.0-1) unstable; urgency=medium + + * New upstream release. + * Bump Standards-Version. + + -- Sascha Steinbiss Tue, 13 Nov 2018 14:38:53 +0100 + golang-github-hillu-go-yara (1.0.9-1) unstable; urgency=medium * New upstream release. diff -Nru golang-github-hillu-go-yara-1.0.9/debian/control golang-github-hillu-go-yara-1.1.0/debian/control --- golang-github-hillu-go-yara-1.0.9/debian/control 2018-07-14 16:02:56.000000000 +0000 +++ golang-github-hillu-go-yara-1.1.0/debian/control 2018-11-13 13:38:53.000000000 +0000 @@ -8,7 +8,7 @@ golang-any, pkg-config, libyara-dev (>= 3.4.0) -Standards-Version: 4.1.5 +Standards-Version: 4.2.1 Homepage: https://github.com/hillu/go-yara Vcs-Browser: https://salsa.debian.org/go-team/packages/golang-github-hillu-go-yara Vcs-Git: https://salsa.debian.org/go-team/packages/golang-github-hillu-go-yara.git diff -Nru golang-github-hillu-go-yara-1.0.9/error_yara38.go golang-github-hillu-go-yara-1.1.0/error_yara38.go --- golang-github-hillu-go-yara-1.0.9/error_yara38.go 1970-01-01 00:00:00.000000000 +0000 +++ golang-github-hillu-go-yara-1.1.0/error_yara38.go 2018-11-07 09:10:02.000000000 +0000 @@ -0,0 +1,23 @@ +// Copyright © 2018 Hilko Bengen +// All rights reserved. +// +// Use of this source code is governed by the license that can be +// found in the LICENSE file. + +// This file contains additional error codes introduced with yara 3.7.0. + +// +build !yara3.3,!yara3.4,!yara3.5,!yara3.6,!yara3.7 + +package yara + +// #include +import "C" + +func init() { + errorStrings[C.ERROR_CALLBACK_REQUIRED] = "callback required" + errorStrings[C.ERROR_INVALID_OPERAND] = "invalid operand" + errorStrings[C.ERROR_COULD_NOT_READ_FILE] = "could not read file" + errorStrings[C.ERROR_DUPLICATED_EXTERNAL_VARIABLE] = "duplicated external variable" + errorStrings[C.ERROR_INVALID_MODULE_DATA] = "invalid module data" + errorStrings[C.ERROR_WRITING_FILE] = "error writing file" +} diff -Nru golang-github-hillu-go-yara-1.0.9/internal/callbackdata/pool.go golang-github-hillu-go-yara-1.1.0/internal/callbackdata/pool.go --- golang-github-hillu-go-yara-1.0.9/internal/callbackdata/pool.go 2018-07-10 12:28:32.000000000 +0000 +++ golang-github-hillu-go-yara-1.1.0/internal/callbackdata/pool.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,103 +0,0 @@ -// Copyright © 2018 Hilko Bengen -// All rights reserved. -// -// Use of this source code is governed by the license that can be -// found in the LICENSE file. - -package callbackdata - -import ( - "reflect" - "runtime" - "sync" - "unsafe" -) - -// #include -import "C" - -// Pool implements a key/value store for data that is safe to pass as -// callback data through CGO functions. -// -// The keys are pointers which do not directly reference the stored -// values, therefore any "Go pointer to Go pointer" errors are avoided. -type Pool struct { - indices []int - objects []interface{} - m sync.RWMutex -} - -// MakePool creates a Pool that can hold n elements. -func MakePool(n int) *Pool { - p := &Pool{ - indices: make([]int, 0), - objects: make([]interface{}, n), - } - hdr := (*reflect.SliceHeader)(unsafe.Pointer(&p.indices)) - hdr.Data = uintptr(C.calloc(C.size_t(n), C.size_t(unsafe.Sizeof(int(0))))) - hdr.Len = n - runtime.SetFinalizer(p, (*Pool).Finalize) - return p -} - -// Put adds an element to the Pool, returning a stable pointer -// suitable for passing through CGO. It panics if the pool is full. -func (p *Pool) Put(obj interface{}) unsafe.Pointer { - p.m.Lock() - defer p.m.Unlock() - for id, val := range p.indices { - if val != 0 { - continue - } - p.indices[id] = id + 1 - p.objects[id] = obj - return unsafe.Pointer(&p.indices[id]) - } - panic("Pool storage exhausted") -} - -func (p *Pool) checkPointer(ptr unsafe.Pointer) { - if uintptr(ptr) < uintptr(unsafe.Pointer(&p.indices[0])) || - uintptr(unsafe.Pointer(&p.indices[len(p.indices)-1])) < uintptr(ptr) { - panic("Attempt to access pool using invalid pointer") - } -} - -// Put accesses an element stored in the Pool, using a pointer -// previously returned by Put. It panics if the pointer is invalid or -// if it references an empty slot. -func (p *Pool) Get(ptr unsafe.Pointer) interface{} { - p.m.RLock() - defer p.m.RUnlock() - p.checkPointer(ptr) - id := *(*int)(ptr) - 1 - if id == -1 { - panic("Attempt to get nonexistent value from pool") - } - return p.objects[id] -} - -// Delete removes an element from the Pool, using a pointer previously -// returned by Put. It panics if the pointer is invalid or if it -// references an empty slot. -func (p *Pool) Delete(ptr unsafe.Pointer) { - p.m.Lock() - defer p.m.Unlock() - p.checkPointer(ptr) - id := *(*int)(ptr) - 1 - if id == -1 { - panic("Attempt to delete nonexistent value from pool") - } - p.indices[id] = 0 - p.objects[id] = nil - return -} - -func (p *Pool) Finalize() { - p.m.Lock() - defer p.m.Unlock() - if p.indices != nil { - C.free(unsafe.Pointer(&p.indices[0])) - p.indices = nil - } -} diff -Nru golang-github-hillu-go-yara-1.0.9/internal/callbackdata/pool_test.go golang-github-hillu-go-yara-1.1.0/internal/callbackdata/pool_test.go --- golang-github-hillu-go-yara-1.0.9/internal/callbackdata/pool_test.go 2018-07-10 12:28:32.000000000 +0000 +++ golang-github-hillu-go-yara-1.1.0/internal/callbackdata/pool_test.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,63 +0,0 @@ -// Copyright © 2018 Hilko Bengen -// All rights reserved. -// -// Use of this source code is governed by the license that can be -// found in the LICENSE file. - -package callbackdata - -import ( - "testing" -) - -func TestBasic(t *testing.T) { - pool := MakePool(32) - p1 := pool.Put("asdf") - p2 := pool.Put("ghjk") - s1, ok := pool.Get(p1).(string) - if !ok || s1 != "asdf" { - t.Errorf("s1: expected 'asdf', got '%v'", s1) - } - pool.Delete(p1) - i := func() interface{} { - defer func() { - if x := recover(); x != nil { - t.Logf("Get: Got expected panic: %v", x) - } - }() - x := pool.Get(p1) - t.Error("Get: No panic was triggered.") - return x - }() - if s1, ok := i.(string); ok || s1 == "asdf" { - t.Errorf("s1: expected nil, got '%v'", s1) - } - s2, ok := pool.Get(p2).(string) - if !ok || s2 != "ghjk" { - t.Errorf("s1: expected 'hjkl', got '%v'", s1) - } - pool.Delete(p2) - func() { - defer func() { - if x := recover(); x != nil { - t.Logf("Delete: Got expected panic: %v", x) - } - }() - pool.Delete(p2) - t.Error("Delete: No panic was triggered.") - }() - - // Fill pool - for i := 0; i < 32; i++ { - pool.Put(i) - } - func() { - defer func() { - if x := recover(); x != nil { - t.Logf("full pool: Got expected panic: %v", x) - } - }() - pool.Put(100) - t.Error("full pool: No panic was triggered.") - }() -} diff -Nru golang-github-hillu-go-yara-1.0.9/main.go golang-github-hillu-go-yara-1.1.0/main.go --- golang-github-hillu-go-yara-1.0.9/main.go 1970-01-01 00:00:00.000000000 +0000 +++ golang-github-hillu-go-yara-1.1.0/main.go 2018-11-07 09:10:02.000000000 +0000 @@ -0,0 +1,37 @@ +// Copyright © 2015-2018 Hilko Bengen +// All rights reserved. +// +// Use of this source code is governed by the license that can be +// found in the LICENSE file. + +package yara + +/* +#include +*/ +import "C" + +func init() { + if err := initialize(); err != nil { + panic(err) + } +} + +// Prepares the library to be used. +func initialize() error { + return newError(C.yr_initialize()) +} + +// Finalize releases all the resources allocated by the YARA library. +// It should be called by the program when it no longer needs YARA, +// e.g. just before the program exits. It is not strictly necessary to +// call Finalize because the allocated memory will be freed on program +// exit; however, explicitly-freed resources will not show up as a +// leak in memory profiling tools. +// +// A good practice is calling Finalize as a deferred function in the +// program's main function: +// defer yara.Finalize() +func Finalize() error { + return newError(C.yr_finalize()) +} diff -Nru golang-github-hillu-go-yara-1.0.9/main_test.go golang-github-hillu-go-yara-1.1.0/main_test.go --- golang-github-hillu-go-yara-1.0.9/main_test.go 2018-07-10 12:28:32.000000000 +0000 +++ golang-github-hillu-go-yara-1.1.0/main_test.go 2018-11-07 09:10:02.000000000 +0000 @@ -1,17 +1,32 @@ package yara import ( + "io/ioutil" + "log" "os" "testing" ) +var compiledTestRulesPath string + func TestMain(m *testing.M) { - if r, err := Compile(`rule test : tag1 { meta: author = "Hilko Bengen" strings: $a = "abc" fullword condition: $a }`, nil); err != nil { - os.Exit(1) - } else if err = r.Save("testrules.yac"); err != nil { - os.Exit(1) + r, err := Compile(`rule test : tag1 { meta: author = "Hilko Bengen" strings: $a = "abc" fullword condition: $a }`, nil) + if err != nil { + log.Fatalf("Compile: %v", err) + } + + f, err := ioutil.TempFile("", "testrules.yac") + if err != nil { + log.Fatalf("ioutil.TempFile: %v", err) } + compiledTestRulesPath = f.Name() + + if err := r.Save(compiledTestRulesPath); err != nil { + os.Remove(compiledTestRulesPath) + log.Fatalf("Save(%q): %v", compiledTestRulesPath, err) + } + rc := m.Run() - os.Remove("testrules.yac") + os.Remove(compiledTestRulesPath) os.Exit(rc) } diff -Nru golang-github-hillu-go-yara-1.0.9/README.md golang-github-hillu-go-yara-1.1.0/README.md --- golang-github-hillu-go-yara-1.0.9/README.md 2018-07-10 12:28:32.000000000 +0000 +++ golang-github-hillu-go-yara-1.1.0/README.md 2018-11-07 09:10:02.000000000 +0000 @@ -88,15 +88,18 @@ ## Build Tags _go-yara_ is tested with the latest stable version of YARA, currently -3.7. If you need to to build with an older version of YARA, certain +3.8. If you need to to build with an older version of YARA, certain features that are not present in older versions can be excluded by -passing a build tag such as `yara3.3`, `yara3.4`, `yara3.5`. If you -want to build with a git snapshot of YARA, you may use a build tag +passing a build tag such as `yara3.7`, `yara3.6`, `yara3.5`, etc.. If +you want to build with a git snapshot of YARA, you may use a build tag corresponding to the upcoming stable YARA version, currently -`yara3.8`. +`yara3.9`. You also need to pass the tag when you build your own +project. The build tag `yara_static` can be used to tell the Go toolchain to -run _pkg-config_ with the `--static` switch. +run _pkg-config_ with the `--static` switch. This is not enough for a +static build; the appropriate linker flags (e.g. `-extldflags +"-static"`) still need to be passed to the _go_ tool. The build tag `no_pkg_config` can be used to tell the Go toolchain not to use _pkg-config_'s output. In this case, any compiler or linker diff -Nru golang-github-hillu-go-yara-1.0.9/rule.go golang-github-hillu-go-yara-1.1.0/rule.go --- golang-github-hillu-go-yara-1.0.9/rule.go 2018-07-10 12:28:32.000000000 +0000 +++ golang-github-hillu-go-yara-1.1.0/rule.go 2018-11-07 09:10:02.000000000 +0000 @@ -89,7 +89,6 @@ */ import "C" -import "unsafe" // Rule represents a single rule as part of a ruleset type Rule struct{ cptr *C.YR_RULE } @@ -188,11 +187,6 @@ return } -// Data returns the blob of data associated with the string match -func (m *Match) Data() []byte { - return C.GoBytes(unsafe.Pointer(m.cptr.data), C.int(m.cptr.data_length)) -} - // Offset returns the offset at which the string match occurred func (m *Match) Offset() int64 { return int64(m.cptr.offset) diff -Nru golang-github-hillu-go-yara-1.0.9/rules_callback.go golang-github-hillu-go-yara-1.1.0/rules_callback.go --- golang-github-hillu-go-yara-1.0.9/rules_callback.go 2018-07-10 12:28:32.000000000 +0000 +++ golang-github-hillu-go-yara-1.1.0/rules_callback.go 2018-11-07 09:10:02.000000000 +0000 @@ -3,6 +3,11 @@ /* #include #include + +// Constant not defined until YARA 3.5 +#ifndef CALLBACK_MSG_MODULE_IMPORTED +# define CALLBACK_MSG_MODULE_IMPORTED 5 +#endif */ import "C" import ( @@ -51,43 +56,65 @@ ModuleImported(*Object) (bool, error) } +// scanCallbackContainer is used by (*Rules).Scan* methods and +// scanCallbackFunc(). It stores the public callback interface and a +// list of C pointers that need to be freed later. +type scanCallbackContainer struct { + ScanCallback + cdata []unsafe.Pointer +} + +// addCPointer adds a C pointer that can later be freed using free(). +func (c *scanCallbackContainer) addCPointer(p unsafe.Pointer) { c.cdata = append(c.cdata, p) } + +// destroy frees stored C pointers +func (c *scanCallbackContainer) destroy() { + for _, p := range c.cdata { + C.free(p) + } + c.cdata = nil +} + //export scanCallbackFunc func scanCallbackFunc(message C.int, messageData, userData unsafe.Pointer) C.int { - cb := callbackData.Get(userData) + cbc, ok := callbackData.Get(userData).(*scanCallbackContainer) + if !ok { + return C.CALLBACK_ERROR + } var abort bool var err error switch message { case C.CALLBACK_MSG_RULE_MATCHING: - if c, ok := cb.(ScanCallbackMatch); ok { + if c, ok := cbc.ScanCallback.(ScanCallbackMatch); ok { r := (*C.YR_RULE)(messageData) abort, err = c.RuleMatching(&Rule{r}) } case C.CALLBACK_MSG_RULE_NOT_MATCHING: - if c, ok := cb.(ScanCallbackNoMatch); ok { + if c, ok := cbc.ScanCallback.(ScanCallbackNoMatch); ok { r := (*C.YR_RULE)(messageData) abort, err = c.RuleNotMatching(&Rule{r}) } case C.CALLBACK_MSG_SCAN_FINISHED: - if c, ok := cb.(ScanCallbackFinished); ok { + if c, ok := cbc.ScanCallback.(ScanCallbackFinished); ok { abort, err = c.ScanFinished() } case C.CALLBACK_MSG_IMPORT_MODULE: - if c, ok := cb.(ScanCallbackModuleImport); ok { + if c, ok := cbc.ScanCallback.(ScanCallbackModuleImport); ok { mi := (*C.YR_MODULE_IMPORT)(messageData) var buf []byte if buf, abort, err = c.ImportModule(C.GoString(mi.module_name)); len(buf) == 0 { break } - // FIXME: Memory leak: When/how should this buffer be free()d? cbuf := C.calloc(1, C.size_t(len(buf))) outbuf := make([]byte, 0) hdr := (*reflect.SliceHeader)(unsafe.Pointer(&outbuf)) hdr.Data, hdr.Len = uintptr(cbuf), len(buf) copy(outbuf, buf) mi.module_data, mi.module_data_size = unsafe.Pointer(&outbuf[0]), C.size_t(len(outbuf)) + cbc.addCPointer(cbuf) } case C.CALLBACK_MSG_MODULE_IMPORTED: - if c, ok := cb.(ScanCallbackModuleImportFinished); ok { + if c, ok := cbc.ScanCallback.(ScanCallbackModuleImportFinished); ok { obj := (*C.YR_OBJECT)(messageData) abort, err = c.ModuleImported(&Object{obj}) } diff -Nru golang-github-hillu-go-yara-1.0.9/rules.go golang-github-hillu-go-yara-1.1.0/rules.go --- golang-github-hillu-go-yara-1.0.9/rules.go 2018-07-10 12:28:32.000000000 +0000 +++ golang-github-hillu-go-yara-1.1.0/rules.go 2018-11-07 09:10:02.000000000 +0000 @@ -48,10 +48,6 @@ Data []byte } -func init() { - _ = C.yr_initialize() -} - // ScanFlags are used to tweak the behavior of Scan* functions. type ScanFlags int @@ -82,7 +78,9 @@ if len(buf) > 0 { ptr = (*C.uint8_t)(unsafe.Pointer(&(buf[0]))) } - id := callbackData.Put(cb) + cbc := &scanCallbackContainer{ScanCallback: cb} + defer cbc.destroy() + id := callbackData.Put(cbc) defer callbackData.Delete(id) err = newError(C.yr_rules_scan_mem( r.cptr, @@ -111,7 +109,9 @@ func (r *Rules) ScanFileWithCallback(filename string, flags ScanFlags, timeout time.Duration, cb ScanCallback) (err error) { cfilename := C.CString(filename) defer C.free(unsafe.Pointer(cfilename)) - id := callbackData.Put(cb) + cbc := &scanCallbackContainer{ScanCallback: cb} + defer cbc.destroy() + id := callbackData.Put(cbc) defer callbackData.Delete(id) err = newError(C.yr_rules_scan_file( r.cptr, @@ -137,7 +137,9 @@ // every event emitted by libyara, the appropriate method on the // ScanCallback object is called. func (r *Rules) ScanProcWithCallback(pid int, flags ScanFlags, timeout time.Duration, cb ScanCallback) (err error) { - id := callbackData.Put(cb) + cbc := &scanCallbackContainer{ScanCallback: cb} + defer cbc.destroy() + id := callbackData.Put(cbc) defer callbackData.Delete(id) err = newError(C.yr_rules_scan_proc( r.cptr, diff -Nru golang-github-hillu-go-yara-1.0.9/rules_test.go golang-github-hillu-go-yara-1.1.0/rules_test.go --- golang-github-hillu-go-yara-1.0.9/rules_test.go 2018-07-10 12:28:32.000000000 +0000 +++ golang-github-hillu-go-yara-1.1.0/rules_test.go 2018-11-07 09:10:02.000000000 +0000 @@ -96,14 +96,14 @@ } func TestLoad(t *testing.T) { - r, err := LoadRules("testrules.yac") + r, err := LoadRules(compiledTestRulesPath) if r == nil || err != nil { t.Fatalf("LoadRules: %s", err) } } func TestReader(t *testing.T) { - rd, err := os.Open("testrules.yac") + rd, err := os.Open(compiledTestRulesPath) if err != nil { t.Fatalf("os.Open: %s", err) } @@ -119,7 +119,7 @@ } func TestWriter(t *testing.T) { - rd, err := os.Open("testrules.yac") + rd, err := os.Open(compiledTestRulesPath) if err != nil { t.Fatalf("os.Open: %s", err) } @@ -242,33 +242,79 @@ } } -type mycb struct{ t *testing.T } +type testCallback struct { + t *testing.T + finished bool + modules map[string]struct{} + matched map[string]struct{} + notMatched map[string]struct{} +} + +func newTestCallback(t *testing.T) *testCallback { + return &testCallback{ + t, false, + make(map[string]struct{}), + make(map[string]struct{}), + make(map[string]struct{}), + } +} -func (c mycb) RuleMatching(r *Rule) (bool, error) { +func (c *testCallback) RuleMatching(r *Rule) (bool, error) { c.t.Logf("RuleMatching callback called: rule=%s", r.Identifier()) + c.matched[r.Identifier()] = struct{}{} return false, nil } -func (c mycb) RuleNotMatching(r *Rule) (bool, error) { +func (c *testCallback) RuleNotMatching(r *Rule) (bool, error) { c.t.Logf("RuleNotMatching callback called: rule=%s", r.Identifier()) + c.notMatched[r.Identifier()] = struct{}{} return false, nil } -func (c mycb) ScanFinished() (bool, error) { +func (c *testCallback) ScanFinished() (bool, error) { c.t.Log("ScanFinished callback called") + c.finished = true return false, nil } -func (c *mycb) ImportModule(s string) ([]byte, bool, error) { +func (c *testCallback) ImportModule(s string) ([]byte, bool, error) { c.t.Logf("ImportModule callback called: module=%s", s) - return []byte("{}"), false, nil + c.modules[s] = struct{}{} + if s == "tests" { + return []byte("callback-data-for-tests-module"), false, nil + } + return nil, false, nil } -func (c *mycb) ModuleImported(*Object) (bool, error) { +func (c *testCallback) ModuleImported(*Object) (bool, error) { c.t.Log("ModuleImported callback called") return false, nil } func TestImportDataCallback(t *testing.T) { - r := makeRules(t, `import "tests" import "pe" rule t1 { condition: true } rule t2 { condition: false }`) - if err := r.ScanMemWithCallback([]byte(""), 0, 0, &mycb{t}); err != nil { + cb := newTestCallback(t) + r := makeRules(t, ` + import "tests" + import "pe" + rule t1 { condition: true } + rule t2 { condition: false } + rule t3 { + condition: tests.module_data == "callback-data-for-tests-module" + }`) + if err := r.ScanMemWithCallback([]byte(""), 0, 0, cb); err != nil { t.Error(err) } + for _, module := range []string{"tests", "pe"} { + if _, ok := cb.modules[module]; !ok { + t.Errorf("ImportModule was not called for %s", module) + } + } + for _, rule := range []string{"t1", "t3"} { + if _, ok := cb.matched["t1"]; !ok { + t.Errorf("RuleMatching was not called for %s", rule) + } + } + if _, ok := cb.notMatched["t2"]; !ok { + t.Errorf("RuleNotMatching was not called for %s", "t2") + } + if !cb.finished { + t.Errorf("ScanFinished was not called") + } runtime.GC() } diff -Nru golang-github-hillu-go-yara-1.0.9/rules_yara34.go golang-github-hillu-go-yara-1.1.0/rules_yara34.go --- golang-github-hillu-go-yara-1.0.9/rules_yara34.go 2018-07-10 12:28:32.000000000 +0000 +++ golang-github-hillu-go-yara-1.1.0/rules_yara34.go 2018-11-07 09:10:02.000000000 +0000 @@ -14,6 +14,7 @@ #include #ifdef _WIN32 +#include // Helper function that is merely used to cast fd from int to HANDLE. // CGO treats HANDLE (void*) to an unsafe.Pointer. This confuses the // go1.4 garbage collector, leading to runtime errors such as: @@ -27,7 +28,7 @@ void* user_data, int timeout) { - return yr_rules_scan_fd(rules, (YR_FILE_DESCRIPTOR)fd, flags, callback, user_data, timeout); + return yr_rules_scan_fd(rules, (YR_FILE_DESCRIPTOR)(intptr_t)fd, flags, callback, user_data, timeout); } #else #define _yr_rules_scan_fd yr_rules_scan_fd @@ -58,7 +59,9 @@ // emitted by libyara, the appropriate method on the ScanCallback // object is called. func (r *Rules) ScanFileDescriptorWithCallback(fd uintptr, flags ScanFlags, timeout time.Duration, cb ScanCallback) (err error) { - id := callbackData.Put(cb) + cbc := &scanCallbackContainer{ScanCallback: cb} + defer cbc.destroy() + id := callbackData.Put(cbc) defer callbackData.Delete(id) err = newError(C._yr_rules_scan_fd( r.cptr, diff -Nru golang-github-hillu-go-yara-1.0.9/rule_yara34.go golang-github-hillu-go-yara-1.1.0/rule_yara34.go --- golang-github-hillu-go-yara-1.0.9/rule_yara34.go 1970-01-01 00:00:00.000000000 +0000 +++ golang-github-hillu-go-yara-1.1.0/rule_yara34.go 2018-11-07 09:10:02.000000000 +0000 @@ -0,0 +1,31 @@ +// Copyright © 2018 Hilko Bengen +// All rights reserved. +// +// Use of this source code is governed by the license that can be +// found in the LICENSE file. + +// +build yara3.3 yara3.4 + +package yara + +// #include +import "C" +import ( + "reflect" + "unsafe" +) + +// Data returns the blob of data associated with the string match +func (m *Match) Data() []byte { + tmpbuf := []byte{} + // Use unsafe instead of C.GoBytes to avoid "cgo argument has Go + // pointer to Go pointer" panic (see + // https://github.com/hillu/go-yara/issues/5) + hdr := (*reflect.SliceHeader)(unsafe.Pointer(&tmpbuf)) + hdr.Data = uintptr(*(*unsafe.Pointer)(unsafe.Pointer(&(m.cptr.anon0)))) + hdr.Len = int(m.cptr.length) + hdr.Cap = int(m.cptr.length) + buf := make([]byte, len(tmpbuf)) + copy(buf, tmpbuf) + return buf +} diff -Nru golang-github-hillu-go-yara-1.0.9/rule_yara35.go golang-github-hillu-go-yara-1.1.0/rule_yara35.go --- golang-github-hillu-go-yara-1.0.9/rule_yara35.go 1970-01-01 00:00:00.000000000 +0000 +++ golang-github-hillu-go-yara-1.1.0/rule_yara35.go 2018-11-07 09:10:02.000000000 +0000 @@ -0,0 +1,18 @@ +// Copyright © 2018 Hilko Bengen +// All rights reserved. +// +// Use of this source code is governed by the license that can be +// found in the LICENSE file. + +// +build !yara3.3,!yara3.4 + +package yara + +// #include +import "C" +import "unsafe" + +// Data returns the blob of data associated with the string match +func (m *Match) Data() []byte { + return C.GoBytes(unsafe.Pointer(m.cptr.data), C.int(m.cptr.data_length)) +} diff -Nru golang-github-hillu-go-yara-1.0.9/.travis.yml golang-github-hillu-go-yara-1.1.0/.travis.yml --- golang-github-hillu-go-yara-1.0.9/.travis.yml 2018-07-10 12:28:32.000000000 +0000 +++ golang-github-hillu-go-yara-1.1.0/.travis.yml 2018-11-07 09:10:02.000000000 +0000 @@ -19,8 +19,8 @@ - make - gcc before_install: - - wget --no-verbose -O- https://github.com/VirusTotal/yara/archive/v3.7.0.tar.gz | tar -C ${TRAVIS_BUILD_DIR} -xzf - - - ( cd ${TRAVIS_BUILD_DIR}/yara-3.7.0 && ./bootstrap.sh && ./configure && make ) - - export CGO_CFLAGS=-I${TRAVIS_BUILD_DIR}/yara-3.7.0/libyara/include - - export CGO_LDFLAGS=-L${TRAVIS_BUILD_DIR}/yara-3.7.0/libyara/.libs - - export LD_LIBRARY_PATH=${TRAVIS_BUILD_DIR}/yara-3.7.0/libyara/.libs + - wget --no-verbose -O- https://github.com/VirusTotal/yara/archive/v3.8.1.tar.gz | tar -C ${TRAVIS_BUILD_DIR} -xzf - + - ( cd ${TRAVIS_BUILD_DIR}/yara-3.8.1 && ./bootstrap.sh && ./configure && make ) + - export CGO_CFLAGS=-I${TRAVIS_BUILD_DIR}/yara-3.8.1/libyara/include + - export CGO_LDFLAGS=-L${TRAVIS_BUILD_DIR}/yara-3.8.1/libyara/.libs + - export LD_LIBRARY_PATH=${TRAVIS_BUILD_DIR}/yara-3.8.1/libyara/.libs diff -Nru golang-github-hillu-go-yara-1.0.9/util.go golang-github-hillu-go-yara-1.1.0/util.go --- golang-github-hillu-go-yara-1.0.9/util.go 2018-07-10 12:28:32.000000000 +0000 +++ golang-github-hillu-go-yara-1.1.0/util.go 2018-11-07 09:10:02.000000000 +0000 @@ -6,11 +6,7 @@ package yara -import ( - "github.com/hillu/go-yara/internal/callbackdata" -) - -var callbackData = callbackdata.MakePool(256) +var callbackData = makecbPool(256) func toint64(number interface{}) int64 { switch number.(type) {