/* Concrete implementation of sm_context. Copyright (C) 2019-2026 Free Software Foundation, Inc. Contributed by David Malcolm . 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 . */ #ifndef GCC_ANALYZER_IMPL_SM_CONTEXT #define GCC_ANALYZER_IMPL_SM_CONTEXT namespace ana { /* Concrete implementation of sm_context, wiring it up to the rest of this file. */ class impl_sm_context : public sm_context { public: impl_sm_context (exploded_graph &eg, int sm_idx, const state_machine &sm, exploded_node *enode_for_diag, const program_state *old_state, program_state *new_state, const sm_state_map *old_smap, sm_state_map *new_smap, path_context *path_ctxt, bool unknown_side_effects = false) : sm_context (sm_idx, sm), m_logger (eg.get_logger ()), m_eg (eg), m_enode_for_diag (enode_for_diag), m_old_state (old_state), m_new_state (new_state), m_old_smap (old_smap), m_new_smap (new_smap), m_path_ctxt (path_ctxt), m_unknown_side_effects (unknown_side_effects) { } logger *get_logger () const { return m_logger.get_logger (); } tree get_fndecl_for_call (const gcall &call) final override { impl_region_model_context old_ctxt (m_eg, m_enode_for_diag, nullptr, nullptr, nullptr/*m_enode->get_state ()*/, nullptr, &call); region_model *model = m_new_state->m_region_model; return model->get_fndecl_for_call (call, &old_ctxt); } state_machine::state_t get_state (tree var) final override { logger * const logger = get_logger (); LOG_FUNC (logger); /* Use nullptr ctxt on this get_rvalue call to avoid triggering uninitialized value warnings. */ const svalue *var_old_sval = m_old_state->m_region_model->get_rvalue (var, nullptr); state_machine::state_t current = m_old_smap->get_state (var_old_sval, m_eg.get_ext_state ()); return current; } state_machine::state_t get_state (const svalue *sval) final override { logger * const logger = get_logger (); LOG_FUNC (logger); state_machine::state_t current = m_old_smap->get_state (sval, m_eg.get_ext_state ()); return current; } void set_next_state (tree var, state_machine::state_t to, tree origin) final override { logger * const logger = get_logger (); LOG_FUNC (logger); const svalue *var_new_sval = m_new_state->m_region_model->get_rvalue (var, nullptr); const svalue *origin_new_sval = m_new_state->m_region_model->get_rvalue (origin, nullptr); /* We use the new sval here to avoid issues with uninitialized values. */ state_machine::state_t current = m_old_smap->get_state (var_new_sval, m_eg.get_ext_state ()); if (logger) logger->log ("%s: state transition of %qE: %s -> %s", m_sm.get_name (), var, current->get_name (), to->get_name ()); m_new_smap->set_state (m_new_state->m_region_model, var_new_sval, to, origin_new_sval, m_eg.get_ext_state ()); } void set_next_state (const svalue *sval, state_machine::state_t to, tree origin) final override { logger * const logger = get_logger (); LOG_FUNC (logger); impl_region_model_context old_ctxt (m_eg, m_enode_for_diag, nullptr, nullptr, nullptr/*m_enode->get_state ()*/, nullptr, nullptr); const svalue *origin_new_sval = m_new_state->m_region_model->get_rvalue (origin, nullptr); state_machine::state_t current = m_old_smap->get_state (sval, m_eg.get_ext_state ()); if (logger) { logger->start_log_line (); logger->log_partial ("%s: state transition of ", m_sm.get_name ()); sval->dump_to_pp (logger->get_printer (), true); logger->log_partial (": %s -> %s", current->get_name (), to->get_name ()); logger->end_log_line (); } m_new_smap->set_state (m_new_state->m_region_model, sval, to, origin_new_sval, m_eg.get_ext_state ()); } void warn (tree var, std::unique_ptr d) final override { LOG_FUNC (get_logger ()); gcc_assert (d); const svalue *var_old_sval = m_old_state->m_region_model->get_rvalue (var, nullptr); state_machine::state_t current = (var ? m_old_smap->get_state (var_old_sval, m_eg.get_ext_state ()) : m_old_smap->get_global_state ()); bool terminate_path = d->terminate_path_p (); pending_location ploc (m_enode_for_diag); m_eg.get_diagnostic_manager ().add_diagnostic (&m_sm, std::move (ploc), var, var_old_sval, current, std::move (d)); if (m_path_ctxt && terminate_path && flag_analyzer_suppress_followups) m_path_ctxt->terminate_path (); } void warn (const svalue *sval, std::unique_ptr d) final override { LOG_FUNC (get_logger ()); gcc_assert (d); state_machine::state_t current = (sval ? m_old_smap->get_state (sval, m_eg.get_ext_state ()) : m_old_smap->get_global_state ()); bool terminate_path = d->terminate_path_p (); pending_location ploc (m_enode_for_diag); m_eg.get_diagnostic_manager ().add_diagnostic (&m_sm, std::move (ploc), NULL_TREE, sval, current, std::move (d)); if (m_path_ctxt && terminate_path && flag_analyzer_suppress_followups) m_path_ctxt->terminate_path (); } /* Hook for picking more readable trees for SSA names of temporaries, so that rather than e.g. "double-free of ''" we can print: "double-free of 'inbuf.data'". */ tree get_diagnostic_tree (tree expr) final override { /* Only for SSA_NAMEs of temporaries; otherwise, return EXPR, as it's likely to be the least surprising tree to report. */ if (TREE_CODE (expr) != SSA_NAME) return expr; if (SSA_NAME_VAR (expr) != NULL) return expr; gcc_assert (m_new_state); const svalue *sval = m_new_state->m_region_model->get_rvalue (expr, nullptr); /* Find trees for all regions storing the value. */ if (tree t = m_new_state->m_region_model->get_representative_tree (sval)) return t; else return expr; } tree get_diagnostic_tree (const svalue *sval) final override { return m_new_state->m_region_model->get_representative_tree (sval); } state_machine::state_t get_global_state () const final override { return m_old_state->m_checker_states[m_sm_idx]->get_global_state (); } void set_global_state (state_machine::state_t state) final override { m_new_state->m_checker_states[m_sm_idx]->set_global_state (state); } void clear_all_per_svalue_state () final override { m_new_state->m_checker_states[m_sm_idx]->clear_all_per_svalue_state (); } void on_custom_transition (custom_transition *transition) final override { transition->impl_transition (&m_eg, const_cast (m_enode_for_diag), m_sm_idx); } tree is_zero_assignment (const gimple *stmt) final override { const gassign *assign_stmt = dyn_cast (stmt); if (!assign_stmt) return NULL_TREE; impl_region_model_context old_ctxt (m_eg, m_enode_for_diag, m_old_state, m_new_state, nullptr, nullptr, stmt); if (const svalue *sval = m_new_state->m_region_model->get_gassign_result (assign_stmt, &old_ctxt)) if (tree cst = sval->maybe_get_constant ()) if (::zerop(cst)) return gimple_assign_lhs (assign_stmt); return NULL_TREE; } path_context *get_path_context () const final override { return m_path_ctxt; } bool unknown_side_effects_p () const final override { return m_unknown_side_effects; } const program_state *get_old_program_state () const final override { return m_old_state; } const program_state *get_new_program_state () const final override { return m_new_state; } location_t get_emission_location () const final override { return pending_location (m_enode_for_diag).get_location (); } log_user m_logger; exploded_graph &m_eg; exploded_node *m_enode_for_diag; const program_state *m_old_state; program_state *m_new_state; const sm_state_map *m_old_smap; sm_state_map *m_new_smap; path_context *m_path_ctxt; /* Are we handling an external function with unknown side effects? */ bool m_unknown_side_effects; }; } // namespace ana #endif /* GCC_ANALYZER_IMPL_SM_CONTEXT */