]> git.ipfire.org Git - thirdparty/gcc.git/blobdiff - gcc/analyzer/region-model.h
Update copyright years.
[thirdparty/gcc.git] / gcc / analyzer / region-model.h
index a734f9f7315388e5846e96b8eb3dcaae1fca8308..4729d1546a883b928b849bbf646eb0967423dce5 100644 (file)
@@ -1,5 +1,5 @@
 /* Classes for modeling the state of memory.
-   Copyright (C) 2019-2021 Free Software Foundation, Inc.
+   Copyright (C) 2019-2024 Free Software Foundation, Inc.
    Contributed by David Malcolm <dmalcolm@redhat.com>.
 
 This file is part of GCC.
@@ -26,8 +26,15 @@ along with GCC; see the file COPYING3.  If not see
       (Zhongxing Xu, Ted Kremenek, and Jian Zhang)
      http://lcs.ios.ac.cn/~xuzb/canalyze/memmodel.pdf  */
 
+#include "bitmap.h"
+#include "stringpool.h"
+#include "attribs.h" // for rdwr_map
+#include "selftest.h"
 #include "analyzer/svalue.h"
 #include "analyzer/region.h"
+#include "analyzer/known-function-manager.h"
+#include "analyzer/region-model-manager.h"
+#include "analyzer/pending-diagnostic.h"
 
 using namespace ana;
 
@@ -168,6 +175,8 @@ public:
   void dump_to_pp (pretty_printer *pp, bool simple, bool multiline) const;
   void dump (bool simple) const;
 
+  json::object *to_json () const;
+
   bool can_merge_with_p (const region_to_value_map &other,
                         region_to_value_map *out) const;
 
@@ -189,6 +198,7 @@ struct purge_stats
     m_num_regions (0),
     m_num_equiv_classes (0),
     m_num_constraints (0),
+    m_num_bounded_ranges_constraints (0),
     m_num_client_items (0)
   {}
 
@@ -196,6 +206,7 @@ struct purge_stats
   int m_num_regions;
   int m_num_equiv_classes;
   int m_num_constraints;
+  int m_num_bounded_ranges_constraints;
   int m_num_client_items;
 };
 
@@ -222,283 +233,17 @@ public:
   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 ();
