commit acf2c4b79c6e00b4d03fc9659cc00b2d09e784bb Author: David Malcolm dmalcolm@redhat.com Date: Fri Sep 2 11:59:17 2011 -0400
cpychecker: when handling PyArg_ParseTuple in the refcount checker, write back (unknown) values to the output regions
libcpychecker/PyArg_ParseTuple.py | 4 ++ libcpychecker/refcounts.py | 53 ++++++++++++++++-- .../PyArg_ParseTuple/correct_simple/input.c | 57 ++++++++++++++++++++ .../PyArg_ParseTuple/correct_simple/script.py | 22 ++++++++ .../PyArg_ParseTuple/correct_simple/stdout.txt | 43 +++++++++++++++ 5 files changed, 173 insertions(+), 6 deletions(-) --- diff --git a/libcpychecker/PyArg_ParseTuple.py b/libcpychecker/PyArg_ParseTuple.py index 6c7817e..a99fd36 100644 --- a/libcpychecker/PyArg_ParseTuple.py +++ b/libcpychecker/PyArg_ParseTuple.py @@ -352,6 +352,10 @@ class PyArgParseFmt: self.fmt_string = fmt_string self.args = []
+ def __repr__(self): + return ('PyArgParseFmt(fmt_string=%r, args=%r)' + % (self.fmt_string, self.args)) + def add_argument(self, code, expected_types): self.args.append(ConcreteUnit(code, expected_types))
diff --git a/libcpychecker/refcounts.py b/libcpychecker/refcounts.py index bae6220..526459c 100644 --- a/libcpychecker/refcounts.py +++ b/libcpychecker/refcounts.py @@ -27,8 +27,9 @@ from gccutils import cfg_to_dot, invoke_dot, get_src_for_loc, check_isinstance
from libcpychecker.absinterp import * from libcpychecker.diagnostics import Reporter, Annotator, Note -from libcpychecker.PyArg_ParseTuple import log +from libcpychecker.PyArg_ParseTuple import PyArgParseFmt, FormatStringError from libcpychecker.types import is_py3k, is_debug_build, get_PyObjectPtr +from libcpychecker.utils import log
# I found myself regularly getting State and Transition instances confused. To # ameliorate that, here are some naming conventions and abbreviations: @@ -580,11 +581,13 @@ class MyState(State): ######################################################################## # PyArg_* ######################################################################## - def impl_PyArg_ParseTuple(self, stmt): - # Decl: - # PyAPI_FUNC(int) PyArg_ParseTuple(PyObject *, const char *, ...) Py_FORMAT_PARSETUPLE(PyArg_ParseTuple, 2, 3); - # Also: - # #define PyArg_ParseTuple _PyArg_ParseTuple_SizeT + def _handle_PyArg_function(self, stmt, v_fmt, v_varargs, with_size_t): + """ + Handle one of the various PyArg_Parse* functions + """ + check_isinstance(v_fmt, AbstractValue) + check_isinstance(v_varargs, list) # of AbstractValue + check_isinstance(with_size_t, bool)
s_success = self.mkstate_concrete_return_of(stmt, 1)
@@ -593,8 +596,46 @@ class MyState(State): # e.g. for the case of the wrong number of arguments: s_failure.set_exception('PyExc_TypeError')
+ # Parse the format string, and figure out what the effects of a + # successful parsing are: + + def _get_format_string(v_fmt): + if isinstance(v_fmt, PointerToRegion): + if isinstance(v_fmt.region, RegionForStringConstant): + return v_fmt.region.text + + def _handle_successful_parse(fmt): + exptypes = fmt.iter_exp_types() + for v_vararg, (unit, exptype) in zip(v_varargs, exptypes): + # print('v_vararg: %r' % v_vararg) + # print(' unit: %r' % unit) + # print(' exptype: %r %s' % (exptype, exptype)) + if isinstance(v_vararg, PointerToRegion): + s_success.value_for_region[v_vararg.region] = UnknownValue(exptype.dereference, stmt.loc) + + fmt_string = _get_format_string(v_fmt) + if fmt_string: + try: + fmt = PyArgParseFmt.from_string(fmt_string, with_size_t) + _handle_successful_parse(fmt) + except FormatStringError: + pass + return self.make_transitions_for_fncall(stmt, s_success, s_failure)
+ def impl_PyArg_ParseTuple(self, stmt): + # Decl: + # PyAPI_FUNC(int) PyArg_ParseTuple(PyObject *, const char *, ...) Py_FORMAT_PARSETUPLE(PyArg_ParseTuple, 2, 3); + # Also: + # #define PyArg_ParseTuple _PyArg_ParseTuple_SizeT + + args = self.eval_stmt_args(stmt) + v_args = args[0] + v_fmt = args[1] + v_varargs = args[2:] + return self._handle_PyArg_function(stmt, v_fmt, v_varargs, with_size_t=False) + + ######################################################################## # PyDict_* ######################################################################## diff --git a/tests/cpychecker/refcounts/PyArg_ParseTuple/correct_simple/input.c b/tests/cpychecker/refcounts/PyArg_ParseTuple/correct_simple/input.c new file mode 100644 index 0000000..509ca09 --- /dev/null +++ b/tests/cpychecker/refcounts/PyArg_ParseTuple/correct_simple/input.c @@ -0,0 +1,57 @@ +/* + Copyright 2011 David Malcolm dmalcolm@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/. +*/ + +#include <Python.h> + +/* + Test of correct reference-handling in a call to PyArg_ParseTuple + that uses simple format codes that don't affect reference counting +*/ + +extern void foo(int i, const char *msg, Py_UNICODE *unicode, int size); + +PyObject * +test(PyObject *self, PyObject *args) +{ + int i; + const char *msg; + Py_UNICODE *unicode; + int size; + + if (!PyArg_ParseTuple(args, "isu#:test", + &i, &msg, &unicode, &size)) { + return NULL; + } + + foo(i, msg, unicode, size); + + Py_RETURN_NONE; +} +static PyMethodDef test_methods[] = { + {"test_method", test, METH_VARARGS, NULL}, + {NULL, NULL, 0, NULL} /* Sentinel */ +}; + +/* + PEP-7 +Local variables: +c-basic-offset: 4 +indent-tabs-mode: nil +End: +*/ diff --git a/tests/cpychecker/refcounts/PyArg_ParseTuple/correct_simple/script.py b/tests/cpychecker/refcounts/PyArg_ParseTuple/correct_simple/script.py new file mode 100644 index 0000000..fdd5ba3 --- /dev/null +++ b/tests/cpychecker/refcounts/PyArg_ParseTuple/correct_simple/script.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- +# Copyright 2011 David Malcolm dmalcolm@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(verify_refcounting=True, + dump_traces=True, + show_traces=False) diff --git a/tests/cpychecker/refcounts/PyArg_ParseTuple/correct_simple/stdout.txt b/tests/cpychecker/refcounts/PyArg_ParseTuple/correct_simple/stdout.txt new file mode 100644 index 0000000..33b3a84 --- /dev/null +++ b/tests/cpychecker/refcounts/PyArg_ParseTuple/correct_simple/stdout.txt @@ -0,0 +1,43 @@ +Trace 0: + Transitions: + 'PyArg_ParseTuple() succeeds' + 'taking False path' + 'returning' + Return value: + repr(): PointerToRegion(gcctype='struct PyObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/PyArg_ParseTuple/correct_simple/input.c', line=44), region=RegionForGlobal(gcc.VarDecl('_Py_NoneStruct'))) + str(): (struct PyObject *)&RegionForGlobal(gcc.VarDecl('_Py_NoneStruct')) from tests/cpychecker/refcounts/PyArg_ParseTuple/correct_simple/input.c:44 + r->ob_refcnt: refs: 1 + N where N >= 1 + r->ob_type: None + Region("region-for-arg-gcc.ParmDecl('self')"): + repr(): Region("region-for-arg-gcc.ParmDecl('self')") + str(): Region("region-for-arg-gcc.ParmDecl('self')") + r->ob_refcnt: refs: 0 + N where N >= 1 + r->ob_type: PointerToRegion(gcctype='struct PyTypeObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/PyArg_ParseTuple/correct_simple/input.c', line=30), region=Region("region-for-type-of-arg-gcc.ParmDecl('self')")) + Region("region-for-arg-gcc.ParmDecl('args')"): + repr(): Region("region-for-arg-gcc.ParmDecl('args')") + str(): Region("region-for-arg-gcc.ParmDecl('args')") + r->ob_refcnt: refs: 0 + N where N >= 1 + r->ob_type: PointerToRegion(gcctype='struct PyTypeObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/PyArg_ParseTuple/correct_simple/input.c', line=30), region=Region("region-for-type-of-arg-gcc.ParmDecl('args')")) + Exception: + (struct PyObject *)0 from tests/cpychecker/refcounts/PyArg_ParseTuple/correct_simple/input.c:31 + +Trace 1: + Transitions: + 'PyArg_ParseTuple() fails' + 'taking True path' + 'returning' + Return value: + repr(): ConcreteValue(gcctype='struct PyObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/PyArg_ParseTuple/correct_simple/input.c', line=39), value=0) + str(): (struct PyObject *)0 from tests/cpychecker/refcounts/PyArg_ParseTuple/correct_simple/input.c:39 + Region("region-for-arg-gcc.ParmDecl('self')"): + repr(): Region("region-for-arg-gcc.ParmDecl('self')") + str(): Region("region-for-arg-gcc.ParmDecl('self')") + r->ob_refcnt: refs: 0 + N where N >= 1 + r->ob_type: PointerToRegion(gcctype='struct PyTypeObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/PyArg_ParseTuple/correct_simple/input.c', line=30), region=Region("region-for-type-of-arg-gcc.ParmDecl('self')")) + Region("region-for-arg-gcc.ParmDecl('args')"): + repr(): Region("region-for-arg-gcc.ParmDecl('args')") + str(): Region("region-for-arg-gcc.ParmDecl('args')") + r->ob_refcnt: refs: 0 + N where N >= 1 + r->ob_type: PointerToRegion(gcctype='struct PyTypeObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/PyArg_ParseTuple/correct_simple/input.c', line=30), region=Region("region-for-type-of-arg-gcc.ParmDecl('args')")) + Exception: + RegionForGlobal(gcc.VarDecl('PyExc_TypeError'))
gcc-python-plugin-commits@lists.stg.fedorahosted.org