mirror of
https://github.com/autc04/Retro68.git
synced 2024-06-03 00:29:47 +00:00
1383 lines
43 KiB
C++
1383 lines
43 KiB
C++
/* Classes for modeling the state of memory.
|
|
Copyright (C) 2019-2022 Free Software Foundation, Inc.
|
|
Contributed by David Malcolm <dmalcolm@redhat.com>.
|
|
|
|
This file is part of GCC.
|
|
|
|
GCC 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, or (at your option)
|
|
any later version.
|
|
|
|
GCC 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 GCC; see the file COPYING3. If not see
|
|
<http://www.gnu.org/licenses/>. */
|
|
|
|
#ifndef GCC_ANALYZER_REGION_MODEL_H
|
|
#define GCC_ANALYZER_REGION_MODEL_H
|
|
|
|
/* Implementation of the region-based ternary model described in:
|
|
"A Memory Model for Static Analysis of C Programs"
|
|
(Zhongxing Xu, Ted Kremenek, and Jian Zhang)
|
|
http://lcs.ios.ac.cn/~xuzb/canalyze/memmodel.pdf */
|
|
|
|
#include "analyzer/svalue.h"
|
|
#include "analyzer/region.h"
|
|
|
|
using namespace ana;
|
|
|
|
namespace inchash
|
|
{
|
|
extern void add_path_var (path_var pv, hash &hstate);
|
|
} // namespace inchash
|
|
|
|
namespace ana {
|
|
|
|
template <typename T>
|
|
class one_way_id_map
|
|
{
|
|
public:
|
|
one_way_id_map (int num_ids);
|
|
void put (T src, T dst);
|
|
T get_dst_for_src (T src) const;
|
|
void dump_to_pp (pretty_printer *pp) const;
|
|
void dump () const;
|
|
void update (T *) const;
|
|
|
|
private:
|
|
auto_vec<T> m_src_to_dst;
|
|
};
|
|
|
|
/* class one_way_id_map. */
|
|
|
|
/* one_way_id_map's ctor, which populates the map with dummy null values. */
|
|
|
|
template <typename T>
|
|
inline one_way_id_map<T>::one_way_id_map (int num_svalues)
|
|
: m_src_to_dst (num_svalues)
|
|
{
|
|
for (int i = 0; i < num_svalues; i++)
|
|
m_src_to_dst.quick_push (T::null ());
|
|
}
|
|
|
|
/* Record that SRC is to be mapped to DST. */
|
|
|
|
template <typename T>
|
|
inline void
|
|
one_way_id_map<T>::put (T src, T dst)
|
|
{
|
|
m_src_to_dst[src.as_int ()] = dst;
|
|
}
|
|
|
|
/* Get the new value for SRC within the map. */
|
|
|
|
template <typename T>
|
|
inline T
|
|
one_way_id_map<T>::get_dst_for_src (T src) const
|
|
{
|
|
if (src.null_p ())
|
|
return src;
|
|
return m_src_to_dst[src.as_int ()];
|
|
}
|
|
|
|
/* Dump this map to PP. */
|
|
|
|
template <typename T>
|
|
inline void
|
|
one_way_id_map<T>::dump_to_pp (pretty_printer *pp) const
|
|
{
|
|
pp_string (pp, "src to dst: {");
|
|
unsigned i;
|
|
T *dst;
|
|
FOR_EACH_VEC_ELT (m_src_to_dst, i, dst)
|
|
{
|
|
if (i > 0)
|
|
pp_string (pp, ", ");
|
|
T src (T::from_int (i));
|
|
src.print (pp);
|
|
pp_string (pp, " -> ");
|
|
dst->print (pp);
|
|
}
|
|
pp_string (pp, "}");
|
|
pp_newline (pp);
|
|
}
|
|
|
|
/* Dump this map to stderr. */
|
|
|
|
template <typename T>
|
|
DEBUG_FUNCTION inline void
|
|
one_way_id_map<T>::dump () const
|
|
{
|
|
pretty_printer pp;
|
|
pp.buffer->stream = stderr;
|
|
dump_to_pp (&pp);
|
|
pp_flush (&pp);
|
|
}
|
|
|
|
/* Update *ID from the old value to its new value in this map. */
|
|
|
|
template <typename T>
|
|
inline void
|
|
one_way_id_map<T>::update (T *id) const
|
|
{
|
|
*id = get_dst_for_src (*id);
|
|
}
|
|
|
|
/* A mapping from region to svalue for use when tracking state. */
|
|
|
|
class region_to_value_map
|
|
{
|
|
public:
|
|
typedef hash_map<const region *, const svalue *> hash_map_t;
|
|
typedef hash_map_t::iterator iterator;
|
|
|
|
region_to_value_map () : m_hash_map () {}
|
|
region_to_value_map (const region_to_value_map &other)
|
|
: m_hash_map (other.m_hash_map) {}
|
|
region_to_value_map &operator= (const region_to_value_map &other);
|
|
|
|
bool operator== (const region_to_value_map &other) const;
|
|
bool operator!= (const region_to_value_map &other) const
|
|
{
|
|
return !(*this == other);
|
|
}
|
|
|
|
iterator begin () const { return m_hash_map.begin (); }
|
|
iterator end () const { return m_hash_map.end (); }
|
|
|
|
const svalue * const *get (const region *reg) const
|
|
{
|
|
return const_cast <hash_map_t &> (m_hash_map).get (reg);
|
|
}
|
|
void put (const region *reg, const svalue *sval)
|
|
{
|
|
m_hash_map.put (reg, sval);
|
|
}
|
|
void remove (const region *reg)
|
|
{
|
|
m_hash_map.remove (reg);
|
|
}
|
|
|
|
bool is_empty () const { return m_hash_map.is_empty (); }
|
|
|
|
void dump_to_pp (pretty_printer *pp, bool simple, bool multiline) const;
|
|
void dump (bool simple) const;
|
|
|
|
bool can_merge_with_p (const region_to_value_map &other,
|
|
region_to_value_map *out) const;
|
|
|
|
void purge_state_involving (const svalue *sval);
|
|
|
|
private:
|
|
hash_map_t m_hash_map;
|
|
};
|
|
|
|
/* Various operations delete information from a region_model.
|
|
|
|
This struct tracks how many of each kind of entity were purged (e.g.
|
|
for selftests, and for debugging). */
|
|
|
|
struct purge_stats
|
|
{
|
|
purge_stats ()
|
|
: m_num_svalues (0),
|
|
m_num_regions (0),
|
|
m_num_equiv_classes (0),
|
|
m_num_constraints (0),
|
|
m_num_bounded_ranges_constraints (0),
|
|
m_num_client_items (0)
|
|
{}
|
|
|
|
int m_num_svalues;
|
|
int m_num_regions;
|
|
int m_num_equiv_classes;
|
|
int m_num_constraints;
|
|
int m_num_bounded_ranges_constraints;
|
|
int m_num_client_items;
|
|
};
|
|
|
|
/* A base class for visiting regions and svalues, with do-nothing
|
|
base implementations of the per-subclass vfuncs. */
|
|
|
|
class visitor
|
|
{
|
|
public:
|
|
virtual void visit_region_svalue (const region_svalue *) {}
|
|
virtual void visit_constant_svalue (const constant_svalue *) {}
|
|
virtual void visit_unknown_svalue (const unknown_svalue *) {}
|
|
virtual void visit_poisoned_svalue (const poisoned_svalue *) {}
|
|
virtual void visit_setjmp_svalue (const setjmp_svalue *) {}
|
|
virtual void visit_initial_svalue (const initial_svalue *) {}
|
|
virtual void visit_unaryop_svalue (const unaryop_svalue *) {}
|
|
virtual void visit_binop_svalue (const binop_svalue *) {}
|
|
virtual void visit_sub_svalue (const sub_svalue *) {}
|
|
virtual void visit_repeated_svalue (const repeated_svalue *) {}
|
|
virtual void visit_bits_within_svalue (const bits_within_svalue *) {}
|
|
virtual void visit_unmergeable_svalue (const unmergeable_svalue *) {}
|
|
virtual void visit_placeholder_svalue (const placeholder_svalue *) {}
|
|
virtual void visit_widening_svalue (const widening_svalue *) {}
|
|
virtual void visit_compound_svalue (const compound_svalue *) {}
|
|
virtual void visit_conjured_svalue (const conjured_svalue *) {}
|
|
virtual void visit_asm_output_svalue (const asm_output_svalue *) {}
|
|
virtual void visit_const_fn_result_svalue (const const_fn_result_svalue *) {}
|
|
|
|
virtual void visit_region (const region *) {}
|
|
};
|
|
|
|
} // namespace ana
|
|
|
|
namespace ana {
|
|
|
|
/* A class responsible for owning and consolidating region and svalue
|
|
instances.
|
|
region and svalue instances are immutable as far as clients are
|
|
concerned, so they are provided as "const" ptrs. */
|
|
|
|
class region_model_manager
|
|
{
|
|
public:
|
|
region_model_manager (logger *logger = NULL);
|
|
~region_model_manager ();
|
|
|
|
/* svalue consolidation. */
|
|
const svalue *get_or_create_constant_svalue (tree cst_expr);
|
|
const svalue *get_or_create_int_cst (tree type, poly_int64);
|
|
const svalue *get_or_create_unknown_svalue (tree type);
|
|
const svalue *get_or_create_setjmp_svalue (const setjmp_record &r,
|
|
tree type);
|
|
const svalue *get_or_create_poisoned_svalue (enum poison_kind kind,
|
|
tree type);
|
|
const svalue *get_or_create_initial_value (const region *reg);
|
|
const svalue *get_ptr_svalue (tree ptr_type, const region *pointee);
|
|
const svalue *get_or_create_unaryop (tree type, enum tree_code op,
|
|
const svalue *arg);
|
|
const svalue *get_or_create_cast (tree type, const svalue *arg);
|
|
const svalue *get_or_create_binop (tree type,
|
|
enum tree_code op,
|
|
const svalue *arg0, const svalue *arg1);
|
|
const svalue *get_or_create_sub_svalue (tree type,
|
|
const svalue *parent_svalue,
|
|
const region *subregion);
|
|
const svalue *get_or_create_repeated_svalue (tree type,
|
|
const svalue *outer_size,
|
|
const svalue *inner_svalue);
|
|
const svalue *get_or_create_bits_within (tree type,
|
|
const bit_range &bits,
|
|
const svalue *inner_svalue);
|
|
const svalue *get_or_create_unmergeable (const svalue *arg);
|
|
const svalue *get_or_create_widening_svalue (tree type,
|
|
const program_point &point,
|
|
const svalue *base_svalue,
|
|
const svalue *iter_svalue);
|
|
const svalue *get_or_create_compound_svalue (tree type,
|
|
const binding_map &map);
|
|
const svalue *get_or_create_conjured_svalue (tree type, const gimple *stmt,
|
|
const region *id_reg,
|
|
const conjured_purge &p);
|
|
const svalue *
|
|
get_or_create_asm_output_svalue (tree type,
|
|
const gasm *asm_stmt,
|
|
unsigned output_idx,
|
|
const vec<const svalue *> &inputs);
|
|
const svalue *
|
|
get_or_create_const_fn_result_svalue (tree type,
|
|
tree fndecl,
|
|
const vec<const svalue *> &inputs);
|
|
|
|
const svalue *maybe_get_char_from_string_cst (tree string_cst,
|
|
tree byte_offset_cst);
|
|
|
|
/* Dynamically-allocated svalue instances.
|
|
The number of these within the analysis can grow arbitrarily.
|
|
They are still owned by the manager. */
|
|
const svalue *create_unique_svalue (tree type);
|
|
|
|
/* region consolidation. */
|
|
const stack_region * get_stack_region () const { return &m_stack_region; }
|
|
const heap_region *get_heap_region () const { return &m_heap_region; }
|
|
const code_region *get_code_region () const { return &m_code_region; }
|
|
const globals_region *get_globals_region () const
|
|
{
|
|
return &m_globals_region;
|
|
}
|
|
const function_region *get_region_for_fndecl (tree fndecl);
|
|
const label_region *get_region_for_label (tree label);
|
|
const decl_region *get_region_for_global (tree expr);
|
|
const region *get_field_region (const region *parent, tree field);
|
|
const region *get_element_region (const region *parent,
|
|
tree element_type,
|
|
const svalue *index);
|
|
const region *get_offset_region (const region *parent,
|
|
tree type,
|
|
const svalue *byte_offset);
|
|
const region *get_sized_region (const region *parent,
|
|
tree type,
|
|
const svalue *byte_size_sval);
|
|
const region *get_cast_region (const region *original_region,
|
|
tree type);
|
|
const frame_region *get_frame_region (const frame_region *calling_frame,
|
|
function *fun);
|
|
const region *get_symbolic_region (const svalue *sval);
|
|
const string_region *get_region_for_string (tree string_cst);
|
|
const region *get_bit_range (const region *parent, tree type,
|
|
const bit_range &bits);
|
|
|
|
const region *get_unknown_symbolic_region (tree region_type);
|
|
|
|
const region *
|
|
get_region_for_unexpected_tree_code (region_model_context *ctxt,
|
|
tree t,
|
|
const dump_location_t &loc);
|
|
|
|
unsigned alloc_region_id () { return m_next_region_id++; }
|
|
|
|
store_manager *get_store_manager () { return &m_store_mgr; }
|
|
bounded_ranges_manager *get_range_manager () const { return m_range_mgr; }
|
|
|
|
/* Dynamically-allocated region instances.
|
|
The number of these within the analysis can grow arbitrarily.
|
|
They are still owned by the manager. */
|
|
const region *create_region_for_heap_alloc ();
|
|
const region *create_region_for_alloca (const frame_region *frame);
|
|
|
|
void log_stats (logger *logger, bool show_objs) const;
|
|
|
|
void begin_checking_feasibility (void) { m_checking_feasibility = true; }
|
|
void end_checking_feasibility (void) { m_checking_feasibility = false; }
|
|
|
|
logger *get_logger () const { return m_logger; }
|
|
|
|
void dump_untracked_regions () const;
|
|
|
|
private:
|
|
bool too_complex_p (const complexity &c) const;
|
|
bool reject_if_too_complex (svalue *sval);
|
|
|
|
const svalue *maybe_fold_unaryop (tree type, enum tree_code op,
|
|
const svalue *arg);
|
|
const svalue *maybe_fold_binop (tree type, enum tree_code op,
|
|
const svalue *arg0, const svalue *arg1);
|
|
const svalue *maybe_fold_sub_svalue (tree type,
|
|
const svalue *parent_svalue,
|
|
const region *subregion);
|
|
const svalue *maybe_fold_repeated_svalue (tree type,
|
|
const svalue *outer_size,
|
|
const svalue *inner_svalue);
|
|
const svalue *maybe_fold_bits_within_svalue (tree type,
|
|
const bit_range &bits,
|
|
const svalue *inner_svalue);
|
|
const svalue *maybe_undo_optimize_bit_field_compare (tree type,
|
|
const compound_svalue *compound_sval,
|
|
tree cst, const svalue *arg1);
|
|
const svalue *maybe_fold_asm_output_svalue (tree type,
|
|
const vec<const svalue *> &inputs);
|
|
|
|
logger *m_logger;
|
|
|
|
unsigned m_next_region_id;
|
|
root_region m_root_region;
|
|
stack_region m_stack_region;
|
|
heap_region m_heap_region;
|
|
|
|
/* svalue consolidation. */
|
|
typedef hash_map<tree, constant_svalue *> constants_map_t;
|
|
constants_map_t m_constants_map;
|
|
|
|
typedef hash_map<tree, unknown_svalue *> unknowns_map_t;
|
|
unknowns_map_t m_unknowns_map;
|
|
const unknown_svalue *m_unknown_NULL;
|
|
|
|
typedef hash_map<poisoned_svalue::key_t,
|
|
poisoned_svalue *> poisoned_values_map_t;
|
|
poisoned_values_map_t m_poisoned_values_map;
|
|
|
|
typedef hash_map<setjmp_svalue::key_t,
|
|
setjmp_svalue *> setjmp_values_map_t;
|
|
setjmp_values_map_t m_setjmp_values_map;
|
|
|
|
typedef hash_map<const region *, initial_svalue *> initial_values_map_t;
|
|
initial_values_map_t m_initial_values_map;
|
|
|
|
typedef hash_map<region_svalue::key_t, region_svalue *> pointer_values_map_t;
|
|
pointer_values_map_t m_pointer_values_map;
|
|
|
|
typedef hash_map<unaryop_svalue::key_t,
|
|
unaryop_svalue *> unaryop_values_map_t;
|
|
unaryop_values_map_t m_unaryop_values_map;
|
|
|
|
typedef hash_map<binop_svalue::key_t, binop_svalue *> binop_values_map_t;
|
|
binop_values_map_t m_binop_values_map;
|
|
|
|
typedef hash_map<sub_svalue::key_t, sub_svalue *> sub_values_map_t;
|
|
sub_values_map_t m_sub_values_map;
|
|
|
|
typedef hash_map<repeated_svalue::key_t,
|
|
repeated_svalue *> repeated_values_map_t;
|
|
repeated_values_map_t m_repeated_values_map;
|
|
|
|
typedef hash_map<bits_within_svalue::key_t,
|
|
bits_within_svalue *> bits_within_values_map_t;
|
|
bits_within_values_map_t m_bits_within_values_map;
|
|
|
|
typedef hash_map<const svalue *,
|
|
unmergeable_svalue *> unmergeable_values_map_t;
|
|
unmergeable_values_map_t m_unmergeable_values_map;
|
|
|
|
typedef hash_map<widening_svalue::key_t,
|
|
widening_svalue */*,
|
|
widening_svalue::key_t::hash_map_traits*/>
|
|
widening_values_map_t;
|
|
widening_values_map_t m_widening_values_map;
|
|
|
|
typedef hash_map<compound_svalue::key_t,
|
|
compound_svalue *> compound_values_map_t;
|
|
compound_values_map_t m_compound_values_map;
|
|
|
|
typedef hash_map<conjured_svalue::key_t,
|
|
conjured_svalue *> conjured_values_map_t;
|
|
conjured_values_map_t m_conjured_values_map;
|
|
|
|
typedef hash_map<asm_output_svalue::key_t,
|
|
asm_output_svalue *> asm_output_values_map_t;
|
|
asm_output_values_map_t m_asm_output_values_map;
|
|
|
|
typedef hash_map<const_fn_result_svalue::key_t,
|
|
const_fn_result_svalue *> const_fn_result_values_map_t;
|
|
const_fn_result_values_map_t m_const_fn_result_values_map;
|
|
|
|
bool m_checking_feasibility;
|
|
|
|
/* "Dynamically-allocated" svalue instances.
|
|
The number of these within the analysis can grow arbitrarily.
|
|
They are still owned by the manager. */
|
|
auto_delete_vec<svalue> m_managed_dynamic_svalues;
|
|
|
|
/* Maximum complexity of svalues that weren't rejected. */
|
|
complexity m_max_complexity;
|
|
|
|
/* region consolidation. */
|
|
|
|
code_region m_code_region;
|
|
typedef hash_map<tree, function_region *> fndecls_map_t;
|
|
typedef fndecls_map_t::iterator fndecls_iterator_t;
|
|
fndecls_map_t m_fndecls_map;
|
|
|
|
typedef hash_map<tree, label_region *> labels_map_t;
|
|
typedef labels_map_t::iterator labels_iterator_t;
|
|
labels_map_t m_labels_map;
|
|
|
|
globals_region m_globals_region;
|
|
typedef hash_map<tree, decl_region *> globals_map_t;
|
|
typedef globals_map_t::iterator globals_iterator_t;
|
|
globals_map_t m_globals_map;
|
|
|
|
consolidation_map<field_region> m_field_regions;
|
|
consolidation_map<element_region> m_element_regions;
|
|
consolidation_map<offset_region> m_offset_regions;
|
|
consolidation_map<sized_region> m_sized_regions;
|
|
consolidation_map<cast_region> m_cast_regions;
|
|
consolidation_map<frame_region> m_frame_regions;
|
|
consolidation_map<symbolic_region> m_symbolic_regions;
|
|
|
|
typedef hash_map<tree, string_region *> string_map_t;
|
|
string_map_t m_string_map;
|
|
|
|
consolidation_map<bit_range_region> m_bit_range_regions;
|
|
|
|
store_manager m_store_mgr;
|
|
|
|
bounded_ranges_manager *m_range_mgr;
|
|
|
|
/* "Dynamically-allocated" region instances.
|
|
The number of these within the analysis can grow arbitrarily.
|
|
They are still owned by the manager. */
|
|
auto_delete_vec<region> m_managed_dynamic_regions;
|
|
};
|
|
|
|
struct append_regions_cb_data;
|
|
|
|
/* Helper class for handling calls to functions with known behavior.
|
|
Implemented in region-model-impl-calls.c. */
|
|
|
|
class call_details
|
|
{
|
|
public:
|
|
call_details (const gcall *call, region_model *model,
|
|
region_model_context *ctxt);
|
|
|
|
region_model_manager *get_manager () const;
|
|
region_model_context *get_ctxt () const { return m_ctxt; }
|
|
uncertainty_t *get_uncertainty () const;
|
|
tree get_lhs_type () const { return m_lhs_type; }
|
|
const region *get_lhs_region () const { return m_lhs_region; }
|
|
|
|
bool maybe_set_lhs (const svalue *result) const;
|
|
|
|
unsigned num_args () const;
|
|
|
|
const gcall *get_call_stmt () const { return m_call; }
|
|
|
|
tree get_arg_tree (unsigned idx) const;
|
|
tree get_arg_type (unsigned idx) const;
|
|
const svalue *get_arg_svalue (unsigned idx) const;
|
|
const char *get_arg_string_literal (unsigned idx) const;
|
|
|
|
tree get_fndecl_for_call () const;
|
|
|
|
void dump_to_pp (pretty_printer *pp, bool simple) const;
|
|
void dump (bool simple) const;
|
|
|
|
const svalue *get_or_create_conjured_svalue (const region *) const;
|
|
|
|
private:
|
|
const gcall *m_call;
|
|
region_model *m_model;
|
|
region_model_context *m_ctxt;
|
|
tree m_lhs_type;
|
|
const region *m_lhs_region;
|
|
};
|
|
|
|
/* A region_model encapsulates a representation of the state of memory, with
|
|
a tree of regions, along with their associated values.
|
|
The representation is graph-like because values can be pointers to
|
|
regions.
|
|
It also stores:
|
|
- a constraint_manager, capturing relationships between the values, and
|
|
- dynamic extents, mapping dynamically-allocated regions to svalues (their
|
|
capacities). */
|
|
|
|
class region_model
|
|
{
|
|
public:
|
|
typedef region_to_value_map dynamic_extents_t;
|
|
|
|
region_model (region_model_manager *mgr);
|
|
region_model (const region_model &other);
|
|
~region_model ();
|
|
region_model &operator= (const region_model &other);
|
|
|
|
bool operator== (const region_model &other) const;
|
|
bool operator!= (const region_model &other) const
|
|
{
|
|
return !(*this == other);
|
|
}
|
|
|
|
hashval_t hash () const;
|
|
|
|
void print (pretty_printer *pp) const;
|
|
|
|
void dump_to_pp (pretty_printer *pp, bool simple, bool multiline) const;
|
|
void dump (FILE *fp, bool simple, bool multiline) const;
|
|
void dump (bool simple) const;
|
|
|
|
void debug () const;
|
|
|
|
void validate () const;
|
|
|
|
void canonicalize ();
|
|
bool canonicalized_p () const;
|
|
|
|
void
|
|
on_stmt_pre (const gimple *stmt,
|
|
bool *out_terminate_path,
|
|
bool *out_unknown_side_effects,
|
|
region_model_context *ctxt);
|
|
|
|
void on_assignment (const gassign *stmt, region_model_context *ctxt);
|
|
const svalue *get_gassign_result (const gassign *assign,
|
|
region_model_context *ctxt);
|
|
void on_asm_stmt (const gasm *asm_stmt, region_model_context *ctxt);
|
|
bool on_call_pre (const gcall *stmt, region_model_context *ctxt,
|
|
bool *out_terminate_path);
|
|
void on_call_post (const gcall *stmt,
|
|
bool unknown_side_effects,
|
|
region_model_context *ctxt);
|
|
|
|
void purge_state_involving (const svalue *sval, region_model_context *ctxt);
|
|
|
|
/* Specific handling for on_call_pre. */
|
|
void impl_call_alloca (const call_details &cd);
|
|
void impl_call_analyzer_describe (const gcall *call,
|
|
region_model_context *ctxt);
|
|
void impl_call_analyzer_dump_capacity (const gcall *call,
|
|
region_model_context *ctxt);
|
|
void impl_call_analyzer_dump_escaped (const gcall *call);
|
|
void impl_call_analyzer_eval (const gcall *call,
|
|
region_model_context *ctxt);
|
|
void impl_call_builtin_expect (const call_details &cd);
|
|
void impl_call_calloc (const call_details &cd);
|
|
bool impl_call_error (const call_details &cd, unsigned min_args,
|
|
bool *out_terminate_path);
|
|
void impl_call_fgets (const call_details &cd);
|
|
void impl_call_fread (const call_details &cd);
|
|
void impl_call_free (const call_details &cd);
|
|
void impl_call_malloc (const call_details &cd);
|
|
void impl_call_memcpy (const call_details &cd);
|
|
void impl_call_memset (const call_details &cd);
|
|
void impl_call_realloc (const call_details &cd);
|
|
void impl_call_strchr (const call_details &cd);
|
|
void impl_call_strcpy (const call_details &cd);
|
|
void impl_call_strlen (const call_details &cd);
|
|
void impl_call_operator_new (const call_details &cd);
|
|
void impl_call_operator_delete (const call_details &cd);
|
|
void impl_deallocation_call (const call_details &cd);
|
|
|
|
void handle_unrecognized_call (const gcall *call,
|
|
region_model_context *ctxt);
|
|
void get_reachable_svalues (svalue_set *out,
|
|
const svalue *extra_sval,
|
|
const uncertainty_t *uncertainty);
|
|
|
|
void on_return (const greturn *stmt, region_model_context *ctxt);
|
|
void on_setjmp (const gcall *stmt, const exploded_node *enode,
|
|
region_model_context *ctxt);
|
|
void on_longjmp (const gcall *longjmp_call, const gcall *setjmp_call,
|
|
int setjmp_stack_depth, region_model_context *ctxt);
|
|
|
|
void update_for_phis (const supernode *snode,
|
|
const cfg_superedge *last_cfg_superedge,
|
|
region_model_context *ctxt);
|
|
|
|
void handle_phi (const gphi *phi, tree lhs, tree rhs,
|
|
const region_model &old_state,
|
|
region_model_context *ctxt);
|
|
|
|
bool maybe_update_for_edge (const superedge &edge,
|
|
const gimple *last_stmt,
|
|
region_model_context *ctxt,
|
|
rejected_constraint **out);
|
|
|
|
void update_for_gcall (const gcall *call_stmt,
|
|
region_model_context *ctxt,
|
|
function *callee = NULL);
|
|
|
|
void update_for_return_gcall (const gcall *call_stmt,
|
|
region_model_context *ctxt);
|
|
|
|
const region *push_frame (function *fun, const vec<const svalue *> *arg_sids,
|
|
region_model_context *ctxt);
|
|
const frame_region *get_current_frame () const { return m_current_frame; }
|
|
function * get_current_function () const;
|
|
void pop_frame (tree result_lvalue,
|
|
const svalue **out_result,
|
|
region_model_context *ctxt);
|
|
int get_stack_depth () const;
|
|
const frame_region *get_frame_at_index (int index) const;
|
|
|
|
const region *get_lvalue (path_var pv, region_model_context *ctxt) const;
|
|
const region *get_lvalue (tree expr, region_model_context *ctxt) const;
|
|
const svalue *get_rvalue (path_var pv, region_model_context *ctxt) const;
|
|
const svalue *get_rvalue (tree expr, region_model_context *ctxt) const;
|
|
|
|
const region *deref_rvalue (const svalue *ptr_sval, tree ptr_tree,
|
|
region_model_context *ctxt) const;
|
|
|
|
const svalue *get_rvalue_for_bits (tree type,
|
|
const region *reg,
|
|
const bit_range &bits,
|
|
region_model_context *ctxt) const;
|
|
|
|
void set_value (const region *lhs_reg, const svalue *rhs_sval,
|
|
region_model_context *ctxt);
|
|
void set_value (tree lhs, tree rhs, region_model_context *ctxt);
|
|
void clobber_region (const region *reg);
|
|
void purge_region (const region *reg);
|
|
void fill_region (const region *reg, const svalue *sval);
|
|
void zero_fill_region (const region *reg);
|
|
void mark_region_as_unknown (const region *reg, uncertainty_t *uncertainty);
|
|
|
|
tristate eval_condition (const svalue *lhs,
|
|
enum tree_code op,
|
|
const svalue *rhs) const;
|
|
tristate eval_condition_without_cm (const svalue *lhs,
|
|
enum tree_code op,
|
|
const svalue *rhs) const;
|
|
tristate compare_initial_and_pointer (const initial_svalue *init,
|
|
const region_svalue *ptr) const;
|
|
tristate eval_condition (tree lhs,
|
|
enum tree_code op,
|
|
tree rhs,
|
|
region_model_context *ctxt);
|
|
bool add_constraint (tree lhs, enum tree_code op, tree rhs,
|
|
region_model_context *ctxt);
|
|
bool add_constraint (tree lhs, enum tree_code op, tree rhs,
|
|
region_model_context *ctxt,
|
|
rejected_constraint **out);
|
|
|
|
const region *create_region_for_heap_alloc (const svalue *size_in_bytes,
|
|
region_model_context *ctxt);
|
|
const region *create_region_for_alloca (const svalue *size_in_bytes,
|
|
region_model_context *ctxt);
|
|
|
|
tree get_representative_tree (const svalue *sval) const;
|
|
path_var
|
|
get_representative_path_var (const svalue *sval,
|
|
svalue_set *visited) const;
|
|
path_var
|
|
get_representative_path_var (const region *reg,
|
|
svalue_set *visited) const;
|
|
|
|
/* For selftests. */
|
|
constraint_manager *get_constraints ()
|
|
{
|
|
return m_constraints;
|
|
}
|
|
|
|
store *get_store () { return &m_store; }
|
|
const store *get_store () const { return &m_store; }
|
|
|
|
const dynamic_extents_t &
|
|
get_dynamic_extents () const
|
|
{
|
|
return m_dynamic_extents;
|
|
}
|
|
const svalue *get_dynamic_extents (const region *reg) const;
|
|
void set_dynamic_extents (const region *reg,
|
|
const svalue *size_in_bytes,
|
|
region_model_context *ctxt);
|
|
void unset_dynamic_extents (const region *reg);
|
|
|
|
region_model_manager *get_manager () const { return m_mgr; }
|
|
bounded_ranges_manager *get_range_manager () const
|
|
{
|
|
return m_mgr->get_range_manager ();
|
|
}
|
|
|
|
void unbind_region_and_descendents (const region *reg,
|
|
enum poison_kind pkind);
|
|
|
|
bool can_merge_with_p (const region_model &other_model,
|
|
const program_point &point,
|
|
region_model *out_model,
|
|
const extrinsic_state *ext_state = NULL,
|
|
const program_state *state_a = NULL,
|
|
const program_state *state_b = NULL) const;
|
|
|
|
tree get_fndecl_for_call (const gcall *call,
|
|
region_model_context *ctxt);
|
|
|
|
void get_regions_for_current_frame (auto_vec<const decl_region *> *out) const;
|
|
static void append_regions_cb (const region *base_reg,
|
|
struct append_regions_cb_data *data);
|
|
|
|
const svalue *get_store_value (const region *reg,
|
|
region_model_context *ctxt) const;
|
|
|
|
bool region_exists_p (const region *reg) const;
|
|
|
|
void loop_replay_fixup (const region_model *dst_state);
|
|
|
|
const svalue *get_capacity (const region *reg) const;
|
|
|
|
/* Implemented in sm-malloc.cc */
|
|
void on_realloc_with_move (const call_details &cd,
|
|
const svalue *old_ptr_sval,
|
|
const svalue *new_ptr_sval);
|
|
|
|
private:
|
|
const region *get_lvalue_1 (path_var pv, region_model_context *ctxt) const;
|
|
const svalue *get_rvalue_1 (path_var pv, region_model_context *ctxt) const;
|
|
|
|
path_var
|
|
get_representative_path_var_1 (const svalue *sval,
|
|
svalue_set *visited) const;
|
|
path_var
|
|
get_representative_path_var_1 (const region *reg,
|
|
svalue_set *visited) const;
|
|
|
|
bool add_constraint (const svalue *lhs,
|
|
enum tree_code op,
|
|
const svalue *rhs,
|
|
region_model_context *ctxt);
|
|
bool add_constraints_from_binop (const svalue *outer_lhs,
|
|
enum tree_code outer_op,
|
|
const svalue *outer_rhs,
|
|
bool *out,
|
|
region_model_context *ctxt);
|
|
|
|
void update_for_call_superedge (const call_superedge &call_edge,
|
|
region_model_context *ctxt);
|
|
void update_for_return_superedge (const return_superedge &return_edge,
|
|
region_model_context *ctxt);
|
|
void update_for_call_summary (const callgraph_superedge &cg_sedge,
|
|
region_model_context *ctxt);
|
|
bool apply_constraints_for_gcond (const cfg_superedge &edge,
|
|
const gcond *cond_stmt,
|
|
region_model_context *ctxt,
|
|
rejected_constraint **out);
|
|
bool apply_constraints_for_gswitch (const switch_cfg_superedge &edge,
|
|
const gswitch *switch_stmt,
|
|
region_model_context *ctxt,
|
|
rejected_constraint **out);
|
|
bool apply_constraints_for_exception (const gimple *last_stmt,
|
|
region_model_context *ctxt,
|
|
rejected_constraint **out);
|
|
|
|
int poison_any_pointers_to_descendents (const region *reg,
|
|
enum poison_kind pkind);
|
|
|
|
void on_top_level_param (tree param, region_model_context *ctxt);
|
|
|
|
bool called_from_main_p () const;
|
|
const svalue *get_initial_value_for_global (const region *reg) const;
|
|
|
|
const svalue *check_for_poison (const svalue *sval,
|
|
tree expr,
|
|
region_model_context *ctxt) const;
|
|
const region * get_region_for_poisoned_expr (tree expr) const;
|
|
|
|
void check_dynamic_size_for_taint (enum memory_space mem_space,
|
|
const svalue *size_in_bytes,
|
|
region_model_context *ctxt) const;
|
|
|
|
void check_region_for_taint (const region *reg,
|
|
enum access_direction dir,
|
|
region_model_context *ctxt) const;
|
|
|
|
void check_for_writable_region (const region* dest_reg,
|
|
region_model_context *ctxt) const;
|
|
void check_region_access (const region *reg,
|
|
enum access_direction dir,
|
|
region_model_context *ctxt) const;
|
|
void check_region_for_write (const region *dest_reg,
|
|
region_model_context *ctxt) const;
|
|
void check_region_for_read (const region *src_reg,
|
|
region_model_context *ctxt) const;
|
|
|
|
void check_call_args (const call_details &cd) const;
|
|
void check_external_function_for_access_attr (const gcall *call,
|
|
tree callee_fndecl,
|
|
region_model_context *ctxt) const;
|
|
|
|
/* Storing this here to avoid passing it around everywhere. */
|
|
region_model_manager *const m_mgr;
|
|
|
|
store m_store;
|
|
|
|
constraint_manager *m_constraints; // TODO: embed, rather than dynalloc?
|
|
|
|
const frame_region *m_current_frame;
|
|
|
|
/* Map from base region to size in bytes, for tracking the sizes of
|
|
dynamically-allocated regions.
|
|
This is part of the region_model rather than the region to allow for
|
|
memory regions to be resized (e.g. by realloc). */
|
|
dynamic_extents_t m_dynamic_extents;
|
|
};
|
|
|
|
/* Some region_model activity could lead to warnings (e.g. attempts to use an
|
|
uninitialized value). This abstract base class encapsulates an interface
|
|
for the region model to use when emitting such warnings.
|
|
|
|
Having this as an abstract base class allows us to support the various
|
|
operations needed by program_state in the analyzer within region_model,
|
|
whilst keeping them somewhat modularized. */
|
|
|
|
class region_model_context
|
|
{
|
|
public:
|
|
/* Hook for clients to store pending diagnostics.
|
|
Return true if the diagnostic was stored, or false if it was deleted. */
|
|
virtual bool warn (pending_diagnostic *d) = 0;
|
|
|
|
/* Hook for clients to add a note to the last previously stored pending diagnostic.
|
|
Takes ownership of the pending_node (or deletes it). */
|
|
virtual void add_note (pending_note *pn) = 0;
|
|
|
|
/* Hook for clients to be notified when an SVAL that was reachable
|
|
in a previous state is no longer live, so that clients can emit warnings
|
|
about leaks. */
|
|
virtual void on_svalue_leak (const svalue *sval) = 0;
|
|
|
|
/* Hook for clients to be notified when the set of explicitly live
|
|
svalues changes, so that they can purge state relating to dead
|
|
svalues. */
|
|
virtual void on_liveness_change (const svalue_set &live_svalues,
|
|
const region_model *model) = 0;
|
|
|
|
virtual logger *get_logger () = 0;
|
|
|
|
/* Hook for clients to be notified when the condition
|
|
"LHS OP RHS" is added to the region model.
|
|
This exists so that state machines can detect tests on edges,
|
|
and use them to trigger sm-state transitions (e.g. transitions due
|
|
to ptrs becoming known to be NULL or non-NULL, rather than just
|
|
"unchecked") */
|
|
virtual void on_condition (const svalue *lhs,
|
|
enum tree_code op,
|
|
const svalue *rhs) = 0;
|
|
|
|
/* Hooks for clients to be notified when an unknown change happens
|
|
to SVAL (in response to a call to an unknown function). */
|
|
virtual void on_unknown_change (const svalue *sval, bool is_mutable) = 0;
|
|
|
|
/* Hooks for clients to be notified when a phi node is handled,
|
|
where RHS is the pertinent argument. */
|
|
virtual void on_phi (const gphi *phi, tree rhs) = 0;
|
|
|
|
/* Hooks for clients to be notified when the region model doesn't
|
|
know how to handle the tree code of T at LOC. */
|
|
virtual void on_unexpected_tree_code (tree t,
|
|
const dump_location_t &loc) = 0;
|
|
|
|
/* Hook for clients to be notified when a function_decl escapes. */
|
|
virtual void on_escaped_function (tree fndecl) = 0;
|
|
|
|
virtual uncertainty_t *get_uncertainty () = 0;
|
|
|
|
/* Hook for clients to purge state involving SVAL. */
|
|
virtual void purge_state_involving (const svalue *sval) = 0;
|
|
|
|
/* Hook for clients to split state with a non-standard path.
|
|
Take ownership of INFO. */
|
|
virtual void bifurcate (custom_edge_info *info) = 0;
|
|
|
|
/* Hook for clients to terminate the standard path. */
|
|
virtual void terminate_path () = 0;
|
|
|
|
virtual const extrinsic_state *get_ext_state () const = 0;
|
|
|
|
/* Hook for clients to access the "malloc" state machine in
|
|
any underlying program_state. */
|
|
virtual bool get_malloc_map (sm_state_map **out_smap,
|
|
const state_machine **out_sm,
|
|
unsigned *out_sm_idx) = 0;
|
|
/* Likewise for the "taint" state machine. */
|
|
virtual bool get_taint_map (sm_state_map **out_smap,
|
|
const state_machine **out_sm,
|
|
unsigned *out_sm_idx) = 0;
|
|
|
|
/* Get the current statement, if any. */
|
|
virtual const gimple *get_stmt () const = 0;
|
|
};
|
|
|
|
/* A "do nothing" subclass of region_model_context. */
|
|
|
|
class noop_region_model_context : public region_model_context
|
|
{
|
|
public:
|
|
bool warn (pending_diagnostic *) OVERRIDE { return false; }
|
|
void add_note (pending_note *pn) OVERRIDE;
|
|
void on_svalue_leak (const svalue *) OVERRIDE {}
|
|
void on_liveness_change (const svalue_set &,
|
|
const region_model *) OVERRIDE {}
|
|
logger *get_logger () OVERRIDE { return NULL; }
|
|
void on_condition (const svalue *lhs ATTRIBUTE_UNUSED,
|
|
enum tree_code op ATTRIBUTE_UNUSED,
|
|
const svalue *rhs ATTRIBUTE_UNUSED) OVERRIDE
|
|
{
|
|
}
|
|
void on_unknown_change (const svalue *sval ATTRIBUTE_UNUSED,
|
|
bool is_mutable ATTRIBUTE_UNUSED) OVERRIDE
|
|
{
|
|
}
|
|
void on_phi (const gphi *phi ATTRIBUTE_UNUSED,
|
|
tree rhs ATTRIBUTE_UNUSED) OVERRIDE
|
|
{
|
|
}
|
|
void on_unexpected_tree_code (tree, const dump_location_t &) OVERRIDE {}
|
|
|
|
void on_escaped_function (tree) OVERRIDE {}
|
|
|
|
uncertainty_t *get_uncertainty () OVERRIDE { return NULL; }
|
|
|
|
void purge_state_involving (const svalue *sval ATTRIBUTE_UNUSED) OVERRIDE {}
|
|
|
|
void bifurcate (custom_edge_info *info) OVERRIDE;
|
|
void terminate_path () OVERRIDE;
|
|
|
|
const extrinsic_state *get_ext_state () const OVERRIDE { return NULL; }
|
|
|
|
bool get_malloc_map (sm_state_map **,
|
|
const state_machine **,
|
|
unsigned *) OVERRIDE
|
|
{
|
|
return false;
|
|
}
|
|
bool get_taint_map (sm_state_map **,
|
|
const state_machine **,
|
|
unsigned *) OVERRIDE
|
|
{
|
|
return false;
|
|
}
|
|
|
|
const gimple *get_stmt () const OVERRIDE { return NULL; }
|
|
};
|
|
|
|
/* A subclass of region_model_context for determining if operations fail
|
|
e.g. "can we generate a region for the lvalue of EXPR?". */
|
|
|
|
class tentative_region_model_context : public noop_region_model_context
|
|
{
|
|
public:
|
|
tentative_region_model_context () : m_num_unexpected_codes (0) {}
|
|
|
|
void on_unexpected_tree_code (tree, const dump_location_t &)
|
|
FINAL OVERRIDE
|
|
{
|
|
m_num_unexpected_codes++;
|
|
}
|
|
|
|
bool had_errors_p () const { return m_num_unexpected_codes > 0; }
|
|
|
|
private:
|
|
int m_num_unexpected_codes;
|
|
};
|
|
|
|
/* Subclass of region_model_context that wraps another context, allowing
|
|
for extra code to be added to the various hooks. */
|
|
|
|
class region_model_context_decorator : public region_model_context
|
|
{
|
|
public:
|
|
bool warn (pending_diagnostic *d) OVERRIDE
|
|
{
|
|
return m_inner->warn (d);
|
|
}
|
|
|
|
void add_note (pending_note *pn) OVERRIDE
|
|
{
|
|
m_inner->add_note (pn);
|
|
}
|
|
|
|
void on_svalue_leak (const svalue *sval) OVERRIDE
|
|
{
|
|
m_inner->on_svalue_leak (sval);
|
|
}
|
|
|
|
void on_liveness_change (const svalue_set &live_svalues,
|
|
const region_model *model) OVERRIDE
|
|
{
|
|
m_inner->on_liveness_change (live_svalues, model);
|
|
}
|
|
|
|
logger *get_logger () OVERRIDE
|
|
{
|
|
return m_inner->get_logger ();
|
|
}
|
|
|
|
void on_condition (const svalue *lhs,
|
|
enum tree_code op,
|
|
const svalue *rhs) OVERRIDE
|
|
{
|
|
m_inner->on_condition (lhs, op, rhs);
|
|
}
|
|
|
|
void on_unknown_change (const svalue *sval, bool is_mutable) OVERRIDE
|
|
{
|
|
m_inner->on_unknown_change (sval, is_mutable);
|
|
}
|
|
|
|
void on_phi (const gphi *phi, tree rhs) OVERRIDE
|
|
{
|
|
m_inner->on_phi (phi, rhs);
|
|
}
|
|
|
|
void on_unexpected_tree_code (tree t,
|
|
const dump_location_t &loc) OVERRIDE
|
|
{
|
|
m_inner->on_unexpected_tree_code (t, loc);
|
|
}
|
|
|
|
void on_escaped_function (tree fndecl) OVERRIDE
|
|
{
|
|
m_inner->on_escaped_function (fndecl);
|
|
}
|
|
|
|
uncertainty_t *get_uncertainty () OVERRIDE
|
|
{
|
|
return m_inner->get_uncertainty ();
|
|
}
|
|
|
|
void purge_state_involving (const svalue *sval) OVERRIDE
|
|
{
|
|
m_inner->purge_state_involving (sval);
|
|
}
|
|
|
|
void bifurcate (custom_edge_info *info) OVERRIDE
|
|
{
|
|
m_inner->bifurcate (info);
|
|
}
|
|
|
|
void terminate_path () OVERRIDE
|
|
{
|
|
m_inner->terminate_path ();
|
|
}
|
|
|
|
const extrinsic_state *get_ext_state () const OVERRIDE
|
|
{
|
|
return m_inner->get_ext_state ();
|
|
}
|
|
|
|
bool get_malloc_map (sm_state_map **out_smap,
|
|
const state_machine **out_sm,
|
|
unsigned *out_sm_idx) OVERRIDE
|
|
{
|
|
return m_inner->get_malloc_map (out_smap, out_sm, out_sm_idx);
|
|
}
|
|
|
|
bool get_taint_map (sm_state_map **out_smap,
|
|
const state_machine **out_sm,
|
|
unsigned *out_sm_idx) OVERRIDE
|
|
{
|
|
return m_inner->get_taint_map (out_smap, out_sm, out_sm_idx);
|
|
}
|
|
|
|
const gimple *get_stmt () const OVERRIDE
|
|
{
|
|
return m_inner->get_stmt ();
|
|
}
|
|
|
|
protected:
|
|
region_model_context_decorator (region_model_context *inner)
|
|
: m_inner (inner)
|
|
{
|
|
gcc_assert (m_inner);
|
|
}
|
|
|
|
region_model_context *m_inner;
|
|
};
|
|
|
|
/* Subclass of region_model_context_decorator that adds a note
|
|
when saving diagnostics. */
|
|
|
|
class note_adding_context : public region_model_context_decorator
|
|
{
|
|
public:
|
|
bool warn (pending_diagnostic *d) OVERRIDE
|
|
{
|
|
if (m_inner->warn (d))
|
|
{
|
|
add_note (make_note ());
|
|
return true;
|
|
}
|
|
else
|
|
return false;
|
|
}
|
|
|
|
/* Hook to make the new note. */
|
|
virtual pending_note *make_note () = 0;
|
|
|
|
protected:
|
|
note_adding_context (region_model_context *inner)
|
|
: region_model_context_decorator (inner)
|
|
{
|
|
}
|
|
};
|
|
|
|
/* A bundle of data for use when attempting to merge two region_model
|
|
instances to make a third. */
|
|
|
|
struct model_merger
|
|
{
|
|
model_merger (const region_model *model_a,
|
|
const region_model *model_b,
|
|
const program_point &point,
|
|
region_model *merged_model,
|
|
const extrinsic_state *ext_state,
|
|
const program_state *state_a,
|
|
const program_state *state_b)
|
|
: m_model_a (model_a), m_model_b (model_b),
|
|
m_point (point),
|
|
m_merged_model (merged_model),
|
|
m_ext_state (ext_state),
|
|
m_state_a (state_a), m_state_b (state_b)
|
|
{
|
|
}
|
|
|
|
void dump_to_pp (pretty_printer *pp, bool simple) const;
|
|
void dump (FILE *fp, bool simple) const;
|
|
void dump (bool simple) const;
|
|
|
|
region_model_manager *get_manager () const
|
|
{
|
|
return m_model_a->get_manager ();
|
|
}
|
|
|
|
bool mergeable_svalue_p (const svalue *) const;
|
|
|
|
const region_model *m_model_a;
|
|
const region_model *m_model_b;
|
|
const program_point &m_point;
|
|
region_model *m_merged_model;
|
|
|
|
const extrinsic_state *m_ext_state;
|
|
const program_state *m_state_a;
|
|
const program_state *m_state_b;
|
|
};
|
|
|
|
/* A record that can (optionally) be written out when
|
|
region_model::add_constraint fails. */
|
|
|
|
class rejected_constraint
|
|
{
|
|
public:
|
|
virtual ~rejected_constraint () {}
|
|
virtual void dump_to_pp (pretty_printer *pp) const = 0;
|
|
|
|
const region_model &get_model () const { return m_model; }
|
|
|
|
protected:
|
|
rejected_constraint (const region_model &model)
|
|
: m_model (model)
|
|
{}
|
|
|
|
region_model m_model;
|
|
};
|
|
|
|
class rejected_op_constraint : public rejected_constraint
|
|
{
|
|
public:
|
|
rejected_op_constraint (const region_model &model,
|
|
tree lhs, enum tree_code op, tree rhs)
|
|
: rejected_constraint (model),
|
|
m_lhs (lhs), m_op (op), m_rhs (rhs)
|
|
{}
|
|
|
|
void dump_to_pp (pretty_printer *pp) const FINAL OVERRIDE;
|
|
|
|
tree m_lhs;
|
|
enum tree_code m_op;
|
|
tree m_rhs;
|
|
};
|
|
|
|
class rejected_ranges_constraint : public rejected_constraint
|
|
{
|
|
public:
|
|
rejected_ranges_constraint (const region_model &model,
|
|
tree expr, const bounded_ranges *ranges)
|
|
: rejected_constraint (model),
|
|
m_expr (expr), m_ranges (ranges)
|
|
{}
|
|
|
|
void dump_to_pp (pretty_printer *pp) const FINAL OVERRIDE;
|
|
|
|
private:
|
|
tree m_expr;
|
|
const bounded_ranges *m_ranges;
|
|
};
|
|
|
|
/* A bundle of state. */
|
|
|
|
class engine
|
|
{
|
|
public:
|
|
engine (const supergraph *sg = NULL, logger *logger = NULL);
|
|
const supergraph *get_supergraph () { return m_sg; }
|
|
region_model_manager *get_model_manager () { return &m_mgr; }
|
|
|
|
void log_stats (logger *logger) const;
|
|
|
|
private:
|
|
const supergraph *m_sg;
|
|
region_model_manager m_mgr;
|
|
};
|
|
|
|
} // namespace ana
|
|
|
|
extern void debug (const region_model &rmodel);
|
|
|
|
namespace ana {
|
|
|
|
#if CHECKING_P
|
|
|
|
namespace selftest {
|
|
|
|
using namespace ::selftest;
|
|
|
|
/* An implementation of region_model_context for use in selftests, which
|
|
stores any pending_diagnostic instances passed to it. */
|
|
|
|
class test_region_model_context : public noop_region_model_context
|
|
{
|
|
public:
|
|
bool warn (pending_diagnostic *d) FINAL OVERRIDE
|
|
{
|
|
m_diagnostics.safe_push (d);
|
|
return true;
|
|
}
|
|
|
|
unsigned get_num_diagnostics () const { return m_diagnostics.length (); }
|
|
|
|
void on_unexpected_tree_code (tree t, const dump_location_t &)
|
|
FINAL OVERRIDE
|
|
{
|
|
internal_error ("unhandled tree code: %qs",
|
|
get_tree_code_name (TREE_CODE (t)));
|
|
}
|
|
|
|
private:
|
|
/* Implicitly delete any diagnostics in the dtor. */
|
|
auto_delete_vec<pending_diagnostic> m_diagnostics;
|
|
};
|
|
|
|
/* Attempt to add the constraint (LHS OP RHS) to MODEL.
|
|
Verify that MODEL remains satisfiable. */
|
|
|
|
#define ADD_SAT_CONSTRAINT(MODEL, LHS, OP, RHS) \
|
|
SELFTEST_BEGIN_STMT \
|
|
bool sat = (MODEL).add_constraint (LHS, OP, RHS, NULL); \
|
|
ASSERT_TRUE (sat); \
|
|
SELFTEST_END_STMT
|
|
|
|
/* Attempt to add the constraint (LHS OP RHS) to MODEL.
|
|
Verify that the result is not satisfiable. */
|
|
|
|
#define ADD_UNSAT_CONSTRAINT(MODEL, LHS, OP, RHS) \
|
|
SELFTEST_BEGIN_STMT \
|
|
bool sat = (MODEL).add_constraint (LHS, OP, RHS, NULL); \
|
|
ASSERT_FALSE (sat); \
|
|
SELFTEST_END_STMT
|
|
|
|
/* Implementation detail of the ASSERT_CONDITION_* macros. */
|
|
|
|
void assert_condition (const location &loc,
|
|
region_model &model,
|
|
const svalue *lhs, tree_code op, const svalue *rhs,
|
|
tristate expected);
|
|
|
|
void assert_condition (const location &loc,
|
|
region_model &model,
|
|
tree lhs, tree_code op, tree rhs,
|
|
tristate expected);
|
|
|
|
/* Assert that REGION_MODEL evaluates the condition "LHS OP RHS"
|
|
as "true". */
|
|
|
|
#define ASSERT_CONDITION_TRUE(REGION_MODEL, LHS, OP, RHS) \
|
|
SELFTEST_BEGIN_STMT \
|
|
assert_condition (SELFTEST_LOCATION, REGION_MODEL, LHS, OP, RHS, \
|
|
tristate (tristate::TS_TRUE)); \
|
|
SELFTEST_END_STMT
|
|
|
|
/* Assert that REGION_MODEL evaluates the condition "LHS OP RHS"
|
|
as "false". */
|
|
|
|
#define ASSERT_CONDITION_FALSE(REGION_MODEL, LHS, OP, RHS) \
|
|
SELFTEST_BEGIN_STMT \
|
|
assert_condition (SELFTEST_LOCATION, REGION_MODEL, LHS, OP, RHS, \
|
|
tristate (tristate::TS_FALSE)); \
|
|
SELFTEST_END_STMT
|
|
|
|
/* Assert that REGION_MODEL evaluates the condition "LHS OP RHS"
|
|
as "unknown". */
|
|
|
|
#define ASSERT_CONDITION_UNKNOWN(REGION_MODEL, LHS, OP, RHS) \
|
|
SELFTEST_BEGIN_STMT \
|
|
assert_condition (SELFTEST_LOCATION, REGION_MODEL, LHS, OP, RHS, \
|
|
tristate (tristate::TS_UNKNOWN)); \
|
|
SELFTEST_END_STMT
|
|
|
|
} /* end of namespace selftest. */
|
|
|
|
#endif /* #if CHECKING_P */
|
|
|
|
} // namespace ana
|
|
|
|
#endif /* GCC_ANALYZER_REGION_MODEL_H */
|