Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 50 additions & 36 deletions yara-python.c
Original file line number Diff line number Diff line change
Expand Up @@ -47,14 +47,20 @@ typedef long Py_hash_t;

#if PY_MAJOR_VERSION >= 3
#define PY_STRING(x) PyUnicode_DecodeUTF8(x, strlen(x), "ignore" )
#define PY_STRING_FORMAT(...) PyUnicode_FromFormat(__VA_ARGS__)
#define PY_STRING_TO_C(x) PyUnicode_AsUTF8(x)
#define PY_STRING_CHECK(x) PyUnicode_Check(x)
#else
#define PY_STRING(x) PyString_FromString(x)
#define PY_STRING_FORMAT(...) PyString_FromFormat(__VA_ARGS__)
#define PY_STRING_TO_C(x) PyString_AsString(x)
#define PY_STRING_CHECK(x) (PyString_Check(x) || PyUnicode_Check(x))
#endif

#if PY_VERSION_HEX < 0x03020000
#define PyDescr_NAME(x) (((PyDescrObject*)x)->d_name)
#endif

/* Module globals */

static PyObject* YaraError = NULL;
Expand Down Expand Up @@ -1694,33 +1700,6 @@ void raise_exception_on_error(
const YR_RULE* rule,
const char* message,
void* user_data)
{
if (error_level == YARA_ERROR_LEVEL_ERROR)
{
if (file_name != NULL)
PyErr_Format(
YaraSyntaxError,
"%s(%d): %s",
file_name,
line_number,
message);
else
PyErr_Format(
YaraSyntaxError,
"line %d: %s",
line_number,
message);
}
}


void raise_exception_on_error_or_warning(
int error_level,
const char* file_name,
int line_number,
const YR_RULE* rule,
const char* message,
void* user_data)
{
if (error_level == YARA_ERROR_LEVEL_ERROR)
{
Expand All @@ -1740,22 +1719,25 @@ void raise_exception_on_error_or_warning(
}
else
{
PyObject* warnings = (PyObject*)user_data;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After this change we don't need to separate functions raise_exception_on_error and raise_exception_on_error_or_warning. The raise_exception_on_error function can raise the exception in case of error and put warnings in the list.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, I joined these to functions into a one.

PyObject* warning_msg;
if (file_name != NULL)
PyErr_Format(
YaraWarningError,
warning_msg = PY_STRING_FORMAT(
"%s(%d): %s",
file_name,
line_number,
message);
else
PyErr_Format(
YaraWarningError,
warning_msg = PY_STRING_FORMAT(
"line %d: %s",
line_number,
message);
PyList_Append(warnings, warning_msg);
Py_DECREF(warning_msg);
}
}


////////////////////////////////////////////////////////////////////////////////

const char* yara_include_callback(
Expand Down Expand Up @@ -1951,6 +1933,8 @@ static PyObject* yara_compile(
char* filepath = NULL;
char* source = NULL;
char* ns = NULL;
PyObject* warnings = PyList_New(0);
bool warning_error = false;

if (PyArg_ParseTupleAndKeywords(
args,
Expand All @@ -1973,18 +1957,15 @@ static PyObject* yara_compile(
if (error != ERROR_SUCCESS)
return handle_error(error, NULL);

yr_compiler_set_callback(compiler, raise_exception_on_error, NULL);
yr_compiler_set_callback(compiler, raise_exception_on_error, warnings);

if (error_on_warning != NULL)
{
if (PyBool_Check(error_on_warning))
{
if (PyObject_IsTrue(error_on_warning) == 1)
{
yr_compiler_set_callback(
compiler,
raise_exception_on_error_or_warning,
NULL);
warning_error = true;
}
}
else
Expand Down Expand Up @@ -2170,6 +2151,13 @@ static PyObject* yara_compile(
"compile() takes 1 argument");
}

if (warning_error & PyList_Size(warnings) > 0)
{
PyErr_SetObject(YaraWarningError, warnings);
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The reference count for the warnings list is not decremented when PyList_Size(warnings) == 0, causing a memory leak. When PyList_Size(warnings) > 0 apparently PyErr_SetObject takes ownership of the list and you don't need decrement the reference count, but I would double check.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, I missed that case.

Py_DECREF(warnings);

if (PyErr_Occurred() == NULL)
{
rules = Rules_NEW();
Expand Down Expand Up @@ -2371,6 +2359,24 @@ static PyMethodDef yara_methods[] = {
ob = Py_InitModule3(name, methods, doc);
#endif

static PyObject* YaraWarningError_getwarnings(PyObject *self, void* closure)
{
PyObject *args = PyObject_GetAttrString(self, "args");
if (!args) {
return NULL;
}

PyObject* ret = PyTuple_GetItem(args, 0);
Py_XINCREF(ret);
Py_XDECREF(args);
return ret;
}

static PyGetSetDef YaraWarningError_getsetters[] = {
{"warnings", YaraWarningError_getwarnings, NULL, NULL, NULL},
{NULL}
};


MOD_INIT(yara)
{
Expand All @@ -2397,6 +2403,14 @@ MOD_INIT(yara)
YaraSyntaxError = PyErr_NewException("yara.SyntaxError", YaraError, NULL);
YaraTimeoutError = PyErr_NewException("yara.TimeoutError", YaraError, NULL);
YaraWarningError = PyErr_NewException("yara.WarningError", YaraError, NULL);

PyTypeObject *YaraWarningError_type = (PyTypeObject *)YaraWarningError;
PyObject* descr = PyDescr_NewGetSet(YaraWarningError_type, YaraWarningError_getsetters);
if (PyDict_SetItem(YaraWarningError_type->tp_dict, PyDescr_NAME(descr), descr) < 0) {
Py_DECREF(m);
Py_DECREF(descr);
}
Py_DECREF(descr);
#else
YaraError = Py_BuildValue("s", "yara.Error");
YaraSyntaxError = Py_BuildValue("s", "yara.SyntaxError");
Expand Down