[hivex] Segfault for an integer value to node_set_value
Hi, When an integer argument is passed as value, node_set_value segfaults. Reproducer is at the end of this message The backtrace points at hivex-py.c, function get_value. While obj is non-NULL, `bytes = PyUnicode_AsUTF8String (obj);` returns NULL. Kind regards, Peter https://lekensteyn.nl #!/usr/bin/env python3 import hivex, sys h = hivex.Hivex(sys.argv[1]) print(h) val = { 'key': 'broken', 't': 4, 'value': 1234 } print(val) r = h.node_set_value(h.root(), val) print(r)
* Peter Wu:
When an integer argument is passed as value, node_set_value segfaults. Reproducer is at the end of this message
Uh-oh. It looks like the handling of values is broken for non-string cases. bytes = PyUnicode_AsUTF8String (obj); with obj derived from the numeric 1234 causes bytes to be set to null. A line of error handling code tells us why: TypeError: bad argument type for built-in operation For Python2, ret->len is set to a value that is too large for size_t, and thus malloc(), so Python crashes with... RuntimeError: Cannot allocate memory I am looking at fixing this. Cheers, -Hilko
It is even worse, in py2, strings are bytes. In py 3, strings are Unicode. Result: I have now corrupted the registry since I tried to write hex 31 for a dword (instead of a 32-bit into). There is also an invalid reg_sz which should be utf16 instead of the gibberish that is now contained in it. I have not checked with py2, but does it require bytes ("str") or can you also pass an int? Is the type t used? (It probably should) Kind regards, Peter https://lekensteyn.nl (pardon my brevity, top-posting and formatting, sent from my phone) On August 5, 2014 8:58:06 PM CEST, Hilko Bengen <bengen@hilluzination.de> wrote:
* Peter Wu:
When an integer argument is passed as value, node_set_value segfaults. Reproducer is at the end of this message
Uh-oh. It looks like the handling of values is broken for non-string cases.
bytes = PyUnicode_AsUTF8String (obj);
with obj derived from the numeric 1234 causes bytes to be set to null. A line of error handling code tells us why:
TypeError: bad argument type for built-in operation
For Python2, ret->len is set to a value that is too large for size_t, and thus malloc(), so Python crashes with...
RuntimeError: Cannot allocate memory
I am looking at fixing this.
Cheers, -Hilko
The following code, reported by Peter Wu <peter@lekensteyn.nl>, broke on x86_64 with different symptoms on Python2 and Python3 (malloc() failure and segmentation fault, respectively): r = h.node_set_value(h.root(), { 'key': 'broken', 't': 4, 'value': 1234 }) --- generator/generator.ml | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/generator/generator.ml b/generator/generator.ml index bcf1966..0fa5c04 100755 --- a/generator/generator.ml +++ b/generator/generator.ml @@ -2799,6 +2799,7 @@ typedef int Py_ssize_t; #include <stdio.h> #include <stdlib.h> #include <assert.h> +#include <inttypes.h> #include \"hivex.h\" @@ -2868,14 +2869,28 @@ get_value (PyObject *v, hive_set_value *ret) PyErr_SetString (PyExc_RuntimeError, \"no 'value' element in dictionary\"); return -1; } + if (PyInt_Check (obj)) { + uint32_t* v = malloc (sizeof (uint32_t)); + *v = PyInt_AsLong (obj); + ret->value = (char*) v; + ret->len = sizeof (uint32_t); #ifdef HAVE_PYSTRING_ASSTRING - ret->value = PyString_AsString (obj); - ret->len = PyString_Size (obj); + } else if (PyString_Check (obj)) { + ret->value = PyString_AsString (obj); + ret->len = PyString_Size (obj); #else - bytes = PyUnicode_AsUTF8String (obj); - ret->value = PyBytes_AS_STRING (bytes); - ret->len = PyBytes_GET_SIZE (bytes); + } else if (PyUnicode_Check (obj)) { + bytes = PyUnicode_AsUTF8String (obj); + if (!bytes) { + return -1; + } + ret->value = PyBytes_AS_STRING (bytes); + ret->len = PyBytes_GET_SIZE (bytes); #endif + } else { + PyErr_SetString (PyExc_RuntimeError, \"Cannot use 'value' element in dictionary\"); + return -1; + } return 0; } -- 2.0.1
On Tuesday 05 August 2014 22:35:23 Hilko Bengen wrote:
The following code, reported by Peter Wu <peter@lekensteyn.nl>, broke on x86_64 with different symptoms on Python2 and Python3 (malloc() failure and segmentation fault, respectively):
r = h.node_set_value(h.root(), { 'key': 'broken', 't': 4, 'value': 1234 }) --- generator/generator.ml | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-)
diff --git a/generator/generator.ml b/generator/generator.ml index bcf1966..0fa5c04 100755 --- a/generator/generator.ml +++ b/generator/generator.ml @@ -2799,6 +2799,7 @@ typedef int Py_ssize_t; #include <stdio.h> #include <stdlib.h> #include <assert.h> +#include <inttypes.h>
#include \"hivex.h\"
@@ -2868,14 +2869,28 @@ get_value (PyObject *v, hive_set_value *ret) PyErr_SetString (PyExc_RuntimeError, \"no 'value' element in dictionary\"); return -1; } + if (PyInt_Check (obj)) {
Triggers -Wimplicit-function-declaration for me (Python 3.4.1).
+ uint32_t* v = malloc (sizeof (uint32_t)); + *v = PyInt_AsLong (obj);
This also triggers implicit function warning.
+ ret->value = (char*) v; + ret->len = sizeof (uint32_t); #ifdef HAVE_PYSTRING_ASSTRING - ret->value = PyString_AsString (obj); - ret->len = PyString_Size (obj); + } else if (PyString_Check (obj)) { + ret->value = PyString_AsString (obj); + ret->len = PyString_Size (obj); #else - bytes = PyUnicode_AsUTF8String (obj); - ret->value = PyBytes_AS_STRING (bytes); - ret->len = PyBytes_GET_SIZE (bytes); + } else if (PyUnicode_Check (obj)) { + bytes = PyUnicode_AsUTF8String (obj); + if (!bytes) { + return -1; + } + ret->value = PyBytes_AS_STRING (bytes); + ret->len = PyBytes_GET_SIZE (bytes); #endif + } else { + PyErr_SetString (PyExc_RuntimeError, \"Cannot use 'value' element in dictionary\"); + return -1; + }
return 0; }
Reproduced with this one-liner: PYTHONPATH=python/.libs python -c \ "import hivex;h=hivex.Hivex('images/minimal',write=True); h.node_set_value(h.root(), {'key':'x', 't':5, 'value':123})" I'll give a shot at fixing this, the key handling is equally broken: python -c 'import hivex; h=hivex.Hivex("images/minimal");h.node_set_values(h.root(), [{"key":1}])' Kind regards, Peter https://lekensteyn.nl
Hi, Here is a fix for the Python issue I encountered the other day. It adds more validation to the node_set_value wrapper and supports the following types on Python 2 and 3: Unicode strings, bytes and integers (for DWORD/QWORDs). The second patch is a trivial whitespace-only change. Peter Wu (2): Add type checking, support integers as value generator: Fix mixed tabs/spaces configure.ac | 4 - generator/generator.ml | 612 ++++++++++++++++++++++------------------- python/t/300-setvalue-types.py | 76 +++++ 3 files changed, 410 insertions(+), 282 deletions(-) create mode 100644 python/t/300-setvalue-types.py -- 2.0.2
Before this patch, Python would segfault once you pass a non-string key or value to node_set_value. It was also not possible to set bytes on Python 3 as Unicode was assumed (Python 2 was not affected by this). This patch fixes recognition of bytes for Python 3, but in addition it recognizes ints (includes 'long' in Python 2) for DWORD (LE + BE) and QWORDs. For this purpose, a new field was added to the py_set_values struct (words) to simplify memory allocation (otherwise, malloc must be tracked somehow). In addition, more specific exception types have been chosen (TypeError instead of RuntimeError). A test is added to avoid future regressions. Note that this test will crash older versions of hivex. All tests pass with Python 2.7.5 + 3.3.2 (Fedora 20 x86_64), 2.7.8 + 3.4.1 (Arch Linux x86_64) and 2.6.5 (Ubuntu 10.04 i686). --- configure.ac | 4 -- generator/generator.ml | 112 ++++++++++++++++++++++++++++++----------- python/t/300-setvalue-types.py | 76 ++++++++++++++++++++++++++++ 3 files changed, 160 insertions(+), 32 deletions(-) create mode 100644 python/t/300-setvalue-types.py diff --git a/configure.ac b/configure.ac index f50daff..4566a32 100644 --- a/configure.ac +++ b/configure.ac @@ -345,10 +345,6 @@ AS_IF([test "x$enable_python" != "xno"], [AC_DEFINE([HAVE_PYCAPSULE_NEW],1, [Found PyCapsule_New in libpython])], [],[$PYTHON_BLDLIBRARY]) - AC_CHECK_LIB([c],[PyString_AsString], - [AC_DEFINE([HAVE_PYSTRING_ASSTRING],1, - [Found PyString_AsString in libpython])], - [],[$PYTHON_BLDLIBRARY]) LIBS="$old_LIBS" fi diff --git a/generator/generator.ml b/generator/generator.ml index bcf1966..0aeb24e 100755 --- a/generator/generator.ml +++ b/generator/generator.ml @@ -2837,45 +2837,90 @@ put_handle (hive_h *h) * not be freed. */ static int -get_value (PyObject *v, hive_set_value *ret) +get_value (PyObject *v, hive_set_value *ret, uint64_t *word) { PyObject *obj; -#ifndef HAVE_PYSTRING_ASSTRING PyObject *bytes; -#endif + + if (!PyDict_Check (v)) { + PyErr_SetString (PyExc_TypeError, \"expected dictionary type for value\"); + return -1; + } obj = PyDict_GetItemString (v, \"key\"); if (!obj) { - PyErr_SetString (PyExc_RuntimeError, \"no 'key' element in dictionary\"); + PyErr_SetString (PyExc_KeyError, \"no 'key' element in dictionary\"); + return -1; + } + if (PyUnicode_Check (obj)) { + bytes = PyUnicode_AsUTF8String (obj); + if (bytes == NULL) { + return -1; + } + } else if (PyBytes_Check (obj)) { + bytes = obj; + } else { + PyErr_SetString (PyExc_TypeError, \"expected bytes or str type for 'key'\"); return -1; } -#ifdef HAVE_PYSTRING_ASSTRING - ret->key = PyString_AsString (obj); -#else - bytes = PyUnicode_AsUTF8String (obj); ret->key = PyBytes_AS_STRING (bytes); -#endif obj = PyDict_GetItemString (v, \"t\"); if (!obj) { - PyErr_SetString (PyExc_RuntimeError, \"no 't' element in dictionary\"); + PyErr_SetString (PyExc_KeyError, \"no 't' element in dictionary\"); return -1; } ret->t = PyLong_AsLong (obj); + if (PyErr_Occurred ()) { + PyErr_SetString (PyExc_TypeError, \"expected int type for 't'\"); + return -1; + } obj = PyDict_GetItemString (v, \"value\"); if (!obj) { - PyErr_SetString (PyExc_RuntimeError, \"no 'value' element in dictionary\"); + PyErr_SetString (PyExc_KeyError, \"no 'value' element in dictionary\"); + return -1; + } + /* Support bytes and unicode strings. For DWORD and QWORD types, long and + * integers are also supported. Caller must ensure sanity of byte buffer + * lengths */ + if (PyUnicode_Check (obj)) { + bytes = PyUnicode_AsUTF8String (obj); + if (bytes == NULL) + return -1; + ret->len = PyBytes_GET_SIZE (bytes); + ret->value = PyBytes_AS_STRING (bytes); + } else if (PyBytes_Check (obj)) { + ret->len = PyBytes_GET_SIZE (obj); + ret->value = PyBytes_AS_STRING (obj); + } else if (ret->t == hive_t_REG_DWORD || + ret->t == hive_t_REG_DWORD_BIG_ENDIAN) { + uint32_t d = PyLong_AsLong (obj); + if (PyErr_Occurred ()) { + PyErr_SetString (PyExc_TypeError, \"expected int type for DWORD value\"); + return -1; + } + + ret->len = sizeof (d); + ret->value = (char *) word; + if (ret->t == hive_t_REG_DWORD) + *(uint32_t *) ret->value = htole32 (d); + else + *(uint32_t *) ret->value = htobe32 (d); + } else if (ret->t == hive_t_REG_QWORD) { + uint64_t l = PyLong_AsLongLong (obj); + if (PyErr_Occurred ()) { + PyErr_SetString (PyExc_TypeError, \"expected int type for QWORD value\"); + return -1; + } + + ret->len = sizeof (l); + ret->value = (char *) word; + *(uint64_t *) ret->value = htole64 (l); + } else { + PyErr_SetString (PyExc_TypeError, \"expected bytes or str type for 'value'\"); return -1; } -#ifdef HAVE_PYSTRING_ASSTRING - ret->value = PyString_AsString (obj); - ret->len = PyString_Size (obj); -#else - bytes = PyUnicode_AsUTF8String (obj); - ret->value = PyBytes_AS_STRING (bytes); - ret->len = PyBytes_GET_SIZE (bytes); -#endif return 0; } @@ -2883,6 +2928,7 @@ get_value (PyObject *v, hive_set_value *ret) typedef struct py_set_values { size_t nr_values; hive_set_value *values; + uint64_t *words; } py_set_values; static int @@ -2905,13 +2951,21 @@ get_values (PyObject *v, py_set_values *ret) ret->nr_values = len; ret->values = malloc (len * sizeof (hive_set_value)); if (!ret->values) { - PyErr_SetString (PyExc_RuntimeError, strerror (errno)); + PyErr_NoMemory (); + return -1; + } + /* if the value is a dword/qword, it will be stored here */ + ret->words = malloc (len * sizeof (uint64_t)); + if (!ret->words) { + free (ret->values); + PyErr_NoMemory (); return -1; } for (i = 0; i < len; ++i) { - if (get_value (PyList_GetItem (v, i), &(ret->values[i])) == -1) { + if (get_value (PyList_GetItem (v, i), &(ret->values[i]), &(ret->words[i])) == -1) { free (ret->values); + free (ret->words); return -1; } } @@ -3070,9 +3124,10 @@ put_val_type (char *val, size_t len, hive_type t) | ASetValues -> pr " py_set_values values;\n"; pr " PyObject *py_values;\n" - | ASetValue -> - pr " hive_set_value val;\n"; - pr " PyObject *py_val;\n" + | ASetValue -> + pr " hive_set_value val;\n"; + pr " PyObject *py_val;\n"; + pr " uint64_t word;" ) (snd style); pr "\n"; @@ -3136,8 +3191,8 @@ put_val_type (char *val, size_t len, hive_type t) | ASetValues -> pr " if (get_values (py_values, &values) == -1)\n"; pr " return NULL;\n" - | ASetValue -> - pr " if (get_value (py_val, &val) == -1)\n"; + | ASetValue -> + pr " if (get_value (py_val, &val, &word) == -1)\n"; pr " return NULL;\n" ) (snd style); @@ -3151,8 +3206,9 @@ put_val_type (char *val, size_t len, hive_type t) | AString _ | AStringNullable _ | AOpenFlags | AUnusedFlags -> () | ASetValues -> - pr " free (values.values);\n" - | ASetValue -> () + pr " free (values.values);\n"; + pr " free (values.words);\n" + | ASetValue -> () ) (snd style); (* Check for errors from C library. *) diff --git a/python/t/300-setvalue-types.py b/python/t/300-setvalue-types.py new file mode 100644 index 0000000..6d72a59 --- /dev/null +++ b/python/t/300-setvalue-types.py @@ -0,0 +1,76 @@ +# Test various possible types for assignment to setvalue +# Copyright (C) 2014 Peter Wu <peter@lekensteyn.nl> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +import hivex +import os +from sys import getrefcount + +srcdir = "." +if "srcdir" in os.environ and os.environ["srcdir"]: + srcdir = os.environ["srcdir"] + +h = hivex.Hivex ("%s/../images/minimal" % srcdir, + write = True) + +REG_SZ = 1 +REG_BINARY = 3 +REG_DWORD = 4 +REG_DWORD_BIG_ENDIAN = 5 +REG_QWORD = 11 + +def set_value (key="test key", t=REG_BINARY, value=b'Val'): + global h + h.node_set_value (h.root (), { + "key": key, + "t": t, + "value": value + }) + +def test_pass (t, value, exp_bytes): + global h + key = "test_key" + set_value (key, t, value) + val = h.node_get_value (h.root (), key) + ret_type, ret_value = h.value_value (val) + assert t == ret_type, \ + "expected type {0}, got {1}".format(t, ret_type) + assert exp_bytes == ret_value, \ + "expected value {0}, got {1}".format(exp_bytes, ret_value) + +def test_exception (exception_type, **kwargs): + try: + set_value (**kwargs) + raise AssertionError("expected {0}".format(exception_type)) + except exception_type: + pass + +# Good weather tests +test_pass (REG_BINARY, b"\x01\x02", b"\x01\x02") +test_pass (REG_SZ, u"Val", b"\x56\x61\x6c") +test_pass (REG_DWORD, 0xabcdef, b'\xef\xcd\xab\x00') +test_pass (REG_DWORD_BIG_ENDIAN, 0xabcdef, b'\x00\xab\xcd\xef') +test_pass (REG_QWORD, 0x0123456789abcdef, + b'\xef\xcd\xab\x89\x67\x45\x23\x01') + +# *WORDs must still accept bytes (the length is not checked) +test_pass (REG_DWORD, b'\xaa\xbb\xcc', b'\xaa\xbb\xcc') + +# Bad weather tests +test_exception (TypeError, key = 1) +test_exception (TypeError, t = "meh") +test_exception (TypeError, t = REG_SZ, value = 1) +test_exception (TypeError, t = REG_DWORD, value = None) -- 2.0.2
On Fri, Aug 08, 2014 at 08:39:41PM +0200, Peter Wu wrote: [...] In general the problem is this is a mega-patch which fixes and changes lots of different things. So please split it up. More comments below.
diff --git a/generator/generator.ml b/generator/generator.ml index bcf1966..0aeb24e 100755 --- a/generator/generator.ml +++ b/generator/generator.ml @@ -2837,45 +2837,90 @@ put_handle (hive_h *h) * not be freed. */ static int -get_value (PyObject *v, hive_set_value *ret) +get_value (PyObject *v, hive_set_value *ret, uint64_t *word) { PyObject *obj; -#ifndef HAVE_PYSTRING_ASSTRING PyObject *bytes; -#endif + + if (!PyDict_Check (v)) { + PyErr_SetString (PyExc_TypeError, \"expected dictionary type for value\"); + return -1; + }
This on its own is fine, and an example of a single change which should go in a separate patch.
obj = PyDict_GetItemString (v, \"key\"); if (!obj) { - PyErr_SetString (PyExc_RuntimeError, \"no 'key' element in dictionary\"); + PyErr_SetString (PyExc_KeyError, \"no 'key' element in dictionary\");
This is an unrelated change. I don't really know if it's a good or bad change, but it needs to be reviewed in a separate patch, along with other identical changes to the exception type below.
+ return -1; + } + if (PyUnicode_Check (obj)) { + bytes = PyUnicode_AsUTF8String (obj); + if (bytes == NULL) { + return -1;
Don't we need to set PyErr_SetString here?
+ } + } else if (PyBytes_Check (obj)) { + bytes = obj; + } else { + PyErr_SetString (PyExc_TypeError, \"expected bytes or str type for 'key'\"); return -1; } -#ifdef HAVE_PYSTRING_ASSTRING - ret->key = PyString_AsString (obj); -#else - bytes = PyUnicode_AsUTF8String (obj); ret->key = PyBytes_AS_STRING (bytes); -#endif
obj = PyDict_GetItemString (v, \"t\"); if (!obj) { - PyErr_SetString (PyExc_RuntimeError, \"no 't' element in dictionary\"); + PyErr_SetString (PyExc_KeyError, \"no 't' element in dictionary\"); return -1; } ret->t = PyLong_AsLong (obj); + if (PyErr_Occurred ()) { + PyErr_SetString (PyExc_TypeError, \"expected int type for 't'\"); + return -1; + }
obj = PyDict_GetItemString (v, \"value\"); if (!obj) { - PyErr_SetString (PyExc_RuntimeError, \"no 'value' element in dictionary\"); + PyErr_SetString (PyExc_KeyError, \"no 'value' element in dictionary\"); + return -1; + } + /* Support bytes and unicode strings. For DWORD and QWORD types, long and + * integers are also supported. Caller must ensure sanity of byte buffer + * lengths */ + if (PyUnicode_Check (obj)) { + bytes = PyUnicode_AsUTF8String (obj); + if (bytes == NULL) + return -1; + ret->len = PyBytes_GET_SIZE (bytes); + ret->value = PyBytes_AS_STRING (bytes); + } else if (PyBytes_Check (obj)) { + ret->len = PyBytes_GET_SIZE (obj); + ret->value = PyBytes_AS_STRING (obj); + } else if (ret->t == hive_t_REG_DWORD || + ret->t == hive_t_REG_DWORD_BIG_ENDIAN) { + uint32_t d = PyLong_AsLong (obj); + if (PyErr_Occurred ()) { + PyErr_SetString (PyExc_TypeError, \"expected int type for DWORD value\"); + return -1; + }
This is where things start to go wrong. There's no way you can trust the type field (even though it comes from the caller, not the registry in this case). The type field is only a hint, it doesn't reflect the actual type stored. TBH I'm not sure why you're using PyLong_AsLong{,Long} here at all.
+ ret->len = sizeof (d); + ret->value = (char *) word; + if (ret->t == hive_t_REG_DWORD) + *(uint32_t *) ret->value = htole32 (d); + else + *(uint32_t *) ret->value = htobe32 (d); + } else if (ret->t == hive_t_REG_QWORD) { + uint64_t l = PyLong_AsLongLong (obj); + if (PyErr_Occurred ()) { + PyErr_SetString (PyExc_TypeError, \"expected int type for QWORD value\"); + return -1; + } + + ret->len = sizeof (l); + ret->value = (char *) word; + *(uint64_t *) ret->value = htole64 (l); + } else { + PyErr_SetString (PyExc_TypeError, \"expected bytes or str type for 'value'\"); return -1; } -#ifdef HAVE_PYSTRING_ASSTRING - ret->value = PyString_AsString (obj); - ret->len = PyString_Size (obj); -#else - bytes = PyUnicode_AsUTF8String (obj); - ret->value = PyBytes_AS_STRING (bytes); - ret->len = PyBytes_GET_SIZE (bytes); -#endif
return 0; } @@ -2883,6 +2928,7 @@ get_value (PyObject *v, hive_set_value *ret) typedef struct py_set_values { size_t nr_values; hive_set_value *values; + uint64_t *words; } py_set_values;
static int @@ -2905,13 +2951,21 @@ get_values (PyObject *v, py_set_values *ret) ret->nr_values = len; ret->values = malloc (len * sizeof (hive_set_value)); if (!ret->values) { - PyErr_SetString (PyExc_RuntimeError, strerror (errno)); + PyErr_NoMemory (); + return -1; + } + /* if the value is a dword/qword, it will be stored here */ + ret->words = malloc (len * sizeof (uint64_t)); + if (!ret->words) { + free (ret->values); + PyErr_NoMemory (); return -1; }
for (i = 0; i < len; ++i) { - if (get_value (PyList_GetItem (v, i), &(ret->values[i])) == -1) { + if (get_value (PyList_GetItem (v, i), &(ret->values[i]), &(ret->words[i])) == -1) { free (ret->values); + free (ret->words); return -1; } } @@ -3070,9 +3124,10 @@ put_val_type (char *val, size_t len, hive_type t) | ASetValues -> pr " py_set_values values;\n"; pr " PyObject *py_values;\n" - | ASetValue -> - pr " hive_set_value val;\n"; - pr " PyObject *py_val;\n" + | ASetValue -> + pr " hive_set_value val;\n"; + pr " PyObject *py_val;\n"; + pr " uint64_t word;" ) (snd style);
pr "\n"; @@ -3136,8 +3191,8 @@ put_val_type (char *val, size_t len, hive_type t) | ASetValues -> pr " if (get_values (py_values, &values) == -1)\n"; pr " return NULL;\n" - | ASetValue -> - pr " if (get_value (py_val, &val) == -1)\n"; + | ASetValue -> + pr " if (get_value (py_val, &val, &word) == -1)\n"; pr " return NULL;\n" ) (snd style);
@@ -3151,8 +3206,9 @@ put_val_type (char *val, size_t len, hive_type t) | AString _ | AStringNullable _ | AOpenFlags | AUnusedFlags -> () | ASetValues -> - pr " free (values.values);\n" - | ASetValue -> () + pr " free (values.values);\n"; + pr " free (values.words);\n" + | ASetValue -> () ) (snd style);
(* Check for errors from C library. *) diff --git a/python/t/300-setvalue-types.py b/python/t/300-setvalue-types.py new file mode 100644 index 0000000..6d72a59 --- /dev/null +++ b/python/t/300-setvalue-types.py @@ -0,0 +1,76 @@ +# Test various possible types for assignment to setvalue +# Copyright (C) 2014 Peter Wu <peter@lekensteyn.nl> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +import hivex +import os +from sys import getrefcount + +srcdir = "." +if "srcdir" in os.environ and os.environ["srcdir"]: + srcdir = os.environ["srcdir"] + +h = hivex.Hivex ("%s/../images/minimal" % srcdir, + write = True) + +REG_SZ = 1 +REG_BINARY = 3 +REG_DWORD = 4 +REG_DWORD_BIG_ENDIAN = 5 +REG_QWORD = 11 + +def set_value (key="test key", t=REG_BINARY, value=b'Val'): + global h + h.node_set_value (h.root (), { + "key": key, + "t": t, + "value": value + }) + +def test_pass (t, value, exp_bytes): + global h + key = "test_key" + set_value (key, t, value) + val = h.node_get_value (h.root (), key) + ret_type, ret_value = h.value_value (val) + assert t == ret_type, \ + "expected type {0}, got {1}".format(t, ret_type) + assert exp_bytes == ret_value, \ + "expected value {0}, got {1}".format(exp_bytes, ret_value) + +def test_exception (exception_type, **kwargs): + try: + set_value (**kwargs) + raise AssertionError("expected {0}".format(exception_type)) + except exception_type: + pass + +# Good weather tests +test_pass (REG_BINARY, b"\x01\x02", b"\x01\x02") +test_pass (REG_SZ, u"Val", b"\x56\x61\x6c") +test_pass (REG_DWORD, 0xabcdef, b'\xef\xcd\xab\x00') +test_pass (REG_DWORD_BIG_ENDIAN, 0xabcdef, b'\x00\xab\xcd\xef') +test_pass (REG_QWORD, 0x0123456789abcdef, + b'\xef\xcd\xab\x89\x67\x45\x23\x01') + +# *WORDs must still accept bytes (the length is not checked) +test_pass (REG_DWORD, b'\xaa\xbb\xcc', b'\xaa\xbb\xcc') + +# Bad weather tests +test_exception (TypeError, key = 1) +test_exception (TypeError, t = "meh") +test_exception (TypeError, t = REG_SZ, value = 1) +test_exception (TypeError, t = REG_DWORD, value = None)
Rich. -- Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones Read my programming and virtualization blog: http://rwmj.wordpress.com libguestfs lets you edit virtual machines. Supports shell scripting, bindings from many languages. http://libguestfs.org
On Tuesday 12 August 2014 10:45:07 Richard W.M. Jones wrote: [..]
diff --git a/generator/generator.ml b/generator/generator.ml index bcf1966..0aeb24e 100755 --- a/generator/generator.ml +++ b/generator/generator.ml @@ -2837,45 +2837,90 @@ put_handle (hive_h *h) * not be freed. */ static int -get_value (PyObject *v, hive_set_value *ret) +get_value (PyObject *v, hive_set_value *ret, uint64_t *word) { PyObject *obj; -#ifndef HAVE_PYSTRING_ASSTRING PyObject *bytes; -#endif + + if (!PyDict_Check (v)) { + PyErr_SetString (PyExc_TypeError, \"expected dictionary type for value\"); + return -1; + }
This on its own is fine, and an example of a single change which should go in a separate patch.
OK, one patch for type checking of the key and type.
obj = PyDict_GetItemString (v, \"key\"); if (!obj) { - PyErr_SetString (PyExc_RuntimeError, \"no 'key' element in dictionary\"); + PyErr_SetString (PyExc_KeyError, \"no 'key' element in dictionary\");
This is an unrelated change. I don't really know if it's a good or bad change, but it needs to be reviewed in a separate patch, along with other identical changes to the exception type below.
OK, another patch for changing types. RuntimeError is too generic for my taste, this better captures the actual meaning of the error.
+ return -1; + } + if (PyUnicode_Check (obj)) { + bytes = PyUnicode_AsUTF8String (obj); + if (bytes == NULL) { + return -1;
Don't we need to set PyErr_SetString here?
PyUnicode_AsUTF8String should set the error code when it fails. I have not triggered that case yet, but maybe it is better to be more explicit and mention the source of the error.
+ } + } else if (PyBytes_Check (obj)) { + bytes = obj; + } else { + PyErr_SetString (PyExc_TypeError, \"expected bytes or str type for 'key'\"); return -1; } -#ifdef HAVE_PYSTRING_ASSTRING - ret->key = PyString_AsString (obj); -#else - bytes = PyUnicode_AsUTF8String (obj); ret->key = PyBytes_AS_STRING (bytes); -#endif
obj = PyDict_GetItemString (v, \"t\"); if (!obj) { - PyErr_SetString (PyExc_RuntimeError, \"no 't' element in dictionary\"); + PyErr_SetString (PyExc_KeyError, \"no 't' element in dictionary\"); return -1; } ret->t = PyLong_AsLong (obj); + if (PyErr_Occurred ()) { + PyErr_SetString (PyExc_TypeError, \"expected int type for 't'\"); + return -1; + }
obj = PyDict_GetItemString (v, \"value\"); if (!obj) { - PyErr_SetString (PyExc_RuntimeError, \"no 'value' element in dictionary\"); + PyErr_SetString (PyExc_KeyError, \"no 'value' element in dictionary\"); + return -1; + } + /* Support bytes and unicode strings. For DWORD and QWORD types, long and + * integers are also supported. Caller must ensure sanity of byte buffer + * lengths */ + if (PyUnicode_Check (obj)) { + bytes = PyUnicode_AsUTF8String (obj); + if (bytes == NULL) + return -1; + ret->len = PyBytes_GET_SIZE (bytes); + ret->value = PyBytes_AS_STRING (bytes);
I think we should outright reject Unicode strings. (u"" in Python 2 + 3, "" in Python 3.) The previous Python 3 code and the one in this patch tries to be too smart and assumes that the value can be a non-NUL terminated UTF-8 string.
+ } else if (PyBytes_Check (obj)) { + ret->len = PyBytes_GET_SIZE (obj); + ret->value = PyBytes_AS_STRING (obj); + } else if (ret->t == hive_t_REG_DWORD || + ret->t == hive_t_REG_DWORD_BIG_ENDIAN) { + uint32_t d = PyLong_AsLong (obj); + if (PyErr_Occurred ()) { + PyErr_SetString (PyExc_TypeError, \"expected int type for DWORD value\"); + return -1; + }
This is where things start to go wrong. There's no way you can trust the type field (even though it comes from the caller, not the registry in this case). The type field is only a hint, it doesn't reflect the actual type stored.
Indeed, type is a hint, but if you really insist in pushing a QWORD in a DWORD, you can still use the byte type. In C, you cannot detect the type from a pointer, but this is Python where it is possible so this is why I added it for convenience. To make this API better follow the C API, what about dropping support for everything but bytes? If conversion from UTF-8 to nul-terminated UTF-16 is wanted, or conversion from an int to a DWORD, a second API could be created instead (that just uses the thin C <-> Python bindings).
TBH I'm not sure why you're using PyLong_AsLong{,Long} here at all.
To convert a PyObject to a long.
+ ret->len = sizeof (d); + ret->value = (char *) word; + if (ret->t == hive_t_REG_DWORD) + *(uint32_t *) ret->value = htole32 (d); + else + *(uint32_t *) ret->value = htobe32 (d); + } else if (ret->t == hive_t_REG_QWORD) { + uint64_t l = PyLong_AsLongLong (obj); + if (PyErr_Occurred ()) { + PyErr_SetString (PyExc_TypeError, \"expected int type for QWORD value\"); + return -1; + } + + ret->len = sizeof (l); + ret->value = (char *) word; + *(uint64_t *) ret->value = htole64 (l); + } else { + PyErr_SetString (PyExc_TypeError, \"expected bytes or str type for 'value'\"); return -1; } -#ifdef HAVE_PYSTRING_ASSTRING - ret->value = PyString_AsString (obj); - ret->len = PyString_Size (obj); -#else - bytes = PyUnicode_AsUTF8String (obj); - ret->value = PyBytes_AS_STRING (bytes); - ret->len = PyBytes_GET_SIZE (bytes); -#endif
return 0; } @@ -2883,6 +2928,7 @@ get_value (PyObject *v, hive_set_value *ret) typedef struct py_set_values { size_t nr_values; hive_set_value *values; + uint64_t *words; } py_set_values;
static int @@ -2905,13 +2951,21 @@ get_values (PyObject *v, py_set_values *ret) ret->nr_values = len; ret->values = malloc (len * sizeof (hive_set_value)); if (!ret->values) { - PyErr_SetString (PyExc_RuntimeError, strerror (errno)); + PyErr_NoMemory (); + return -1; + } + /* if the value is a dword/qword, it will be stored here */ + ret->words = malloc (len * sizeof (uint64_t)); + if (!ret->words) { + free (ret->values); + PyErr_NoMemory (); return -1; }
for (i = 0; i < len; ++i) { - if (get_value (PyList_GetItem (v, i), &(ret->values[i])) == -1) { + if (get_value (PyList_GetItem (v, i), &(ret->values[i]), &(ret->words[i])) == -1) { free (ret->values); + free (ret->words); return -1; } } @@ -3070,9 +3124,10 @@ put_val_type (char *val, size_t len, hive_type t) | ASetValues -> pr " py_set_values values;\n"; pr " PyObject *py_values;\n" - | ASetValue -> - pr " hive_set_value val;\n"; - pr " PyObject *py_val;\n" + | ASetValue -> + pr " hive_set_value val;\n"; + pr " PyObject *py_val;\n"; + pr " uint64_t word;" ) (snd style);
pr "\n"; @@ -3136,8 +3191,8 @@ put_val_type (char *val, size_t len, hive_type t) | ASetValues -> pr " if (get_values (py_values, &values) == -1)\n"; pr " return NULL;\n" - | ASetValue -> - pr " if (get_value (py_val, &val) == -1)\n"; + | ASetValue -> + pr " if (get_value (py_val, &val, &word) == -1)\n"; pr " return NULL;\n" ) (snd style);
@@ -3151,8 +3206,9 @@ put_val_type (char *val, size_t len, hive_type t) | AString _ | AStringNullable _ | AOpenFlags | AUnusedFlags -> () | ASetValues -> - pr " free (values.values);\n" - | ASetValue -> () + pr " free (values.values);\n"; + pr " free (values.words);\n" + | ASetValue -> () ) (snd style);
(* Check for errors from C library. *) diff --git a/python/t/300-setvalue-types.py b/python/t/300-setvalue-types.py new file mode 100644 index 0000000..6d72a59 --- /dev/null +++ b/python/t/300-setvalue-types.py @@ -0,0 +1,76 @@ +# Test various possible types for assignment to setvalue +# Copyright (C) 2014 Peter Wu <peter@lekensteyn.nl> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +import hivex +import os +from sys import getrefcount + +srcdir = "." +if "srcdir" in os.environ and os.environ["srcdir"]: + srcdir = os.environ["srcdir"] + +h = hivex.Hivex ("%s/../images/minimal" % srcdir, + write = True) + +REG_SZ = 1 +REG_BINARY = 3 +REG_DWORD = 4 +REG_DWORD_BIG_ENDIAN = 5 +REG_QWORD = 11 + +def set_value (key="test key", t=REG_BINARY, value=b'Val'): + global h + h.node_set_value (h.root (), { + "key": key, + "t": t, + "value": value + }) + +def test_pass (t, value, exp_bytes): + global h + key = "test_key" + set_value (key, t, value) + val = h.node_get_value (h.root (), key) + ret_type, ret_value = h.value_value (val) + assert t == ret_type, \ + "expected type {0}, got {1}".format(t, ret_type) + assert exp_bytes == ret_value, \ + "expected value {0}, got {1}".format(exp_bytes, ret_value) + +def test_exception (exception_type, **kwargs): + try: + set_value (**kwargs) + raise AssertionError("expected {0}".format(exception_type)) + except exception_type: + pass + +# Good weather tests +test_pass (REG_BINARY, b"\x01\x02", b"\x01\x02") +test_pass (REG_SZ, u"Val", b"\x56\x61\x6c") +test_pass (REG_DWORD, 0xabcdef, b'\xef\xcd\xab\x00') +test_pass (REG_DWORD_BIG_ENDIAN, 0xabcdef, b'\x00\xab\xcd\xef') +test_pass (REG_QWORD, 0x0123456789abcdef, + b'\xef\xcd\xab\x89\x67\x45\x23\x01') + +# *WORDs must still accept bytes (the length is not checked) +test_pass (REG_DWORD, b'\xaa\xbb\xcc', b'\xaa\xbb\xcc') + +# Bad weather tests +test_exception (TypeError, key = 1) +test_exception (TypeError, t = "meh") +test_exception (TypeError, t = REG_SZ, value = 1) +test_exception (TypeError, t = REG_DWORD, value = None)
I will come later with follow-up patches, thanks for your review! -- Kind regards, Peter https://lekensteyn.nl
On Tue, Aug 12, 2014 at 12:39:08PM +0200, Peter Wu wrote:
TBH I'm not sure why you're using PyLong_AsLong{,Long} here at all.
To convert a PyObject to a long.
OK got it. For some reason I was reading that as conversion in the opposite direction. Rich. -- Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones Read my programming and virtualization blog: http://rwmj.wordpress.com virt-top is 'top' for virtual machines. Tiny program with many powerful monitoring features, net stats, disk stats, logging, etc. http://people.redhat.com/~rjones/virt-top
vim :set ts=8 :retab --- generator/generator.ml | 500 ++++++++++++++++++++++++------------------------- 1 file changed, 250 insertions(+), 250 deletions(-) diff --git a/generator/generator.ml b/generator/generator.ml index 0aeb24e..40c1981 100755 --- a/generator/generator.ml +++ b/generator/generator.ml @@ -1069,10 +1069,10 @@ here. Often it's not documented at all. pr "\n"; if List.mem AUnusedFlags (snd style) then - pr "The flags parameter is unused. Always pass 0.\n\n"; + pr "The flags parameter is unused. Always pass 0.\n\n"; if List.mem ASetValues (snd style) then - pr "C<values> is an array of (key, value) pairs. There + pr "C<values> is an array of (key, value) pairs. There should be C<nr_values> elements in this array. Any existing values stored at the node are discarded, and their @@ -1080,7 +1080,7 @@ C<hive_value_h> handles become invalid. Thus you can remove all values stored at C<node> by passing C<nr_values = 0>.\n\n"; if List.mem ASetValue (snd style) then - pr "C<value> is a single (key, value) pair. + pr "C<value> is a single (key, value) pair. Existing C<hive_value_h> handles become invalid.\n\n"; @@ -1791,8 +1791,8 @@ static void raise_closed (const char *) Noreturn; | ASetValues -> pr " int nrvalues = Wosize_val (valuesv);\n"; pr " hive_set_value *values = HiveSetValues_val (valuesv);\n" - | ASetValue -> - pr " hive_set_value *val = HiveSetValue_val (valv);\n" + | ASetValue -> + pr " hive_set_value *val = HiveSetValue_val (valv);\n" ) (snd style); pr "\n"; @@ -1864,9 +1864,9 @@ static void raise_closed (const char *) Noreturn; | ASetValues -> pr " free (values);\n"; pr "\n"; - | ASetValue -> - pr " free (val);\n"; - pr "\n"; + | ASetValue -> + pr " free (val);\n"; + pr "\n"; ) (snd style); (* Check for errors. *) @@ -1903,7 +1903,7 @@ static void raise_closed (const char *) Noreturn; pr " free (r);\n" | RStringList -> pr " rv = caml_copy_string_array ((const char **) r);\n"; - pr " int i;\n"; + pr " int i;\n"; pr " for (i = 0; r[i] != NULL; ++i) free (r[i]);\n"; pr " free (r);\n" | RLenType -> pr " rv = copy_type_len (len, t);\n" @@ -2220,45 +2220,45 @@ sub open { * Therefore we don't generate prototypes for these two calls: *) if fst style <> RErrDispose && List.hd (snd style) = AHive then ( - let longdesc = replace_str longdesc "C<hivex_" "C<" in - pr "=item %s\n\n " name; - generate_perl_prototype name style; - pr "\n\n"; - pr "%s\n\n" longdesc; - - (match fst style with - | RErr - | RErrDispose - | RHive - | RString - | RStringList - | RLenType - | RLenValue - | RLenTypeVal - | RInt32 - | RInt64 -> () - | RSize -> + let longdesc = replace_str longdesc "C<hivex_" "C<" in + pr "=item %s\n\n " name; + generate_perl_prototype name style; + pr "\n\n"; + pr "%s\n\n" longdesc; + + (match fst style with + | RErr + | RErrDispose + | RHive + | RString + | RStringList + | RLenType + | RLenValue + | RLenTypeVal + | RInt32 + | RInt64 -> () + | RSize -> pr "\ This returns a size.\n\n" - | RNode -> - pr "\ + | RNode -> + pr "\ This returns a node handle.\n\n" - | RNodeNotFound -> - pr "\ + | RNodeNotFound -> + pr "\ This returns a node handle, or C<undef> if the node was not found.\n\n" - | RNodeList -> - pr "\ + | RNodeList -> + pr "\ This returns a list of node handles.\n\n" - | RValue -> - pr "\ + | RValue -> + pr "\ This returns a value handle.\n\n" - | RValueList -> - pr "\ + | RValueList -> + pr "\ This returns a list of value handles.\n\n" - ); + ); - if List.mem ASetValues (snd style) then - pr "C<@values> is an array of (keys, value) pairs. + if List.mem ASetValues (snd style) then + pr "C<@values> is an array of (keys, value) pairs. Each element should be a hashref containing C<key>, C<t> (type) and C<data>. @@ -2524,172 +2524,172 @@ DESTROY (h) fun (name, style, _, longdesc) -> (* The close and open calls are handled specially above. *) if fst style <> RErrDispose && List.hd (snd style) = AHive then ( - (match fst style with - | RErr -> pr "void\n" - | RErrDispose -> failwith "perl bindings cannot handle a call which disposes of the handle" - | RHive -> failwith "perl bindings cannot handle a call which returns a handle" - | RSize - | RNode - | RNodeNotFound - | RValue - | RString -> pr "SV *\n" - | RNodeList - | RValueList - | RStringList - | RLenType - | RLenValue - | RLenTypeVal -> pr "void\n" - | RInt32 -> pr "SV *\n" - | RInt64 -> pr "SV *\n" - ); - - (* Call and arguments. *) - let perl_params = - filter_map (function - | AUnusedFlags -> None - | arg -> Some (name_of_argt arg)) (snd style) in - - let c_params = - List.map (function - | AUnusedFlags -> "0" - | ASetValues -> "values.nr_values, values.values" - | arg -> name_of_argt arg) (snd style) in - - pr "%s (%s)\n" name (String.concat ", " perl_params); - iteri ( + (match fst style with + | RErr -> pr "void\n" + | RErrDispose -> failwith "perl bindings cannot handle a call which disposes of the handle" + | RHive -> failwith "perl bindings cannot handle a call which returns a handle" + | RSize + | RNode + | RNodeNotFound + | RValue + | RString -> pr "SV *\n" + | RNodeList + | RValueList + | RStringList + | RLenType + | RLenValue + | RLenTypeVal -> pr "void\n" + | RInt32 -> pr "SV *\n" + | RInt64 -> pr "SV *\n" + ); + + (* Call and arguments. *) + let perl_params = + filter_map (function + | AUnusedFlags -> None + | arg -> Some (name_of_argt arg)) (snd style) in + + let c_params = + List.map (function + | AUnusedFlags -> "0" + | ASetValues -> "values.nr_values, values.values" + | arg -> name_of_argt arg) (snd style) in + + pr "%s (%s)\n" name (String.concat ", " perl_params); + iteri ( fun i -> function | AHive -> - pr " hive_h *h;\n" - | ANode n - | AValue n -> - pr " int %s;\n" n - | AString n -> - pr " char *%s;\n" n + pr " hive_h *h;\n" + | ANode n + | AValue n -> + pr " int %s;\n" n + | AString n -> + pr " char *%s;\n" n | AStringNullable n -> - (* http://www.perlmonks.org/?node_id=554277 *) - pr " char *%s = SvOK(ST(%d)) ? SvPV_nolen(ST(%d)) : NULL;\n" n i i - | AOpenFlags -> - pr " int flags;\n" - | AUnusedFlags -> () - | ASetValues -> - pr " pl_set_values values = unpack_pl_set_values (ST(%d));\n" i - | ASetValue -> - pr " hive_set_value *val = unpack_set_value (ST(%d));\n" i - ) (snd style); - - let free_args () = - List.iter ( - function - | ASetValues -> - pr " free (values.values);\n" - | ASetValue -> - pr " free (val);\n" - | AHive | ANode _ | AValue _ | AString _ | AStringNullable _ - | AOpenFlags | AUnusedFlags -> () - ) (snd style) - in - - (* Code. *) - (match fst style with - | RErr -> + (* http://www.perlmonks.org/?node_id=554277 *) + pr " char *%s = SvOK(ST(%d)) ? SvPV_nolen(ST(%d)) : NULL;\n" n i i + | AOpenFlags -> + pr " int flags;\n" + | AUnusedFlags -> () + | ASetValues -> + pr " pl_set_values values = unpack_pl_set_values (ST(%d));\n" i + | ASetValue -> + pr " hive_set_value *val = unpack_set_value (ST(%d));\n" i + ) (snd style); + + let free_args () = + List.iter ( + function + | ASetValues -> + pr " free (values.values);\n" + | ASetValue -> + pr " free (val);\n" + | AHive | ANode _ | AValue _ | AString _ | AStringNullable _ + | AOpenFlags | AUnusedFlags -> () + ) (snd style) + in + + (* Code. *) + (match fst style with + | RErr -> pr "PREINIT:\n"; pr " int r;\n"; pr " PPCODE:\n"; pr " r = hivex_%s (%s);\n" - name (String.concat ", " c_params); - free_args (); + name (String.concat ", " c_params); + free_args (); pr " if (r == -1)\n"; pr " croak (\"%%s: %%s\", \"%s\", strerror (errno));\n" - name; + name; - | RErrDispose -> assert false - | RHive -> assert false + | RErrDispose -> assert false + | RHive -> assert false - | RSize - | RNode - | RValue -> + | RSize + | RNode + | RValue -> pr "PREINIT:\n"; - pr " /* hive_node_h = hive_value_h = size_t so we cheat\n"; - pr " here to simplify the generator */\n"; + pr " /* hive_node_h = hive_value_h = size_t so we cheat\n"; + pr " here to simplify the generator */\n"; pr " size_t r;\n"; pr " CODE:\n"; pr " r = hivex_%s (%s);\n" - name (String.concat ", " c_params); - free_args (); + name (String.concat ", " c_params); + free_args (); pr " if (r == 0)\n"; pr " croak (\"%%s: %%s\", \"%s\", strerror (errno));\n" - name; + name; pr " RETVAL = newSViv (r);\n"; pr " OUTPUT:\n"; pr " RETVAL\n" - | RNodeNotFound -> + | RNodeNotFound -> pr "PREINIT:\n"; pr " hive_node_h r;\n"; pr " CODE:\n"; - pr " errno = 0;\n"; + pr " errno = 0;\n"; pr " r = hivex_%s (%s);\n" - name (String.concat ", " c_params); - free_args (); + name (String.concat ", " c_params); + free_args (); pr " if (r == 0 && errno != 0)\n"; pr " croak (\"%%s: %%s\", \"%s\", strerror (errno));\n" - name; - pr " if (r == 0)\n"; + name; + pr " if (r == 0)\n"; pr " RETVAL = &PL_sv_undef;\n"; - pr " else\n"; + pr " else\n"; pr " RETVAL = newSViv (r);\n"; pr " OUTPUT:\n"; pr " RETVAL\n" - | RString -> + | RString -> pr "PREINIT:\n"; pr " char *r;\n"; pr " CODE:\n"; pr " r = hivex_%s (%s);\n" - name (String.concat ", " c_params); - free_args (); + name (String.concat ", " c_params); + free_args (); pr " if (r == NULL)\n"; pr " croak (\"%%s: %%s\", \"%s\", strerror (errno));\n" - name; + name; if f_len_exists name then pr " RETVAL = newSVpvn_utf8 (r, hivex_%s_len (%s), 1);\n" name (String.concat ", " c_params) else pr " RETVAL = newSVpv (r, 0);\n"; - pr " free (r);\n"; + pr " free (r);\n"; pr " OUTPUT:\n"; pr " RETVAL\n" - | RNodeList - | RValueList -> + | RNodeList + | RValueList -> pr "PREINIT:\n"; pr " size_t *r;\n"; pr " int i, n;\n"; pr " PPCODE:\n"; pr " r = hivex_%s (%s);\n" - name (String.concat ", " c_params); - free_args (); + name (String.concat ", " c_params); + free_args (); pr " if (r == NULL)\n"; pr " croak (\"%%s: %%s\", \"%s\", strerror (errno));\n" - name; + name; pr " for (n = 0; r[n] != 0; ++n) /**/;\n"; pr " EXTEND (SP, n);\n"; pr " for (i = 0; i < n; ++i)\n"; pr " PUSHs (sv_2mortal (newSViv (r[i])));\n"; pr " free (r);\n"; - | RStringList -> + | RStringList -> pr "PREINIT:\n"; pr " char **r;\n"; pr " int i, n;\n"; pr " PPCODE:\n"; pr " r = hivex_%s (%s);\n" - name (String.concat ", " c_params); - free_args (); + name (String.concat ", " c_params); + free_args (); pr " if (r == NULL)\n"; pr " croak (\"%%s: %%s\", \"%s\", strerror (errno));\n" - name; + name; pr " for (n = 0; r[n] != NULL; ++n) /**/;\n"; pr " EXTEND (SP, n);\n"; pr " for (i = 0; i < n; ++i) {\n"; @@ -2698,86 +2698,86 @@ DESTROY (h) pr " }\n"; pr " free (r);\n"; - | RLenType -> - pr "PREINIT:\n"; - pr " int r;\n"; - pr " size_t len;\n"; - pr " hive_type type;\n"; - pr " PPCODE:\n"; + | RLenType -> + pr "PREINIT:\n"; + pr " int r;\n"; + pr " size_t len;\n"; + pr " hive_type type;\n"; + pr " PPCODE:\n"; pr " r = hivex_%s (%s, &type, &len);\n" - name (String.concat ", " c_params); - free_args (); + name (String.concat ", " c_params); + free_args (); pr " if (r == -1)\n"; pr " croak (\"%%s: %%s\", \"%s\", strerror (errno));\n" - name; - pr " EXTEND (SP, 2);\n"; - pr " PUSHs (sv_2mortal (newSViv (type)));\n"; - pr " PUSHs (sv_2mortal (newSViv (len)));\n"; - - | RLenValue -> - pr "PREINIT:\n"; - pr " hive_value_h r;\n"; - pr " size_t len;\n"; - pr " PPCODE:\n"; + name; + pr " EXTEND (SP, 2);\n"; + pr " PUSHs (sv_2mortal (newSViv (type)));\n"; + pr " PUSHs (sv_2mortal (newSViv (len)));\n"; + + | RLenValue -> + pr "PREINIT:\n"; + pr " hive_value_h r;\n"; + pr " size_t len;\n"; + pr " PPCODE:\n"; pr " errno = 0;\n"; pr " r = hivex_%s (%s, &len);\n" - name (String.concat ", " c_params); - free_args (); + name (String.concat ", " c_params); + free_args (); pr " if (r == 0 && errno)\n"; pr " croak (\"%%s: \", \"%s\", strerror (errno));\n" - name; - pr " EXTEND (SP, 2);\n"; - pr " PUSHs (sv_2mortal (newSViv (len)));\n"; - pr " PUSHs (sv_2mortal (newSViv (r)));\n"; - - | RLenTypeVal -> - pr "PREINIT:\n"; - pr " char *r;\n"; - pr " size_t len;\n"; - pr " hive_type type;\n"; - pr " PPCODE:\n"; + name; + pr " EXTEND (SP, 2);\n"; + pr " PUSHs (sv_2mortal (newSViv (len)));\n"; + pr " PUSHs (sv_2mortal (newSViv (r)));\n"; + + | RLenTypeVal -> + pr "PREINIT:\n"; + pr " char *r;\n"; + pr " size_t len;\n"; + pr " hive_type type;\n"; + pr " PPCODE:\n"; pr " r = hivex_%s (%s, &type, &len);\n" - name (String.concat ", " c_params); - free_args (); + name (String.concat ", " c_params); + free_args (); pr " if (r == NULL)\n"; pr " croak (\"%%s: %%s\", \"%s\", strerror (errno));\n" - name; - pr " EXTEND (SP, 2);\n"; - pr " PUSHs (sv_2mortal (newSViv (type)));\n"; - pr " PUSHs (sv_2mortal (newSVpvn (r, len)));\n"; - pr " free (r);\n"; + name; + pr " EXTEND (SP, 2);\n"; + pr " PUSHs (sv_2mortal (newSViv (type)));\n"; + pr " PUSHs (sv_2mortal (newSVpvn (r, len)));\n"; + pr " free (r);\n"; - | RInt32 -> + | RInt32 -> pr "PREINIT:\n"; pr " int32_t r;\n"; pr " CODE:\n"; - pr " errno = 0;\n"; + pr " errno = 0;\n"; pr " r = hivex_%s (%s);\n" - name (String.concat ", " c_params); - free_args (); + name (String.concat ", " c_params); + free_args (); pr " if (r == -1 && errno != 0)\n"; pr " croak (\"%%s: %%s\", \"%s\", strerror (errno));\n" - name; + name; pr " RETVAL = newSViv (r);\n"; pr " OUTPUT:\n"; pr " RETVAL\n" - | RInt64 -> + | RInt64 -> pr "PREINIT:\n"; pr " int64_t r;\n"; pr " CODE:\n"; - pr " errno = 0;\n"; + pr " errno = 0;\n"; pr " r = hivex_%s (%s);\n" - name (String.concat ", " c_params); - free_args (); + name (String.concat ", " c_params); + free_args (); pr " if (r == -1 && errno != 0)\n"; pr " croak (\"%%s: %%s\", \"%s\", strerror (errno));\n" - name; + name; pr " RETVAL = my_newSVll (r);\n"; pr " OUTPUT:\n"; pr " RETVAL\n" - ); - pr "\n" + ); + pr "\n" ) ) functions @@ -3055,10 +3055,10 @@ put_val_type (char *val, size_t len, hive_type t) pr " PyObject *py_r;\n"; let error_code = - match fst style with + match fst style with | RErr -> pr " int r;\n"; "-1" - | RErrDispose -> pr " int r;\n"; "-1" - | RHive -> pr " hive_h *r;\n"; "NULL" + | RErrDispose -> pr " int r;\n"; "-1" + | RHive -> pr " hive_h *r;\n"; "NULL" | RSize -> pr " size_t r;\n"; "0" | RNode -> pr " hive_node_h r;\n"; "0" | RNodeNotFound -> @@ -3096,11 +3096,11 @@ put_val_type (char *val, size_t len, hive_type t) (* Call and arguments. *) let c_params = - List.map (function - | AUnusedFlags -> "0" - | ASetValues -> "values.nr_values, values.values" + List.map (function + | AUnusedFlags -> "0" + | ASetValues -> "values.nr_values, values.values" | ASetValue -> "&val" - | arg -> name_of_argt arg) (snd style) in + | arg -> name_of_argt arg) (snd style) in let c_params = match fst style with | RLenType | RLenTypeVal -> c_params @ ["&t"; "&len"] @@ -3110,20 +3110,20 @@ put_val_type (char *val, size_t len, hive_type t) List.iter ( function | AHive -> - pr " hive_h *h;\n"; + pr " hive_h *h;\n"; pr " PyObject *py_h;\n" - | ANode n - | AValue n -> - pr " long %s;\n" n - | AString n + | ANode n + | AValue n -> + pr " long %s;\n" n + | AString n | AStringNullable n -> - pr " char *%s;\n" n - | AOpenFlags -> - pr " int flags;\n" - | AUnusedFlags -> () - | ASetValues -> - pr " py_set_values values;\n"; - pr " PyObject *py_values;\n" + pr " char *%s;\n" n + | AOpenFlags -> + pr " int flags;\n" + | AUnusedFlags -> () + | ASetValues -> + pr " py_set_values values;\n"; + pr " PyObject *py_values;\n" | ASetValue -> pr " hive_set_value val;\n"; pr " PyObject *py_val;\n"; @@ -3137,19 +3137,19 @@ put_val_type (char *val, size_t len, hive_type t) List.iter ( function | AHive -> - pr "O" - | ANode n - | AValue n -> - pr "l" - | AString n -> - pr "s" + pr "O" + | ANode n + | AValue n -> + pr "l" + | AString n -> + pr "s" | AStringNullable n -> - pr "z" - | AOpenFlags -> - pr "i" - | AUnusedFlags -> () - | ASetValues - | ASetValue -> + pr "z" + | AOpenFlags -> + pr "i" + | AUnusedFlags -> () + | ASetValues + | ASetValue -> pr "O" ) (snd style); @@ -3158,19 +3158,19 @@ put_val_type (char *val, size_t len, hive_type t) List.iter ( function | AHive -> - pr ", &py_h" - | ANode n - | AValue n -> - pr ", &%s" n - | AString n + pr ", &py_h" + | ANode n + | AValue n -> + pr ", &%s" n + | AString n | AStringNullable n -> - pr ", &%s" n - | AOpenFlags -> - pr ", &flags" - | AUnusedFlags -> () - | ASetValues -> + pr ", &%s" n + | AOpenFlags -> + pr ", &flags" + | AUnusedFlags -> () + | ASetValues -> pr ", &py_values" - | ASetValue -> + | ASetValue -> pr ", &py_val" ) (snd style); @@ -3182,13 +3182,13 @@ put_val_type (char *val, size_t len, hive_type t) function | AHive -> pr " h = get_handle (py_h);\n" - | ANode _ - | AValue _ - | AString _ + | ANode _ + | AValue _ + | AString _ | AStringNullable _ - | AOpenFlags - | AUnusedFlags -> () - | ASetValues -> + | AOpenFlags + | AUnusedFlags -> () + | ASetValues -> pr " if (get_values (py_values, &values) == -1)\n"; pr " return NULL;\n" | ASetValue -> @@ -3202,10 +3202,10 @@ put_val_type (char *val, size_t len, hive_type t) (* Free up arguments. *) List.iter ( function - | AHive | ANode _ | AValue _ + | AHive | ANode _ | AValue _ | AString _ | AStringNullable _ - | AOpenFlags | AUnusedFlags -> () - | ASetValues -> + | AOpenFlags | AUnusedFlags -> () + | ASetValues -> pr " free (values.values);\n"; pr " free (values.words);\n" | ASetValue -> () @@ -3391,13 +3391,13 @@ class Hivex(object): fun arg -> pr ", "; match arg with - | AHive -> assert false + | AHive -> assert false | ANode n | AValue n | AString n | AStringNullable n -> pr "%s" n - | AOpenFlags + | AOpenFlags | AUnusedFlags -> assert false - | ASetValues -> pr "values" - | ASetValue -> pr "val" + | ASetValues -> pr "values" + | ASetValue -> pr "val" ) args; pr ")\n"; pr "\n" @@ -3434,9 +3434,9 @@ and generate_ruby_c () = #define RSTRING_PTR(r) (RSTRING((r))->ptr) #endif -static VALUE m_hivex; /* hivex module */ -static VALUE c_hivex; /* hive_h handle */ -static VALUE e_Error; /* used for all errors */ +static VALUE m_hivex; /* hivex module */ +static VALUE c_hivex; /* hive_h handle */ +static VALUE e_Error; /* used for all errors */ static void ruby_hivex_free (void *hvp) @@ -3591,10 +3591,10 @@ get_values (VALUE valuesv, size_t *nr_values) pr "\n"; let error_code = - match ret with + match ret with | RErr -> pr " int r;\n"; "-1" - | RErrDispose -> pr " int r;\n"; "-1" - | RHive -> pr " hive_h *r;\n"; "NULL" + | RErrDispose -> pr " int r;\n"; "-1" + | RHive -> pr " hive_h *r;\n"; "NULL" | RSize -> pr " size_t r;\n"; "0" | RNode -> pr " hive_node_h r;\n"; "0" | RNodeNotFound -> -- 2.0.2
participants (3)
-
Hilko Bengen -
Peter Wu -
Richard W.M. Jones