aboutsummaryrefslogtreecommitdiff
path: root/gcc/testsuite
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/testsuite')
-rw-r--r--gcc/testsuite/gcc.dg/plugin/analyzer_cpython_plugin.c385
-rw-r--r--gcc/testsuite/gcc.dg/plugin/cpython-plugin-test-PyList_Append.c (renamed from gcc/testsuite/gcc.dg/plugin/cpython-plugin-test-2.c)56
-rw-r--r--gcc/testsuite/gcc.dg/plugin/cpython-plugin-test-PyList_New.c38
-rw-r--r--gcc/testsuite/gcc.dg/plugin/cpython-plugin-test-PyLong_FromLong.c38
-rw-r--r--gcc/testsuite/gcc.dg/plugin/cpython-plugin-test-no-Python-h.c (renamed from gcc/testsuite/gcc.dg/plugin/cpython-plugin-test-1.c)0
-rw-r--r--gcc/testsuite/gcc.dg/plugin/plugin.exp6
6 files changed, 491 insertions, 32 deletions
diff --git a/gcc/testsuite/gcc.dg/plugin/analyzer_cpython_plugin.c b/gcc/testsuite/gcc.dg/plugin/analyzer_cpython_plugin.c
index 7cd72e8..7af5204 100644
--- a/gcc/testsuite/gcc.dg/plugin/analyzer_cpython_plugin.c
+++ b/gcc/testsuite/gcc.dg/plugin/analyzer_cpython_plugin.c
@@ -44,6 +44,7 @@
#include "analyzer/region-model.h"
#include "analyzer/call-details.h"
#include "analyzer/call-info.h"
+#include "analyzer/exploded-graph.h"
#include "make-unique.h"
int plugin_is_GPL_compatible;
@@ -191,6 +192,381 @@ public:
}
};
+/* This is just a copy of leak_stmt_finder for now (subject to change if
+ * necssary) */
+
+class refcnt_stmt_finder : public stmt_finder
+{
+public:
+ refcnt_stmt_finder (const exploded_graph &eg, tree var)
+ : m_eg (eg), m_var (var)
+ {
+ }
+
+ std::unique_ptr<stmt_finder>
+ clone () const final override
+ {
+ return make_unique<refcnt_stmt_finder> (m_eg, m_var);
+ }
+
+ const gimple *
+ find_stmt (const exploded_path &epath) final override
+ {
+ logger *const logger = m_eg.get_logger ();
+ LOG_FUNC (logger);
+
+ if (m_var && TREE_CODE (m_var) == SSA_NAME)
+ {
+ /* Locate the final write to this SSA name in the path. */
+ const gimple *def_stmt = SSA_NAME_DEF_STMT (m_var);
+
+ int idx_of_def_stmt;
+ bool found = epath.find_stmt_backwards (def_stmt, &idx_of_def_stmt);
+ if (!found)
+ goto not_found;
+
+ /* What was the next write to the underlying var
+ after the SSA name was set? (if any). */
+
+ for (unsigned idx = idx_of_def_stmt + 1; idx < epath.m_edges.length ();
+ ++idx)
+ {
+ const exploded_edge *eedge = epath.m_edges[idx];
+ if (logger)
+ logger->log ("eedge[%i]: EN %i -> EN %i", idx,
+ eedge->m_src->m_index,
+ eedge->m_dest->m_index);
+ const exploded_node *dst_node = eedge->m_dest;
+ const program_point &dst_point = dst_node->get_point ();
+ const gimple *stmt = dst_point.get_stmt ();
+ if (!stmt)
+ continue;
+ if (const gassign *assign = dyn_cast<const gassign *> (stmt))
+ {
+ tree lhs = gimple_assign_lhs (assign);
+ if (TREE_CODE (lhs) == SSA_NAME
+ && SSA_NAME_VAR (lhs) == SSA_NAME_VAR (m_var))
+ return assign;
+ }
+ }
+ }
+
+ not_found:
+
+ /* Look backwards for the first statement with a location. */
+ int i;
+ const exploded_edge *eedge;
+ FOR_EACH_VEC_ELT_REVERSE (epath.m_edges, i, eedge)
+ {
+ if (logger)
+ logger->log ("eedge[%i]: EN %i -> EN %i", i, eedge->m_src->m_index,
+ eedge->m_dest->m_index);
+ const exploded_node *dst_node = eedge->m_dest;
+ const program_point &dst_point = dst_node->get_point ();
+ const gimple *stmt = dst_point.get_stmt ();
+ if (stmt)
+ if (get_pure_location (stmt->location) != UNKNOWN_LOCATION)
+ return stmt;
+ }
+
+ gcc_unreachable ();
+ return NULL;
+ }
+
+private:
+ const exploded_graph &m_eg;
+ tree m_var;
+};
+
+class refcnt_mismatch : public pending_diagnostic_subclass<refcnt_mismatch>
+{
+public:
+ refcnt_mismatch (const region *base_region,
+ const svalue *ob_refcnt,
+ const svalue *actual_refcnt,
+ tree reg_tree)
+ : m_base_region (base_region), m_ob_refcnt (ob_refcnt),
+ m_actual_refcnt (actual_refcnt), m_reg_tree(reg_tree)
+ {
+ }
+
+ const char *
+ get_kind () const final override
+ {
+ return "refcnt_mismatch";
+ }
+
+ bool
+ operator== (const refcnt_mismatch &other) const
+ {
+ return (m_base_region == other.m_base_region
+ && m_ob_refcnt == other.m_ob_refcnt
+ && m_actual_refcnt == other.m_actual_refcnt);
+ }
+
+ int get_controlling_option () const final override
+ {
+ return 0;
+ }
+
+ bool
+ emit (rich_location *rich_loc, logger *) final override
+ {
+ diagnostic_metadata m;
+ bool warned;
+ // just assuming constants for now
+ auto actual_refcnt
+ = m_actual_refcnt->dyn_cast_constant_svalue ()->get_constant ();
+ auto ob_refcnt = m_ob_refcnt->dyn_cast_constant_svalue ()->get_constant ();
+ warned = warning_meta (rich_loc, m, get_controlling_option (),
+ "expected %qE to have "
+ "reference count: %qE but ob_refcnt field is: %qE",
+ m_reg_tree, actual_refcnt, ob_refcnt);
+
+ // location_t loc = rich_loc->get_loc ();
+ // foo (loc);
+ return warned;
+ }
+
+ void mark_interesting_stuff (interesting_t *interest) final override
+ {
+ if (m_base_region)
+ interest->add_region_creation (m_base_region);
+ }
+
+private:
+
+ void foo(location_t loc) const
+ {
+ inform(loc, "something is up right here");
+ }
+ const region *m_base_region;
+ const svalue *m_ob_refcnt;
+ const svalue *m_actual_refcnt;
+ tree m_reg_tree;
+};
+
+/* Retrieves the svalue associated with the ob_refcnt field of the base region.
+ */
+static const svalue *
+retrieve_ob_refcnt_sval (const region *base_reg, const region_model *model,
+ region_model_context *ctxt)
+{
+ region_model_manager *mgr = model->get_manager ();
+ tree ob_refcnt_tree = get_field_by_name (pyobj_record, "ob_refcnt");
+ const region *ob_refcnt_region
+ = mgr->get_field_region (base_reg, ob_refcnt_tree);
+ const svalue *ob_refcnt_sval
+ = model->get_store_value (ob_refcnt_region, ctxt);
+ return ob_refcnt_sval;
+}
+
+static void
+increment_region_refcnt (hash_map<const region *, int> &map, const region *key)
+{
+ bool existed;
+ auto &refcnt = map.get_or_insert (key, &existed);
+ refcnt = existed ? refcnt + 1 : 1;
+}
+
+
+/* Recursively fills in region_to_refcnt with the references owned by
+ pyobj_ptr_sval. */
+static void
+count_pyobj_references (const region_model *model,
+ hash_map<const region *, int> &region_to_refcnt,
+ const svalue *pyobj_ptr_sval,
+ hash_set<const region *> &seen)
+{
+ if (!pyobj_ptr_sval)
+ return;
+
+ const auto *pyobj_region_sval = pyobj_ptr_sval->dyn_cast_region_svalue ();
+ const auto *pyobj_initial_sval = pyobj_ptr_sval->dyn_cast_initial_svalue ();
+ if (!pyobj_region_sval && !pyobj_initial_sval)
+ return;
+
+ // todo: support initial sval (e.g passed in as parameter)
+ if (pyobj_initial_sval)
+ {
+ // increment_region_refcnt (region_to_refcnt,
+ // pyobj_initial_sval->get_region ());
+ return;
+ }
+
+ const region *pyobj_region = pyobj_region_sval->get_pointee ();
+ if (!pyobj_region || seen.contains (pyobj_region))
+ return;
+
+ seen.add (pyobj_region);
+
+ if (pyobj_ptr_sval->get_type () == pyobj_ptr_tree)
+ increment_region_refcnt (region_to_refcnt, pyobj_region);
+
+ const auto *curr_store = model->get_store ();
+ const auto *retval_cluster = curr_store->get_cluster (pyobj_region);
+ if (!retval_cluster)
+ return;
+
+ const auto &retval_binding_map = retval_cluster->get_map ();
+
+ for (const auto &binding : retval_binding_map)
+ {
+ const svalue *binding_sval = binding.second;
+ const svalue *unwrapped_sval = binding_sval->unwrap_any_unmergeable ();
+ const region *pointee = unwrapped_sval->maybe_get_region ();
+
+ if (pointee && pointee->get_kind () == RK_HEAP_ALLOCATED)
+ count_pyobj_references (model, region_to_refcnt, binding_sval, seen);
+ }
+}
+
+/* Compare ob_refcnt field vs the actual reference count of a region */
+static void
+check_refcnt (const region_model *model,
+ const region_model *old_model,
+ region_model_context *ctxt,
+ const hash_map<const ana::region *,
+ int>::iterator::reference_pair region_refcnt)
+{
+ region_model_manager *mgr = model->get_manager ();
+ const auto &curr_region = region_refcnt.first;
+ const auto &actual_refcnt = region_refcnt.second;
+ const svalue *ob_refcnt_sval
+ = retrieve_ob_refcnt_sval (curr_region, model, ctxt);
+ const svalue *actual_refcnt_sval = mgr->get_or_create_int_cst (
+ ob_refcnt_sval->get_type (), actual_refcnt);
+
+ if (ob_refcnt_sval != actual_refcnt_sval)
+ {
+ const svalue *curr_reg_sval
+ = mgr->get_ptr_svalue (pyobj_ptr_tree, curr_region);
+ tree reg_tree = old_model->get_representative_tree (curr_reg_sval);
+ if (!reg_tree)
+ return;
+
+ const auto &eg = ctxt->get_eg ();
+ refcnt_stmt_finder finder (*eg, reg_tree);
+ auto pd = make_unique<refcnt_mismatch> (curr_region, ob_refcnt_sval,
+ actual_refcnt_sval, reg_tree);
+ if (pd && eg)
+ ctxt->warn (std::move (pd), &finder);
+ }
+}
+
+static void
+check_refcnts (const region_model *model,
+ const region_model *old_model,
+ const svalue *retval,
+ region_model_context *ctxt,
+ hash_map<const region *, int> &region_to_refcnt)
+{
+ for (const auto &region_refcnt : region_to_refcnt)
+ {
+ check_refcnt (model, old_model, ctxt, region_refcnt);
+ }
+}
+
+/* Validates the reference count of all Python objects. */
+void
+pyobj_refcnt_checker (const region_model *model,
+ const region_model *old_model,
+ const svalue *retval,
+ region_model_context *ctxt)
+{
+ if (!ctxt)
+ return;
+
+ auto region_to_refcnt = hash_map<const region *, int> ();
+ auto seen_regions = hash_set<const region *> ();
+
+ count_pyobj_references (model, region_to_refcnt, retval, seen_regions);
+ check_refcnts (model, old_model, retval, ctxt, region_to_refcnt);
+}
+
+/* Counts the actual pyobject references from all clusters in the model's
+ * store. */
+static void
+count_all_references (const region_model *model,
+ hash_map<const region *, int> &region_to_refcnt)
+{
+ for (const auto &cluster : *model->get_store ())
+ {
+ auto curr_region = cluster.first;
+ if (curr_region->get_kind () != RK_HEAP_ALLOCATED)
+ continue;
+
+ increment_region_refcnt (region_to_refcnt, curr_region);
+
+ auto binding_cluster = cluster.second;
+ for (const auto &binding : binding_cluster->get_map ())
+ {
+ const svalue *binding_sval = binding.second;
+
+ const svalue *unwrapped_sval
+ = binding_sval->unwrap_any_unmergeable ();
+ // if (unwrapped_sval->get_type () != pyobj_ptr_tree)
+ // continue;
+
+ const region *pointee = unwrapped_sval->maybe_get_region ();
+ if (!pointee || pointee->get_kind () != RK_HEAP_ALLOCATED)
+ continue;
+
+ increment_region_refcnt (region_to_refcnt, pointee);
+ }
+ }
+}
+
+static void
+dump_refcnt_info (const hash_map<const region *, int> &region_to_refcnt,
+ const region_model *model,
+ region_model_context *ctxt)
+{
+ region_model_manager *mgr = model->get_manager ();
+ pretty_printer pp;
+ pp_format_decoder (&pp) = default_tree_printer;
+ pp_show_color (&pp) = pp_show_color (global_dc->printer);
+ pp.buffer->stream = stderr;
+
+ for (const auto &region_refcnt : region_to_refcnt)
+ {
+ auto region = region_refcnt.first;
+ auto actual_refcnt = region_refcnt.second;
+ const svalue *ob_refcnt_sval
+ = retrieve_ob_refcnt_sval (region, model, ctxt);
+ const svalue *actual_refcnt_sval = mgr->get_or_create_int_cst (
+ ob_refcnt_sval->get_type (), actual_refcnt);
+
+ region->dump_to_pp (&pp, true);
+ pp_string (&pp, " — ob_refcnt: ");
+ ob_refcnt_sval->dump_to_pp (&pp, true);
+ pp_string (&pp, " actual refcnt: ");
+ actual_refcnt_sval->dump_to_pp (&pp, true);
+ pp_newline (&pp);
+ }
+ pp_string (&pp, "~~~~~~~~\n");
+ pp_flush (&pp);
+}
+
+class kf_analyzer_cpython_dump_refcounts : public known_function
+{
+public:
+ bool matches_call_types_p (const call_details &cd) const final override
+ {
+ return cd.num_args () == 0;
+ }
+ void impl_call_pre (const call_details &cd) const final override
+ {
+ region_model_context *ctxt = cd.get_ctxt ();
+ if (!ctxt)
+ return;
+ region_model *model = cd.get_model ();
+ auto region_to_refcnt = hash_map<const region *, int> ();
+ count_all_references(model, region_to_refcnt);
+ dump_refcnt_info(region_to_refcnt, model, ctxt);
+ }
+};
+
/* Some concessions were made to
simplify the analysis process when comparing kf_PyList_Append with the
real implementation. In particular, PyList_Append performs some
@@ -927,6 +1303,10 @@ cpython_analyzer_init_cb (void *gcc_data, void * /*user_data */)
iface->register_known_function ("PyList_New", make_unique<kf_PyList_New> ());
iface->register_known_function ("PyLong_FromLong",
make_unique<kf_PyLong_FromLong> ());
+
+ iface->register_known_function (
+ "__analyzer_cpython_dump_refcounts",
+ make_unique<kf_analyzer_cpython_dump_refcounts> ());
}
} // namespace ana
@@ -940,8 +1320,9 @@ plugin_init (struct plugin_name_args *plugin_info,
const char *plugin_name = plugin_info->base_name;
if (0)
inform (input_location, "got here; %qs", plugin_name);
- ana::register_finish_translation_unit_callback (&stash_named_types);
- ana::register_finish_translation_unit_callback (&stash_global_vars);
+ register_finish_translation_unit_callback (&stash_named_types);
+ register_finish_translation_unit_callback (&stash_global_vars);
+ region_model::register_pop_frame_callback(pyobj_refcnt_checker);
register_callback (plugin_info->base_name, PLUGIN_ANALYZER_INIT,
ana::cpython_analyzer_init_cb,
NULL); /* void *user_data */
diff --git a/gcc/testsuite/gcc.dg/plugin/cpython-plugin-test-2.c b/gcc/testsuite/gcc.dg/plugin/cpython-plugin-test-PyList_Append.c
index 19b5c17..e1efd9e 100644
--- a/gcc/testsuite/gcc.dg/plugin/cpython-plugin-test-2.c
+++ b/gcc/testsuite/gcc.dg/plugin/cpython-plugin-test-PyList_Append.c
@@ -9,40 +9,13 @@
#include "../analyzer/analyzer-decls.h"
PyObject *
-test_PyList_New (Py_ssize_t len)
-{
- PyObject *obj = PyList_New (len);
- if (obj)
- {
- __analyzer_eval (obj->ob_refcnt == 1); /* { dg-warning "TRUE" } */
- __analyzer_eval (PyList_CheckExact (obj)); /* { dg-warning "TRUE" } */
- }
- else
- __analyzer_dump_path (); /* { dg-message "path" } */
- return obj;
-}
-
-PyObject *
-test_PyLong_New (long n)
-{
- PyObject *obj = PyLong_FromLong (n);
- if (obj)
- {
- __analyzer_eval (obj->ob_refcnt == 1); /* { dg-warning "TRUE" } */
- __analyzer_eval (PyLong_CheckExact (obj)); /* { dg-warning "TRUE" } */
- }
- else
- __analyzer_dump_path (); /* { dg-message "path" } */
- return obj;
-}
-
-PyObject *
test_PyListAppend (long n)
{
PyObject *item = PyLong_FromLong (n);
PyObject *list = PyList_New (0);
PyList_Append(list, item);
return list; /* { dg-warning "leak of 'item'" } */
+ /* { dg-warning "expected 'item' to have reference count" "" { target *-*-* } .-1 } */
}
PyObject *
@@ -67,6 +40,7 @@ test_PyListAppend_2 (long n)
else
__analyzer_eval (item->ob_refcnt == 2); /* { dg-warning "TRUE" } */
return list; /* { dg-warning "leak of 'item'" } */
+ /* { dg-warning "expected 'item' to have reference count" "" { target *-*-* } .-1 } */
}
@@ -75,4 +49,30 @@ test_PyListAppend_3 (PyObject *item, PyObject *list)
{
PyList_Append (list, item);
return list;
+}
+
+PyObject *
+test_PyListAppend_4 (long n)
+{
+ PyObject *item = PyLong_FromLong (n);
+ PyObject *list = NULL;
+ PyList_Append(list, item);
+ return list;
+}
+
+PyObject *
+test_PyListAppend_5 ()
+{
+ PyObject *list = PyList_New (0);
+ PyList_Append(list, NULL);
+ return list;
+}
+
+PyObject *
+test_PyListAppend_6 ()
+{
+ PyObject *item = NULL;
+ PyObject *list = NULL;
+ PyList_Append(list, item);
+ return list;
} \ No newline at end of file
diff --git a/gcc/testsuite/gcc.dg/plugin/cpython-plugin-test-PyList_New.c b/gcc/testsuite/gcc.dg/plugin/cpython-plugin-test-PyList_New.c
new file mode 100644
index 0000000..1d28e66
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/plugin/cpython-plugin-test-PyList_New.c
@@ -0,0 +1,38 @@
+/* { dg-do compile } */
+/* { dg-require-effective-target analyzer } */
+/* { dg-options "-fanalyzer" } */
+/* { dg-require-python-h "" } */
+
+
+#define PY_SSIZE_T_CLEAN
+#include <Python.h>
+#include "../analyzer/analyzer-decls.h"
+
+PyObject *
+test_PyList_New (Py_ssize_t len)
+{
+ PyObject *obj = PyList_New (len);
+ if (obj)
+ {
+ __analyzer_eval (obj->ob_refcnt == 1); /* { dg-warning "TRUE" } */
+ __analyzer_eval (PyList_CheckExact (obj)); /* { dg-warning "TRUE" } */
+ }
+ else
+ __analyzer_dump_path (); /* { dg-message "path" } */
+ return obj;
+}
+
+void
+test_PyList_New_2 ()
+{
+ PyObject *obj = PyList_New (0);
+} /* { dg-warning "leak of 'obj'" } */
+
+PyObject *test_stray_incref_PyList ()
+{
+ PyObject *p = PyList_New (2);
+ if (p)
+ Py_INCREF (p);
+ return p;
+ /* { dg-warning "expected 'p' to have reference count" "" { target *-*-* } .-1 } */
+} \ No newline at end of file
diff --git a/gcc/testsuite/gcc.dg/plugin/cpython-plugin-test-PyLong_FromLong.c b/gcc/testsuite/gcc.dg/plugin/cpython-plugin-test-PyLong_FromLong.c
new file mode 100644
index 0000000..6ac59396
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/plugin/cpython-plugin-test-PyLong_FromLong.c
@@ -0,0 +1,38 @@
+/* { dg-do compile } */
+/* { dg-require-effective-target analyzer } */
+/* { dg-options "-fanalyzer" } */
+/* { dg-require-python-h "" } */
+
+
+#define PY_SSIZE_T_CLEAN
+#include <Python.h>
+#include "../analyzer/analyzer-decls.h"
+
+PyObject *
+test_PyLong_New (long n)
+{
+ PyObject *obj = PyLong_FromLong (n);
+ if (obj)
+ {
+ __analyzer_eval (obj->ob_refcnt == 1); /* { dg-warning "TRUE" } */
+ __analyzer_eval (PyLong_CheckExact (obj)); /* { dg-warning "TRUE" } */
+ }
+ else
+ __analyzer_dump_path (); /* { dg-message "path" } */
+ return obj;
+}
+
+void
+test_PyLong_New_2 (long n)
+{
+ PyObject *obj = PyLong_FromLong (n);
+} /* { dg-warning "leak of 'obj'" } */
+
+PyObject *test_stray_incref_PyLong (long val)
+{
+ PyObject *p = PyLong_FromLong (val);
+ if (p)
+ Py_INCREF (p);
+ return p;
+ /* { dg-warning "expected 'p' to have reference count" "" { target *-*-* } .-1 } */
+} \ No newline at end of file
diff --git a/gcc/testsuite/gcc.dg/plugin/cpython-plugin-test-1.c b/gcc/testsuite/gcc.dg/plugin/cpython-plugin-test-no-Python-h.c
index c105074..c105074 100644
--- a/gcc/testsuite/gcc.dg/plugin/cpython-plugin-test-1.c
+++ b/gcc/testsuite/gcc.dg/plugin/cpython-plugin-test-no-Python-h.c
diff --git a/gcc/testsuite/gcc.dg/plugin/plugin.exp b/gcc/testsuite/gcc.dg/plugin/plugin.exp
index e1ed2d2..ed72912 100644
--- a/gcc/testsuite/gcc.dg/plugin/plugin.exp
+++ b/gcc/testsuite/gcc.dg/plugin/plugin.exp
@@ -161,8 +161,10 @@ set plugin_test_list [list \
taint-CVE-2011-0521-6.c \
taint-antipatterns-1.c } \
{ analyzer_cpython_plugin.c \
- cpython-plugin-test-1.c \
- cpython-plugin-test-2.c } \
+ cpython-plugin-test-no-Python-h.c \
+ cpython-plugin-test-PyList_Append.c \
+ cpython-plugin-test-PyList_New.c \
+ cpython-plugin-test-PyLong_FromLong.c } \
]
foreach plugin_test $plugin_test_list {