Unverified Commit fa3795f9 by Jan Teske Committed by GitHub

Allow a Py_buffer as data for Rules_match (#152)

* Allow a Py_buffer as data for Rules_match

This makes rules matching compatible with data objects
`PyArg_ParseTuple` does not consider read-only (even though they might
actually be), such a memoryviews. The main change is replacing the `s#`
formatter with `s*` and replacing the `(pointer, length)` pair with a
`Py_buffer` object accordingly. Additional care must be taken to release
the `Py_buffer` on every error path.

* Rules_match: zero-initialize data

PyArg_ParseTupleAndKeywords does not initialize optional fields unless
they are passed, which means we need to zero-initialize the data buffer
to be sure the later NULL checks always work.

This commit also gets rid of the unneeded has_data flag.

* Add test for matching on a memoryview
parent a1ed3cb5
......@@ -1086,6 +1086,13 @@ class TestYara(unittest.TestCase):
self.assertTrue(r[0].is_global == True)
self.assertTrue(r[1].is_private == True)
def testMatchMemoryview(self):
r = yara.compile(source='rule test { strings: $s = "test" condition: $s }')
data = memoryview(b"test")
self.assertTrue(r.match(data=data))
if __name__ == "__main__":
unittest.main()
......@@ -1372,11 +1372,10 @@ static PyObject* Rules_match(
};
char* filepath = NULL;
char* data = NULL;
Py_buffer data = {0};
int pid = 0;
int timeout = 0;
Py_ssize_t length = 0;
int error = ERROR_SUCCESS;
int fast_mode = 0;
......@@ -1396,12 +1395,11 @@ static PyObject* Rules_match(
if (PyArg_ParseTupleAndKeywords(
args,
keywords,
"|sis#OOOiOOi",
"|sis*OOOiOOi",
kwlist,
&filepath,
&pid,
&data,
&length,
&externals,
&callback_data.callback,
&fast,
......@@ -1410,7 +1408,7 @@ static PyObject* Rules_match(
&callback_data.modules_callback,
&callback_data.which))
{
if (filepath == NULL && data == NULL && pid == 0)
if (filepath == NULL && data.buf == NULL && pid == 0)
{
return PyErr_Format(
PyExc_TypeError,
......@@ -1421,6 +1419,7 @@ static PyObject* Rules_match(
{
if (!PyCallable_Check(callback_data.callback))
{
PyBuffer_Release(&data);
return PyErr_Format(
PyExc_TypeError,
"'callback' must be callable");
......@@ -1431,6 +1430,7 @@ static PyObject* Rules_match(
{
if (!PyCallable_Check(callback_data.modules_callback))
{
PyBuffer_Release(&data);
return PyErr_Format(
PyExc_TypeError,
"'modules_callback' must be callable");
......@@ -1441,6 +1441,7 @@ static PyObject* Rules_match(
{
if (!PyDict_Check(callback_data.modules_data))
{
PyBuffer_Release(&data);
return PyErr_Format(
PyExc_TypeError,
"'modules_data' must be a dictionary");
......@@ -1455,11 +1456,14 @@ static PyObject* Rules_match(
{
// Restore original externals provided during compiling.
process_match_externals(object->externals, object->rules);
PyBuffer_Release(&data);
return NULL;
}
}
else
{
PyBuffer_Release(&data);
return PyErr_Format(
PyExc_TypeError,
"'externals' must be a dictionary");
......@@ -1487,7 +1491,7 @@ static PyObject* Rules_match(
Py_END_ALLOW_THREADS
}
else if (data != NULL)
else if (data.buf != NULL)
{
callback_data.matches = PyList_New(0);
......@@ -1495,8 +1499,8 @@ static PyObject* Rules_match(
error = yr_rules_scan_mem(
object->rules,
(unsigned char*) data,
(size_t) length,
(unsigned char*) data.buf,
(size_t) data.len,
fast_mode ? SCAN_FLAGS_FAST_MODE : 0,
yara_callback,
&callback_data,
......@@ -1521,6 +1525,8 @@ static PyObject* Rules_match(
Py_END_ALLOW_THREADS
}
PyBuffer_Release(&data);
// Restore original externals provided during compiling.
if (object->externals != NULL)
{
......@@ -1542,14 +1548,14 @@ static PyObject* Rules_match(
{
handle_error(error, filepath);
}
else if (data != NULL)
{
handle_error(error, "<data>");
}
else if (pid != 0)
{
handle_error(error, "<proc>");
}
else
{
handle_error(error, "<data>");
}
#ifdef PROFILING_ENABLED
PyObject* exception = PyErr_Occurred();
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment