Here is a patch that adds checking for NULL-termination of keyword
arguments to PyArg_ParseTupleAndKeywords. This problem has bit us in
gdb a couple of times.
I've included a doc update and a test case.
Let me know what you think.
I needed to add an 'operand' field to Unary. After looking at it a
little, I think this code does not model GCC precisely enough.
E.g., 'location' is added just for 'Expression':
if localname == 'Expression':
add_simple_getter('location',
'gcc_python_make_wrapper_location(EXPR_LOCATION(self->t))',
"The source location of this expression")
But, I think this should actually be added for any class that uses
tree_exp as its representation: tcc_expression, tcc_comparison,
tcc_unary, tcc_binary, and tcc_statement.
Tom
diff --git a/docs/tree.rst b/docs/tree.rst
index 8a0a89f..4a8a0fd 100644
--- a/docs/tree.rst
+++ b/docs/tree.rst
@@ -503,6 +503,10 @@ Unary Expressions
Corresponds to the `tcc_unary` value of `enum tree_code_class` within
GCC's own C sources.
+ .. py:attribute:: operand
+
+ The operand of this operator, as a `gcc.Tree`.
+
======================================
==================================================
Subclass Meaning; C/C++ operators
======================================
==================================================
diff --git a/generate-tree-c.py b/generate-tree-c.py
index 419aaaa..4c52c63 100644
--- a/generate-tree-c.py
+++ b/generate-tree-c.py
@@ -253,6 +253,11 @@ PyObject*
pytype.tp_methods = methods.identifier
cu.add_defn(methods.c_defn())
+ if localname == 'Unary':
+ add_simple_getter('operand',
+ 'gcc_python_make_wrapper_tree(TREE_OPERAND (self->t,
0))',
+ 'The operand of this expression, as a gcc.Tree')
+
if localname == 'Expression':
add_simple_getter('location',
'gcc_python_make_wrapper_location(EXPR_LOCATION(self->t))',
diff --git a/libcpychecker/PyArg_ParseTuple.py b/libcpychecker/PyArg_ParseTuple.py
index bf99e35..8e0b977 100644
--- a/libcpychecker/PyArg_ParseTuple.py
+++ b/libcpychecker/PyArg_ParseTuple.py
@@ -542,6 +542,33 @@ def check_pyargs(fun):
if isinstance(operand, gcc.StringCst):
return operand.constant
+ def check_keyword_array(stmt, idx):
+ keywords = stmt.args[idx]
+ if isinstance(keywords, gcc.AddrExpr):
+ operand = keywords.operand
+ if isinstance(operand, gcc.VarDecl):
+ initial = operand.initial
+ if isinstance(initial, gcc.Constructor):
+ elements = [None] * len(initial.elements)
+ for elt in initial.elements:
+ (num, contents) = elt
+ elt_idx = num.constant
+ if isinstance(contents, gcc.NopExpr):
+ contents = contents.operand
+ if isinstance(contents, gcc.AddrExpr):
+ contents = contents.operand
+ if isinstance(contents, gcc.StringCst):
+ elements[elt_idx] = contents.constant
+ elif isinstance(contents, gcc.IntegerCst):
+ elements[elt_idx] = contents.constant
+ if elements[-1] != 0:
+ gcc.permerror(stmt.loc, 'keywords to
PyArg_ParseTupleAndKeywords are not NULL-terminated')
+ i = 0
+ for elt in elements[0:-1]:
+ if not elt:
+ gcc.permerror(stmt.loc, 'keyword argument %d missing in
PyArg_ParseTupleAndKeywords call' % i)
+ i = i + 1
+
def check_callsite(stmt, funcname, format_idx, varargs_idx):
log('got call at %s' % stmt.loc)
log(get_src_for_loc(stmt.loc))
@@ -607,5 +634,6 @@ def check_pyargs(fun):
if stmt.fndecl.name == 'PyArg_ParseTuple':
check_callsite(stmt, 'PyArg_ParseTuple', 1, 2)
elif stmt.fndecl.name ==
'PyArg_ParseTupleAndKeywords':
+ check_keyword_array(stmt, 3)
check_callsite(stmt,
'PyArg_ParseTupleAndKeywords', 2, 4)
diff --git a/tests/cpychecker/PyArg_ParseTuple/keywords/input.c
b/tests/cpychecker/PyArg_ParseTuple/keywords/input.c
new file mode 100644
index 0000000..d9d567e
--- /dev/null
+++ b/tests/cpychecker/PyArg_ParseTuple/keywords/input.c
@@ -0,0 +1,45 @@
+/*
+ Copyright 2011 David Malcolm <dmalcolm(a)redhat.com>
+ Copyright 2011 Red Hat, Inc.
+
+ This 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 3 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, see
+ <
http://www.gnu.org/licenses/>.
+*/
+
+/*
+ A simple check of PyArg_ParseTupleAndKeywords
+*/
+
+#include <Python.h>
+
+static PyObject *
+parse_to_a_typedef(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ int val;
+ static char *keywords[] = { "hi" }; /* Note no NULL terminator. */
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i", keywords, &val)) {
+ return NULL;
+ }
+
+ Py_RETURN_NONE;
+}
+
+/*
+ PEP-7
+Local variables:
+c-basic-offset: 4
+indent-tabs-mode: nil
+End:
+*/
diff --git a/tests/cpychecker/PyArg_ParseTuple/keywords/script.py
b/tests/cpychecker/PyArg_ParseTuple/keywords/script.py
new file mode 100644
index 0000000..e120c53
--- /dev/null
+++ b/tests/cpychecker/PyArg_ParseTuple/keywords/script.py
@@ -0,0 +1,19 @@
+# Copyright 2011 David Malcolm <dmalcolm(a)redhat.com>
+# Copyright 2011 Red Hat, Inc.
+#
+# This 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 3 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, see
+# <
http://www.gnu.org/licenses/>.
+
+from libcpychecker import main
+main()
diff --git a/tests/cpychecker/PyArg_ParseTuple/keywords/stderr.txt
b/tests/cpychecker/PyArg_ParseTuple/keywords/stderr.txt
new file mode 100644
index 0000000..3168ae9
--- /dev/null
+++ b/tests/cpychecker/PyArg_ParseTuple/keywords/stderr.txt
@@ -0,0 +1,2 @@
+tests/cpychecker/PyArg_ParseTuple/keywords/input.c: In function ‘parse_to_a_typedef’:
+tests/cpychecker/PyArg_ParseTuple/keywords/input.c:32:37: error: keywords to
PyArg_ParseTupleAndKeywords are not NULL-terminated [-fpermissive]