commit eeb4d89f6686864e84f085a971f328a44524f305
Author: David Malcolm <dmalcolm(a)redhat.com>
Date: Wed Dec 21 22:41:14 2011 -0500
cpychecker: add bulletproofing to refcount modifications
In many cases, PyObject* values are initialized by the checker to have
an ob_refcnt, but it's possible for CPython.change_refcount() to
encounter a PyObject* for which the ob_refcnt is unknown.
This patch makes it treat such a case as a borrowed reference, rather
than failing with a traceback.
Fixes a traceback seen when running the checker on rpm's python bindings
in python/header-py.c:hdr_reduce where we don't know the ob_refcnt of
Py_TYPE(s) in:
Py_BuildValue("O(O)", Py_TYPE(s), blob);
libcpychecker/refcounts.py | 5 ++-
.../refcounts/Py_BuildValue/Py_TYPE/input.c | 39 ++++++++++++++++++++
.../refcounts/Py_BuildValue/Py_TYPE/script.py | 22 +++++++++++
.../refcounts/Py_BuildValue/Py_TYPE/stdout.txt | 31 ++++++++++++++++
4 files changed, 95 insertions(+), 2 deletions(-)
---
diff --git a/libcpychecker/refcounts.py b/libcpychecker/refcounts.py
index c0f7a0a..8cb6c58 100644
--- a/libcpychecker/refcounts.py
+++ b/libcpychecker/refcounts.py
@@ -304,8 +304,9 @@ class CPython(Facet):
oldvalue = self.state.get_store(ob_refcnt, None, loc) # FIXME: gcctype
check_isinstance(oldvalue, AbstractValue)
log('oldvalue: %r', oldvalue)
- #if isinstance(oldvalue, UnknownValue):
- # self.raise_split_value(oldvalue, loc)
+ # If we never had a ob_refcnt, treat it as a borrowed reference:
+ if isinstance(oldvalue, UnknownValue):
+ oldvalue = RefcountValue.borrowed_ref()
check_isinstance(oldvalue, RefcountValue)
newvalue = fn(oldvalue)
log('newvalue: %r', newvalue)
diff --git a/tests/cpychecker/refcounts/Py_BuildValue/Py_TYPE/input.c
b/tests/cpychecker/refcounts/Py_BuildValue/Py_TYPE/input.c
new file mode 100644
index 0000000..9b8d6e9
--- /dev/null
+++ b/tests/cpychecker/refcounts/Py_BuildValue/Py_TYPE/input.c
@@ -0,0 +1,39 @@
+/*
+ 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/>.
+*/
+
+#include <Python.h>
+
+/*
+ Verify that Py_BuildValue("O") can cope with a PyObject* for which the
+ ob_refcnt is unknown. This happens with PyTypeObject instances.
+*/
+
+PyObject *
+test(PyObject *a)
+{
+ return Py_BuildValue("O", Py_TYPE(a));
+}
+
+/*
+ PEP-7
+Local variables:
+c-basic-offset: 4
+indent-tabs-mode: nil
+End:
+*/
diff --git a/tests/cpychecker/refcounts/Py_BuildValue/Py_TYPE/script.py
b/tests/cpychecker/refcounts/Py_BuildValue/Py_TYPE/script.py
new file mode 100644
index 0000000..fdd5ba3
--- /dev/null
+++ b/tests/cpychecker/refcounts/Py_BuildValue/Py_TYPE/script.py
@@ -0,0 +1,22 @@
+# -*- coding: utf-8 -*-
+# 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(verify_refcounting=True,
+ dump_traces=True,
+ show_traces=False)
diff --git a/tests/cpychecker/refcounts/Py_BuildValue/Py_TYPE/stdout.txt
b/tests/cpychecker/refcounts/Py_BuildValue/Py_TYPE/stdout.txt
new file mode 100644
index 0000000..5583c34
--- /dev/null
+++ b/tests/cpychecker/refcounts/Py_BuildValue/Py_TYPE/stdout.txt
@@ -0,0 +1,31 @@
+Trace 0:
+ Transitions:
+ 'when Py_BuildValue() succeeds'
+ 'returning'
+ Return value:
+ repr(): PointerToRegion(gcctype='struct PyObject *',
loc=gcc.Location(file='tests/cpychecker/refcounts/Py_BuildValue/Py_TYPE/input.c',
line=30), region=RegionOnHeap('new ref from call to Py_BuildValue',
gcc.Location(file='tests/cpychecker/refcounts/Py_BuildValue/Py_TYPE/input.c',
line=30)))
+ str(): (struct PyObject *)&RegionOnHeap('new ref from call to
Py_BuildValue',
gcc.Location(file='tests/cpychecker/refcounts/Py_BuildValue/Py_TYPE/input.c',
line=30)) from tests/cpychecker/refcounts/Py_BuildValue/Py_TYPE/input.c:30
+ r->ob_refcnt: refs: 1 + N where N >= 0
+ r->ob_type: PointerToRegion(gcctype='struct PyTypeObject *',
loc=gcc.Location(file='tests/cpychecker/refcounts/Py_BuildValue/Py_TYPE/input.c',
line=30), region=Region('PyTypeObject for new ref from call to Py_BuildValue'))
+ Region("region-for-arg-gcc.ParmDecl('a')"):
+ repr(): Region("region-for-arg-gcc.ParmDecl('a')")
+ str(): Region("region-for-arg-gcc.ParmDecl('a')")
+ r->ob_refcnt: refs: 0 + N where N >= 1
+ r->ob_type: PointerToRegion(gcctype='struct PyTypeObject *',
loc=gcc.Location(file='tests/cpychecker/refcounts/Py_BuildValue/Py_TYPE/input.c',
line=28), region=Region("region-for-type-of-arg-gcc.ParmDecl('a')"))
+ Exception:
+ (struct PyObject *)0 from
tests/cpychecker/refcounts/Py_BuildValue/Py_TYPE/input.c:29
+
+Trace 1:
+ Transitions:
+ 'when Py_BuildValue() fails'
+ 'returning'
+ Return value:
+ repr(): ConcreteValue(gcctype='struct PyObject *',
loc=gcc.Location(file='tests/cpychecker/refcounts/Py_BuildValue/Py_TYPE/input.c',
line=30), value=0)
+ str(): (struct PyObject *)0 from
tests/cpychecker/refcounts/Py_BuildValue/Py_TYPE/input.c:30
+ Region("region-for-arg-gcc.ParmDecl('a')"):
+ repr(): Region("region-for-arg-gcc.ParmDecl('a')")
+ str(): Region("region-for-arg-gcc.ParmDecl('a')")
+ r->ob_refcnt: refs: 0 + N where N >= 1
+ r->ob_type: PointerToRegion(gcctype='struct PyTypeObject *',
loc=gcc.Location(file='tests/cpychecker/refcounts/Py_BuildValue/Py_TYPE/input.c',
line=28), region=Region("region-for-type-of-arg-gcc.ParmDecl('a')"))
+ Exception:
+ (struct PyObject *)&RegionForGlobal(gcc.VarDecl('PyExc_MemoryError'))
from tests/cpychecker/refcounts/Py_BuildValue/Py_TYPE/input.c:30