-  ~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 svalue *
-  get_or_create_asm_output_svalue (tree type,
-                                  const gasm *asm_stmt,
-                                  unsigned output_idx,
-                                  const vec<const svalue *> &inputs);
-
-  const svalue *maybe_get_char_from_string_cst (tree string_cst,
-                                               tree byte_offset_cst);
-
-  /* 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_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; }
-
-  /* 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 enable_complexity_check (void) { m_check_complexity = true; }
-  void disable_complexity_check (void) { m_check_complexity = false; }
-
-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);
-
-  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;
-
-  bool m_check_complexity;
-
-  /* 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;
-
-  store_manager m_store_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_ssa_names_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_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;
+struct append_regions_cb_data;
 
-  unsigned num_args () const;
-
-  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;
-
-  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;
-};
+typedef void (*pop_frame_callback) (const region_model *model,
+                                   const region_model *prev_model,
+                                   const svalue *retval,
+                                   region_model_context *ctxt);
 
 /* A region_model encapsulates a representation of the state of memory, with
    a tree of regions, along with their associated values.
@@ -535,6 +280,8 @@ class region_model
 
   void debug () const;
 
+  json::object *to_json () const;
+
   void validate () const;
 
   void canonicalize ();
@@ -542,7 +289,6 @@ class region_model
 
   void
   on_stmt_pre (const gimple *stmt,
-              bool *out_terminate_path,
               bool *out_unknown_side_effects,
               region_model_context *ctxt);
 
@@ -550,39 +296,24 @@ class region_model
   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);
+  bool on_call_pre (const gcall *stmt, region_model_context *ctxt);
   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_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_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);
 
+  const svalue *maybe_get_copy_bounds (const region *src_reg,
+                                      const svalue *num_bytes_sval);
+  void update_for_int_cst_return (const call_details &cd,
+                                 int retval,
+                                 bool unmergeable);
+  void update_for_zero_return (const call_details &cd,
+                              bool unmergeable);
+  void update_for_nonzero_return (const call_details &cd);
+
   void handle_unrecognized_call (const gcall *call,
                                 region_model_context *ctxt);
   void get_reachable_svalues (svalue_set *out,
@@ -601,12 +332,13 @@ class region_model
 
   void handle_phi (const gphi *phi, tree lhs, tree rhs,
                   const region_model &old_state,
+                  hash_set<const svalue *> &svals_changing_meaning,
                   region_model_context *ctxt);
 
   bool maybe_update_for_edge (const superedge &edge,
                              const gimple *last_stmt,
                              region_model_context *ctxt,
-                             rejected_constraint **out);
+                             std::unique_ptr<rejected_constraint> *out);
 
   void update_for_gcall (const gcall *call_stmt,
                          region_model_context *ctxt,
@@ -619,9 +351,10 @@ class region_model
                            region_model_context *ctxt);
   const frame_region *get_current_frame () const { return m_current_frame; }
   function * get_current_function () const;
-  void pop_frame (const region *result_dst,
+  void pop_frame (tree result_lvalue,
                  const svalue **out_result,
-                 region_model_context *ctxt);
+                 region_model_context *ctxt,
+                 bool eval_return_svalue = true);
   int get_stack_depth () const;
   const frame_region *get_frame_at_index (int index) const;
 
@@ -631,7 +364,8 @@ class region_model
   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;
+                             region_model_context *ctxt,
+                             bool add_nonnull_constraint = true) const;
 
   const svalue *get_rvalue_for_bits (tree type,
                                     const region *reg,
@@ -643,34 +377,56 @@ class region_model
   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 fill_region (const region *reg,
+                   const svalue *sval,
+                   region_model_context *ctxt);
+  void zero_fill_region (const region *reg,
+                        region_model_context *ctxt);
+  void write_bytes (const region *dest_reg,
+                   const svalue *num_bytes_sval,
+                   const svalue *sval,
+                   region_model_context *ctxt);
+  const svalue *read_bytes (const region *src_reg,
+                           tree src_ptr_expr,
+                           const svalue *num_bytes_sval,
+                           region_model_context *ctxt) const;
+  void copy_bytes (const region *dest_reg,
+                  const region *src_reg,
+                  tree src_ptr_expr,
+                  const svalue *num_bytes_sval,
+                  region_model_context *ctxt);
   void mark_region_as_unknown (const region *reg, uncertainty_t *uncertainty);
 
-  void copy_region (const region *dst_reg, const region *src_reg,
-                   region_model_context *ctxt);
   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 symbolic_greater_than (const binop_svalue *a,
+                                 const svalue *b) const;
+  tristate structural_equality (const svalue *a, const svalue *b) const;
   tristate eval_condition (tree lhs,
                           enum tree_code op,
                           tree rhs,
-                          region_model_context *ctxt);
+                          region_model_context *ctxt) const;
   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);
+                      std::unique_ptr<rejected_constraint> *out);
+
+       const region *
+       get_or_create_region_for_heap_alloc (const svalue *size_in_bytes,
+                               region_model_context *ctxt,
+                               bool update_state_machine = false,
+                               const call_details *cd = nullptr);
 
-  const region *create_region_for_heap_alloc (const svalue *size_in_bytes);
-  const region *create_region_for_alloca (const svalue *size_in_bytes);
+  const region *create_region_for_alloca (const svalue *size_in_bytes,
+                                         region_model_context *ctxt);
+  void get_referenced_base_regions (auto_bitmap &out_ids) const;
 
   tree get_representative_tree (const svalue *sval) const;
+  tree get_representative_tree (const region *reg) const;
   path_var
   get_representative_path_var (const svalue *sval,
                               svalue_set *visited) const;
@@ -694,28 +450,42 @@ class region_model
   }
   const svalue *get_dynamic_extents (const region *reg) const;
   void set_dynamic_extents (const region *reg,
-                           const svalue *size_in_bytes);
+                           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;
+                        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_ssa_name_regions_for_current_frame
-    (auto_vec<const decl_region *> *out) const;
-  static void append_ssa_names_cb (const region *base_reg,
-                                  struct append_ssa_names_cb_data *data);
+  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;
+  const svalue *get_store_bytes (const region *base_reg,
+                                const byte_range &bytes,
+                                region_model_context *ctxt) const;
+  const svalue *scan_for_null_terminator (const region *reg,
+                                         tree expr,
+                                         const svalue **out_sval,
+                                         region_model_context *ctxt) const;
 
   bool region_exists_p (const region *reg) const;
 
@@ -723,7 +493,79 @@ class region_model
 
   const svalue *get_capacity (const region *reg) const;
 
- private:
+  bool replay_call_summary (call_summary_replay &r,
+                           const region_model &summary);
+
+  void maybe_complain_about_infoleak (const region *dst_reg,
+                                     const svalue *copied_sval,
+                                     const region *src_reg,
+                                     region_model_context *ctxt);
+
+  void set_errno (const call_details &cd);
+
+  /* Implemented in sm-fd.cc  */
+  void mark_as_valid_fd (const svalue *sval, region_model_context *ctxt);
+
+  /* Implemented in sm-malloc.cc  */
+  void on_realloc_with_move (const call_details &cd,
+                            const svalue *old_ptr_sval,
+                            const svalue *new_ptr_sval);
+
+  /* Implemented in sm-malloc.cc.  */
+  void
+  transition_ptr_sval_non_null (region_model_context *ctxt,
+      const svalue *new_ptr_sval);
+
+  /* Implemented in sm-taint.cc.  */
+  void mark_as_tainted (const svalue *sval,
+                       region_model_context *ctxt);
+
+  bool add_constraint (const svalue *lhs,
+                      enum tree_code op,
+                      const svalue *rhs,
+                      region_model_context *ctxt);
+
+  const svalue *check_for_poison (const svalue *sval,
+                                 tree expr,
+                                 const region *src_region,
+                                 region_model_context *ctxt) const;
+
+  void check_region_for_write (const region *dest_reg,
+                              const svalue *sval_hint,
+                              region_model_context *ctxt) const;
+
+  const svalue *
+  check_for_null_terminated_string_arg (const call_details &cd,
+                                       unsigned idx) const;
+  const svalue *
+  check_for_null_terminated_string_arg (const call_details &cd,
+                                       unsigned idx,
+                                       bool include_terminator,
+                                       const svalue **out_sval) const;
+
+  const builtin_known_function *
+  get_builtin_kf (const gcall *call,
+                 region_model_context *ctxt = NULL) const;
+
+  static void
+  register_pop_frame_callback (const pop_frame_callback &callback)
+  {
+    pop_frame_callbacks.safe_push (callback);
+  }
+
+  static void
+  notify_on_pop_frame (const region_model *model,
+                      const region_model *prev_model,
+                      const svalue *retval,
+                      region_model_context *ctxt)
+  {
+    for (auto &callback : pop_frame_callbacks)
+       callback (model, prev_model, retval, ctxt);
+  }
+
+  bool called_from_main_p () const;
+
+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;
 
@@ -734,10 +576,10 @@ class region_model
   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);
+  const known_function *get_known_function (tree fndecl,
+                                           const call_details &cd) const;
+  const known_function *get_known_function (enum internal_fn) const;
+
   bool add_constraints_from_binop (const svalue *outer_lhs,
                                   enum tree_code outer_op,
                                   const svalue *outer_rhs,
@@ -748,42 +590,86 @@ class region_model
                                  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);
+                                   std::unique_ptr<rejected_constraint> *out);
   bool apply_constraints_for_gswitch (const switch_cfg_superedge &edge,
                                      const gswitch *switch_stmt,
                                      region_model_context *ctxt,
-                                     rejected_constraint **out);
+                                     std::unique_ptr<rejected_constraint> *out);
+  bool apply_constraints_for_ggoto (const cfg_superedge &edge,
+                                   const ggoto *goto_stmt,
+                                   region_model_context *ctxt);
   bool apply_constraints_for_exception (const gimple *last_stmt,
                                        region_model_context *ctxt,
-                                       rejected_constraint **out);
+                                       std::unique_ptr<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);
+  void on_top_level_param (tree param,
+                          bool nonnull,
+                          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_dynamic_size_for_floats (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,
+  bool check_region_access (const region *reg,
                            enum access_direction dir,
+                           const svalue *sval_hint,
                            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,
+  bool check_region_for_read (const region *src_reg,
                              region_model_context *ctxt) const;
+  void check_region_size (const region *lhs_reg, const svalue *rhs_sval,
+                         region_model_context *ctxt) const;
+
+  /* Implemented in bounds-checking.cc  */
+  bool check_symbolic_bounds (const region *base_reg,
+                             const svalue *sym_byte_offset,
+                             const svalue *num_bytes_sval,
+                             const svalue *capacity,
+                             enum access_direction dir,
+                             const svalue *sval_hint,
+                             region_model_context *ctxt) const;
+  bool check_region_bounds (const region *reg, enum access_direction dir,
+                           const svalue *sval_hint,
+                           region_model_context *ctxt) const;
 
+  void check_call_args (const call_details &cd) const;
+  void check_call_format_attr (const call_details &cd,
+                              tree format_attr) const;
+  void check_function_attr_access (const gcall *call,
+                                  tree callee_fndecl,
+                                  region_model_context *ctxt,
+                                  rdwr_map &rdwr_idx) const;
+  void check_function_attr_null_terminated_string_arg (const gcall *call,
+                                                      tree callee_fndecl,
+                                                      region_model_context *ctxt,
+                                                      rdwr_map &rdwr_idx);
+  void check_one_function_attr_null_terminated_string_arg (const gcall *call,
+                                                          tree callee_fndecl,
+                                                          region_model_context *ctxt,
+                                                          rdwr_map &rdwr_idx,
+                                                          tree attr);
+  void check_function_attrs (const gcall *call,
+                            tree callee_fndecl,
+                            region_model_context *ctxt);
+
+  static auto_vec<pop_frame_callback> pop_frame_callbacks;
   /* Storing this here to avoid passing it around everywhere.  */
   region_model_manager *const m_mgr;
 
@@ -812,8 +698,18 @@ 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;
+     Return true if the diagnostic was stored, or false if it was deleted.
+     Optionally provide a custom stmt_finder.  */
+  virtual bool warn (std::unique_ptr<pending_diagnostic> d,
+                    const stmt_finder *custom_finder = NULL) = 0;
+
+  /* Hook for clients to add a note to the last previously stored
+     pending diagnostic.  */
+  virtual void add_note (std::unique_ptr<pending_note> pn) = 0;
+
+  /* Hook for clients to add an event to the last previously stored
+     pending diagnostic.  */
+  virtual void add_event (std::unique_ptr<checker_event> event) = 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
@@ -838,6 +734,16 @@ class region_model_context
                             enum tree_code op,
                             const svalue *rhs) = 0;
 
+  /* Hook for clients to be notified when the condition that
+     SVAL is within RANGES is added to the region model.
+     Similar to on_condition, but for use when handling switch statements.
+     RANGES is non-empty.  */
+  virtual void on_bounded_ranges (const svalue &sval,
+                                 const bounded_ranges &ranges) = 0;
+
+  /* Hook for clients to be notified when a frame is popped from the stack.  */
+  virtual void on_pop_frame (const frame_region *) = 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;
@@ -858,6 +764,57 @@ class region_model_context
 
   /* 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.  */
+  virtual void bifurcate (std::unique_ptr<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 a specific state machine in
+     any underlying program_state.  */
+  virtual bool
+  get_state_map_by_name (const char *name,
+                        sm_state_map **out_smap,
+                        const state_machine **out_sm,
+                        unsigned *out_sm_idx,
+                        std::unique_ptr<sm_context> *out_sm_context) = 0;
+
+  /* Precanned ways for clients to access specific state machines.  */
+  bool get_fd_map (sm_state_map **out_smap,
+                  const state_machine **out_sm,
+                  unsigned *out_sm_idx,
+                  std::unique_ptr<sm_context> *out_sm_context)
+  {
+    return get_state_map_by_name ("file-descriptor", out_smap, out_sm,
+                                 out_sm_idx, out_sm_context);
+  }
+  bool get_malloc_map (sm_state_map **out_smap,
+                      const state_machine **out_sm,
+                      unsigned *out_sm_idx)
+  {
+    return get_state_map_by_name ("malloc", out_smap, out_sm, out_sm_idx, NULL);
+  }
+  bool get_taint_map (sm_state_map **out_smap,
+                     const state_machine **out_sm,
+                     unsigned *out_sm_idx)
+  {
+    return get_state_map_by_name ("taint", out_smap, out_sm, out_sm_idx, NULL);
+  }
+
+  bool possibly_tainted_p (const svalue *sval);
+
+  /* Get the current statement, if any.  */
+  virtual const gimple *get_stmt () const = 0;
+
+  virtual const exploded_graph *get_eg () const = 0;
+
+  /* Hooks for detecting infinite loops.  */
+  virtual void maybe_did_work () = 0;
+  virtual bool checking_for_infinite_loop_p () const = 0;
+  virtual void on_unusable_in_infinite_loop () = 0;
 };
 
 /* A "do nothing" subclass of region_model_context.  */
@@ -865,31 +822,59 @@ class region_model_context
 class noop_region_model_context : public region_model_context
 {
 public:
-  bool warn (pending_diagnostic *) OVERRIDE { return false; }
-  void on_svalue_leak (const svalue *) OVERRIDE {}
+  bool warn (std::unique_ptr<pending_diagnostic>,
+            const stmt_finder *) override { return false; }
+  void add_note (std::unique_ptr<pending_note>) override;
+  void add_event (std::unique_ptr<checker_event>) 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; }
+                          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
+                    const svalue *rhs ATTRIBUTE_UNUSED) override
   {
   }
+  void on_bounded_ranges (const svalue &,
+                         const bounded_ranges &) override
+  {
+  }
+  void on_pop_frame (const frame_region *) override {}
   void on_unknown_change (const svalue *sval ATTRIBUTE_UNUSED,
-                         bool is_mutable ATTRIBUTE_UNUSED) OVERRIDE
+                         bool is_mutable ATTRIBUTE_UNUSED) override
   {
   }
   void on_phi (const gphi *phi ATTRIBUTE_UNUSED,
-              tree rhs ATTRIBUTE_UNUSED) OVERRIDE
+              tree rhs ATTRIBUTE_UNUSED) override
   {
   }
