commit 5fdceb1d7653e2a2309a5188c6164bcbad908eb4 Author: David Malcolm dmalcolm@redhat.com Date: Tue Sep 6 13:45:14 2011 -0400
cpychecker: initial implementation of PyMem_Malloc and PyMem_Free
libcpychecker/refcounts.py | 61 ++++++++++++++++++++ .../PyMem_Malloc/correct/alloc-and-free/input.c | 54 +++++++++++++++++ .../PyMem_Malloc/correct/alloc-and-free/script.py | 22 +++++++ .../PyMem_Malloc/correct/alloc-and-free/stdout.txt | 45 ++++++++++++++ .../PyMem_Malloc/correct/free-NULL/input.c | 46 +++++++++++++++ .../PyMem_Malloc/correct/free-NULL/script.py | 22 +++++++ .../PyMem_Malloc/correct/free-NULL/stdout.txt | 21 +++++++ 7 files changed, 271 insertions(+), 0 deletions(-) --- diff --git a/libcpychecker/refcounts.py b/libcpychecker/refcounts.py index dda2cd4..5ee4716 100644 --- a/libcpychecker/refcounts.py +++ b/libcpychecker/refcounts.py @@ -1101,6 +1101,67 @@ class MyState(State): return [success, failure]
######################################################################## + # PyMem_* + ######################################################################## + def impl_PyMem_Free(self, stmt): + # http://docs.python.org/c-api/memory.html#PyMem_Free + fnname = 'PyMem_Free' + args = self.eval_stmt_args(stmt) + v_ptr, = args + + # FIXME: it's unsafe to call repeatedly, or on the wrong memory region + + s_new = self.copy() + s_new.loc = self.loc.next_loc() + desc = None + + # It's safe to call on NULL + if isinstance(v_ptr, ConcreteValue): + if v_ptr.is_null_ptr(): + desc = 'calling PyMem_Free on NULL' + elif isinstance(v_ptr, PointerToRegion): + # Mark the arg as being deallocated: + region = v_ptr.region + check_isinstance(region, Region) + + # Get the description of the region before trashing it: + desc = 'calling PyMem_Free on %s' % region + #t_temp = state.mktrans_assignment(stmt.lhs, + # UnknownValue(None, stmt.loc), + # 'calling tp_dealloc on %s' % region) + + # Mark the region as deallocated + # Since regions are shared with other states, we have to set this up + # for this state by assigning it with a special "DeallocatedMemory" + # value + # Clear the value for any fields within the region: + for k, v in region.fields.items(): + if v in s_new.value_for_region: + del s_new.value_for_region[v] + # Set the default value for the whole region to be "DeallocatedMemory" + s_new.region_for_var[region] = region + s_new.value_for_region[region] = DeallocatedMemory(None, stmt.loc) + + return [Transition(self, s_new, desc)] + + def impl_PyMem_Malloc(self, stmt): + # http://docs.python.org/c-api/memory.html#PyMem_Malloc + fnname = 'PyMem_Malloc' + returntype = stmt.fn.type.dereference.type + args = self.eval_stmt_args(stmt) + v_size, = args + r_nonnull = self.make_heap_region('PyMem_Malloc', stmt) + v_nonnull = PointerToRegion(returntype, stmt.loc, r_nonnull) + # FIXME: it hasn't been initialized + t_success = self.mktrans_assignment(stmt.lhs, + v_nonnull, + '%s() succeeds' % fnname) + t_failure = self.mktrans_assignment(stmt.lhs, + ConcreteValue(returntype, stmt.loc, 0), + '%s() fails' % fnname) + return [t_success, t_failure] + + ######################################################################## # PyModule_* ######################################################################## def impl_PyModule_AddIntConstant(self, stmt): diff --git a/tests/cpychecker/refcounts/PyMem_Malloc/correct/alloc-and-free/input.c b/tests/cpychecker/refcounts/PyMem_Malloc/correct/alloc-and-free/input.c new file mode 100644 index 0000000..d17ea54 --- /dev/null +++ b/tests/cpychecker/refcounts/PyMem_Malloc/correct/alloc-and-free/input.c @@ -0,0 +1,54 @@ +/* + 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 a correct call to PyMem_Malloc (and PyMem_Free) +*/ + +extern void foo(char *buf, size_t n); + +PyObject * +test(PyObject *self, PyObject *args) +{ + char *buf = PyMem_New(char, 4096); + + if (!buf) { + return PyErr_NoMemory(); + } + + foo(buf, 4096); + + PyMem_Free(buf); + + 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/PyMem_Malloc/correct/alloc-and-free/script.py b/tests/cpychecker/refcounts/PyMem_Malloc/correct/alloc-and-free/script.py new file mode 100644 index 0000000..fdd5ba3 --- /dev/null +++ b/tests/cpychecker/refcounts/PyMem_Malloc/correct/alloc-and-free/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/PyMem_Malloc/correct/alloc-and-free/stdout.txt b/tests/cpychecker/refcounts/PyMem_Malloc/correct/alloc-and-free/stdout.txt new file mode 100644 index 0000000..d4217ee --- /dev/null +++ b/tests/cpychecker/refcounts/PyMem_Malloc/correct/alloc-and-free/stdout.txt @@ -0,0 +1,45 @@ +Trace 0: + Transitions: + 'PyMem_Malloc() succeeds' + 'taking False path' + 'calling PyMem_Free on PyMem_Malloc allocated at tests/cpychecker/refcounts/PyMem_Malloc/correct/alloc-and-free/input.c:31' + 'returning' + Return value: + repr(): PointerToRegion(gcctype='struct PyObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/PyMem_Malloc/correct/alloc-and-free/input.c', line=41), region=RegionForGlobal(gcc.VarDecl('_Py_NoneStruct'))) + str(): (struct PyObject *)&RegionForGlobal(gcc.VarDecl('_Py_NoneStruct')) from tests/cpychecker/refcounts/PyMem_Malloc/correct/alloc-and-free/input.c:41 + 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/PyMem_Malloc/correct/alloc-and-free/input.c', line=29), 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/PyMem_Malloc/correct/alloc-and-free/input.c', line=29), region=Region("region-for-type-of-arg-gcc.ParmDecl('args')")) + Exception: + (struct PyObject *)0 from tests/cpychecker/refcounts/PyMem_Malloc/correct/alloc-and-free/input.c:30 + +Trace 1: + Transitions: + 'PyMem_Malloc() fails' + 'taking True path' + 'PyErr_NoMemory()' + 'returning' + Return value: + repr(): ConcreteValue(gcctype='struct PyObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/PyMem_Malloc/correct/alloc-and-free/input.c', line=34), value=0) + str(): (struct PyObject *)0 from tests/cpychecker/refcounts/PyMem_Malloc/correct/alloc-and-free/input.c:34 + 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/PyMem_Malloc/correct/alloc-and-free/input.c', line=29), 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/PyMem_Malloc/correct/alloc-and-free/input.c', line=29), region=Region("region-for-type-of-arg-gcc.ParmDecl('args')")) + Exception: + RegionForGlobal(gcc.VarDecl('PyExc_MemoryError')) diff --git a/tests/cpychecker/refcounts/PyMem_Malloc/correct/free-NULL/input.c b/tests/cpychecker/refcounts/PyMem_Malloc/correct/free-NULL/input.c new file mode 100644 index 0000000..e929f13 --- /dev/null +++ b/tests/cpychecker/refcounts/PyMem_Malloc/correct/free-NULL/input.c @@ -0,0 +1,46 @@ +/* + 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 a correct call to PyMem_Free(NULL) +*/ + +extern void foo(char *buf, size_t n); + +PyObject * +test(PyObject *self, PyObject *args) +{ + PyMem_Free(NULL); + + 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/PyMem_Malloc/correct/free-NULL/script.py b/tests/cpychecker/refcounts/PyMem_Malloc/correct/free-NULL/script.py new file mode 100644 index 0000000..fdd5ba3 --- /dev/null +++ b/tests/cpychecker/refcounts/PyMem_Malloc/correct/free-NULL/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/PyMem_Malloc/correct/free-NULL/stdout.txt b/tests/cpychecker/refcounts/PyMem_Malloc/correct/free-NULL/stdout.txt new file mode 100644 index 0000000..10bd3b4 --- /dev/null +++ b/tests/cpychecker/refcounts/PyMem_Malloc/correct/free-NULL/stdout.txt @@ -0,0 +1,21 @@ +Trace 0: + Transitions: + 'calling PyMem_Free on NULL' + 'returning' + Return value: + repr(): PointerToRegion(gcctype='struct PyObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/PyMem_Malloc/correct/free-NULL/input.c', line=33), region=RegionForGlobal(gcc.VarDecl('_Py_NoneStruct'))) + str(): (struct PyObject *)&RegionForGlobal(gcc.VarDecl('_Py_NoneStruct')) from tests/cpychecker/refcounts/PyMem_Malloc/correct/free-NULL/input.c:33 + 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/PyMem_Malloc/correct/free-NULL/input.c', line=29), 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/PyMem_Malloc/correct/free-NULL/input.c', line=29), region=Region("region-for-type-of-arg-gcc.ParmDecl('args')")) + Exception: + (struct PyObject *)0 from tests/cpychecker/refcounts/PyMem_Malloc/correct/free-NULL/input.c:30
gcc-python-plugin-commits@lists.stg.fedorahosted.org