Skip to content

Commit a42cf8d

Browse files
committed
Enhance the Error type to provide more information about the error.
This introduces the following changes.: * The `Error` code now has a `Code` field that exposes the YARA API error code to Go users, and also exposes the error codes as constants that can be used in Go code (example: `err.Code == yara.ERROR_TIMEOUT`). Sometimes is useful to get a numeric error code instead of a text string. * When `Error` is converted to a string, it includes the name of the rule causing the error. For example, instead of getting an error like `syntax error, unexpected identifier, expecting <condition>`, you get `rule \"foo\": syntax error, unexpected identifier, expecting <condition>`.
1 parent 11d1088 commit a42cf8d

File tree

4 files changed

+129
-15
lines changed

4 files changed

+129
-15
lines changed

compiler.go

+15-1
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,19 @@ package yara
1010
#include <yara.h>
1111
#include "compat.h"
1212
13+
// rule_identifier is a union accessor function.
14+
static const char* rule_identifier(YR_RULE* r) {
15+
return r->identifier;
16+
}
17+
1318
void compilerCallback(int, char*, int, YR_RULE*, char*, void*);
1419
char* includeCallback(char*, char*, char*, void*);
1520
void freeCallback(char*, void*);
1621
*/
1722
import "C"
1823
import (
1924
"errors"
25+
"fmt"
2026
"os"
2127
"reflect"
2228
"runtime"
@@ -26,10 +32,18 @@ import (
2632
//export compilerCallback
2733
func compilerCallback(errorLevel C.int, filename *C.char, linenumber C.int, rule *C.YR_RULE, message *C.char, userData unsafe.Pointer) {
2834
c := cgoHandle(*(*uintptr)(userData)).Value().(*Compiler)
35+
var text string
36+
if rule != nil {
37+
text = fmt.Sprintf("rule \"%s\": %s",
38+
C.GoString(C.rule_identifier(rule)),
39+
C.GoString(message))
40+
} else {
41+
text = C.GoString(message)
42+
}
2943
msg := CompilerMessage{
3044
Filename: C.GoString(filename),
3145
Line: int(linenumber),
32-
Text: C.GoString(message),
46+
Text: text,
3347
}
3448
if rule != nil {
3549
// Rule object implicitly relies on the compiler object and is destroyed when the compiler is destroyed.

compiler_test.go

+15-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@
66

77
package yara
88

9-
import "testing"
9+
import (
10+
"testing"
11+
)
1012

1113
func TestCompiler(t *testing.T) {
1214
c, _ := NewCompiler()
@@ -43,6 +45,18 @@ func TestWarnings(t *testing.T) {
4345
t.Logf("Recorded Errors=%#v, Warnings=%#v", c.Errors, c.Warnings)
4446
}
4547

48+
func TestErrors(t *testing.T) {
49+
c, _ := NewCompiler()
50+
c.AddString("rule foo { bar }", "")
51+
if len(c.Errors) == 0 {
52+
t.Error("did not recognize error")
53+
}
54+
expectedError := "rule \"foo\": syntax error, unexpected identifier, expecting <condition>"
55+
if c.Errors[0].Text != expectedError {
56+
t.Errorf("expected error: %#v, got %#v", expectedError, c.Errors[0].Text)
57+
}
58+
}
59+
4660
func setupCompiler(t *testing.T) *Compiler {
4761
c, err := NewCompiler()
4862
if err != nil {

error.go

+77-8
Original file line numberDiff line numberDiff line change
@@ -8,25 +8,94 @@ package yara
88

99
// #include <yara.h>
1010
import "C"
11-
import "strconv"
11+
import (
12+
"fmt"
13+
)
1214

1315
// Error encapsulates the C API error codes.
14-
type Error int
16+
type Error struct {
17+
// YARA error code.
18+
Code int
19+
// Namespace in which the error occurred, if applicable. It can be empty.
20+
Namespace string
21+
// Rule in which the error occurred, if applicable. It can be empty.
22+
RuleIdentifier string
23+
// String in which the error occurred, if applicable. It can be empty.
24+
StringIdentifier string
25+
}
1526

16-
func (e Error) Error() string {
17-
if str, ok := errorStrings[int(e)]; ok {
27+
func (e Error) Error() (errorString string) {
28+
if e.Namespace != "" && e.RuleIdentifier != "" {
29+
errorString = fmt.Sprintf("%s caused by rule \"%s:%s\"",
30+
errorCodeToString(e.Code), e.Namespace, e.RuleIdentifier)
31+
if e.StringIdentifier != "" {
32+
errorString += fmt.Sprintf(" string %s", e.StringIdentifier)
33+
}
34+
} else {
35+
errorString = errorCodeToString(e.Code)
36+
}
37+
return errorString
38+
}
39+
40+
func errorCodeToString(errorCode int) string {
41+
if str, ok := errorStrings[errorCode]; ok {
1842
return str
1943
}
20-
return "unknown YARA error " + strconv.Itoa(int(e))
44+
return fmt.Sprintf("unknown error %d", errorCode)
2145
}
2246

2347
func newError(code C.int) error {
24-
if code != 0 {
25-
return Error(code)
48+
if code == C.ERROR_SUCCESS {
49+
return nil
2650
}
27-
return nil
51+
return Error{Code: int(code)}
2852
}
2953

54+
const (
55+
ERROR_SUCCESS = C.ERROR_SUCCESS
56+
ERROR_INSUFFICIENT_MEMORY = C.ERROR_INSUFFICIENT_MEMORY
57+
ERROR_COULD_NOT_ATTACH_TO_PROCESS = C.ERROR_COULD_NOT_ATTACH_TO_PROCESS
58+
ERROR_COULD_NOT_OPEN_FILE = C.ERROR_COULD_NOT_OPEN_FILE
59+
ERROR_COULD_NOT_MAP_FILE = C.ERROR_COULD_NOT_MAP_FILE
60+
ERROR_INVALID_FILE = C.ERROR_INVALID_FILE
61+
ERROR_CORRUPT_FILE = C.ERROR_CORRUPT_FILE
62+
ERROR_UNSUPPORTED_FILE_VERSION = C.ERROR_UNSUPPORTED_FILE_VERSION
63+
ERROR_INVALID_REGULAR_EXPRESSION = C.ERROR_INVALID_REGULAR_EXPRESSION
64+
ERROR_INVALID_HEX_STRING = C.ERROR_INVALID_HEX_STRING
65+
ERROR_SYNTAX_ERROR = C.ERROR_SYNTAX_ERROR
66+
ERROR_LOOP_NESTING_LIMIT_EXCEEDED = C.ERROR_LOOP_NESTING_LIMIT_EXCEEDED
67+
ERROR_DUPLICATED_LOOP_IDENTIFIER = C.ERROR_DUPLICATED_LOOP_IDENTIFIER
68+
ERROR_DUPLICATED_IDENTIFIER = C.ERROR_DUPLICATED_IDENTIFIER
69+
ERROR_DUPLICATED_TAG_IDENTIFIER = C.ERROR_DUPLICATED_TAG_IDENTIFIER
70+
ERROR_DUPLICATED_META_IDENTIFIER = C.ERROR_DUPLICATED_META_IDENTIFIER
71+
ERROR_DUPLICATED_STRING_IDENTIFIER = C.ERROR_DUPLICATED_STRING_IDENTIFIER
72+
ERROR_UNREFERENCED_STRING = C.ERROR_UNREFERENCED_STRING
73+
ERROR_UNDEFINED_STRING = C.ERROR_UNDEFINED_STRING
74+
ERROR_UNDEFINED_IDENTIFIER = C.ERROR_UNDEFINED_IDENTIFIER
75+
ERROR_MISPLACED_ANONYMOUS_STRING = C.ERROR_MISPLACED_ANONYMOUS_STRING
76+
ERROR_INCLUDES_CIRCULAR_REFERENCE = C.ERROR_INCLUDES_CIRCULAR_REFERENCE
77+
ERROR_INCLUDE_DEPTH_EXCEEDED = C.ERROR_INCLUDE_DEPTH_EXCEEDED
78+
ERROR_WRONG_TYPE = C.ERROR_WRONG_TYPE
79+
ERROR_EXEC_STACK_OVERFLOW = C.ERROR_EXEC_STACK_OVERFLOW
80+
ERROR_SCAN_TIMEOUT = C.ERROR_SCAN_TIMEOUT
81+
ERROR_TOO_MANY_SCAN_THREADS = C.ERROR_TOO_MANY_SCAN_THREADS
82+
ERROR_CALLBACK_ERROR = C.ERROR_CALLBACK_ERROR
83+
ERROR_INVALID_ARGUMENT = C.ERROR_INVALID_ARGUMENT
84+
ERROR_TOO_MANY_MATCHES = C.ERROR_TOO_MANY_MATCHES
85+
ERROR_INTERNAL_FATAL_ERROR = C.ERROR_INTERNAL_FATAL_ERROR
86+
ERROR_NESTED_FOR_OF_LOOP = C.ERROR_NESTED_FOR_OF_LOOP
87+
ERROR_INVALID_FIELD_NAME = C.ERROR_INVALID_FIELD_NAME
88+
ERROR_UNKNOWN_MODULE = C.ERROR_UNKNOWN_MODULE
89+
ERROR_NOT_A_STRUCTURE = C.ERROR_NOT_A_STRUCTURE
90+
ERROR_NOT_INDEXABLE = C.ERROR_NOT_INDEXABLE
91+
ERROR_NOT_A_FUNCTION = C.ERROR_NOT_A_FUNCTION
92+
ERROR_INVALID_FORMAT = C.ERROR_INVALID_FORMAT
93+
ERROR_TOO_MANY_ARGUMENTS = C.ERROR_TOO_MANY_ARGUMENTS
94+
ERROR_WRONG_ARGUMENTS = C.ERROR_WRONG_ARGUMENTS
95+
ERROR_WRONG_RETURN_TYPE = C.ERROR_WRONG_RETURN_TYPE
96+
ERROR_DUPLICATED_STRUCTURE_MEMBER = C.ERROR_DUPLICATED_STRUCTURE_MEMBER
97+
)
98+
3099
// FIXME: This should be generated from yara/error.h
31100
var errorStrings = map[int]string{
32101
C.ERROR_INSUFICIENT_MEMORY: "insufficient memory",

scanner.go

+22-5
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,23 @@ type Scanner struct {
4141
userData *cgoHandle
4242
}
4343

44+
// Creates a new error that includes information a about the rule
45+
// causing the error.
46+
func (s *Scanner) newScanError(code C.int) error {
47+
if code == C.ERROR_SUCCESS {
48+
return nil
49+
}
50+
err := Error{Code: int(code)}
51+
if rule := s.GetLastErrorRule(); rule != nil {
52+
err.RuleIdentifier = rule.Identifier()
53+
err.Namespace = rule.Namespace()
54+
}
55+
if str := s.GetLastErrorString(); str != nil {
56+
err.StringIdentifier = str.Identifier()
57+
}
58+
return err
59+
}
60+
4461
// NewScanner creates a YARA scanner.
4562
func NewScanner(r *Rules) (*Scanner, error) {
4663
var yrScanner *C.YR_SCANNER
@@ -153,7 +170,7 @@ func (s *Scanner) ScanMem(buf []byte) (err error) {
153170
}
154171
s.putCallbackData()
155172
C.yr_scanner_set_flags(s.cptr, s.flags.withReportFlags(s.Callback))
156-
err = newError(C.yr_scanner_scan_mem(
173+
err = s.newScanError(C.yr_scanner_scan_mem(
157174
s.cptr,
158175
ptr,
159176
C.size_t(len(buf))))
@@ -176,7 +193,7 @@ func (s *Scanner) ScanFile(filename string) (err error) {
176193
defer C.free(unsafe.Pointer(cfilename))
177194
s.putCallbackData()
178195
C.yr_scanner_set_flags(s.cptr, s.flags.withReportFlags(s.Callback))
179-
err = newError(C.yr_scanner_scan_file(
196+
err = s.newScanError(C.yr_scanner_scan_file(
180197
s.cptr,
181198
cfilename,
182199
))
@@ -191,7 +208,7 @@ func (s *Scanner) ScanFile(filename string) (err error) {
191208
func (s *Scanner) ScanFileDescriptor(fd uintptr) (err error) {
192209
s.putCallbackData()
193210
C.yr_scanner_set_flags(s.cptr, s.flags.withReportFlags(s.Callback))
194-
err = newError(C._yr_scanner_scan_fd(
211+
err = s.newScanError(C._yr_scanner_scan_fd(
195212
s.cptr,
196213
C.int(fd),
197214
))
@@ -206,7 +223,7 @@ func (s *Scanner) ScanFileDescriptor(fd uintptr) (err error) {
206223
func (s *Scanner) ScanProc(pid int) (err error) {
207224
s.putCallbackData()
208225
C.yr_scanner_set_flags(s.cptr, s.flags.withReportFlags(s.Callback))
209-
err = newError(C.yr_scanner_scan_proc(
226+
err = s.newScanError(C.yr_scanner_scan_proc(
210227
s.cptr,
211228
C.int(pid),
212229
))
@@ -226,7 +243,7 @@ func (s *Scanner) ScanMemBlocks(mbi MemoryBlockIterator) (err error) {
226243
defer ((*cgoHandle)(cmbi.context)).Delete()
227244
s.putCallbackData()
228245
C.yr_scanner_set_flags(s.cptr, s.flags.withReportFlags(s.Callback)|C.SCAN_FLAGS_NO_TRYCATCH)
229-
err = newError(C.yr_scanner_scan_mem_blocks(
246+
err = s.newScanError(C.yr_scanner_scan_mem_blocks(
230247
s.cptr,
231248
cmbi,
232249
))

0 commit comments

Comments
 (0)