-  void on_unexpected_tree_code (tree, const dump_location_t &) 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 on_escaped_function (tree) OVERRIDE {}
+  void purge_state_involving (const svalue *sval ATTRIBUTE_UNUSED) override {}
 
-  uncertainty_t *get_uncertainty () OVERRIDE { return NULL; }
+  void bifurcate (std::unique_ptr<custom_edge_info> info) override;
+  void terminate_path () override;
 
-  void purge_state_involving (const svalue *sval ATTRIBUTE_UNUSED) OVERRIDE {}
+  const extrinsic_state *get_ext_state () const override { return NULL; }
+
+  bool get_state_map_by_name (const char *,
+                             sm_state_map **,
+                             const state_machine **,
+                             unsigned *,
+                             std::unique_ptr<sm_context> *) override
+  {
+    return false;
+  }
+
+  const gimple *get_stmt () const override { return NULL; }
+  const exploded_graph *get_eg () const override { return NULL; }
+  void maybe_did_work () override {}
+  bool checking_for_infinite_loop_p () const override { return false; }
+  void on_unusable_in_infinite_loop () override {}
 };
 
 /* A subclass of region_model_context for determining if operations fail
@@ -901,7 +886,7 @@ public:
   tentative_region_model_context () : m_num_unexpected_codes (0) {}
 
   void on_unexpected_tree_code (tree, const dump_location_t &)
-    FINAL OVERRIDE
+    final override
   {
     m_num_unexpected_codes++;
   }
@@ -912,6 +897,214 @@ 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 (std::unique_ptr<pending_diagnostic> d,
+            const stmt_finder *custom_finder) override
+  {
+    if (m_inner)
+      return m_inner->warn (std::move (d), custom_finder);
+    else
+      return false;
+  }
+
+  void add_note (std::unique_ptr<pending_note> pn) override
+  {
+    if (m_inner)
+      m_inner->add_note (std::move (pn));
+  }
+  void add_event (std::unique_ptr<checker_event> event) override;
+
+  void on_svalue_leak (const svalue *sval) override
+  {
+    if (m_inner)
+      m_inner->on_svalue_leak (sval);
+  }
+
+  void on_liveness_change (const svalue_set &live_svalues,
+                          const region_model *model) override
+  {
+    if (m_inner)
+      m_inner->on_liveness_change (live_svalues, model);
+  }
+
+  logger *get_logger () override
+  {
+    if (m_inner)
+      return m_inner->get_logger ();
+    else
+      return nullptr;
+  }
+
+  void on_condition (const svalue *lhs,
+                    enum tree_code op,
+                    const svalue *rhs) override
+  {
+    if (m_inner)
+      m_inner->on_condition (lhs, op, rhs);
+  }
+
+  void on_bounded_ranges (const svalue &sval,
+                         const bounded_ranges &ranges) override
+  {
+    if (m_inner)
+      m_inner->on_bounded_ranges (sval, ranges);
+  }
+
+  void on_pop_frame (const frame_region *frame_reg) override
+  {
+    if (m_inner)
+      m_inner->on_pop_frame (frame_reg);
+  }
+
+  void on_unknown_change (const svalue *sval, bool is_mutable) override
+  {
+    if (m_inner)
+      m_inner->on_unknown_change (sval, is_mutable);
+  }
+
+  void on_phi (const gphi *phi, tree rhs) override
+  {
+    if (m_inner)
+      m_inner->on_phi (phi, rhs);
+  }
+
+  void on_unexpected_tree_code (tree t,
+                               const dump_location_t &loc) override
+  {
+    if (m_inner)
+      m_inner->on_unexpected_tree_code (t, loc);
+  }
+
+  void on_escaped_function (tree fndecl) override
+  {
+    if (m_inner)
+      m_inner->on_escaped_function (fndecl);
+  }
+
+  uncertainty_t *get_uncertainty () override
+  {
+    if (m_inner)
+      return m_inner->get_uncertainty ();
+    else
+      return nullptr;
+  }
+
+  void purge_state_involving (const svalue *sval) override
+  {
+    if (m_inner)
+      m_inner->purge_state_involving (sval);
+  }
+
+  void bifurcate (std::unique_ptr<custom_edge_info> info) override
+  {
+    if (m_inner)
+      m_inner->bifurcate (std::move (info));
+  }
+
+  void terminate_path () override
+  {
+    if (m_inner)
+      m_inner->terminate_path ();
+  }
+
+  const extrinsic_state *get_ext_state () const override
+  {
+    if (m_inner)
+      return m_inner->get_ext_state ();
+    else
+      return nullptr;
+  }
+
+  bool get_state_map_by_name (const char *name,
+                             sm_state_map **out_smap,
+                             const state_machine **out_sm,
+                             unsigned *out_sm_idx,
+                             std::unique_ptr<sm_context> *out_sm_context)
+    override
+  {
+    if (m_inner)
+      return m_inner->get_state_map_by_name (name, out_smap, out_sm, out_sm_idx,
+                                            out_sm_context);
+    else
+      return false;
+  }
+
+  const gimple *get_stmt () const override
+  {
+    if (m_inner)
+      return m_inner->get_stmt ();
+    else
+      return nullptr;
+  }
+
+  const exploded_graph *get_eg () const override
+  {
+    if (m_inner)
+       return m_inner->get_eg ();
+    else
+       return nullptr;
+  }
+
+  void maybe_did_work () override
+  {
+    if (m_inner)
+      m_inner->maybe_did_work ();
+  }
+
+  bool checking_for_infinite_loop_p () const override
+  {
+    if (m_inner)
+      return m_inner->checking_for_infinite_loop_p ();
+    return false;
+  }
+  void on_unusable_in_infinite_loop () override
+  {
+    if (m_inner)
+      m_inner->on_unusable_in_infinite_loop ();
+  }
+
+protected:
+  region_model_context_decorator (region_model_context *inner)
+  : m_inner (inner)
+  {
+  }
+
+  region_model_context *m_inner;
+};
+
+/* Subclass of region_model_context_decorator with a hook for adding
+   notes/events when saving diagnostics.  */
+
+class annotating_context : public region_model_context_decorator
+{
+public:
+  bool warn (std::unique_ptr<pending_diagnostic> d,
+            const stmt_finder *custom_finder) override
+  {
+    if (m_inner)
+      if (m_inner->warn (std::move (d), custom_finder))
+       {
+         add_annotations ();
+         return true;
+       }
+    return false;
+  }
+
+  /* Hook to add new event(s)/note(s)  */
+  virtual void add_annotations () = 0;
+
+protected:
+  annotating_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.  */
 
@@ -920,10 +1113,15 @@ struct model_merger
   model_merger (const region_model *model_a,
                const region_model *model_b,
                const program_point &point,
-               region_model *merged_model)
+               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_merged_model (merged_model),
+    m_ext_state (ext_state),
+    m_state_a (state_a), m_state_b (state_b)
   {
   }
 
@@ -936,42 +1134,105 @@ struct model_merger
     return m_model_a->get_manager ();
   }
 
