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): ...@@ -1086,6 +1086,13 @@ class TestYara(unittest.TestCase):
self.assertTrue(r[0].is_global == True) self.assertTrue(r[0].is_global == True)
self.assertTrue(r[1].is_private == 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__": if __name__ == "__main__":
unittest.main() unittest.main()
...@@ -1372,11 +1372,10 @@ static PyObject* Rules_match( ...@@ -1372,11 +1372,10 @@ static PyObject* Rules_match(
}; };
char* filepath = NULL; char* filepath = NULL;
char* data = NULL; Py_buffer data = {0};
int pid = 0; int pid = 0;
int timeout = 0; int timeout = 0;
Py_ssize_t length = 0;
int error = ERROR_SUCCESS; int error = ERROR_SUCCESS;
int fast_mode = 0; int fast_mode = 0;
...@@ -1396,12 +1395,11 @@ static PyObject* Rules_match( ...@@ -1396,12 +1395,11 @@ static PyObject* Rules_match(
if (PyArg_ParseTupleAndKeywords( if (PyArg_ParseTupleAndKeywords(
args, args,
keywords, keywords,
"|sis#OOOiOOi", "|sis*OOOiOOi",
kwlist, kwlist,
&filepath, &filepath,
&pid, &pid,
&data, &data,
&length,
&externals, &externals,
&callback_data.callback, &callback_data.callback,
&fast, &fast,
...@@ -1410,7 +1408,7 @@ static PyObject* Rules_match( ...@@ -1410,7 +1408,7 @@ static PyObject* Rules_match(
&callback_data.modules_callback, &callback_data.modules_callback,
&callback_data.which)) &callback_data.which))
{ {
if (filepath == NULL && data == NULL && pid == 0) if (filepath == NULL && data.buf == NULL && pid == 0)
{ {
return PyErr_Format( return PyErr_Format(
PyExc_TypeError, PyExc_TypeError,
...@@ -1421,6 +1419,7 @@ static PyObject* Rules_match( ...@@ -1421,6 +1419,7 @@ static PyObject* Rules_match(
{ {
if (!PyCallable_Check(callback_data.callback)) if (!PyCallable_Check(callback_data.callback))
{ {
PyBuffer_Release(&data);
return PyErr_Format( return PyErr_Format(
PyExc_TypeError, PyExc_TypeError,
"'callback' must be callable"); "'callback' must be callable");
...@@ -1431,6 +1430,7 @@ static PyObject* Rules_match( ...@@ -1431,6 +1430,7 @@ static PyObject* Rules_match(
{ {
if (!PyCallable_Check(callback_data.modules_callback)) if (!PyCallable_Check(callback_data.modules_callback))
{ {
PyBuffer_Release(&data);
return PyErr_Format( return PyErr_Format(
PyExc_TypeError, PyExc_TypeError,
"'modules_callback' must be callable"); "'modules_callback' must be callable");
...@@ -1441,6 +1441,7 @@ static PyObject* Rules_match( ...@@ -1441,6 +1441,7 @@ static PyObject* Rules_match(
{ {
if (!PyDict_Check(callback_data.modules_data)) if (!PyDict_Check(callback_data.modules_data))
{ {
PyBuffer_Release(&data);
return PyErr_Format( return PyErr_Format(
PyExc_TypeError, PyExc_TypeError,
"'modules_data' must be a dictionary"); "'modules_data' must be a dictionary");
...@@ -1455,11 +1456,14 @@ static PyObject* Rules_match( ...@@ -1455,11 +1456,14 @@ static PyObject* Rules_match(
{ {
// Restore original externals provided during compiling. // Restore original externals provided during compiling.
process_match_externals(object->externals, object->rules); process_match_externals(object->externals, object->rules);
PyBuffer_Release(&data);
return NULL; return NULL;
} }
} }
else else
{ {
PyBuffer_Release(&data);
return PyErr_Format( return PyErr_Format(
PyExc_TypeError, PyExc_TypeError,
"'externals' must be a dictionary"); "'externals' must be a dictionary");
...@@ -1487,7 +1491,7 @@ static PyObject* Rules_match( ...@@ -1487,7 +1491,7 @@ static PyObject* Rules_match(
Py_END_ALLOW_THREADS Py_END_ALLOW_THREADS
} }
else if (data != NULL) else if (data.buf != NULL)
{ {
callback_data.matches = PyList_New(0); callback_data.matches = PyList_New(0);
...@@ -1495,8 +1499,8 @@ static PyObject* Rules_match( ...@@ -1495,8 +1499,8 @@ static PyObject* Rules_match(
error = yr_rules_scan_mem( error = yr_rules_scan_mem(
object->rules, object->rules,
(unsigned char*) data, (unsigned char*) data.buf,
(size_t) length, (size_t) data.len,
fast_mode ? SCAN_FLAGS_FAST_MODE : 0, fast_mode ? SCAN_FLAGS_FAST_MODE : 0,
yara_callback, yara_callback,
&callback_data, &callback_data,
...@@ -1521,6 +1525,8 @@ static PyObject* Rules_match( ...@@ -1521,6 +1525,8 @@ static PyObject* Rules_match(
Py_END_ALLOW_THREADS Py_END_ALLOW_THREADS
} }
PyBuffer_Release(&data);
// Restore original externals provided during compiling. // Restore original externals provided during compiling.
if (object->externals != NULL) if (object->externals != NULL)
{ {
...@@ -1542,14 +1548,14 @@ static PyObject* Rules_match( ...@@ -1542,14 +1548,14 @@ static PyObject* Rules_match(
{ {
handle_error(error, filepath); handle_error(error, filepath);
} }
else if (data != NULL)
{
handle_error(error, "<data>");
}
else if (pid != 0) else if (pid != 0)
{ {
handle_error(error, "<proc>"); handle_error(error, "<proc>");
} }
else
{
handle_error(error, "<data>");
}
#ifdef PROFILING_ENABLED #ifdef PROFILING_ENABLED
PyObject* exception = PyErr_Occurred(); 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