[gcc-python-plugin] cpychecker: implement PySequence_Concat and PySequence_GetSlice
by David Hugh Malcolm
commit c545e5f2abc003f1568598698b3325b67c3c49fd
Author: David Malcolm <dmalcolm(a)redhat.com>
Date: Tue Feb 7 15:19:53 2012 -0500
cpychecker: implement PySequence_Concat and PySequence_GetSlice
libcpychecker/refcounts.py | 24 ++++++++++++++++++++++++
1 files changed, 24 insertions(+), 0 deletions(-)
---
diff --git a/libcpychecker/refcounts.py b/libcpychecker/refcounts.py
index 2a4b02e..4489a1d 100644
--- a/libcpychecker/refcounts.py
+++ b/libcpychecker/refcounts.py
@@ -2327,6 +2327,18 @@ class CPython(Facet):
########################################################################
# PySequence_*
########################################################################
+ def impl_PySequence_Concat(self, stmt, v_o1, v_o2):
+ fnmeta = FnMeta(name='PySequence_Concat',
+ docurl='http://docs.python.org/c-api/sequence.html#PySequence_Concat',
+ declared_in='abstract.h',
+ prototype='PyAPI_FUNC(PyObject *) PySequence_Concat(PyObject *o1, PyObject *o2);',
+ defined_in='Objects/abstract.c')
+ # safely handles NULL for either argument via null_error(), with PyExc_SystemError
+
+ return self.make_transitions_for_new_ref_or_fail(stmt,
+ fnmeta,
+ 'new ref from %s' % fnmeta.name)
+
def impl_PySequence_GetItem(self, stmt, v_o, v_i):
fnmeta = FnMeta(name='PySequence_GetItem',
docurl='http://docs.python.org/c-api/sequence.html#PySequence_GetItem',
@@ -2348,6 +2360,18 @@ class CPython(Facet):
fnmeta,
'new ref from %s' % fnmeta.name)
+ def impl_PySequence_GetSlice(self, stmt, v_o, v_i1, v_i2):
+ fnmeta = FnMeta(name='PySequence_GetSlice',
+ docurl='http://docs.python.org/c-api/sequence.html#PySequence_GetSlice',
+ declared_in='abstract.h',
+ prototype='PyAPI_FUNC(PyObject *) PySequence_GetSlice(PyObject *o, Py_ssize_t i1, Py_ssize_t i2);',
+ defined_in='Objects/abstract.c')
+ # safely handles NULL for the obj via null_error(), with PyExc_SystemError
+
+ return self.make_transitions_for_new_ref_or_fail(stmt,
+ fnmeta,
+ 'new ref from %s' % fnmeta.name)
+
########################################################################
# PyString_*
########################################################################
12 years, 3 months
[gcc-python-plugin] cpychecker: warn about NULL args to PyDict_GetItem and PyDict_GetItemString
by David Hugh Malcolm
commit 6c56964374bdb7cb311c8aea9e57ea027757135c
Author: David Malcolm <dmalcolm(a)redhat.com>
Date: Tue Feb 7 15:11:38 2012 -0500
cpychecker: warn about NULL args to PyDict_GetItem and PyDict_GetItemString
libcpychecker/refcounts.py | 14 ++++++++++
.../refcounts/PyDict_GetItem/correct/stdout.txt | 20 +++++++++++++++
.../refcounts/PyDict_GetItem/incorrect/stderr.txt | 26 ++++++++++---------
.../refcounts/PyDict_GetItem/incorrect/stdout.txt | 20 +++++++++++++++
.../PyDict_GetItemString/correct/stdout.txt | 20 +++++++++++++++
.../PyDict_GetItemString/incorrect/stderr.txt | 26 ++++++++++---------
.../PyDict_GetItemString/incorrect/stdout.txt | 20 +++++++++++++++
7 files changed, 122 insertions(+), 24 deletions(-)
---
diff --git a/libcpychecker/refcounts.py b/libcpychecker/refcounts.py
index a6b32f1..2a4b02e 100644
--- a/libcpychecker/refcounts.py
+++ b/libcpychecker/refcounts.py
@@ -1166,6 +1166,12 @@ class CPython(Facet):
notes=('Returns a borrowed reference, or NULL if not'
' found. It does *not* set an exception (for'
' historical reasons)'''))
+ self.state.raise_any_null_ptr_func_arg(stmt, 0, v_mp,
+ why=invokes_Py_TYPE_via_macro(fnmeta,
+ 'PyDict_Check'))
+ self.state.raise_any_null_ptr_func_arg(stmt, 1, v_key,
+ why=invokes_Py_TYPE_via_macro(fnmeta,
+ 'PyString_CheckExact'))
s_success = self.mkstate_borrowed_ref(stmt, fnmeta)
t_notfound = self.state.mktrans_assignment(stmt.lhs,
make_null_pyobject_ptr(stmt),
@@ -1181,6 +1187,14 @@ class CPython(Facet):
defined_in='Objects/dictobject.c',
notes=('Returns a borrowed ref, or NULL if not found'
' (can also return NULL and set MemoryError)'))
+ self.state.raise_any_null_ptr_func_arg(stmt, 0, v_dp,
+ why=invokes_Py_TYPE_via_macro(fnmeta,
+ 'PyDict_Check'))
+ # (within PyDict_GetItem)
+
+ self.state.raise_any_null_ptr_func_arg(stmt, 1, v_key,
+ why='%s() invokes PyString_FromString()' % fnmeta.name)
+
s_success = self.mkstate_borrowed_ref(stmt, fnmeta)
t_notfound = self.state.mktrans_assignment(stmt.lhs,
make_null_pyobject_ptr(stmt),
diff --git a/tests/cpychecker/refcounts/PyDict_GetItem/correct/stdout.txt b/tests/cpychecker/refcounts/PyDict_GetItem/correct/stdout.txt
index d146827..4014da6 100644
--- a/tests/cpychecker/refcounts/PyDict_GetItem/correct/stdout.txt
+++ b/tests/cpychecker/refcounts/PyDict_GetItem/correct/stdout.txt
@@ -1,5 +1,6 @@
Trace 0:
Transitions:
+ 'when treating unknown struct PyObject * from tests/cpychecker/refcounts/PyDict_GetItem/correct/input.c:26 as non-NULL'
'when PyDict_GetItem() succeeds'
'taking True path'
'returning'
@@ -23,6 +24,7 @@ Trace 0:
Trace 1:
Transitions:
+ 'when treating unknown struct PyObject * from tests/cpychecker/refcounts/PyDict_GetItem/correct/input.c:26 as non-NULL'
'when PyDict_GetItem does not find item'
'taking False path'
'calling PyErr_SetString()'
@@ -42,3 +44,21 @@ Trace 1:
r->ob_type: PointerToRegion(gcctype='struct PyTypeObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/PyDict_GetItem/correct/input.c', line=29), region=Region("region-for-type-of-arg-gcc.ParmDecl('args')"))
Exception:
unknown struct PyObject * from /usr/include/python?.?/pyerrors.h:nn
+
+Trace 2:
+ Transitions:
+ 'when treating unknown struct PyObject * from tests/cpychecker/refcounts/PyDict_GetItem/correct/input.c:26 as NULL'
+ error: NullPtrArgument()
+ error: possibly calling PyDict_GetItem with NULL as argument 1 (some_dict.0) at tests/cpychecker/refcounts/PyDict_GetItem/correct/input.c:32
+ 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/PyDict_GetItem/correct/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/PyDict_GetItem/correct/input.c', line=29), region=Region("region-for-type-of-arg-gcc.ParmDecl('args')"))
+ Exception:
+ (struct PyObject *)0 from tests/cpychecker/refcounts/PyDict_GetItem/correct/input.c:30
diff --git a/tests/cpychecker/refcounts/PyDict_GetItem/incorrect/stderr.txt b/tests/cpychecker/refcounts/PyDict_GetItem/incorrect/stderr.txt
index d72319a..ea3fcb6 100644
--- a/tests/cpychecker/refcounts/PyDict_GetItem/incorrect/stderr.txt
+++ b/tests/cpychecker/refcounts/PyDict_GetItem/incorrect/stderr.txt
@@ -1,13 +1,15 @@
tests/cpychecker/refcounts/PyDict_GetItem/incorrect/input.c: In function 'test':
-tests/cpychecker/refcounts/PyDict_GetItem/incorrect/input.c:35:5: warning: ob_refcnt of return value is 1 too low [enabled by default]
-tests/cpychecker/refcounts/PyDict_GetItem/incorrect/input.c:35:5: note: was expecting final ob_refcnt to be N + 1 (for some unknown N)
-tests/cpychecker/refcounts/PyDict_GetItem/incorrect/input.c:35:5: note: due to object being referenced by: return value
-tests/cpychecker/refcounts/PyDict_GetItem/incorrect/input.c:35:5: note: but final ob_refcnt is N + 0
-tests/cpychecker/refcounts/PyDict_GetItem/incorrect/input.c:35:5: note: borrowed reference returned by PyDict_GetItem() allocated at: return PyDict_GetItem(some_dict, args);
-tests/cpychecker/refcounts/PyDict_GetItem/incorrect/input.c:35:5: note: when PyDict_GetItem() succeeds at: return PyDict_GetItem(some_dict, args);
-tests/cpychecker/refcounts/PyDict_GetItem/incorrect/input.c:35:5: note: ob_refcnt is now refs: 0 + N where N >= 1
-tests/cpychecker/refcounts/PyDict_GetItem/incorrect/input.c:35:5: note: returning at: return PyDict_GetItem(some_dict, args);
-tests/cpychecker/refcounts/PyDict_GetItem/incorrect/input.c:35:5: warning: returning (PyObject*)NULL without setting an exception [enabled by default]
-tests/cpychecker/refcounts/PyDict_GetItem/incorrect/input.c:35:5: note: when PyDict_GetItem does not find item at: return PyDict_GetItem(some_dict, args);
-tests/cpychecker/refcounts/PyDict_GetItem/incorrect/input.c:35:5: note: returning at: return PyDict_GetItem(some_dict, args);
-tests/cpychecker/refcounts/PyDict_GetItem/incorrect/input.c:30:1: note: graphical error report for function 'test' written out to 'tests/cpychecker/refcounts/PyDict_GetItem/incorrect/input.c.test-refcount-errors.html'
+tests/cpychecker/refcounts/PyDict_GetItem/incorrect/input.c:35:nn: warning: ob_refcnt of return value is 1 too low [enabled by default]
+tests/cpychecker/refcounts/PyDict_GetItem/incorrect/input.c:35:nn: note: was expecting final ob_refcnt to be N + 1 (for some unknown N)
+tests/cpychecker/refcounts/PyDict_GetItem/incorrect/input.c:35:nn: note: due to object being referenced by: return value
+tests/cpychecker/refcounts/PyDict_GetItem/incorrect/input.c:35:nn: note: but final ob_refcnt is N + 0
+tests/cpychecker/refcounts/PyDict_GetItem/incorrect/input.c:35:nn: note: borrowed reference returned by PyDict_GetItem() allocated at: return PyDict_GetItem(some_dict, args);
+tests/cpychecker/refcounts/PyDict_GetItem/incorrect/input.c:35:nn: note: when treating unknown struct PyObject * from tests/cpychecker/refcounts/PyDict_GetItem/incorrect/input.c:26 as non-NULL at: return PyDict_GetItem(some_dict, args);
+tests/cpychecker/refcounts/PyDict_GetItem/incorrect/input.c:35:nn: note: when PyDict_GetItem() succeeds at: return PyDict_GetItem(some_dict, args);
+tests/cpychecker/refcounts/PyDict_GetItem/incorrect/input.c:35:nn: note: ob_refcnt is now refs: 0 + N where N >= 1
+tests/cpychecker/refcounts/PyDict_GetItem/incorrect/input.c:35:nn: note: returning at: return PyDict_GetItem(some_dict, args);
+tests/cpychecker/refcounts/PyDict_GetItem/incorrect/input.c:35:nn: warning: returning (PyObject*)NULL without setting an exception [enabled by default]
+tests/cpychecker/refcounts/PyDict_GetItem/incorrect/input.c:35:nn: note: when treating unknown struct PyObject * from tests/cpychecker/refcounts/PyDict_GetItem/incorrect/input.c:26 as non-NULL at: return PyDict_GetItem(some_dict, args);
+tests/cpychecker/refcounts/PyDict_GetItem/incorrect/input.c:35:nn: note: when PyDict_GetItem does not find item at: return PyDict_GetItem(some_dict, args);
+tests/cpychecker/refcounts/PyDict_GetItem/incorrect/input.c:35:nn: note: returning at: return PyDict_GetItem(some_dict, args);
+tests/cpychecker/refcounts/PyDict_GetItem/incorrect/input.c:30:nn: note: graphical error report for function 'test' written out to 'tests/cpychecker/refcounts/PyDict_GetItem/incorrect/input.c.test-refcount-errors.html'
diff --git a/tests/cpychecker/refcounts/PyDict_GetItem/incorrect/stdout.txt b/tests/cpychecker/refcounts/PyDict_GetItem/incorrect/stdout.txt
index 0fd1554..7c77da6 100644
--- a/tests/cpychecker/refcounts/PyDict_GetItem/incorrect/stdout.txt
+++ b/tests/cpychecker/refcounts/PyDict_GetItem/incorrect/stdout.txt
@@ -1,5 +1,6 @@
Trace 0:
Transitions:
+ 'when treating unknown struct PyObject * from tests/cpychecker/refcounts/PyDict_GetItem/incorrect/input.c:26 as non-NULL'
'when PyDict_GetItem() succeeds'
'returning'
Return value:
@@ -22,6 +23,7 @@ Trace 0:
Trace 1:
Transitions:
+ 'when treating unknown struct PyObject * from tests/cpychecker/refcounts/PyDict_GetItem/incorrect/input.c:26 as non-NULL'
'when PyDict_GetItem does not find item'
'returning'
Return value:
@@ -39,3 +41,21 @@ Trace 1:
r->ob_type: PointerToRegion(gcctype='struct PyTypeObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/PyDict_GetItem/incorrect/input.c', line=29), region=Region("region-for-type-of-arg-gcc.ParmDecl('args')"))
Exception:
(struct PyObject *)0 from tests/cpychecker/refcounts/PyDict_GetItem/incorrect/input.c:30
+
+Trace 2:
+ Transitions:
+ 'when treating unknown struct PyObject * from tests/cpychecker/refcounts/PyDict_GetItem/incorrect/input.c:26 as NULL'
+ error: NullPtrArgument()
+ error: possibly calling PyDict_GetItem with NULL as argument 1 (some_dict.0) at tests/cpychecker/refcounts/PyDict_GetItem/incorrect/input.c:35
+ 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/PyDict_GetItem/incorrect/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/PyDict_GetItem/incorrect/input.c', line=29), region=Region("region-for-type-of-arg-gcc.ParmDecl('args')"))
+ Exception:
+ (struct PyObject *)0 from tests/cpychecker/refcounts/PyDict_GetItem/incorrect/input.c:30
diff --git a/tests/cpychecker/refcounts/PyDict_GetItemString/correct/stdout.txt b/tests/cpychecker/refcounts/PyDict_GetItemString/correct/stdout.txt
index d3fd145..ad5e91c 100644
--- a/tests/cpychecker/refcounts/PyDict_GetItemString/correct/stdout.txt
+++ b/tests/cpychecker/refcounts/PyDict_GetItemString/correct/stdout.txt
@@ -1,5 +1,6 @@
Trace 0:
Transitions:
+ 'when treating unknown struct PyObject * from tests/cpychecker/refcounts/PyDict_GetItemString/correct/input.c:26 as non-NULL'
'when PyDict_GetItemString() succeeds'
'taking True path'
'returning'
@@ -23,6 +24,7 @@ Trace 0:
Trace 1:
Transitions:
+ 'when treating unknown struct PyObject * from tests/cpychecker/refcounts/PyDict_GetItemString/correct/input.c:26 as non-NULL'
'PyDict_GetItemString does not find string'
'taking False path'
'calling PyErr_SetString()'
@@ -42,3 +44,21 @@ Trace 1:
r->ob_type: PointerToRegion(gcctype='struct PyTypeObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/PyDict_GetItemString/correct/input.c', line=29), region=Region("region-for-type-of-arg-gcc.ParmDecl('args')"))
Exception:
unknown struct PyObject * from /usr/include/python?.?/pyerrors.h:nn
+
+Trace 2:
+ Transitions:
+ 'when treating unknown struct PyObject * from tests/cpychecker/refcounts/PyDict_GetItemString/correct/input.c:26 as NULL'
+ error: NullPtrArgument()
+ error: possibly calling PyDict_GetItemString with NULL as argument 1 (some_dict.0) at tests/cpychecker/refcounts/PyDict_GetItemString/correct/input.c:32
+ 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/PyDict_GetItemString/correct/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/PyDict_GetItemString/correct/input.c', line=29), region=Region("region-for-type-of-arg-gcc.ParmDecl('args')"))
+ Exception:
+ (struct PyObject *)0 from tests/cpychecker/refcounts/PyDict_GetItemString/correct/input.c:30
diff --git a/tests/cpychecker/refcounts/PyDict_GetItemString/incorrect/stderr.txt b/tests/cpychecker/refcounts/PyDict_GetItemString/incorrect/stderr.txt
index f5697be..de87d38 100644
--- a/tests/cpychecker/refcounts/PyDict_GetItemString/incorrect/stderr.txt
+++ b/tests/cpychecker/refcounts/PyDict_GetItemString/incorrect/stderr.txt
@@ -1,13 +1,15 @@
tests/cpychecker/refcounts/PyDict_GetItemString/incorrect/input.c: In function 'test':
-tests/cpychecker/refcounts/PyDict_GetItemString/incorrect/input.c:35:5: warning: ob_refcnt of return value is 1 too low [enabled by default]
-tests/cpychecker/refcounts/PyDict_GetItemString/incorrect/input.c:35:5: note: was expecting final ob_refcnt to be N + 1 (for some unknown N)
-tests/cpychecker/refcounts/PyDict_GetItemString/incorrect/input.c:35:5: note: due to object being referenced by: return value
-tests/cpychecker/refcounts/PyDict_GetItemString/incorrect/input.c:35:5: note: but final ob_refcnt is N + 0
-tests/cpychecker/refcounts/PyDict_GetItemString/incorrect/input.c:35:5: note: borrowed reference returned by PyDict_GetItemString() allocated at: return PyDict_GetItemString(some_dict, "item");
-tests/cpychecker/refcounts/PyDict_GetItemString/incorrect/input.c:35:5: note: when PyDict_GetItemString() succeeds at: return PyDict_GetItemString(some_dict, "item");
-tests/cpychecker/refcounts/PyDict_GetItemString/incorrect/input.c:35:5: note: ob_refcnt is now refs: 0 + N where N >= 1
-tests/cpychecker/refcounts/PyDict_GetItemString/incorrect/input.c:35:5: note: returning at: return PyDict_GetItemString(some_dict, "item");
-tests/cpychecker/refcounts/PyDict_GetItemString/incorrect/input.c:35:5: warning: returning (PyObject*)NULL without setting an exception [enabled by default]
-tests/cpychecker/refcounts/PyDict_GetItemString/incorrect/input.c:35:5: note: PyDict_GetItemString does not find string at: return PyDict_GetItemString(some_dict, "item");
-tests/cpychecker/refcounts/PyDict_GetItemString/incorrect/input.c:35:5: note: returning at: return PyDict_GetItemString(some_dict, "item");
-tests/cpychecker/refcounts/PyDict_GetItemString/incorrect/input.c:30:1: note: graphical error report for function 'test' written out to 'tests/cpychecker/refcounts/PyDict_GetItemString/incorrect/input.c.test-refcount-errors.html'
+tests/cpychecker/refcounts/PyDict_GetItemString/incorrect/input.c:35:nn: warning: ob_refcnt of return value is 1 too low [enabled by default]
+tests/cpychecker/refcounts/PyDict_GetItemString/incorrect/input.c:35:nn: note: was expecting final ob_refcnt to be N + 1 (for some unknown N)
+tests/cpychecker/refcounts/PyDict_GetItemString/incorrect/input.c:35:nn: note: due to object being referenced by: return value
+tests/cpychecker/refcounts/PyDict_GetItemString/incorrect/input.c:35:nn: note: but final ob_refcnt is N + 0
+tests/cpychecker/refcounts/PyDict_GetItemString/incorrect/input.c:35:nn: note: borrowed reference returned by PyDict_GetItemString() allocated at: return PyDict_GetItemString(some_dict, "item");
+tests/cpychecker/refcounts/PyDict_GetItemString/incorrect/input.c:35:nn: note: when treating unknown struct PyObject * from tests/cpychecker/refcounts/PyDict_GetItemString/incorrect/input.c:26 as non-NULL at: return PyDict_GetItemString(some_dict, "item");
+tests/cpychecker/refcounts/PyDict_GetItemString/incorrect/input.c:35:nn: note: when PyDict_GetItemString() succeeds at: return PyDict_GetItemString(some_dict, "item");
+tests/cpychecker/refcounts/PyDict_GetItemString/incorrect/input.c:35:nn: note: ob_refcnt is now refs: 0 + N where N >= 1
+tests/cpychecker/refcounts/PyDict_GetItemString/incorrect/input.c:35:nn: note: returning at: return PyDict_GetItemString(some_dict, "item");
+tests/cpychecker/refcounts/PyDict_GetItemString/incorrect/input.c:35:nn: warning: returning (PyObject*)NULL without setting an exception [enabled by default]
+tests/cpychecker/refcounts/PyDict_GetItemString/incorrect/input.c:35:nn: note: when treating unknown struct PyObject * from tests/cpychecker/refcounts/PyDict_GetItemString/incorrect/input.c:26 as non-NULL at: return PyDict_GetItemString(some_dict, "item");
+tests/cpychecker/refcounts/PyDict_GetItemString/incorrect/input.c:35:nn: note: PyDict_GetItemString does not find string at: return PyDict_GetItemString(some_dict, "item");
+tests/cpychecker/refcounts/PyDict_GetItemString/incorrect/input.c:35:nn: note: returning at: return PyDict_GetItemString(some_dict, "item");
+tests/cpychecker/refcounts/PyDict_GetItemString/incorrect/input.c:30:nn: note: graphical error report for function 'test' written out to 'tests/cpychecker/refcounts/PyDict_GetItemString/incorrect/input.c.test-refcount-errors.html'
diff --git a/tests/cpychecker/refcounts/PyDict_GetItemString/incorrect/stdout.txt b/tests/cpychecker/refcounts/PyDict_GetItemString/incorrect/stdout.txt
index 1b7dba1..9adfd8a 100644
--- a/tests/cpychecker/refcounts/PyDict_GetItemString/incorrect/stdout.txt
+++ b/tests/cpychecker/refcounts/PyDict_GetItemString/incorrect/stdout.txt
@@ -1,5 +1,6 @@
Trace 0:
Transitions:
+ 'when treating unknown struct PyObject * from tests/cpychecker/refcounts/PyDict_GetItemString/incorrect/input.c:26 as non-NULL'
'when PyDict_GetItemString() succeeds'
'returning'
Return value:
@@ -22,6 +23,7 @@ Trace 0:
Trace 1:
Transitions:
+ 'when treating unknown struct PyObject * from tests/cpychecker/refcounts/PyDict_GetItemString/incorrect/input.c:26 as non-NULL'
'PyDict_GetItemString does not find string'
'returning'
Return value:
@@ -39,3 +41,21 @@ Trace 1:
r->ob_type: PointerToRegion(gcctype='struct PyTypeObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/PyDict_GetItemString/incorrect/input.c', line=29), region=Region("region-for-type-of-arg-gcc.ParmDecl('args')"))
Exception:
(struct PyObject *)0 from tests/cpychecker/refcounts/PyDict_GetItemString/incorrect/input.c:30
+
+Trace 2:
+ Transitions:
+ 'when treating unknown struct PyObject * from tests/cpychecker/refcounts/PyDict_GetItemString/incorrect/input.c:26 as NULL'
+ error: NullPtrArgument()
+ error: possibly calling PyDict_GetItemString with NULL as argument 1 (some_dict.0) at tests/cpychecker/refcounts/PyDict_GetItemString/incorrect/input.c:35
+ 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/PyDict_GetItemString/incorrect/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/PyDict_GetItemString/incorrect/input.c', line=29), region=Region("region-for-type-of-arg-gcc.ParmDecl('args')"))
+ Exception:
+ (struct PyObject *)0 from tests/cpychecker/refcounts/PyDict_GetItemString/incorrect/input.c:30
12 years, 3 months
[gcc-python-plugin] cpychecker: implement PySys_GetObject
by David Hugh Malcolm
commit bf1c2b052a586fbf728f39f2dfc5f84976db8c02
Author: David Malcolm <dmalcolm(a)redhat.com>
Date: Tue Feb 7 15:05:00 2012 -0500
cpychecker: implement PySys_GetObject
libcpychecker/refcounts.py | 18 +++++++++
.../refcounts/PySys_GetObject/incorrect/input.c | 40 ++++++++++++++++++++
.../PySys_GetObject/incorrect/metadata.ini | 3 +
.../refcounts/PySys_GetObject/incorrect/script.py | 22 +++++++++++
.../refcounts/PySys_GetObject/incorrect/stderr.txt | 13 ++++++
.../refcounts/PySys_GetObject/incorrect/stdout.txt | 21 ++++++++++
6 files changed, 117 insertions(+), 0 deletions(-)
---
diff --git a/libcpychecker/refcounts.py b/libcpychecker/refcounts.py
index 71467ab..a6b32f1 100644
--- a/libcpychecker/refcounts.py
+++ b/libcpychecker/refcounts.py
@@ -2588,6 +2588,24 @@ class CPython(Facet):
########################################################################
# PySys_*
########################################################################
+ def impl_PySys_GetObject(self, stmt, v_name):
+ fnmeta = FnMeta(name='PySys_GetObject',
+ declared_in='sysmodule.h',
+ defined_in='Python/sysmodule.c',
+ prototype='PyAPI_FUNC(PyObject *) PySys_GetObject(char *);',
+ docurl='http://docs.python.org/c-api/sys.html#PySys_GetObject')
+
+ self.state.raise_any_null_ptr_func_arg(stmt, 0, v_name,
+ why='%s() invokes PyString_FromString()' % fnmeta.name)
+
+ s_success = self.mkstate_borrowed_ref(stmt, fnmeta)
+ t_notfound = self.state.mktrans_assignment(stmt.lhs,
+ make_null_pyobject_ptr(stmt),
+ '%s does not find string' % fnmeta.name)
+ return [self.state.mktrans_from_fncall_state(stmt, s_success,
+ 'succeeds', True),
+ t_notfound]
+
def impl_PySys_SetObject(self, stmt, v_name, v_value):
fnmeta = FnMeta(name='PySys_SetObject',
declared_in='sysmodule.h',
diff --git a/tests/cpychecker/refcounts/PySys_GetObject/incorrect/input.c b/tests/cpychecker/refcounts/PySys_GetObject/incorrect/input.c
new file mode 100644
index 0000000..7e442a6
--- /dev/null
+++ b/tests/cpychecker/refcounts/PySys_GetObject/incorrect/input.c
@@ -0,0 +1,40 @@
+/*
+ Copyright 2012 David Malcolm <dmalcolm(a)redhat.com>
+ Copyright 2012 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 incorrect reference-handling in a call to PySys_GetObject
+*/
+
+PyObject *
+test(void)
+{
+ return PySys_GetObject("foo");
+ /* BUG: only a borrowed reference */
+ /* BUG: if it fails, doesn't set exception */
+}
+
+/*
+ PEP-7
+Local variables:
+c-basic-offset: 4
+indent-tabs-mode: nil
+End:
+*/
diff --git a/tests/cpychecker/refcounts/PySys_GetObject/incorrect/metadata.ini b/tests/cpychecker/refcounts/PySys_GetObject/incorrect/metadata.ini
new file mode 100644
index 0000000..0d5711c
--- /dev/null
+++ b/tests/cpychecker/refcounts/PySys_GetObject/incorrect/metadata.ini
@@ -0,0 +1,3 @@
+[ExpectedBehavior]
+# We expect only compilation *warnings*, so we expect a 0 exit code
+exitcode = 0
diff --git a/tests/cpychecker/refcounts/PySys_GetObject/incorrect/script.py b/tests/cpychecker/refcounts/PySys_GetObject/incorrect/script.py
new file mode 100644
index 0000000..fdd5ba3
--- /dev/null
+++ b/tests/cpychecker/refcounts/PySys_GetObject/incorrect/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/PySys_GetObject/incorrect/stderr.txt b/tests/cpychecker/refcounts/PySys_GetObject/incorrect/stderr.txt
new file mode 100644
index 0000000..1c7d273
--- /dev/null
+++ b/tests/cpychecker/refcounts/PySys_GetObject/incorrect/stderr.txt
@@ -0,0 +1,13 @@
+tests/cpychecker/refcounts/PySys_GetObject/incorrect/input.c: In function 'test':
+tests/cpychecker/refcounts/PySys_GetObject/incorrect/input.c:29:nn: warning: ob_refcnt of return value is 1 too low [enabled by default]
+tests/cpychecker/refcounts/PySys_GetObject/incorrect/input.c:29:nn: note: was expecting final ob_refcnt to be N + 1 (for some unknown N)
+tests/cpychecker/refcounts/PySys_GetObject/incorrect/input.c:29:nn: note: due to object being referenced by: return value
+tests/cpychecker/refcounts/PySys_GetObject/incorrect/input.c:29:nn: note: but final ob_refcnt is N + 0
+tests/cpychecker/refcounts/PySys_GetObject/incorrect/input.c:29:nn: note: borrowed reference returned by PySys_GetObject() allocated at: return PySys_GetObject("foo");
+tests/cpychecker/refcounts/PySys_GetObject/incorrect/input.c:29:nn: note: when PySys_GetObject() succeeds at: return PySys_GetObject("foo");
+tests/cpychecker/refcounts/PySys_GetObject/incorrect/input.c:29:nn: note: ob_refcnt is now refs: 0 + N where N >= 1
+tests/cpychecker/refcounts/PySys_GetObject/incorrect/input.c:29:nn: note: returning at: return PySys_GetObject("foo");
+tests/cpychecker/refcounts/PySys_GetObject/incorrect/input.c:29:nn: warning: returning (PyObject*)NULL without setting an exception [enabled by default]
+tests/cpychecker/refcounts/PySys_GetObject/incorrect/input.c:29:nn: note: PySys_GetObject does not find string at: return PySys_GetObject("foo");
+tests/cpychecker/refcounts/PySys_GetObject/incorrect/input.c:29:nn: note: returning at: return PySys_GetObject("foo");
+tests/cpychecker/refcounts/PySys_GetObject/incorrect/input.c:28:nn: note: graphical error report for function 'test' written out to 'tests/cpychecker/refcounts/PySys_GetObject/incorrect/input.c.test-refcount-errors.html'
diff --git a/tests/cpychecker/refcounts/PySys_GetObject/incorrect/stdout.txt b/tests/cpychecker/refcounts/PySys_GetObject/incorrect/stdout.txt
new file mode 100644
index 0000000..6b640f5
--- /dev/null
+++ b/tests/cpychecker/refcounts/PySys_GetObject/incorrect/stdout.txt
@@ -0,0 +1,21 @@
+Trace 0:
+ Transitions:
+ 'when PySys_GetObject() succeeds'
+ 'returning'
+ Return value:
+ repr(): PointerToRegion(gcctype='struct PyObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/PySys_GetObject/incorrect/input.c', line=29), region=RegionOnHeap('borrowed reference returned by PySys_GetObject()', gcc.Location(file='tests/cpychecker/refcounts/PySys_GetObject/incorrect/input.c', line=29)))
+ str(): (struct PyObject *)&RegionOnHeap('borrowed reference returned by PySys_GetObject()', gcc.Location(file='tests/cpychecker/refcounts/PySys_GetObject/incorrect/input.c', line=29)) from tests/cpychecker/refcounts/PySys_GetObject/incorrect/input.c:29
+ r->ob_refcnt: refs: 0 + N where N >= 1
+ r->ob_type: PointerToRegion(gcctype='struct PyTypeObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/PySys_GetObject/incorrect/input.c', line=29), region=Region('PyTypeObject for borrowed reference returned by PySys_GetObject()'))
+ Exception:
+ (struct PyObject *)0 from tests/cpychecker/refcounts/PySys_GetObject/incorrect/input.c:28
+
+Trace 1:
+ Transitions:
+ 'PySys_GetObject does not find string'
+ 'returning'
+ Return value:
+ repr(): ConcreteValue(gcctype='struct PyObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/PySys_GetObject/incorrect/input.c', line=29), value=0)
+ str(): (struct PyObject *)0 from tests/cpychecker/refcounts/PySys_GetObject/incorrect/input.c:29
+ Exception:
+ (struct PyObject *)0 from tests/cpychecker/refcounts/PySys_GetObject/incorrect/input.c:28
12 years, 3 months
[gcc-python-plugin] cpychecker: implement PyTuple_GetItem
by David Hugh Malcolm
commit 57901bbfb5e9b8aa3c6e035f4d6a781a832ae3de
Author: David Malcolm <dmalcolm(a)redhat.com>
Date: Tue Feb 7 14:39:58 2012 -0500
cpychecker: implement PyTuple_GetItem
libcpychecker/refcounts.py | 13 +++++
.../refcounts/PyTuple_GetItem/correct/input.c | 50 ++++++++++++++++++++
.../refcounts/PyTuple_GetItem/correct/script.py | 22 +++++++++
.../refcounts/PyTuple_GetItem/correct/stdout.txt | 40 ++++++++++++++++
.../refcounts/PyTuple_GetItem/incorrect/input.c | 46 ++++++++++++++++++
.../PyTuple_GetItem/incorrect/metadata.ini | 3 +
.../refcounts/PyTuple_GetItem/incorrect/script.py | 22 +++++++++
.../refcounts/PyTuple_GetItem/incorrect/stderr.txt | 12 +++++
.../refcounts/PyTuple_GetItem/incorrect/stdout.txt | 40 ++++++++++++++++
9 files changed, 248 insertions(+), 0 deletions(-)
---
diff --git a/libcpychecker/refcounts.py b/libcpychecker/refcounts.py
index 67418df..71467ab 100644
--- a/libcpychecker/refcounts.py
+++ b/libcpychecker/refcounts.py
@@ -2613,6 +2613,19 @@ class CPython(Facet):
########################################################################
# PyTuple_*
########################################################################
+ def impl_PyTuple_GetItem(self, stmt, v_op, v_i):
+ fnmeta = FnMeta(name='PyTuple_GetItem',
+ docurl='http://docs.python.org/c-api/tuple.html#PyTuple_GetItem',
+ defined_in='Objects/tupleobject.c')
+ self.state.raise_any_null_ptr_func_arg(stmt, 0, v_op,
+ why=invokes_Py_TYPE_via_macro(fnmeta,
+ 'PyTuple_Check'))
+ # FIXME: for now, simply return a borrowed ref, rather than
+ # trying to track indices and the array:
+ s_success = self.mkstate_borrowed_ref(stmt,
+ fnmeta)
+ return [Transition(self.state, s_success, None)]
+
def impl_PyTuple_New(self, stmt, v_len):
fnmeta = FnMeta(name='PyTuple_New',
docurl='http://docs.python.org/c-api/tuple.html#PyTuple_New')
diff --git a/tests/cpychecker/refcounts/PyTuple_GetItem/correct/input.c b/tests/cpychecker/refcounts/PyTuple_GetItem/correct/input.c
new file mode 100644
index 0000000..d27eaa6
--- /dev/null
+++ b/tests/cpychecker/refcounts/PyTuple_GetItem/correct/input.c
@@ -0,0 +1,50 @@
+/*
+ Copyright 2012 David Malcolm <dmalcolm(a)redhat.com>
+ Copyright 2012 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 PyTuple_GetItem
+*/
+
+extern PyObject *some_tuple;
+
+PyObject *
+test(PyObject *self, PyObject *args)
+{
+ PyObject *item;
+ item = PyTuple_GetItem(some_tuple, 3);
+ if (item) {
+ /*
+ If it succeeds, it only returns a borrowed ref, so we must incref
+ it:
+ */
+ Py_INCREF(item);
+ return item;
+ }
+ return NULL;
+}
+
+/*
+ PEP-7
+Local variables:
+c-basic-offset: 4
+indent-tabs-mode: nil
+End:
+*/
diff --git a/tests/cpychecker/refcounts/PyTuple_GetItem/correct/script.py b/tests/cpychecker/refcounts/PyTuple_GetItem/correct/script.py
new file mode 100644
index 0000000..fdd5ba3
--- /dev/null
+++ b/tests/cpychecker/refcounts/PyTuple_GetItem/correct/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/PyTuple_GetItem/correct/stdout.txt b/tests/cpychecker/refcounts/PyTuple_GetItem/correct/stdout.txt
new file mode 100644
index 0000000..82ce81d
--- /dev/null
+++ b/tests/cpychecker/refcounts/PyTuple_GetItem/correct/stdout.txt
@@ -0,0 +1,40 @@
+Trace 0:
+ Transitions:
+ 'when treating unknown struct PyObject * from tests/cpychecker/refcounts/PyTuple_GetItem/correct/input.c:26 as non-NULL'
+ 'taking True path'
+ 'returning'
+ Return value:
+ repr(): PointerToRegion(gcctype='struct PyObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/PyTuple_GetItem/correct/input.c', line=32), region=RegionOnHeap('borrowed reference returned by PyTuple_GetItem()', gcc.Location(file='tests/cpychecker/refcounts/PyTuple_GetItem/correct/input.c', line=32)))
+ str(): (struct PyObject *)&RegionOnHeap('borrowed reference returned by PyTuple_GetItem()', gcc.Location(file='tests/cpychecker/refcounts/PyTuple_GetItem/correct/input.c', line=32)) from tests/cpychecker/refcounts/PyTuple_GetItem/correct/input.c:32
+ r->ob_refcnt: refs: 1 + N where N >= 1
+ r->ob_type: PointerToRegion(gcctype='struct PyTypeObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/PyTuple_GetItem/correct/input.c', line=32), region=Region('PyTypeObject for borrowed reference returned by PyTuple_GetItem()'))
+ 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/PyTuple_GetItem/correct/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/PyTuple_GetItem/correct/input.c', line=29), region=Region("region-for-type-of-arg-gcc.ParmDecl('args')"))
+ Exception:
+ (struct PyObject *)0 from tests/cpychecker/refcounts/PyTuple_GetItem/correct/input.c:30
+
+Trace 1:
+ Transitions:
+ 'when treating unknown struct PyObject * from tests/cpychecker/refcounts/PyTuple_GetItem/correct/input.c:26 as NULL'
+ error: NullPtrArgument()
+ error: possibly calling PyTuple_GetItem with NULL as argument 1 (some_tuple.0) at tests/cpychecker/refcounts/PyTuple_GetItem/correct/input.c:32
+ 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/PyTuple_GetItem/correct/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/PyTuple_GetItem/correct/input.c', line=29), region=Region("region-for-type-of-arg-gcc.ParmDecl('args')"))
+ Exception:
+ (struct PyObject *)0 from tests/cpychecker/refcounts/PyTuple_GetItem/correct/input.c:30
diff --git a/tests/cpychecker/refcounts/PyTuple_GetItem/incorrect/input.c b/tests/cpychecker/refcounts/PyTuple_GetItem/incorrect/input.c
new file mode 100644
index 0000000..33eba14
--- /dev/null
+++ b/tests/cpychecker/refcounts/PyTuple_GetItem/incorrect/input.c
@@ -0,0 +1,46 @@
+/*
+ Copyright 2012 David Malcolm <dmalcolm(a)redhat.com>
+ Copyright 2012 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 incorrect reference-handling in a call to PyTuple_GetItem
+*/
+
+extern PyObject *some_tuple;
+
+PyObject *
+test(PyObject *self, PyObject *args)
+{
+ PyObject *item;
+ item = PyTuple_GetItem(some_tuple, 3);
+ if (item) {
+ /* BUG: this is only a borrowed ref; we ought to INCREF it: */
+ return item;
+ }
+ return NULL;
+}
+
+/*
+ PEP-7
+Local variables:
+c-basic-offset: 4
+indent-tabs-mode: nil
+End:
+*/
diff --git a/tests/cpychecker/refcounts/PyTuple_GetItem/incorrect/metadata.ini b/tests/cpychecker/refcounts/PyTuple_GetItem/incorrect/metadata.ini
new file mode 100644
index 0000000..0d5711c
--- /dev/null
+++ b/tests/cpychecker/refcounts/PyTuple_GetItem/incorrect/metadata.ini
@@ -0,0 +1,3 @@
+[ExpectedBehavior]
+# We expect only compilation *warnings*, so we expect a 0 exit code
+exitcode = 0
diff --git a/tests/cpychecker/refcounts/PyTuple_GetItem/incorrect/script.py b/tests/cpychecker/refcounts/PyTuple_GetItem/incorrect/script.py
new file mode 100644
index 0000000..fdd5ba3
--- /dev/null
+++ b/tests/cpychecker/refcounts/PyTuple_GetItem/incorrect/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/PyTuple_GetItem/incorrect/stderr.txt b/tests/cpychecker/refcounts/PyTuple_GetItem/incorrect/stderr.txt
new file mode 100644
index 0000000..6778447
--- /dev/null
+++ b/tests/cpychecker/refcounts/PyTuple_GetItem/incorrect/stderr.txt
@@ -0,0 +1,12 @@
+tests/cpychecker/refcounts/PyTuple_GetItem/incorrect/input.c: In function 'test':
+tests/cpychecker/refcounts/PyTuple_GetItem/incorrect/input.c:38:nn: warning: ob_refcnt of return value is 1 too low [enabled by default]
+tests/cpychecker/refcounts/PyTuple_GetItem/incorrect/input.c:38:nn: note: was expecting final ob_refcnt to be N + 1 (for some unknown N)
+tests/cpychecker/refcounts/PyTuple_GetItem/incorrect/input.c:38:nn: note: due to object being referenced by: return value
+tests/cpychecker/refcounts/PyTuple_GetItem/incorrect/input.c:38:nn: note: but final ob_refcnt is N + 0
+tests/cpychecker/refcounts/PyTuple_GetItem/incorrect/input.c:32:nn: note: borrowed reference returned by PyTuple_GetItem() allocated at: item = PyTuple_GetItem(some_tuple, 3);
+tests/cpychecker/refcounts/PyTuple_GetItem/incorrect/input.c:32:nn: note: when treating unknown struct PyObject * from tests/cpychecker/refcounts/PyTuple_GetItem/incorrect/input.c:26 as non-NULL at: item = PyTuple_GetItem(some_tuple, 3);
+tests/cpychecker/refcounts/PyTuple_GetItem/incorrect/input.c:32:nn: note: ob_refcnt is now refs: 0 + N where N >= 1
+tests/cpychecker/refcounts/PyTuple_GetItem/incorrect/input.c:33:nn: note: taking True path at: if (item) {
+tests/cpychecker/refcounts/PyTuple_GetItem/incorrect/input.c:35:nn: note: reaching: return item;
+tests/cpychecker/refcounts/PyTuple_GetItem/incorrect/input.c:38:nn: note: returning
+tests/cpychecker/refcounts/PyTuple_GetItem/incorrect/input.c:30:nn: note: graphical error report for function 'test' written out to 'tests/cpychecker/refcounts/PyTuple_GetItem/incorrect/input.c.test-refcount-errors.html'
diff --git a/tests/cpychecker/refcounts/PyTuple_GetItem/incorrect/stdout.txt b/tests/cpychecker/refcounts/PyTuple_GetItem/incorrect/stdout.txt
new file mode 100644
index 0000000..cac8599
--- /dev/null
+++ b/tests/cpychecker/refcounts/PyTuple_GetItem/incorrect/stdout.txt
@@ -0,0 +1,40 @@
+Trace 0:
+ Transitions:
+ 'when treating unknown struct PyObject * from tests/cpychecker/refcounts/PyTuple_GetItem/incorrect/input.c:26 as non-NULL'
+ 'taking True path'
+ 'returning'
+ Return value:
+ repr(): PointerToRegion(gcctype='struct PyObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/PyTuple_GetItem/incorrect/input.c', line=32), region=RegionOnHeap('borrowed reference returned by PyTuple_GetItem()', gcc.Location(file='tests/cpychecker/refcounts/PyTuple_GetItem/incorrect/input.c', line=32)))
+ str(): (struct PyObject *)&RegionOnHeap('borrowed reference returned by PyTuple_GetItem()', gcc.Location(file='tests/cpychecker/refcounts/PyTuple_GetItem/incorrect/input.c', line=32)) from tests/cpychecker/refcounts/PyTuple_GetItem/incorrect/input.c:32
+ r->ob_refcnt: refs: 0 + N where N >= 1
+ r->ob_type: PointerToRegion(gcctype='struct PyTypeObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/PyTuple_GetItem/incorrect/input.c', line=32), region=Region('PyTypeObject for borrowed reference returned by PyTuple_GetItem()'))
+ 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/PyTuple_GetItem/incorrect/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/PyTuple_GetItem/incorrect/input.c', line=29), region=Region("region-for-type-of-arg-gcc.ParmDecl('args')"))
+ Exception:
+ (struct PyObject *)0 from tests/cpychecker/refcounts/PyTuple_GetItem/incorrect/input.c:30
+
+Trace 1:
+ Transitions:
+ 'when treating unknown struct PyObject * from tests/cpychecker/refcounts/PyTuple_GetItem/incorrect/input.c:26 as NULL'
+ error: NullPtrArgument()
+ error: possibly calling PyTuple_GetItem with NULL as argument 1 (some_tuple.0) at tests/cpychecker/refcounts/PyTuple_GetItem/incorrect/input.c:32
+ 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/PyTuple_GetItem/incorrect/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/PyTuple_GetItem/incorrect/input.c', line=29), region=Region("region-for-type-of-arg-gcc.ParmDecl('args')"))
+ Exception:
+ (struct PyObject *)0 from tests/cpychecker/refcounts/PyTuple_GetItem/incorrect/input.c:30
12 years, 3 months
[gcc-python-plugin] cpychecker: implement PyObject_GetAttrString
by David Hugh Malcolm
commit c119b4e04de05285f63c55744f7ca3ecda3b1582
Author: David Malcolm <dmalcolm(a)redhat.com>
Date: Tue Feb 7 14:26:13 2012 -0500
cpychecker: implement PyObject_GetAttrString
libcpychecker/refcounts.py | 13 ++++++
.../PyObject_GetAttrString/correct/input.c | 38 ++++++++++++++++++
.../PyObject_GetAttrString/correct/script.py | 22 ++++++++++
.../PyObject_GetAttrString/correct/stdout.txt | 41 ++++++++++++++++++++
.../PyObject_GetAttrString/incorrect/input.c | 38 ++++++++++++++++++
.../PyObject_GetAttrString/incorrect/metadata.ini | 3 +
.../PyObject_GetAttrString/incorrect/script.py | 22 ++++++++++
.../PyObject_GetAttrString/incorrect/stderr.txt | 4 ++
.../PyObject_GetAttrString/incorrect/stdout.txt | 16 ++++++++
9 files changed, 197 insertions(+), 0 deletions(-)
---
diff --git a/libcpychecker/refcounts.py b/libcpychecker/refcounts.py
index 0025c72..67418df 100644
--- a/libcpychecker/refcounts.py
+++ b/libcpychecker/refcounts.py
@@ -2135,6 +2135,19 @@ class CPython(Facet):
return self.make_transitions_for_new_ref_or_fail(stmt, fnmeta)
+ def impl_PyObject_GetAttrString(self, stmt, v_v, v_name):
+ fnmeta = FnMeta(name='PyObject_GetAttrString',
+ docurl='http://docs.python.org/c-api/object.html#PyObject_GetAttrString',
+ defined_in='Objects/object.c',
+ prototype='PyObject* PyObject_GetAttrString(PyObject *v, const char *name)')
+ self.state.raise_any_null_ptr_func_arg(stmt, 0, v_v,
+ why=invokes_Py_TYPE(fnmeta))
+ self.state.raise_any_null_ptr_func_arg(stmt, 1, v_name,
+ why=('%s() can call PyString_InternFromString(), '
+ 'which calls PyString_FromString(), '
+ 'which requires a non-NULL pointer' % fnmeta.name))
+ return self.make_transitions_for_new_ref_or_fail(stmt, fnmeta)
+
def impl_PyObject_GenericGetAttr(self, stmt, v_o, v_name):
fnmeta = FnMeta(name='PyObject_GenericGetAttr',
docurl='http://docs.python.org/c-api/object.html#PyObject_GenericGetAttr',
diff --git a/tests/cpychecker/refcounts/PyObject_GetAttrString/correct/input.c b/tests/cpychecker/refcounts/PyObject_GetAttrString/correct/input.c
new file mode 100644
index 0000000..938077e
--- /dev/null
+++ b/tests/cpychecker/refcounts/PyObject_GetAttrString/correct/input.c
@@ -0,0 +1,38 @@
+/*
+ Copyright 2012 David Malcolm <dmalcolm(a)redhat.com>
+ Copyright 2012 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 PyObject_GetAttrString
+*/
+
+PyObject *
+test(PyObject *self, PyObject *args)
+{
+ return PyObject_GetAttrString(self, "name");
+}
+
+/*
+ PEP-7
+Local variables:
+c-basic-offset: 4
+indent-tabs-mode: nil
+End:
+*/
diff --git a/tests/cpychecker/refcounts/PyObject_GetAttrString/correct/script.py b/tests/cpychecker/refcounts/PyObject_GetAttrString/correct/script.py
new file mode 100644
index 0000000..fdd5ba3
--- /dev/null
+++ b/tests/cpychecker/refcounts/PyObject_GetAttrString/correct/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/PyObject_GetAttrString/correct/stdout.txt b/tests/cpychecker/refcounts/PyObject_GetAttrString/correct/stdout.txt
new file mode 100644
index 0000000..af86282
--- /dev/null
+++ b/tests/cpychecker/refcounts/PyObject_GetAttrString/correct/stdout.txt
@@ -0,0 +1,41 @@
+Trace 0:
+ Transitions:
+ 'when PyObject_GetAttrString() succeeds'
+ 'returning'
+ Return value:
+ repr(): PointerToRegion(gcctype='struct PyObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/PyObject_GetAttrString/correct/input.c', line=29), region=RegionOnHeap('new ref from call to PyObject_GetAttrString', gcc.Location(file='tests/cpychecker/refcounts/PyObject_GetAttrString/correct/input.c', line=29)))
+ str(): (struct PyObject *)&RegionOnHeap('new ref from call to PyObject_GetAttrString', gcc.Location(file='tests/cpychecker/refcounts/PyObject_GetAttrString/correct/input.c', line=29)) from tests/cpychecker/refcounts/PyObject_GetAttrString/correct/input.c:29
+ r->ob_refcnt: refs: 1 + N where N >= 0
+ r->ob_type: PointerToRegion(gcctype='struct PyTypeObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/PyObject_GetAttrString/correct/input.c', line=29), region=Region('PyTypeObject for new ref from call to PyObject_GetAttrString'))
+ 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/PyObject_GetAttrString/correct/input.c', line=27), 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/PyObject_GetAttrString/correct/input.c', line=27), region=Region("region-for-type-of-arg-gcc.ParmDecl('args')"))
+ Exception:
+ (struct PyObject *)0 from tests/cpychecker/refcounts/PyObject_GetAttrString/correct/input.c:28
+
+Trace 1:
+ Transitions:
+ 'when PyObject_GetAttrString() fails'
+ 'returning'
+ Return value:
+ repr(): ConcreteValue(gcctype='struct PyObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/PyObject_GetAttrString/correct/input.c', line=29), value=0)
+ str(): (struct PyObject *)0 from tests/cpychecker/refcounts/PyObject_GetAttrString/correct/input.c:29
+ 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/PyObject_GetAttrString/correct/input.c', line=27), 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/PyObject_GetAttrString/correct/input.c', line=27), region=Region("region-for-type-of-arg-gcc.ParmDecl('args')"))
+ Exception:
+ (struct PyObject *)&RegionForGlobal(gcc.VarDecl('PyExc_MemoryError')) from tests/cpychecker/refcounts/PyObject_GetAttrString/correct/input.c:29
diff --git a/tests/cpychecker/refcounts/PyObject_GetAttrString/incorrect/input.c b/tests/cpychecker/refcounts/PyObject_GetAttrString/incorrect/input.c
new file mode 100644
index 0000000..2733639
--- /dev/null
+++ b/tests/cpychecker/refcounts/PyObject_GetAttrString/incorrect/input.c
@@ -0,0 +1,38 @@
+/*
+ Copyright 2012 David Malcolm <dmalcolm(a)redhat.com>
+ Copyright 2012 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 NULL argument in a call to PyObject_GetAttrString
+*/
+
+PyObject *
+test(PyObject *self, PyObject *args)
+{
+ return PyObject_GetAttrString(self, NULL);
+}
+
+/*
+ PEP-7
+Local variables:
+c-basic-offset: 4
+indent-tabs-mode: nil
+End:
+*/
diff --git a/tests/cpychecker/refcounts/PyObject_GetAttrString/incorrect/metadata.ini b/tests/cpychecker/refcounts/PyObject_GetAttrString/incorrect/metadata.ini
new file mode 100644
index 0000000..0d5711c
--- /dev/null
+++ b/tests/cpychecker/refcounts/PyObject_GetAttrString/incorrect/metadata.ini
@@ -0,0 +1,3 @@
+[ExpectedBehavior]
+# We expect only compilation *warnings*, so we expect a 0 exit code
+exitcode = 0
diff --git a/tests/cpychecker/refcounts/PyObject_GetAttrString/incorrect/script.py b/tests/cpychecker/refcounts/PyObject_GetAttrString/incorrect/script.py
new file mode 100644
index 0000000..fdd5ba3
--- /dev/null
+++ b/tests/cpychecker/refcounts/PyObject_GetAttrString/incorrect/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/PyObject_GetAttrString/incorrect/stderr.txt b/tests/cpychecker/refcounts/PyObject_GetAttrString/incorrect/stderr.txt
new file mode 100644
index 0000000..69ae721
--- /dev/null
+++ b/tests/cpychecker/refcounts/PyObject_GetAttrString/incorrect/stderr.txt
@@ -0,0 +1,4 @@
+tests/cpychecker/refcounts/PyObject_GetAttrString/incorrect/input.c: In function 'test':
+tests/cpychecker/refcounts/PyObject_GetAttrString/incorrect/input.c:29:nn: warning: calling PyObject_GetAttrString with NULL as argument 2 (0B) at tests/cpychecker/refcounts/PyObject_GetAttrString/incorrect/input.c:29 [enabled by default]
+tests/cpychecker/refcounts/PyObject_GetAttrString/incorrect/input.c:29:nn: note: PyObject_GetAttrString() can call PyString_InternFromString(), which calls PyString_FromString(), which requires a non-NULL pointer
+tests/cpychecker/refcounts/PyObject_GetAttrString/incorrect/input.c:28:nn: note: graphical error report for function 'test' written out to 'tests/cpychecker/refcounts/PyObject_GetAttrString/incorrect/input.c.test-refcount-errors.html'
diff --git a/tests/cpychecker/refcounts/PyObject_GetAttrString/incorrect/stdout.txt b/tests/cpychecker/refcounts/PyObject_GetAttrString/incorrect/stdout.txt
new file mode 100644
index 0000000..94b9c22
--- /dev/null
+++ b/tests/cpychecker/refcounts/PyObject_GetAttrString/incorrect/stdout.txt
@@ -0,0 +1,16 @@
+Trace 0:
+ Transitions:
+ error: NullPtrArgument()
+ error: calling PyObject_GetAttrString with NULL as argument 2 (0B) at tests/cpychecker/refcounts/PyObject_GetAttrString/incorrect/input.c:29
+ 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/PyObject_GetAttrString/incorrect/input.c', line=27), 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/PyObject_GetAttrString/incorrect/input.c', line=27), region=Region("region-for-type-of-arg-gcc.ParmDecl('args')"))
+ Exception:
+ (struct PyObject *)0 from tests/cpychecker/refcounts/PyObject_GetAttrString/incorrect/input.c:28
12 years, 3 months
[gcc-python-plugin] mass-rebuild.py: remove hardcoded list of SRPMs; reenable --init
by David Hugh Malcolm
commit 1c9dd1af41629140fd54f13142342b438fc56f74
Author: David Malcolm <dmalcolm(a)redhat.com>
Date: Tue Feb 7 12:31:38 2012 -0500
mass-rebuild.py: remove hardcoded list of SRPMs; reenable --init
misc/fedora/mass-rebuild.py | 15 ++++++++-------
1 files changed, 8 insertions(+), 7 deletions(-)
---
diff --git a/misc/fedora/mass-rebuild.py b/misc/fedora/mass-rebuild.py
index 8bbae42..7f76d97 100644
--- a/misc/fedora/mass-rebuild.py
+++ b/misc/fedora/mass-rebuild.py
@@ -15,6 +15,7 @@
# along with this program. If not, see
# <http://www.gnu.org/licenses/>.
+import glob
import os
import re
import shutil
@@ -64,7 +65,7 @@ for srpmname in get_local_python_srpms():
def local_rebuild_of_srpm_in_mock(srpmpath, mockcfg):
"""
Rebuild the given SRPM locally within mock, injecting the cpychecker
- code in RPM form
+ code in RPM form; gathering the results to a subdir within "LOGS"
"""
def run_mock(commands, captureOut=False, captureErr=False):
cmds = ['mock', '-r', mockcfg] + commands
@@ -81,12 +82,13 @@ def local_rebuild_of_srpm_in_mock(srpmpath, mockcfg):
return out, err
n, v, r = nvr_from_srpm_path(srpmpath)
- resultdir = '%s-%s-%s' % (n, v, r)
+ resultdir = 'LOGS/%s-%s-%s' % (n, v, r)
if os.path.exists(resultdir):
shutil.rmtree(resultdir)
os.mkdir(resultdir)
- if 0:
+ # Experimenting with the script is much faster if we remove the --init here:
+ if 1:
run_mock(['--init'])
run_mock(['--installdeps', srpmpath])
run_mock(['install', PLUGIN_PATH]) # this doesn't work when cleaned: can't open state.log
@@ -118,8 +120,7 @@ def local_rebuild_of_srpm_in_mock(srpmpath, mockcfg):
PLUGIN_PATH='gcc-python2-plugin-0.9-1.fc16.x86_64.rpm'
MOCK_CONFIG='fedora-16-x86_64'
-# For now, just an experimental hardcoded list of srpms:
-local_rebuild_of_srpm_in_mock('MySQL-python-1.2.3-3.fc16.src.rpm')
-local_rebuild_of_srpm_in_mock('python-crypto-2.3-5.fc16.src.rpm')
-local_rebuild_of_srpm_in_mock('rpm-4.9.1.2-1.fc16.src.rpm')
+# Rebuild all src.rpm files found in "SRPMS":
+for srpmpath in glob.glob('SRPMS/*.src.rpm'):
+ local_rebuild_of_srpm_in_mock(srpmpath, MOCK_CONFIG)
12 years, 3 months
[gcc-python-plugin] work-in-progress script for performing mass-rebuilds of Python extensions using cpychecker
by David Hugh Malcolm
commit a480f80caecb1ed92ec226a65e11a85f094debde
Author: David Malcolm <dmalcolm(a)redhat.com>
Date: Tue Feb 7 12:13:39 2012 -0500
work-in-progress script for performing mass-rebuilds of Python extensions using cpychecker
This is just a work-in-progress, and doesn't do much yet
misc/fedora/mass-rebuild.py | 125 +++++++++++++++++++++++++++++++++++++++++++
1 files changed, 125 insertions(+), 0 deletions(-)
---
diff --git a/misc/fedora/mass-rebuild.py b/misc/fedora/mass-rebuild.py
new file mode 100644
index 0000000..8bbae42
--- /dev/null
+++ b/misc/fedora/mass-rebuild.py
@@ -0,0 +1,125 @@
+# Copyright 2012 David Malcolm <dmalcolm(a)redhat.com>
+# Copyright 2012 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/>.
+
+import os
+import re
+import shutil
+from subprocess import check_output, Popen, PIPE
+import sys
+
+def nvr_from_srpm_path(path):
+ filename = os.path.basename(path)
+ m = re.match('(.+)-(.+)-(.+).src.rpm', filename)
+ name, version, release = m.groups()
+ return name, version, release
+
+def get_local_python_srpms():
+ """
+ Extract a list of srpm names that require python 2 to build
+ """
+ cmd = ['rpm',
+ '-q',
+ '--qf=%{sourcerpm}\\n',
+ '--whatrequires',
+ 'libpython2.7.so.1.0()(64bit)']
+ out = check_output(cmd)
+ result = set()
+ for line in out.splitlines():
+ m = re.match('(.+)-(.+)-(.+)', line)
+ name, version, release = m.groups()
+ result.add(name)
+ return sorted(result)
+
+#print(get_local_python_srpms())
+#sys.exit(0)
+
+"""
+for srpmname in get_local_python_srpms():
+ cmd = ['mock',
+ '-r', 'fedora-16-x86_64',
+ '--scm-enable',
+ '--scm-option', 'method=git',
+ '--scm-option', 'git_get="git clone SCM_BRN git://pkgs.fedoraproject.org/SCM_PKG.git SCM_PKG"',
+ '--scm-option', 'package=%s' % srpmname,
+ '-v']
+ p = Popen(cmd)
+ p.communicate()
+"""
+
+
+def local_rebuild_of_srpm_in_mock(srpmpath, mockcfg):
+ """
+ Rebuild the given SRPM locally within mock, injecting the cpychecker
+ code in RPM form
+ """
+ def run_mock(commands, captureOut=False, captureErr=False):
+ cmds = ['mock', '-r', mockcfg] + commands
+ print('--------------------------------------------------------------')
+ print(' '.join(cmds))
+ print('--------------------------------------------------------------')
+ args = {}
+ if captureOut:
+ args['stdout'] = PIPE
+ if captureErr:
+ args['stderr'] = PIPE
+ p = Popen(cmds, **args)
+ out, err = p.communicate()
+ return out, err
+
+ n, v, r = nvr_from_srpm_path(srpmpath)
+ resultdir = '%s-%s-%s' % (n, v, r)
+ if os.path.exists(resultdir):
+ shutil.rmtree(resultdir)
+ os.mkdir(resultdir)
+
+ if 0:
+ run_mock(['--init'])
+ run_mock(['--installdeps', srpmpath])
+ run_mock(['install', PLUGIN_PATH]) # this doesn't work when cleaned: can't open state.log
+ out, err = run_mock(['--chroot', 'rpm --eval "%{__global_cflags}"'],
+ captureOut=True)
+ global_cflags = out.strip()
+ # print('global_cflags: %r' % global_cflags)
+
+ # Create script within chroot:
+ SCRIPT_PATH='/test.py'
+ run_mock(['--copyin', 'test.py', SCRIPT_PATH])
+
+ # Rebuild src.rpm, using the script:
+ run_mock(['--rebuild', srpmpath,
+
+ '--no-clean',
+
+ # setting revised cflags so as to use the script:
+ # FIXME: calling this repeatedly with --init disabled leads to
+ # an accumulation of multiple copies of our flags at the front of __global_cflags
+ ('--define=__global_cflags -fplugin=python2 -fplugin-arg-python2-script=%s %s'
+ % (SCRIPT_PATH, global_cflags))
+ ])
+
+ # Extract build logs:
+ shutil.copy('/var/lib/mock/%s/result/build.log' % mockcfg,
+ resultdir)
+
+PLUGIN_PATH='gcc-python2-plugin-0.9-1.fc16.x86_64.rpm'
+MOCK_CONFIG='fedora-16-x86_64'
+
+# For now, just an experimental hardcoded list of srpms:
+local_rebuild_of_srpm_in_mock('MySQL-python-1.2.3-3.fc16.src.rpm')
+local_rebuild_of_srpm_in_mock('python-crypto-2.3-5.fc16.src.rpm')
+local_rebuild_of_srpm_in_mock('rpm-4.9.1.2-1.fc16.src.rpm')
+
12 years, 3 months
[gcc-python-plugin] Created tag v0.9
by David Hugh Malcolm
The unsigned tag 'v0.9' was created.
Tagger: David Malcolm <dmalcolm(a)redhat.com>
Date: Mon Feb 6 12:29:01 2012 -0500
0.9
Changes since the last tag 'v0.8':
David Malcolm (38):
add notes to self on making a release
docs: move section on callbacks into its own page
minor PEP8 fix in example code
docs: add tables of callback event IDs
docs: document gcc.PLUGIN_ATTRIBUTES (ticket #5)
docs: minor PEP8 fixes
cpychecker: fix a traceback seen in array handling
move gcc_python_get_callgraph_nodes from gcc-python.c to gcc-python-callgraph.c
cpychecker: implement PyString_Concat
cpychecker: implement PyString_ConcatAndDel
move version handling into its own file
allow registration of callbacks on gcc.PLUGIN_FINISH
tidy up selftests for callbacks
allow registration of callbacks on gcc.PLUGIN_GGC_{START,MARKING,FINISH}
integrate with GCC's garbage collector (ticket #1)
docs: remove note about garbage collector
Makefile: add -fno-strict-aliasing to CFLAGS
cpychecker: rewrite of how values are compared
cpychecker: extend detection of PyObject* subclasses
add metaclass to PyGccWrapper to fix garbage-collection segfault on user-defined classes
tidy up insides of gcc_python_wrapper_meta_tp_new
exclude PyString_Concat[AndDel] selftests on Python 3
add a configuration phase and use it to support PLUGIN_FINISH_DECL
cpychecker: fix lookup of exception/type objects under gcc 4.7 (issue #21)
cpychecker: fix missing import
when working with gcc 4.7, fix/disable various selftests for now
py3k compat: make SkipTest be an Exception
py3k fixes
remove stray assignment
fix bugs in test code visible with -Wformat
add -fPIC
cpychecker: split up check_refcounts() into more manageable chunks
cpychecker: more cleanups
cpychecker: warn about function call arguments that point to deallocated memory
cpychecker: further fixes to loop-handling
fix 8 memory leaks within the plugin
fixes for selftests on 32-bit
prepare for 0.9 release
Tom Tromey (3):
patch to add ArrayType.range
fix issue #6 by documenting that define_macro accepts a definition as well
document gcc.IntegerField min_value and max_value (issue #31)
12 years, 4 months
[gcc-python-plugin] prepare for 0.9 release
by David Hugh Malcolm
commit 357bf26aa3e5d5ce8af8212ae06c3db35140b571
Author: David Malcolm <dmalcolm(a)redhat.com>
Date: Mon Feb 6 12:27:42 2012 -0500
prepare for 0.9 release
Makefile | 15 +++++-
docs/0.9.rst | 147 ++++++++++++++++++++++++++++++++++++++++++++++++
docs/conf.py | 4 +-
docs/release-notes.rst | 1 +
gcc-python-plugin.spec | 5 +-
5 files changed, 168 insertions(+), 4 deletions(-)
---
diff --git a/Makefile b/Makefile
index 0d78abe..c366408 100644
--- a/Makefile
+++ b/Makefile
@@ -231,8 +231,19 @@ tarball:
#
# * add release notes to docs
#
+# Test the candidate tarball via a scratch SRPM build locally (this
+# achieves test coverage against python 2 and 3, for both debug and
+# optimized python, on one arch, against the locally-installed version of
+# gcc):
+#
+# $ make srpm VERSION=fixme
+#
+# $ make rpm VERSION=fixme
+#
# Test the candidate tarball via a scratch SRPM build in Koji (this
-# achieves test coverage against python 2 and 3, on both i686 and x86_64)
+# achieves test coverage against python 2 and 3, for both debug and
+# optimized python, on both i686 and x86_64, against another version of
+# gcc):
#
# $ make koji VERSION=fixme
#
@@ -256,6 +267,8 @@ tarball:
# To: gcc(a)gcc.gnu.org, gcc-python-plugin(a)lists.fedorahosted.org, python-announce-list(a)python.org
# Subject: ANN: gcc-python-plugin $(VERSION)
# (etc)
+#
+# * build it into Fedora
# Utility target, for building test rpms:
srpm:
diff --git a/docs/0.9.rst b/docs/0.9.rst
new file mode 100644
index 0000000..d7f0bb2
--- /dev/null
+++ b/docs/0.9.rst
@@ -0,0 +1,147 @@
+.. Copyright 2012 David Malcolm <dmalcolm(a)redhat.com>
+ Copyright 2012 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/>.
+
+.. covers up to 4642a564e03c9e2c8114bca206205ad9c8fbc308
+
+0.9
+~~~
+
+Changes to the GCC Python Plugin
+================================
+
+The plugin now works with GCC 4.7 prereleases (ticket `#21 <https://fedorahosted.org/gcc-python-plugin/ticket/21>`_).
+
+The plugin is now integrated with GCC's garbage collector: Python wrapper
+objects keep their underlying GCC objects alive when GCC's garbage collector
+runs, preventing segfaults that could occur if the underlying objects were
+swept away from under us
+(ticket `#1 <https://fedorahosted.org/gcc-python-plugin/ticket/1>`_).
+
+It's now possible to attach Python callbacks to more GCC events:
+:py:data:`gcc.PLUGIN_FINISH`, :py:data:`gcc.PLUGIN_GGC_START`,
+:py:data:`gcc.PLUGIN_GGC_MARKING`, :py:data:`gcc.PLUGIN_GGC_FINISH`,
+:py:data:`gcc.PLUGIN_FINISH_DECL` (gcc 4.7)
+
+:py:class:`gcc.ArrayType` has gained a "range" attribute, allowing scripts
+to detect out-of-bounds conditions in array-handling.
+
+A number of memory leaks were fixed: these were found by
+`running the plugin on itself
+<http://git.fedorahosted.org/git/?p=gcc-python-plugin.git;a=commitdiff;h=4...>`_.
+
+Various documentation improvements
+(ticket `#6 <https://fedorahosted.org/gcc-python-plugin/ticket/6>`_,
+ticket `#31 <https://fedorahosted.org/gcc-python-plugin/ticket/31>`_).
+
+
+Improvements to :doc:`gcc-with-cpychecker </cpychecker>`
+========================================================
+
+The :doc:`gcc-with-cpychecker </cpychecker>` tool has received some deep
+internal improvements in this release.
+
+The logic for analyzing the outcome of comparisons has been rewritten for this
+release, fixing some significant bugs that could lead to the analyzer
+incorrectly deciding whether or not a block of code was reachable.
+
+Similarly, the logic for detecting loops has been rewritten, elimininating a
+bug in which the checker would prematurely stop analyzing loops with
+complicated termination conditions, and not analyze the body of the loop.
+
+Doing so extended the reach of the checker, and enabled it to find the memory
+leaks referred to above.
+
+In addition, the checker now emits more detailed information on the ranges of
+possible values it's considering when a comparison occurs against an unknown
+value::
+
+ input.c: In function 'test':
+ input.c:41:5: warning: comparison against uninitialized data (item) at input.c:41 [enabled by default]
+ input.c:34:12: note: when PyList_New() succeeds at: result = PyList_New(len);
+ input.c:35:8: note: taking False path at: if (!result) {
+ input.c:39:12: note: reaching: for (i = 0; i < len; i++) {
+ input.c:39:5: note: when considering range: 1 <= value <= 0x7fffffff at: for (i = 0; i < len; i++) {
+ input.c:39:5: note: taking True path at: for (i = 0; i < len; i++) {
+ input.c:41:5: note: reaching: if (!item) {
+
+The checker should do a better job of identifying PyObject subclasses.
+Previously it was treating any struct beginning with "ob_refcnt" and "ob_type"
+as a Python object (with some tweaks for python 3 and debug builds). It now
+also covers structs that begin with a field that's a PyObject (or subclass),
+since these are likely to also be PyObject subclasses.
+
+
+Usage of deallocated memory
+---------------------------
+Previously, the checker would warn about paths through a function that could
+return a pointer to deallocated memory, or which tried to read through such
+a pointer. With this release, the checker will now also warn about paths
+through a function in which a pointer to deallocated memory is passed to a
+function.
+
+For example, given this buggy code:
+
+.. code-block:: c
+
+ extern void some_function(PyObject *);
+
+ void
+ test(PyObject *self, PyObject *args)
+ {
+ /* Create an object: */
+ PyObject *tmp = PyLong_FromLong(0x1000);
+
+ if (!tmp) {
+ return;
+ }
+
+ /*
+ Now decref the object. Depending on what other references are owned
+ on the object, it can reach a refcount of zero, and thus be deallocated:
+ */
+ Py_DECREF(tmp);
+
+ /* BUG: the object being returned may have been deallocated */
+ some_function(tmp);
+ }
+
+the checker will emit this warning::
+
+ input.c: In function 'test':
+ input.c:45: warning: passing pointer to deallocated memory as argument 1 of function at input.c:45: memory deallocated at input.c:42 [enabled by default]
+ input.c:32: note: when PyLong_FromLong() succeeds at: PyObject *tmp = PyLong_FromLong(0x1000);
+ input.c:34: note: taking False path at: if (!tmp) {
+ input.c:42: note: reaching: Py_DECREF(tmp);
+ input.c:42: note: when taking False path at: Py_DECREF(tmp);
+ input.c:42: note: reaching: Py_DECREF(tmp);
+ input.c:42: note: calling tp_dealloc on PyLongObject allocated at input.c:32 at: Py_DECREF(tmp);
+ input.c:45: note: reaching: foo(tmp);
+ input.c:30: note: graphical error report for function 'passing_dead_object_to_function' written out to 'input.c.test-refcount-errors.html'
+
+
+Coverage of the CPython API
+---------------------------
+
+This release adds heuristics for the behavior of the following CPython API
+entrypoints:
+
+ * PyString_Concat
+ * PyString_ConcatAndDel
+
+along with various other bugfixes and documentation improvements.
+
+..
diff --git a/docs/conf.py b/docs/conf.py
index 6a65104..1498782 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -65,9 +65,9 @@ copyright = u'2011, David Malcolm'
# built documents.
#
# The short X.Y version.
-version = '0.8'
+version = '0.9'
# The full version, including alpha/beta/rc tags.
-release = '0.8'
+release = '0.9'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
diff --git a/docs/release-notes.rst b/docs/release-notes.rst
index e18c307..0d58c47 100644
--- a/docs/release-notes.rst
+++ b/docs/release-notes.rst
@@ -20,5 +20,6 @@ Release Notes
.. toctree::
+ 0.9.rst
0.8.rst
0.7.rst
diff --git a/gcc-python-plugin.spec b/gcc-python-plugin.spec
index 8e33205..3113206 100644
--- a/gcc-python-plugin.spec
+++ b/gcc-python-plugin.spec
@@ -1,5 +1,5 @@
Name: gcc-python-plugin
-Version: 0.8
+Version: 0.9
Release: 1%{?dist}
Summary: GCC plugin that embeds Python
@@ -333,6 +333,9 @@ CheckPlugin \
%doc examples
%changelog
+* Mon Feb 6 2012 David Malcolm <dmalcolm(a)redhat.com> - 0.9-1
+- 0.9
+
* Tue Jan 10 2012 David Malcolm <dmalcolm(a)redhat.com> - 0.8-1
- 0.8
12 years, 4 months
[gcc-python-plugin] fixes for selftests on 32-bit
by David Hugh Malcolm
commit 96b315be0d2b082649f196e86502c175872506df
Author: David Malcolm <dmalcolm(a)redhat.com>
Date: Mon Feb 6 12:25:04 2012 -0500
fixes for selftests on 32-bit
run-test-suite.py | 3 +++
1 files changed, 3 insertions(+), 0 deletions(-)
---
diff --git a/run-test-suite.py b/run-test-suite.py
index 2bccd24..a68331a 100644
--- a/run-test-suite.py
+++ b/run-test-suite.py
@@ -113,6 +113,7 @@ class TestStream:
# (e.g. from "D.12021->fieldA" to "D.nnnnn->fieldA"):
line = re.sub('D.([0-9]+)', 'D.nnnnn', line)
line = re.sub('VarDecl\(([0-9]+)\)', 'VarDecl(nnnn)', line)
+ line = re.sub('ParmDecl\(([0-9]+)\)', 'ParmDecl(nnnn)', line)
line = re.sub('LabelDecl\(([0-9]+)\)', 'LabelDecl(nnnn)', line)
# Remove exact numbers from types
@@ -146,7 +147,9 @@ class TestStream:
# Avoid further 32-bit vs 64-bit differences due to int vs long
# overflow:
line = re.sub('0x7fffffffL', '0x7fffffff', line)
+ line = re.sub('0x7ffffffeL', '0x7ffffffe', line)
line = re.sub('0xffffffffL', '0xffffffff', line)
+ line = re.sub('0xfffffffeL', '0xfffffffe', line)
# GCC 4.7 tracks macro expansions, and this can change the column
# numbers in error reports:
12 years, 4 months