+  bool mergeable_svalue_p (const svalue *) const;
+  const function_point &get_function_point () const
+  {
+    return m_point.get_function_point ();
+  }
+
+  void on_widening_reuse (const widening_svalue *widening_sval);
+
   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;
+
+  hash_set<const svalue *> m_svals_changing_meaning;
 };
 
 /* A record that can (optionally) be written out when
    region_model::add_constraint fails.  */
 
-struct rejected_constraint
+class rejected_constraint
 {
-  rejected_constraint (const region_model &model,
-                    tree lhs, enum tree_code op, tree rhs)
-  : m_model (model), m_lhs (lhs), m_op (op), m_rhs (rhs)
-  {}
+public:
+  virtual ~rejected_constraint () {}
+  virtual void dump_to_pp (pretty_printer *pp) const = 0;
 
-  void dump_to_pp (pretty_printer *pp) const;
+  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_default_case : public rejected_constraint
+{
+public:
+  rejected_default_case (const region_model &model)
+  : rejected_constraint (model)
+  {}
+
+  void dump_to_pp (pretty_printer *pp) const final override;
+};
+
+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; }
+  known_function_manager *get_known_function_manager ()
+  {
+    return m_mgr.get_known_function_manager ();
+  }
 
   void log_stats (logger *logger) const;
 
 private:
+  const supergraph *m_sg;
   region_model_manager m_mgr;
-
 };
 
 } // namespace ana
@@ -992,16 +1253,17 @@ using namespace ::selftest;
 class test_region_model_context : public noop_region_model_context
 {
 public:
-  bool warn (pending_diagnostic *d) FINAL OVERRIDE
+  bool warn (std::unique_ptr<pending_diagnostic> d,
+            const stmt_finder *) final override
   {
-    m_diagnostics.safe_push (d);
+    m_diagnostics.safe_push (d.release ());
     return true;
   }
 
   unsigned get_num_diagnostics () const { return m_diagnostics.length (); }
 
   void on_unexpected_tree_code (tree t, const dump_location_t &)
-    FINAL OVERRIDE
+    final override
   {
     internal_error ("unhandled tree code: %qs",
                    get_tree_code_name (TREE_CODE (t)));