diff options
Diffstat (limited to 'gcc/analyzer')
81 files changed, 7077 insertions, 3498 deletions
diff --git a/gcc/analyzer/ChangeLog b/gcc/analyzer/ChangeLog index 443dc6d..aca8186 100644 --- a/gcc/analyzer/ChangeLog +++ b/gcc/analyzer/ChangeLog @@ -1,3 +1,848 @@ +2025-08-29 David Malcolm <dmalcolm@redhat.com> + + * pending-diagnostic.cc: Include "diagnostics/logging.h". + (diagnostic_emission_context::warn): Add logging. + (diagnostic_emission_context::inform): Likewise. + +2025-07-25 David Malcolm <dmalcolm@redhat.com> + + * program-point.cc: Make diagnostics::context::m_source_printing + private. + +2025-07-25 David Malcolm <dmalcolm@redhat.com> + + * pending-diagnostic.cc: Update for diagnostic_t becoming + enum class diagnostics::kind. + * program-point.cc: Likewise. + +2025-07-25 David Malcolm <dmalcolm@redhat.com> + + * program-point.cc: : Update for diagnostic_context becoming + diagnostics::context, and for diagnostic_source_print_policy + becoming diagnostics::source_print_policy. + +2025-07-25 David Malcolm <dmalcolm@redhat.com> + + * ana-state-to-diagnostic-state.cc: Update for move of + diagnostics output formats into namespace "diagnostics" as + "sinks". + * bounds-checking.cc: Likewise. + * call-details.cc: Likewise. + * checker-event.cc: Likewise. + * checker-event.h: Likewise. + * diagnostic-manager.cc: Likewise. + * diagnostic-manager.h: Likewise. + * infinite-loop.cc: Likewise. + * infinite-recursion.cc: Likewise. + * pending-diagnostic.h: Likewise. + * region-model.cc: Likewise. + * sm-taint.cc: Likewise. + +2025-07-25 David Malcolm <dmalcolm@redhat.com> + + * access-diagram.cc: Update for changes to diagnostic paths: + "diagnostic-path.h" moving to "diagnostics/paths.h", + "diagnostic-event-id.h" moving to "diagnostics/event-id.h", + diagnostic_event_id_t to diagnostics::paths::event_id_t, + diagnostic_path to diagnostics::paths::path, and + diagnostic_event to diagnostics::paths::event. + * access-diagram.h: Likewise. + * analyzer.cc: Likewise. + * bounds-checking.cc: Likewise. + * call-info.cc: Likewise. + * checker-event.cc: Likewise. + * checker-event.h: Likewise. + * checker-path.cc: Likewise. + * checker-path.h: Likewise. + * common.h: Likewise. + * diagnostic-manager.cc: Likewise. + * pending-diagnostic.cc: Likewise. + * pending-diagnostic.h: Likewise. + * program-point.cc: Likewise. + * program-state.cc: Likewise. + * region-model.cc: Likewise. + * sm-fd.cc: Likewise. + * sm-file.cc: Likewise. + * sm-malloc.cc: Likewise. + * sm-pattern-test.cc: Likewise. + * sm-sensitive.cc: Likewise. + * sm-signal.cc: Likewise. + * sm-taint.cc: Likewise. + * varargs.cc: Likewise. + +2025-07-25 David Malcolm <dmalcolm@redhat.com> + + * bounds-checking.cc: Update #include for move of + "diagnostic-diagram.h" to "diagnostics/diagram.h". Update for + move of diagnostic_diagram to diagnostics::diagram. + +2025-07-25 David Malcolm <dmalcolm@redhat.com> + + * diagnostic-manager.cc: Update for move of diagnostic_metadata to + diagnostics::metadata. + * kf.cc: Likewise. + * pending-diagnostic.h: Likewise; also, update #include for move + of "diagnostic-metadata.h" to "diagnostics/metadata.h". + +2025-07-25 David Malcolm <dmalcolm@redhat.com> + + * checker-event.h (checker_event::get_logical_location): Update + for conversion of logical_location to + diagnostics::logical_locations::key. + (checker_event::m_logical_loc): Likewise. + * diagnostic-manager.cc + (diagnostic_manager::get_logical_location_manager): Likewise. + * diagnostic-manager.h + (diagnostic_manager::get_logical_location_manager): Likewise. + +2025-07-25 David Malcolm <dmalcolm@redhat.com> + + * ana-state-to-diagnostic-state.cc: Update #include for move of + "diagnostic-state-graphs.h" to "diagnostics/state-graphs.h". + * ana-state-to-diagnostic-state.h: Likewise. + * checker-event.cc: Likewise. + * checker-event.h: Update #include for move of + "diagnostic-digraphs.h" to "diagnostics/digraphs.h". + * program-state.cc: : Update #include for move of + "diagnostic-state-graphs.h" to "diagnostics/state-graphs.h". + +2025-07-21 Stefan Schulze Frielinghaus <stefansf@gcc.gnu.org> + + * region-model-asm.cc (region_model::on_asm_stmt): Pass null + pointer to parse_{input,output}_constraint(). + +2025-07-11 David Malcolm <dmalcolm@redhat.com> + + * ana-state-to-diagnostic-state.cc: Reimplement, replacing + XML-based implementation with one based on state graphs. + * ana-state-to-diagnostic-state.h: Likewise. + * checker-event.cc: Replace include of "xml.h" with include of + "diagnostic-state-graphs.h". + (checker_event::maybe_make_xml_state): Replace with... + (checker_event::maybe_make_diagnostic_state_graph): ...this. + * checker-event.h: Add include of "diagnostic-digraphs.h". + (checker_event::maybe_make_xml_state): Replace decl with... + (checker_event::maybe_make_diagnostic_state_graph): ...this. + * engine.cc (exploded_node::on_stmt_pre): Replace + "_analyzer_dump_xml" with "__analyzer_dump_sarif". + * program-state.cc: Replace include of "diagnostic-state.h" with + "diagnostic-state-graphs.h". + (program_state::dump_dot): Port from XML to state graphs. + * program-state.h: Drop reduntant forward decl of xml::document. + (program_state::make_xml): Replace decl with... + (program_state::make_diagnostic_state_graph): ...this. + (program_state::dump_xml_to_pp): Drop decl. + (program_state::dump_xml_to_file): Drop decl. + (program_state::dump_xml): Drop decl. + (program_state::dump_dump_sarif): New decl. + * sm-malloc.cc (get_dynalloc_state_for_state): New. + (malloc_state_machine::add_state_to_xml): Replace with... + (malloc_state_machine::add_state_to_state_graph): ...this. + * sm.cc (state_machine::add_state_to_xml): Replace with... + (state_machine::add_state_to_state_graph): ...this. + (state_machine::add_global_state_to_xml): Replace with... + (state_machine::add_global_state_to_state_graph): ...this. + * sm.h (class xml_state): Drop forward decl. + (class analyzer_state_graph): New forward decl. + (state_machine::add_state_to_xml): Replace decl with... + (state_machine::add_state_to_state_graph): ...this. + (state_machine::add_global_state_to_xml): Replace decl with... + (state_machine::add_global_state_to_state_graph): ...this. + +2025-06-30 David Malcolm <dmalcolm@redhat.com> + + * access-diagram.cc: Use nullptr rather than NULL where + appropriate. + * analyzer-language.cc: Likewise. + * analyzer-language.h: Likewise. + * analyzer-logging.h: Likewise. + * analyzer-pass.cc: Likewise. + * analyzer.cc: Likewise. + * bounds-checking.cc: Likewise. + * call-details.cc: Likewise. + * call-string.cc: Likewise. + * call-string.h: Likewise. + * call-summary.cc: Likewise. + * checker-event.cc: Likewise. + * common.h: Likewise. + * constraint-manager.cc: Likewise. + * constraint-manager.h: Likewise. + * diagnostic-manager.cc: Likewise. + * engine.cc: Likewise. + * exploded-graph.h: Likewise. + * function-set.cc: Likewise + * infinite-recursion.cc: Likewise + * inlining-iterator.h: Likewise + * kf.cc: Likewise + * known-function-manager.cc: Likewise + * pending-diagnostic.cc: Likewise + * program-point.cc: Likewise + * program-point.h: Likewise + * program-state.cc: Likewise + * program-state.h: Likewise + * record-layout.cc: Likewise + * region-model-asm.cc: Likewise + * region-model-manager.cc: Likewise + * region-model-manager.h: Likewise + * region-model-reachability.cc: Likewise + * region-model.cc: Likewise + * region-model.h: Likewise + * region.cc: Likewise + * region.h: Likewise + * sm-fd.cc: Likewise + * sm-malloc.cc: Likewise + * sm-pattern-test.cc: Likewise + * sm-signal.cc: Likewise + * sm-taint.cc: Likewise + * sm.cc: Likewise + * sm.h: Likewise + * state-purge.cc: Likewise + * state-purge.h: Likewise + * store.cc: Likewise + * store.h: Likewise + * supergraph.cc: Likewise + * supergraph.h: Likewise + * svalue.cc: Likewise + * svalue.h: Likewise + * varargs.cc: Likewise + +2025-06-30 David Malcolm <dmalcolm@redhat.com> + + * checker-event.cc (function_entry_event::get_meaning): Convert + diagnostic_event::meaning enums to enum class. + (cfg_edge_event::get_meaning): Likewise. + (call_event::get_meaning): Likewise. + (return_event::get_meaning): Likewise. + (start_consolidated_cfg_edges_event::get_meaning): Likewise. + (inlined_call_event::get_meaning): Likewise. + (warning_event::get_meaning): Likewise. + * sm-fd.cc (fd_diagnostic::get_meaning_for_state_change): + Likewise. + * sm-file.cc (file_diagnostic::get_meaning_for_state_change): + Likewise. + * sm-malloc.cc (malloc_diagnostic::get_meaning_for_state_change): + Likewise. + * sm-sensitive.cc + (exposure_through_output_file::get_meaning_for_state_change): + Likewise. + * sm-taint.cc (taint_diagnostic::get_meaning_for_state_change): + Likewise. + * varargs.cc + (va_list_sm_diagnostic::get_meaning_for_state_change): Likewise. + +2025-06-30 Jakub Jelinek <jakub@redhat.com> + + PR c/120520 + PR c/117023 + * sm-malloc.cc (malloc_state_machine::on_stmt): Handle 3 argument + nonnull_if_nonzero attribute. + +2025-06-23 David Malcolm <dmalcolm@redhat.com> + + * region-model.cc + (exception_thrown_from_unrecognized_call::print): Add + "final override" to vfunc. + +2025-06-23 David Malcolm <dmalcolm@redhat.com> + + PR other/116792 + * ana-state-to-diagnostic-state.cc: New file. + * ana-state-to-diagnostic-state.h: New file. + * checker-event.cc: Include "xml.h". + (checker_event::checker_event): Initialize m_path. + (checker_event::prepare_for_emission): Store the path pointer into + m_path. + (checker_event::maybe_make_xml_state): New. + (function_entry_event::function_entry_event): Add "state" param + and use it to initialize m_state. + (superedge_event::get_program_state): New. + (call_event::get_program_state): New. + (warning_event::get_program_state): New. + * checker-event.h (checker_event::get_program_state): New vfunc. + (checker_event::maybe_make_xml_state): New decl. + (checker_event::m_path): New field. + (statement_event::get_program_state): New vfunc impl. + (function_entry_event::function_entry_event): Add "state" param. + (function_entry_event::get_program_state): New vfunc impl. + (function_entry_event::m_state): New field. + (state_change_event::get_program_state): New vfunc impl. + (superedge_event::get_program_state): New vfunc decl. + (warning_event::warning_event): Add "program_state_" param and + copy it. + (warning_event::get_program_state): New vfunc decl. + (warning_event::m_program_state): New field. + * checker-path.h (checker_path::checker_path): Add ext_state param. + (checker_path::get_ext_state): New accessor. + (checker_path::m_ext_state): New field. + * common.h: Define INCLUDE_MAP and INCLUDE_STRING. + * diagnostic-manager.cc (saved_diagnostic::operator==): Don't + deduplicate dump_path_diagnostic instances. + (diagnostic_manager::emit_saved_diagnostic): Pass ext_state to + checker_path ctor. + * engine.cc: + (impl_region_model_context::on_state_leak): Pass old and new state + to state_machine::on_leak. + (exploded_node::on_stmt_pre): Implement __analyzer_dump_xml and + __analyzer_dump_dot. + * exploded-graph.h (impl_region_model_context::get_state): New. + * infinite-recursion.cc + (recursive_function_entry_event::recursive_function_entry_event): + Add "dst_state" param and pass to function_entry_event ctor. + (infinite_recursion_diagnostic::add_function_entry_event): Pass state + to event ctor. + * kf-analyzer.cc: Include "analyzer/program-state.h" + (dump_path_diagnostic::dump_path_diagnostic): Add "state" param. + (dump_path_diagnostic::get_final_state): New. + (dump_path_diagnostic::m_state): New field. + (kf_analyzer_dump_path::impl_call_pre): Pass state to warning. + * pending-diagnostic.cc + (pending_diagnostic::add_function_entry_event): Pass state to + function_entry_event. + (pending_diagnostic::add_final_event): Likewise to warning_event. + * pending-diagnostic.h (pending_diagnostic::get_final_state): New + vfunc decl. + * program-state.cc: Include "diagnostic-state.h", "graphviz.h" and + "analyzer/ana-state-to-diagnostic-state.h". + (program_state::dump_dot): New. + * program-state.h: Include "text-art/tree-widget.h" and + "analyzer/store.h". + (class xml::document): New forward decl. + (make_xml): New. + (dump_xml_to_pp): New. + (dump_xml_to_file): New. + (dump_xml): New. + (dump_dot): New. + * record-layout.cc (record_layout::record_layout): Make param + const_tree. + * record-layout.h (item::item): Likewise. + (item::m_field): Likewise. + (record_layout::record_layout): Likewise. + (record_layout::begin): New. + (record_layout::end): New. + * region-model.cc + (exposure_through_uninit_copy::complain_about_fully_uninit_item): + Use const_tree. + (exposure_through_uninit_copy::complain_about_partially_uninit_item): + Likewise. + * region-model.h (region_model_context::get_state): New vfunc. + (noop_region_model_context::get_state): New. + (region_model_context_decorator::get_state): New. + * sm-fd.cc (fd_leak::fd_leak): Add "final_state" param and capture + it if present. + (fd_leak::get_final_state): New. + (fd_leak::m_final_state): New. + (fd_state_machine::on_open): Pass nullptr for new "final_state" + param. + (fd_state_machine::on_creat): Likewise. + (fd_state_machine::on_socket): Likewise. + (fd_state_machine::on_accept): Likewise. + (fd_state_machine::on_leak): Add state params and pass new state + as final state to fd_leak ctor. + * sm-file.cc: Include "analyzer/program-state.h". + (file_leak::file_leak): Add "final_state" param and capture it if + present. + (file_leak::get_final_state): New. + (file_leak::m_final_state): New. + (fileptr_state_machine::on_leak): Add state params and pass new + state as final state to fd_leak ctor. + * sm-malloc.cc: Include + "analyzer/ana-state-to-diagnostic-state.h". + (malloc_leak::malloc_leak): Add "final_state" param and use it. + (malloc_leak::get_final_state): New vfunc impl. + (malloc_leak::m_final_state): New field. + (malloc_state_machine::on_leak): Add state params; capture final + state. + (malloc_state_machine::add_state_to_xml): New. + * sm.cc (state_machine::on_leak): Add "old_state" and "new_state" + params. Use nullptr. + (state_machine::add_state_to_xml): New. + (state_machine::add_global_state_to_xml): New. + * sm.h (class xml_state): New forward decl. + (state_machine::on_leak): Add state params. + (state_machine::add_state_to_xml): New vfunc decl. + (state_machine::add_global_state_to_xml): New vfunc decl. + * store.h (bit_range::operator<): New. + * varargs.cc (va_list_leak::va_list_leak): Add final_state param + and capture it if non-null. + (va_list_leak::get_final_state): New. + (va_list_leak::m_final_state): New. + (va_list_state_machine::on_leak): Add state params and pass final + state to va_list_leak ctor. + +2025-06-18 David Malcolm <dmalcolm@redhat.com> + + * checker-event.h (checker_event::get_kind): New accessor. + (checker_event::m_kind): Make private. + * checker-path.cc (checker_path::maybe_log): Use accessor for + checker_event::m_kind. + (checker_path::add_event): Likewise. + (checker_path::debug): Likewise. + (checker_path::cfg_edge_pair_at_p): Likewise. + (checker_path::inject_any_inlined_call_events): Likewise. + * diagnostic-manager.cc + (diagnostic_manager::prune_for_sm_diagnostic): Likewise. + (diagnostic_manager::prune_for_sm_diagnostic): Likewise. + (diagnostic_manager::consolidate_conditions): Likewise. + (diagnostic_manager::consolidate_unwind_events): Likewise. + (diagnostic_manager::finish_pruning): Likewise. + +2025-05-06 David Malcolm <dmalcolm@redhat.com> + + * checker-event.cc (checker_event::checker_event): Update + initialization of m_logical_loc. + (checker_event::maybe_add_sarif_properties): Add "builder" param. + Replace call to make_sarif_logical_location_object with call to + sarif_property_bag::set_logical_location. + (superedge_event::maybe_add_sarif_properties): Add "builder" + param. + * checker-event.h (checker_event::get_logical_location): + Reimplement. + (checker_event::maybe_add_sarif_properties): Add "builder" param. + (checker_event::maybe_add_sarif_properties): Add "builder" param. + (checker_event::m_logical_loc): Convert from tree_logical_location + to logical_location. + (superedge_event::maybe_add_sarif_properties): Add sarif_builder + param. + * checker-path.h (checker_path::checker_path): Add logical_loc_mgr + param. + * diagnostic-manager.cc + (diagnostic_manager::emit_saved_diagnostic): Pass logical location + manager to emission_path ctor. + (diagnostic_manager::get_logical_location_manager): New. + * diagnostic-manager.h + (diagnostic_manager::get_logical_location_manager): New decl. + +2025-04-30 David Malcolm <dmalcolm@redhat.com> + + * sm-malloc.cc (malloc_diagnostic::describe_state_change): Tweak + the "EXPR is NULL" message for the case where EXPR is a null + pointer. + +2025-04-29 Marc Poulhiès <dkm@kataplop.net> + + * exploded-graph.h (set_status): Rename parameter. + * constraint-manager.cc (bound::ensure_closed): Likewise. + (range::add_bound): Likewise. + +2025-04-28 David Malcolm <dmalcolm@redhat.com> + + PR analyzer/111536 + * engine.cc (maybe_update_for_edge): Update for new call_stmt + param to region_model::push_frame. + * program-state.cc (program_state::push_frame): Likewise. + * region-model.cc (region_model::update_for_gcall): Likewise. + (region_model::push_frame): Add "call_stmt" param. + Handle DECL_RESULT with DECL_BY_REFERENCE set on it by stashing + the region of the lhs of the call_stmt in the caller frame, + and writing a reference to it within the "result" in the callee + frame. + (region_model::pop_frame): Don't write back to the LHS for + DECL_BY_REFERENCE results. + (selftest::test_stack_frames): Update for new call_stmt param to + region_model::push_frame. + (selftest::test_get_representative_path_var): Likewise. + (selftest::test_state_merging): Likewise. + (selftest::test_alloca): Likewise. + * region-model.h (region_model::push_frame): Add "call_stmt" + param. + * region.cc: Include "tree-ssa.h". + (region::can_have_initial_svalue_p): Use ssa_defined_default_def_p + for ssa names, rather than special-casing it for just parameters. + This should now also cover DECL_RESULT with DECL_BY_REFERENCE and + hard registers. + * sm-signal.cc (update_model_for_signal_handler): Update for new + call_stmt param to region_model::push_frame. + * state-purge.cc (state_purge_per_decl::process_worklists): + Likewise. + +2025-04-28 David Malcolm <dmalcolm@redhat.com> + + PR analyzer/109366 + * region-model-manager.cc + (region_model_manager::maybe_fold_sub_svalue): Sub-values of zero + constants are zero. + +2025-04-28 David Malcolm <dmalcolm@redhat.com> + + PR analyzer/97111 + * analyzer.cc (is_cxa_throw_p): New. + (is_cxa_rethrow_p): New. + * analyzer.opt (Wanalyzer-throw-of-unexpected-type): New. + * analyzer.opt.urls: Regenerate. + * call-info.cc (custom_edge_info::create_enode): New. + * call-info.h (call_info::print): Drop "final". + (call_info::add_events_to_path): Likewise. + * checker-event.cc (event_kind_to_string): Add cases for + event_kind::catch_, event_kind::throw_, and event_kind::unwind. + (explicit_throw_event::print_desc): New. + (throw_from_call_to_external_fn_event::print_desc): New. + (unwind_event::print_desc): New. + * checker-event.h (enum class event_kind): Add catch_, throw_, + and unwind. + (class catch_cfg_edge_event): New. + (class throw_event): New. + (class explicit_throw_event): New. + (class throw_from_call_to_external_fn_event): New. + (class unwind_event): New. + * common.h (class eh_dispatch_cfg_superedge): New forward decl. + (class eh_dispatch_try_cfg_superedge): New forward decl. + (class eh_dispatch_allowed_cfg_superedge): New forward decl. + (custom_edge_info::create_enode): New vfunc decl. + (is_cxa_throw_p): New decl. + (is_cxa_rethrow_p): New decl. + * diagnostic-manager.cc + (diagnostic_manager::add_events_for_superedge): Special-case edges + for eh_dispach_try. + (diagnostic_manager::prune_path): Call consolidate_unwind_events. + (diagnostic_manager::prune_for_sm_diagnostic): Don't filter the new + event_kinds. + (diagnostic_manager::consolidate_unwind_events): New. + * diagnostic-manager.h + (diagnostic_manager::consolidate_unwind_events): New decl. + * engine.cc (exploded_node::on_stmt_pre): Handle "__cxa_throw", + "__cxa_rethrow", and resx statements. + (class throw_custom_edge): New. + (class unwind_custom_edge): New. + (get_eh_outedge): New. + (exploded_graph::unwind_from_exception): New. + (exploded_node::on_throw): New. + (exploded_node::on_resx): New. + (exploded_graph::get_or_create_node): Add "add_to_worklist" param + and use it. + (exploded_graph::process_node): Use edge_info's create_enode vfunc + to create enodes, rather than calling get_or_create_node directly. + Ignore CFG edges in the sgraph flagged with EH whilst we're + exploring the egraph. + (exploded_graph_annotator::print_enode): Handle case + exploded_node::status::special. + * exploded-graph.h (exploded_node::status): Add value "special". + (exploded_node::on_throw): New decl. + (exploded_node::on_resx): New decl. + (exploded_graph::get_or_create_node): Add optional + "add_to_worklist" param. + (exploded_graph::unwind_from_exception): New decl. + * kf-lang-cp.cc (class kf_cxa_allocate_exception): New. + (class kf_cxa_begin_catch): New. + (class kf_cxa_end_catch): New. + (class throw_of_unexpected_type): New. + (class kf_cxa_call_unexpected): New. + (register_known_functions_lang_cp): Register known functions + "__cxa_allocate_exception", "__cxa_begin_catch", + "__cxa_end_catch", and "__cxa_call_unexpected". + * kf.cc (class kf_eh_pointer): New. + (register_known_functions): Register it for BUILT_IN_EH_POINTER. + * region-model.cc: Include "analyzer/function-set.h". + (exception_node::operator==): New. + (exception_node::dump_to_pp): New. + (exception_node::dump): New. + (exception_node::to_json): New. + (exception_node::make_dump_widget): New. + (exception_node::maybe_get_type): New. + (exception_node::add_to_reachable_regions): New. + (region_model::region_model): Initialize + m_thrown_exceptions_stack and m_caught_exceptions_stack. + (region_model::operator=): Likewise. + (region_model::operator==): Compare them. + (region_model::dump_to_pp): Dump exception stacks. + (region_model::to_json): Add exception stacks. + (region_model::make_dump_widget): Likewise. + (class exception_thrown_from_unrecognized_call): New. + (get_fns_assumed_not_to_throw): New. + (can_throw_p): New. + (region_model::check_for_throw_inside_call): New. + (region_model::on_call_pre): Call check_for_throw_inside_call + on unknown fns or those we don't have a body for. + (region_model::maybe_update_for_edge): Handle eh_dispatch_stmt + statements. Drop old code that called + apply_constraints_for_exception on EDGE_EH edges. + (class rejected_eh_dispatch): New. + (exception_matches_type_p): New. + (matches_any_exception_type_p): New. + (region_model::apply_constraints_for_eh_dispatch): New. + (region_model::apply_constraints_for_eh_dispatch_try): New. + (region_model::apply_constraints_for_eh_dispatch_allowed): New. + (region_model::apply_constraints_for_exception): Delete. + (region_model::can_merge_with_p): Don't merge models with + non-equal exception stacks. + (region_model::get_referenced_base_regions): Add regions from + exception stacks. + * region-model.h (struct exception_node): New. + (region_model::push_thrown_exception): New. + (region_model::get_current_thrown_exception): New. + (region_model::pop_thrown_exception): New. + (region_model::push_caught_exception): New. + (region_model::get_current_caught_exception): New. + (region_model::pop_caught_exception): New. + (region_model::apply_constraints_for_eh_dispatch_try): New decl. + (region_model::apply_constraints_for_eh_dispatch_allowed) New decl. + (region_model::apply_constraints_for_exception): Delete. + (region_model::apply_constraints_for_eh_dispatch): New decl. + (region_model::check_for_throw_inside_call): New decl. + (region_model::m_thrown_exceptions_stack): New field. + (region_model::m_caught_exceptions_stack): New field. + * supergraph.cc: Include "except.h" and "analyzer/region-model.h". + (supergraph::add_cfg_edge): Special-case eh_dispatch edges. + (superedge::get_description): Use default_tree_printer. + (get_catch): New. + (eh_dispatch_cfg_superedge::make): New. + (eh_dispatch_cfg_superedge::eh_dispatch_cfg_superedge): New. + (eh_dispatch_cfg_superedge::get_eh_status): New. + (eh_dispatch_try_cfg_superedge::dump_label_to_pp): New. + (eh_dispatch_try_cfg_superedge::apply_constraints): New. + (eh_dispatch_allowed_cfg_superedge::eh_dispatch_allowed_cfg_superedge): + New. + (eh_dispatch_allowed_cfg_superedge::dump_label_to_pp): New. + (eh_dispatch_allowed_cfg_superedge::apply_constraints): New. + * supergraph.h: Include "except.h". + (superedge::dyn_cast_eh_dispatch_cfg_superedge): New vfunc. + (superedge::dyn_cast_eh_dispatch_try_cfg_superedge): New vfunc. + (superedge::dyn_cast_eh_dispatch_allowed_cfg_superedge): New + vfunc. + (class eh_dispatch_cfg_superedge): New. + (is_a_helper <const eh_dispatch_cfg_superedge *>::test): New. + (class eh_dispatch_try_cfg_superedge): New. + (is_a_helper <const eh_dispatch_try_cfg_superedge *>::test): New. + (class eh_dispatch_allowed_cfg_superedge): New. + (is_a_helper <const eh_dispatch_allowed_cfg_superedge *>::test): + New. + * svalue.cc (svalue::maybe_get_type_from_typeinfo): New. + * svalue.h (svalue::maybe_get_type_from_typeinfo): New decl. + +2025-04-28 David Malcolm <dmalcolm@redhat.com> + + * access-diagram.cc: Replace uses of ::make_unique with + std::make_unique. + * analyzer.cc: Likewise. + * bounds-checking.cc: Likewise. + * call-details.cc: Likewise. + * call-info.cc: Likewise. + * call-string.cc: Likewise. + * checker-path.cc: Likewise. + * common.h: Drop include of "make-unique.h". + * constraint-manager.cc: Replace uses of ::make_unique with + std::make_unique. + * diagnostic-manager.cc: Likewise. + * engine.cc: Likewise. + * infinite-loop.cc: Likewise. + * infinite-recursion.cc: Likewise. + * kf-analyzer.cc: Likewise. + * kf-lang-cp.cc: Likewise. + * kf.cc: Likewise. + * pending-diagnostic.cc: Likewise. + * program-point.cc: Likewise; drop #include. + * program-state.cc: Likewise. + * ranges.cc: Likewise. + * region-model.cc: Likewise. + * region.cc: Likewise; drop #include. + * sm-fd.cc: Likewise. + * sm-file.cc: Likewise. + * sm-malloc.cc: Likewise. + * sm-pattern-test.cc: Likewise. + * sm-sensitive.cc: Likewise. + * sm-signal.cc: Likewise. + * sm-taint.cc: Likewise. + * sm.cc: Likewise. + * store.cc: Likewise. + * supergraph.cc: Likewise. + * svalue.cc: Likewise; drop #include. + * varargs.cc: Likewise. + +2025-04-28 David Malcolm <dmalcolm@redhat.com> + + * engine.cc (class plugin_analyzer_init_impl): Convert + "m_checkers" to use std::vector of std::unique_ptr. Convert + "m_known_fn_mgr" to a reference. + (impl_run_checkers): Convert "checkers" to use std::vector of + std::unique_ptr and move it into the extrinsic_state. + * program-state.cc (extrinsic_state::dump_to_pp): Update for + changes to m_checkers. + (extrinsic_state::to_json): Likewise. + (extrinsic_state::get_sm_idx_by_name): Likewise. + (selftest::test_sm_state_map): Update to use std::unique_ptr + for state machines. + (selftest::test_program_state_1): Likewise. + (selftest::test_program_state_2): Likewise. + (selftest::test_program_state_merging): Likewise. + (selftest::test_program_state_merging_2): Likewise. + * program-state.h (class extrinsic_state): Convert "m_checkers" to + use std::vector of std::unique_ptr and to be owned by this object, + rather than a reference. Add ctor for use in selftests. + * sm-fd.cc (make_fd_state_machine): Update to use std::unique_ptr. + * sm-file.cc (make_fileptr_state_machine): Likewise. + * sm-malloc.cc (make_malloc_state_machine): Likewise. + * sm-pattern-test.cc (make_pattern_test_state_machine): Likewise. + * sm-sensitive.cc (make_sensitive_state_machine): Likewise. + * sm-signal.cc (make_signal_state_machine): Likewise. + * sm-taint.cc (make_taint_state_machine): Likewise. + * sm.cc: Define INCLUDE_LIST. + (make_checkers): Return the vector directly, rather than pass it + in by reference. Update to use std::unique_ptr throughout. Use + an intermediate list, and use that to filter with + flag_analyzer_checker, fixing memory leak for this case. + * sm.h: (make_checkers): Return the vector directly, rather than + pass it in by reference, and use std::vector of std::unique_ptr. + (make_malloc_state_machine): Convert return type to use std::unique_ptr. + (make_fileptr_state_machine): Likewise. + (make_taint_state_machine): Likewise. + (make_sensitive_state_machine): Likewise. + (make_signal_state_machine): Likewise. + (make_pattern_test_state_machine): Likewise. + (make_va_list_state_machine): Likewise. + (make_fd_state_machine): Likewise. + * varargs.cc (make_va_list_state_machine): Update to use + std::unique_ptr. + +2025-04-28 David Malcolm <dmalcolm@redhat.com> + + * call-summary.cc (call_summary_replay::call_summary_replay): + Convert "summary" from call_summary * to call_summary &. + (call_summary_replay::dump_to_pp): Likewise for m_summary. + * call-summary.h (call_summary_replay::call_summary_replay): + Likewise for "summary". + (call_summary_replay::m_summary): Likewise. + * engine.cc (call_summary_edge_info::call_summary_edge_info): + Likewise. + (call_summary_edge_info::update_state): Likewise. + (call_summary_edge_info::update_model): Likewise. + (call_summary_edge_info::print_desc): Likewise for m_summary. + (call_summary_edge_info::m_summary): Likewise. + (exploded_node::replay_call_summaries): Update for change to + replay_call_summary. + (exploded_node::replay_call_summary): Convert "summary" from + call_summary * to call_summary &. + * exploded-graph.h (exploded_node::replay_call_summary): Likewise. + +2025-04-28 David Malcolm <dmalcolm@redhat.com> + + * analyzer.cc: Convert gcall * to gcall & where we know the + pointer must be non-null. + * call-details.cc: Likewise. + * call-details.h: Likewise. + * call-info.cc: Likewise. + * call-info.h: Likewise. + * call-summary.h: Likewise. + * checker-event.cc: Likewise. + * checker-event.h: Likewise. + * common.h: Likewise. + * diagnostic-manager.cc: Likewise. + * engine.cc: Likewise. + * exploded-graph.h: Likewise. + * kf-analyzer.cc: Likewise. + * kf-lang-cp.cc: Likewise. + * kf.cc: Likewise. + * known-function-manager.cc: Likewise. + * program-state.cc: Likewise. + * program-state.h: Likewise. + * region-model.cc: Likewise. + * region-model.h: Likewise. + * sm-fd.cc: Likewise. + * sm-file.cc: Likewise. + * sm-malloc.cc: Likewise. + * sm-sensitive.cc: Likewise. + * sm-signal.cc: Likewise. + * sm-taint.cc: Likewise. + * sm.h: Likewise. + * store.cc: Likewise. + * store.h: Likewise. + * supergraph.cc: Likewise. + * supergraph.h: Likewise. + * svalue.h: Likewise. + * varargs.cc: Likewise. + +2025-04-28 David Malcolm <dmalcolm@redhat.com> + + * access-diagram.cc: Convert enum access_direction to + "enum class". + * bounds-checking.cc: Likewise. + * checker-event.cc: Convert enum event_kind to "enum class". + * checker-event.h: Likewise. + * checker-path.cc: Likewise. + * common.h: Convert enum access_direction to "enum class". + * constraint-manager.cc: Convert enum bound_kind to "enum class". + * constraint-manager.h: Likewise. + * diagnostic-manager.cc: Convert enum event_kind to "enum class". + * engine.cc: Convert enum status to "enum class". + * exploded-graph.h: Likewise. + * infinite-loop.cc: Likewise. + * kf-lang-cp.cc: Convert enum poison_kind to "enum class". + * kf.cc: Likewise. + * region-model-manager.cc: Likewise. + * region-model.cc: Likewise; also for enum access_direction. + * svalue.cc: Likewise. + * svalue.h: Likewise. + +2025-04-28 David Malcolm <dmalcolm@redhat.com> + + * analyzer.h: Rename to... + * common.h: ...this. Add define of INCLUDE_VECTOR, includes of + "config.h", "system.h", "coretypes.h", "make-unique.h", "tree.h", + "function.h", "basic-block.h", "gimple.h", "options.h", + "bitmap.h", "diagnostic-core.h", and "diagnostic-path.h". + * access-diagram.h: Don't include "analyzer/analyzer.h". + * access-diagram.cc: Reorganize includes to #include + "analyzer/common.h" first, then group by subsystem, dropping + redundant headers. + * analysis-plan.cc: Likewise. + * analyzer-language.cc: Likewise. + * analyzer-pass.cc: Likewise. + * analyzer-selftests.cc: Likewise. + * analyzer.cc: Likewise. + * bounds-checking.cc: Likewise. + * call-details.cc: Likewise. + * call-info.cc: Likewise. + * call-string.cc: Likewise. + * call-summary.cc: Likewise. + * checker-event.cc: Likewise. + * checker-path.cc: Likewise. + * complexity.cc: Likewise. + * constraint-manager.cc: Likewise. + * diagnostic-manager.cc: Likewise. + * engine.cc: Likewise. + * feasible-graph.cc: Likewise. + * infinite-loop.cc: Likewise. + * infinite-recursion.cc: Likewise. + * kf-analyzer.cc: Likewise. + * kf-lang-cp.cc: Likewise. + * kf.cc: Likewise. + * known-function-manager.cc: Likewise. + * pending-diagnostic.cc: Likewise. + * program-point.cc: Likewise. + * program-state.cc: Likewise. + * ranges.cc: Likewise. + * record-layout.cc: Likewise. + * region-model-asm.cc: Likewise. + * region-model-manager.cc: Likewise. + * region-model-reachability.cc: Likewise. + * region-model.cc: Likewise. + * region.cc: Likewise. + * sm-fd.cc: Likewise. + * sm-file.cc: Likewise. + * sm-malloc.cc: Likewise. + * sm-pattern-test.cc: Likewise. + * sm-sensitive.cc: Likewise. + * sm-signal.cc: Likewise. + * sm-taint.cc: Likewise. + * sm.cc: Likewise. + * state-purge.cc: Likewise. + * store.cc: Likewise. + * supergraph.cc: Likewise. + * svalue.cc: Likewise. + * symbol.cc: Likewise. + * trimmed-graph.cc: Likewise. + * varargs.cc: Likewise. + +2025-04-28 David Malcolm <dmalcolm@redhat.com> + + * region-model.cc (region_model::on_stmt_pre): Use internal_error + if we see an unexpected gimple stmt code. + +2025-04-28 David Malcolm <dmalcolm@redhat.com> + + * call-details.cc (call_details::dump): New overload. + (call_details::make_dump_widget): New. + * call-details.h (call_details::dump): Declare new overload. + (call_details::make_dump_widget): New decl. + 2025-03-14 Jakub Jelinek <jakub@redhat.com> PR analyzer/119278 diff --git a/gcc/analyzer/access-diagram.cc b/gcc/analyzer/access-diagram.cc index e1596bb..166be08 100644 --- a/gcc/analyzer/access-diagram.cc +++ b/gcc/analyzer/access-diagram.cc @@ -17,28 +17,18 @@ 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/>. */ -#include "config.h" #define INCLUDE_ALGORITHM #define INCLUDE_MAP #define INCLUDE_SET -#define INCLUDE_VECTOR -#include "system.h" -#include "coretypes.h" -#include "coretypes.h" -#include "tree.h" -#include "function.h" -#include "basic-block.h" -#include "gimple.h" -#include "diagnostic-core.h" -#include "diagnostic.h" +#include "analyzer/common.h" + +#include "fold-const.h" #include "intl.h" -#include "make-unique.h" -#include "tree-diagnostic.h" /* for default_tree_printer. */ -#include "analyzer/analyzer.h" + +#include "text-art/ruler.h" + #include "analyzer/region-model.h" #include "analyzer/access-diagram.h" -#include "text-art/ruler.h" -#include "fold-const.h" #include "analyzer/analyzer-selftests.h" #if ENABLE_ANALYZER @@ -245,7 +235,7 @@ get_access_size_str (style_manager &sm, pp_format_decoder (&pp) = default_tree_printer; if (num_bits.maybe_print_for_user (&pp, op.m_model)) { - if (op.m_dir == DIR_READ) + if (op.m_dir == access_direction::read) return fmt_styled_string (sm, _("read of %qT (%s)"), type, @@ -257,7 +247,7 @@ get_access_size_str (style_manager &sm, pp_formatted_text (&pp)); } } - if (op.m_dir == DIR_READ) + if (op.m_dir == access_direction::read) { if (auto p = num_bits.maybe_get_formatted_str (sm, op.m_model, @@ -284,13 +274,13 @@ get_access_size_str (style_manager &sm, if (type) { - if (op.m_dir == DIR_READ) + if (op.m_dir == access_direction::read) return fmt_styled_string (sm, _("read of %qT"), type); else return fmt_styled_string (sm, _("write of %qT"), type); } - if (op.m_dir == DIR_READ) + if (op.m_dir == access_direction::read) return styled_string (sm, _("read")); else return styled_string (sm, _("write")); @@ -375,11 +365,11 @@ bit_size_expr::maybe_get_formatted_str (text_art::style_manager &sm, if (!wi::fits_uhwi_p (concrete_num_bytes)) return nullptr; if (concrete_num_bytes == 1) - return ::make_unique <text_art::styled_string> + return std::make_unique <text_art::styled_string> (fmt_styled_string (sm, concrete_single_byte_fmt, concrete_num_bytes.to_uhwi ())); else - return ::make_unique <text_art::styled_string> + return std::make_unique <text_art::styled_string> (fmt_styled_string (sm, concrete_plural_bytes_fmt, concrete_num_bytes.to_uhwi ())); } @@ -389,7 +379,7 @@ bit_size_expr::maybe_get_formatted_str (text_art::style_manager &sm, pp_format_decoder (&pp) = default_tree_printer; if (!num_bytes->maybe_print_for_user (&pp, model)) return nullptr; - return ::make_unique <text_art::styled_string> + return std::make_unique <text_art::styled_string> (fmt_styled_string (sm, symbolic_bytes_fmt, pp_formatted_text (&pp))); } @@ -400,11 +390,11 @@ bit_size_expr::maybe_get_formatted_str (text_art::style_manager &sm, if (!wi::fits_uhwi_p (concrete_num_bits)) return nullptr; if (concrete_num_bits == 1) - return ::make_unique <text_art::styled_string> + return std::make_unique <text_art::styled_string> (fmt_styled_string (sm, concrete_single_bit_fmt, concrete_num_bits.to_uhwi ())); else - return ::make_unique <text_art::styled_string> + return std::make_unique <text_art::styled_string> (fmt_styled_string (sm, concrete_plural_bits_fmt, concrete_num_bits.to_uhwi ())); } @@ -414,7 +404,7 @@ bit_size_expr::maybe_get_formatted_str (text_art::style_manager &sm, pp_format_decoder (&pp) = default_tree_printer; if (!m_num_bits.maybe_print_for_user (&pp, model)) return nullptr; - return ::make_unique <text_art::styled_string> + return std::make_unique <text_art::styled_string> (fmt_styled_string (sm, symbolic_bits_fmt, pp_formatted_text (&pp))); } @@ -1126,7 +1116,7 @@ public: { logger.start_log_line (); logger.log_partial ("table_x: %i", table_x); - access_range range_for_column (NULL, bit_range (0, 0)); + access_range range_for_column (nullptr, bit_range (0, 0)); if (maybe_get_access_range_for_table_x (table_x, &range_for_column)) { logger.log_partial (": range: "); @@ -1310,7 +1300,7 @@ class valid_region_spatial_item : public spatial_item { public: valid_region_spatial_item (const access_operation &op, - diagnostic_event_id_t region_creation_event_id, + diagnostics::paths::event_id_t region_creation_event_id, const theme &theme) : m_op (op), m_region_creation_event_id (region_creation_event_id), @@ -1533,7 +1523,7 @@ public: private: const access_operation &m_op; - diagnostic_event_id_t m_region_creation_event_id; + diagnostics::paths::event_id_t m_region_creation_event_id; mutable const boundaries *m_boundaries; const svalue *m_existing_sval; std::unique_ptr<spatial_item> m_existing_sval_spatial_item; @@ -1975,11 +1965,11 @@ make_written_svalue_spatial_item (const access_operation &op, if (const initial_svalue *initial_sval = sval.dyn_cast_initial_svalue ()) if (const string_region *string_reg = initial_sval->get_region ()->dyn_cast_string_region ()) - return make_unique <string_literal_spatial_item> + return std::make_unique <string_literal_spatial_item> (sval, actual_bits, *string_reg, theme, svalue_spatial_item::kind::WRITTEN); - return make_unique <written_svalue_spatial_item> (op, sval, actual_bits); + return std::make_unique <written_svalue_spatial_item> (op, sval, actual_bits); } static std::unique_ptr<spatial_item> @@ -2000,7 +1990,7 @@ make_existing_svalue_spatial_item (const svalue *sval, const initial_svalue *initial_sval = (const initial_svalue *)sval; if (const string_region *string_reg = initial_sval->get_region ()->dyn_cast_string_region ()) - return make_unique <string_literal_spatial_item> + return std::make_unique <string_literal_spatial_item> (*sval, bits, *string_reg, theme, svalue_spatial_item::kind::EXISTING); @@ -2008,7 +1998,7 @@ make_existing_svalue_spatial_item (const svalue *sval, } case SK_COMPOUND: - return make_unique<compound_svalue_spatial_item> + return std::make_unique<compound_svalue_spatial_item> (*((const compound_svalue *)sval), bits, svalue_spatial_item::kind::EXISTING, @@ -2022,7 +2012,7 @@ class access_diagram_impl : public vbox_widget { public: access_diagram_impl (const access_operation &op, - diagnostic_event_id_t region_creation_event_id, + diagnostics::paths::event_id_t region_creation_event_id, style_manager &sm, const theme &theme, logger *logger) @@ -2116,7 +2106,7 @@ public: } m_col_widths - = make_unique <table_dimension_sizes> (m_btm.get_num_columns ()); + = std::make_unique <table_dimension_sizes> (m_btm.get_num_columns ()); /* Now create child widgets. */ @@ -2211,8 +2201,8 @@ private: std::unique_ptr<boundaries> find_boundaries () const { - std::unique_ptr<boundaries> result - = make_unique<boundaries> (*m_op.m_base_region, m_logger); + auto result + = std::make_unique<boundaries> (*m_op.m_base_region, m_logger); m_valid_region_spatial_item.add_boundaries (*result, m_logger); m_accessed_region_spatial_item.add_boundaries (*result, m_logger); @@ -2254,7 +2244,7 @@ private: for (int table_x = 0; table_x < t.get_size ().w; table_x++) { const int table_y = 1; - access_range range_for_column (NULL, bit_range (0, 0)); + access_range range_for_column (nullptr, bit_range (0, 0)); if (m_btm.maybe_get_access_range_for_table_x (table_x, &range_for_column)) { @@ -2271,7 +2261,7 @@ private: void add_direction_widget () { - add_child (::make_unique<direction_widget> (*this, m_btm)); + add_child (std::make_unique<direction_widget> (*this, m_btm)); } void add_invalid_accesses_to_region_table (table &t_region) @@ -2382,7 +2372,7 @@ private: bit_size_expr num_before_bits (invalid_before_bits.get_size (m_op.get_manager ())); std::unique_ptr<styled_string> label; - if (m_op.m_dir == DIR_READ) + if (m_op.m_dir == access_direction::read) label = num_before_bits.maybe_get_formatted_str (m_sm, m_op.m_model, _("under-read of %wi bit"), @@ -2423,7 +2413,7 @@ private: maybe_add_gap (w, invalid_before_bits, valid_bits); std::unique_ptr<styled_string> label; - if (m_op.m_dir == DIR_READ) + if (m_op.m_dir == access_direction::read) label = num_valid_bits.maybe_get_formatted_str (m_sm, m_op.m_model, _("size: %wi bit"), @@ -2459,7 +2449,7 @@ private: bit_size_expr num_after_bits (invalid_after_bits.get_size (m_op.get_manager ())); std::unique_ptr<styled_string> label; - if (m_op.m_dir == DIR_READ) + if (m_op.m_dir == access_direction::read) label = num_after_bits.maybe_get_formatted_str (m_sm, m_op.m_model, _("over-read of %wi bit"), @@ -2505,7 +2495,7 @@ private: std::vector<bit_offset_t> bit_sizes (num_columns); for (unsigned table_x = 0; table_x < num_columns; table_x++) { - access_range range_for_column (NULL, bit_range (0, 0)); + access_range range_for_column (nullptr, bit_range (0, 0)); if (m_btm.maybe_get_access_range_for_table_x (table_x, &range_for_column)) { @@ -2574,7 +2564,7 @@ private: } const access_operation &m_op; - diagnostic_event_id_t m_region_creation_event_id; + diagnostics::paths::event_id_t m_region_creation_event_id; style_manager &m_sm; const theme &m_theme; logger *m_logger; @@ -2658,7 +2648,7 @@ direction_widget::paint_to_canvas (canvas &canvas) (canvas, canvas_x, canvas::range_t (get_y_range ()), - (m_dia_impl.get_op ().m_dir == DIR_READ + (m_dia_impl.get_op ().m_dir == access_direction::read ? theme::y_arrow_dir::UP : theme::y_arrow_dir::DOWN), style_id); @@ -2672,15 +2662,16 @@ direction_widget::paint_to_canvas (canvas &canvas) an access_diagram_impl. */ access_diagram::access_diagram (const access_operation &op, - diagnostic_event_id_t region_creation_event_id, + diagnostics::paths::event_id_t region_creation_event_id, style_manager &sm, const theme &theme, logger *logger) -: wrapper_widget (make_unique <access_diagram_impl> (op, - region_creation_event_id, - sm, - theme, - logger)) +: wrapper_widget + (std::make_unique <access_diagram_impl> (op, + region_creation_event_id, + sm, + theme, + logger)) { } diff --git a/gcc/analyzer/access-diagram.h b/gcc/analyzer/access-diagram.h index ba4649a..d558f53 100644 --- a/gcc/analyzer/access-diagram.h +++ b/gcc/analyzer/access-diagram.h @@ -24,7 +24,7 @@ along with GCC; see the file COPYING3. If not see #include "text-art/canvas.h" #include "text-art/theme.h" #include "text-art/widget.h" -#include "analyzer/analyzer.h" + #include "analyzer/store.h" namespace ana { @@ -152,7 +152,7 @@ class access_diagram : public text_art::wrapper_widget { public: access_diagram (const access_operation &op, - diagnostic_event_id_t region_creation_event_id, + diagnostics::paths::event_id_t region_creation_event_id, text_art::style_manager &sm, const text_art::theme &theme, logger *logger); diff --git a/gcc/analyzer/ana-state-to-diagnostic-state.cc b/gcc/analyzer/ana-state-to-diagnostic-state.cc new file mode 100644 index 0000000..996538c --- /dev/null +++ b/gcc/analyzer/ana-state-to-diagnostic-state.cc @@ -0,0 +1,689 @@ +/* Creating diagnostic state graphs from ana::program_state. + Copyright (C) 2025 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/>. */ + +#define INCLUDE_ALGORITHM +#define INCLUDE_MAP +#define INCLUDE_SET +#include "analyzer/common.h" + +#include "diagnostics/state-graphs.h" +#include "diagnostics/sarif-sink.h" + +#include "analyzer/region-model.h" +#include "analyzer/program-state.h" +#include "analyzer/record-layout.h" +#include "analyzer/ana-state-to-diagnostic-state.h" + +#if ENABLE_ANALYZER + +#if __GNUC__ >= 10 +#pragma GCC diagnostic ignored "-Wformat" +#endif + +namespace ana { + +using namespace ::diagnostics::state_graphs; + +static void +set_wi_attr (state_node_ref state_node, + const char *attr_name, + const wide_int_ref &w, + signop sgn) +{ + pretty_printer pp; + pp_wide_int (&pp, w, sgn); + state_node.set_attr (attr_name, pp_formatted_text (&pp)); +} + +static void +set_type_attr (state_node_ref state_node, const_tree type) +{ + gcc_assert (type); + pretty_printer pp; + pp_format_decoder (&pp) = default_tree_printer; + pp_printf (&pp, "%T", type); + state_node.set_type (pp_formatted_text (&pp)); +} + +static void +set_bits_attr (state_node_ref state_node, + bit_range bits) +{ + pretty_printer pp; + bits.dump_to_pp (&pp); + state_node.set_attr ("bits", pp_formatted_text (&pp)); +} + +// class analyzer_state_graph : public diagnostics::digraphs::digraph + +analyzer_state_graph::analyzer_state_graph (const program_state &state, + const extrinsic_state &ext_state) +: m_state (state), + m_ext_state (ext_state), + m_mgr (*ext_state.get_engine ()->get_model_manager ()), + m_next_id (0) +{ + /* Find pointers to heap-allocated regions, and record their types, + so that we have a user-friendly way of showing the memory + (by field, rather than by byte offset). */ + for (auto cluster_iter : *state.m_region_model->get_store ()) + for (auto binding_iter : *cluster_iter.second) + { + const svalue *svalue = binding_iter.second; + if (const region *reg = svalue->maybe_get_region ()) + if (svalue->get_type () && !reg->get_type ()) + { + tree pointed_to_type = TREE_TYPE (svalue->get_type ()); + if (!VOID_TYPE_P (pointed_to_type)) + m_types_for_untyped_regions[reg] = pointed_to_type; + } + } + + /* TODO: look for vtable pointers at the top of dynamically-allocated + regions and use that type as a fallback. */ + + /* Find regions of interest. + Create elements per region, and build into hierarchy. + Add edges for pointers. */ + + /* Create stack, from top to bottom. */ + for (int i = state.m_region_model->get_stack_depth () - 1; i >= 0; --i) + { + const frame_region *reg = state.m_region_model->get_frame_at_index (i); + get_or_create_state_node (*reg); + } + + /* Create bound memory. */ + for (auto iter : *state.m_region_model->get_store ()) + { + const bool create_all = false; // "true" for verbose, for debugging + create_state_nodes_for_binding_cluster (*iter.second, create_all); + } + + /* TODO: Constraints. */ + + /* Annotate with information from state machines. */ + { + int i; + sm_state_map *smap; + FOR_EACH_VEC_ELT (state.m_checker_states, i, smap) + { + auto &sm = ext_state.get_sm (i); + for (const auto &iter : *smap) + sm.add_state_to_state_graph (*this, *iter.first, iter.second.m_state); + if (auto s = smap->get_global_state ()) + sm.add_global_state_to_state_graph (*this, s); + } + } + + /* Process pending edges. */ + while (m_pending_edges.size () > 0) + { + pending_edge item = m_pending_edges.back (); + m_pending_edges.pop_back (); + + /* Ensure we have a node for the dst region. This + could lead to additional pending edges. */ + auto dst_node = get_or_create_state_node (item.m_dst_reg); + add_edge (nullptr, item.m_src_node.m_node, dst_node.m_node); + } +} + +state_node_ref +analyzer_state_graph::get_or_create_state_node (const region ®) +{ + auto existing = m_region_to_state_node_map.find (®); + if (existing != m_region_to_state_node_map.end ()) + return *existing->second; + + auto ref = create_and_add_state_node (reg); + m_region_to_state_node_map[®] = &ref.m_node; + return ref; +} + +state_node_ref +analyzer_state_graph::create_and_add_state_node (const region ®) +{ + auto node = create_state_node (reg); + + state_node_ref result = *node; + if (auto parent_reg = reg.get_parent_region ()) + if (parent_reg->get_kind () != RK_ROOT) + { + auto parent_state_node = get_or_create_state_node (*parent_reg); + parent_state_node.m_node.add_child (std::move (node)); + return result; + } + add_node (std::move (node)); + return result; +} + +std::string +analyzer_state_graph::make_node_id (const char *prefix) +{ + return std::string (prefix) + "-" + std::to_string (m_next_id++); +} + +std::string +analyzer_state_graph::make_node_id (const region ®) +{ + const char *prefix = nullptr; + switch (reg.get_kind ()) + { + case RK_ROOT: + default: + gcc_unreachable (); + break; + + case RK_GLOBALS: + return "globals"; + case RK_CODE: + return "code"; + case RK_STACK: + return "stack"; + case RK_HEAP: + return "heap"; + + case RK_FRAME: + prefix = "frame-region"; + break; + case RK_FUNCTION: + prefix = "function-region"; + break; + case RK_LABEL: + prefix = "label-region"; + break; + case RK_THREAD_LOCAL: + prefix = "thread-local-region"; + break; + case RK_SYMBOLIC: + prefix = "symbolic-region"; + break; + case RK_DECL: + prefix = "decl-region"; + break; + case RK_FIELD: + prefix = "field-region"; + break; + case RK_ELEMENT: + prefix = "element-region"; + break; + case RK_OFFSET: + prefix = "offset-region"; + break; + case RK_SIZED: + prefix = "sized-region"; + break; + case RK_CAST: + prefix = "cast-region"; + break; + case RK_HEAP_ALLOCATED: + prefix = "heap-allocated-region"; + break; + case RK_ALLOCA: + prefix = "alloca-region"; + break; + case RK_STRING: + prefix = "string-region"; + break; + case RK_BIT_RANGE: + prefix = "bit-range-region"; + break; + case RK_VAR_ARG: + prefix = "var-arg-region"; + break; + case RK_ERRNO: + prefix = "errno-region"; + break; + case RK_PRIVATE: + prefix = "private-region"; + break; + case RK_UNKNOWN: + prefix = "unknown-region"; + break; + } + return std::string (prefix) + "-" + std::to_string (reg.get_id ()); +} + +std::unique_ptr<diagnostics::digraphs::node> +analyzer_state_graph:: +make_state_node (diagnostics::state_graphs::node_kind kind, + std::string id) +{ + auto node = std::make_unique<diagnostics::digraphs::node> (*this, std::move (id)); + state_node_ref node_ref (*node); + node_ref.set_node_kind (kind); + return node; +} + +std::unique_ptr<diagnostics::digraphs::node> +analyzer_state_graph:: +make_memspace_state_node (const region ®, + diagnostics::state_graphs::node_kind kind) +{ + return make_state_node (kind, make_node_id (reg)); +} + +std::unique_ptr<diagnostics::digraphs::node> +analyzer_state_graph::create_state_node (const region ®) +{ + std::unique_ptr<diagnostics::digraphs::node> node; + + switch (reg.get_kind ()) + { + default: + gcc_unreachable (); + + case RK_FRAME: + { + const frame_region &frame_reg + = static_cast<const frame_region &> (reg); + + node = make_state_node (diagnostics::state_graphs::node_kind::stack_frame, + make_node_id (reg)); + node->set_logical_loc + (m_logical_loc_mgr.key_from_tree (frame_reg.get_fndecl ())); + { + pretty_printer pp; + pp_format_decoder (&pp) = default_tree_printer; + pp_printf (&pp, "%E", frame_reg.get_fndecl ()); + node->set_attr (STATE_NODE_PREFIX, "function", + pp_formatted_text (&pp)); + } + } + break; + + case RK_GLOBALS: + node = make_memspace_state_node (reg, + diagnostics::state_graphs::node_kind::globals); + break; + case RK_CODE: + node = make_memspace_state_node (reg, + diagnostics::state_graphs::node_kind::code); + break; + case RK_FUNCTION: + node = make_memspace_state_node (reg, + diagnostics::state_graphs::node_kind::function); + // TODO + break; + + case RK_STACK: + node = make_memspace_state_node (reg, + diagnostics::state_graphs::node_kind::stack); + break; + case RK_HEAP: + node = make_memspace_state_node (reg, + diagnostics::state_graphs::node_kind::heap_); + break; + case RK_THREAD_LOCAL: + node = make_memspace_state_node (reg, + diagnostics::state_graphs::node_kind::thread_local_); + break; + case RK_ROOT: + gcc_unreachable (); + break; + case RK_SYMBOLIC: + node = make_memspace_state_node (reg, + diagnostics::state_graphs::node_kind::other); + break; + + case RK_DECL: + { + node = make_state_node (diagnostics::state_graphs::node_kind::variable, + make_node_id (reg)); + const decl_region &decl_reg + = static_cast<const decl_region &> (reg); + state_node_ref node_ref (*node); + { + pretty_printer pp; + pp_format_decoder (&pp) = default_tree_printer; + pp_printf (&pp, "%E", decl_reg.get_decl ()); + node_ref.set_name (pp_formatted_text (&pp)); + } + set_type_attr (*node, TREE_TYPE (decl_reg.get_decl ())); + } + break; + + case RK_FIELD: + case RK_ELEMENT: + /* These should be handled in populate_state_node_for_typed_region. */ + gcc_unreachable (); + break; + + case RK_LABEL: + case RK_OFFSET: + case RK_SIZED: + case RK_CAST: + case RK_STRING: + case RK_BIT_RANGE: + case RK_VAR_ARG: + case RK_ERRNO: + case RK_PRIVATE: + case RK_UNKNOWN: + node = make_state_node (diagnostics::state_graphs::node_kind::other, + make_node_id (reg)); + break; + + case RK_HEAP_ALLOCATED: + case RK_ALLOCA: + node = make_memspace_state_node (reg, + diagnostics::state_graphs::node_kind::dynalloc_buffer); + set_attr_for_dynamic_extents (reg, *node); + break; + } + gcc_assert (node); + + if (reg.get_base_region () == ®) + if (!reg.get_type ()) + { + auto search + = m_types_for_untyped_regions.find (®); + if (search != m_types_for_untyped_regions.end ()) + { + tree type_to_use = search->second; + set_type_attr (*node, type_to_use); + } + } + + return node; +} + +void +analyzer_state_graph:: +create_state_nodes_for_binding_cluster (const binding_cluster &cluster, + bool create_all) +{ + /* TODO: + - symbolic bindings + - get current svalue, so as to get "zeros" and "uninitialized". */ + + concrete_bindings_t conc_bindings; + for (auto iter : cluster) + { + const binding_key *key = iter.first; + const svalue *svalue = iter.second; + if (auto conc_key = key->dyn_cast_concrete_binding ()) + conc_bindings[conc_key->get_bit_range ()] = svalue; + if (const region *reg = svalue->maybe_get_region ()) + get_or_create_state_node (*reg); + } + + auto ref = get_or_create_state_node (*cluster.get_base_region ()); + + ref.m_node.add_child (create_state_node_for_conc_bindings (conc_bindings)); + + const region *typed_reg = cluster.get_base_region (); + if (!typed_reg->get_type ()) + { + auto search + = m_types_for_untyped_regions.find (cluster.get_base_region ()); + if (search != m_types_for_untyped_regions.end ()) + { + tree type_to_use = search->second; + typed_reg = m_mgr.get_cast_region (typed_reg, type_to_use); + } + } + + if (typed_reg->get_type ()) + populate_state_node_for_typed_region (ref, + *typed_reg, + conc_bindings, + create_all); + else + { + // TODO + } +} + +std::unique_ptr<diagnostics::digraphs::node> +analyzer_state_graph::create_state_node_for_conc_bindings (const concrete_bindings_t &conc_bindings) +{ + auto node = make_state_node (diagnostics::state_graphs::node_kind::other, + make_node_id ("concrete-bindings")); + for (auto iter : conc_bindings) + { + const bit_range bits = iter.first; + const svalue *sval = iter.second; + auto binding_state_node + = make_state_node (diagnostics::state_graphs::node_kind::other, + make_node_id ("binding")); + set_bits_attr (*binding_state_node, bits); + { + pretty_printer pp; + pp_format_decoder (&pp) = default_tree_printer; + sval->dump_to_pp (&pp, true); + binding_state_node->set_attr (STATE_NODE_PREFIX, "value", + pp_formatted_text (&pp)); + } + node->add_child (std::move (binding_state_node)); + } + return node; +} + +// Try to get the bit_range of REG within its base region +bool +analyzer_state_graph::get_bit_range_within_base_region (const region ®, + bit_range &out) +{ + region_offset start_offset = reg.get_offset (&m_mgr); + if (!start_offset.concrete_p ()) + return false; + region_offset next_offset = reg.get_next_offset (&m_mgr); + if (!next_offset.concrete_p ()) + return false; + out = bit_range (start_offset.get_bit_offset (), + next_offset.get_bit_offset () + - start_offset.get_bit_offset ()); + return true; +} + +void +analyzer_state_graph:: +populate_state_node_for_typed_region (state_node_ref node, + const region ®, + const concrete_bindings_t &conc_bindings, + bool create_all) +{ + const_tree reg_type = reg.get_type (); + gcc_assert (reg_type); + set_type_attr (node, reg_type); + + bit_range bits (0, 0); + if (get_bit_range_within_base_region (reg, bits)) + { + set_bits_attr (node, bits); + + auto search = conc_bindings.find (bits); + if (search != conc_bindings.end ()) + { + const svalue *bound_sval = search->second; + node.set_json_attr ("value", bound_sval->to_json ()); + if (const region *dst_reg = bound_sval->maybe_get_region ()) + m_pending_edges.push_back ({node, *dst_reg}); + } + } + + switch (TREE_CODE (reg_type)) + { + default: + break; + + case ARRAY_TYPE: + { + tree domain = TYPE_DOMAIN (reg_type); + if (!domain) + return; + const_tree max_idx = TYPE_MAX_VALUE (domain); + if (!max_idx) + return; + if (TREE_CODE (max_idx) != INTEGER_CST) + return; + const_tree min_idx = TYPE_MIN_VALUE (domain); + if (TREE_CODE (min_idx) != INTEGER_CST) + return; + for (offset_int idx = wi::to_offset (min_idx); + idx <= wi::to_offset (max_idx); + ++idx) + { + const_tree element_type = TREE_TYPE (reg_type); + const svalue *sval_index + = m_mgr.get_or_create_int_cst (domain, idx); + const region *child_reg + = m_mgr.get_element_region (®, + const_cast<tree> (element_type), + sval_index); + if (show_child_state_node_for_child_region_p (*child_reg, + conc_bindings, + create_all)) + { + auto child_state_node + = make_state_node + (diagnostics::state_graphs::node_kind::element, + make_node_id (*child_reg)); + set_wi_attr (*child_state_node, "index", idx, UNSIGNED); + + // Recurse: + gcc_assert (element_type); + populate_state_node_for_typed_region (*child_state_node, + *child_reg, + conc_bindings, + create_all); + node.m_node.add_child (std::move (child_state_node)); + } + } + } + break; + + case RECORD_TYPE: + { + const record_layout layout (reg_type); + for (auto item : layout) + { + if (item.m_is_padding) + { + const bit_range bits (0, item.m_bit_range.m_size_in_bits); + const region *child_reg + = m_mgr.get_bit_range (®, NULL_TREE, bits); + if (show_child_state_node_for_child_region_p (*child_reg, + conc_bindings, + create_all)) + { + auto child_state_node + = make_state_node + (diagnostics::state_graphs::node_kind::padding, + make_node_id (*child_reg)); + set_wi_attr (*child_state_node, "num_bits", + item.m_bit_range.m_size_in_bits, SIGNED); + node.m_node.add_child (std::move (child_state_node)); + } + } + else + { + const region *child_reg + = m_mgr.get_field_region (®, + const_cast<tree> (item.m_field)); + if (show_child_state_node_for_child_region_p (*child_reg, + conc_bindings, + create_all)) + { + auto child_state_node + = make_state_node + (diagnostics::state_graphs::node_kind::field, + make_node_id (*child_reg)); + { + pretty_printer pp; + pp_format_decoder (&pp) = default_tree_printer; + pp_printf (&pp, "%D", item.m_field); + child_state_node->set_attr (STATE_NODE_PREFIX, "name", + pp_formatted_text (&pp)); + } + + // Recurse: + populate_state_node_for_typed_region (*child_state_node, + *child_reg, + conc_bindings, + create_all); + node.m_node.add_child (std::move (child_state_node)); + } + } + } + } + break; + } +} + +void +analyzer_state_graph::set_attr_for_dynamic_extents (const region ®, + state_node_ref node_ref) +{ + const svalue *sval = m_state.m_region_model->get_dynamic_extents (®); + if (sval) + { + pretty_printer pp; + pp_format_decoder (&pp) = default_tree_printer; + if (auto cst = sval->maybe_get_constant ()) + pp_wide_int (&pp, wi::to_wide (cst), UNSIGNED); + else + sval->dump_to_pp (&pp, true); + node_ref.set_attr ("dynamic-extents", pp_formatted_text (&pp)); + } +} + +bool +analyzer_state_graph:: +show_child_state_node_for_child_region_p (const region ®, + const concrete_bindings_t &conc_bindings, + bool create_all) +{ + if (create_all) + return true; + bit_range reg_bits (0, 0); + if (!get_bit_range_within_base_region (reg, reg_bits)) + return true; + + /* Is any of "bits" bound? + TODO: ideally there would be a more efficient way to do this, using + spatial relationships. */ + for (auto iter : conc_bindings) + { + const bit_range bound_bits = iter.first; + if (bound_bits.intersects_p (reg_bits)) + return true; + } + return false; +} + +std::unique_ptr<diagnostics::digraphs::digraph> +program_state:: +make_diagnostic_state_graph (const extrinsic_state &ext_state) const +{ + return std::make_unique<analyzer_state_graph> (*this, ext_state); +} + +void +program_state::dump_sarif (const extrinsic_state &ext_state) const +{ + auto g = make_diagnostic_state_graph (ext_state); + g->dump (); +} + +} // namespace ana + +#endif /* #if ENABLE_ANALYZER */ diff --git a/gcc/analyzer/ana-state-to-diagnostic-state.h b/gcc/analyzer/ana-state-to-diagnostic-state.h new file mode 100644 index 0000000..3a5ccc1 --- /dev/null +++ b/gcc/analyzer/ana-state-to-diagnostic-state.h @@ -0,0 +1,106 @@ +/* Creating diagnostic state graphs from ana::program_state. + Copyright (C) 2025 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_ANA_STATE_TO_DIAGNOSTIC_STATE_H +#define GCC_ANALYZER_ANA_STATE_TO_DIAGNOSTIC_STATE_H + +#include "diagnostics/state-graphs.h" +#include "tree-logical-location.h" + +namespace ana { + +class analyzer_state_graph : public diagnostics::digraphs::digraph +{ +public: + analyzer_state_graph (const program_state &state, + const extrinsic_state &ext_state); + diagnostics::state_graphs::state_node_ref + get_or_create_state_node (const region ®); + +private: + struct pending_edge + { + diagnostics::state_graphs::state_node_ref m_src_node; + const region &m_dst_reg; + }; + + diagnostics::state_graphs::state_node_ref + create_and_add_state_node (const region ®); + + std::unique_ptr<diagnostics::digraphs::node> + make_state_node (diagnostics::state_graphs::node_kind kind, + std::string id); + + std::unique_ptr<diagnostics::digraphs::node> + make_memspace_state_node (const region ®, + enum diagnostics::state_graphs::node_kind kind); + + std::unique_ptr<diagnostics::digraphs::node> + create_state_node (const region ®); + + /* Spatially sorted concrete bindings. */ + typedef std::map<bit_range, const svalue *> concrete_bindings_t; + + void + create_state_nodes_for_binding_cluster (const binding_cluster &cluster, + bool create_all); + + std::unique_ptr<diagnostics::digraphs::node> + create_state_node_for_conc_bindings (const concrete_bindings_t &conc_bindings); + + // Try to get the bit_range of REG within its base region + bool + get_bit_range_within_base_region (const region ®, + bit_range &out); + + void + populate_state_node_for_typed_region (diagnostics::state_graphs::state_node_ref, + const region ®, + const concrete_bindings_t &conc_bindings, + bool create_all); + + void + set_attr_for_dynamic_extents (const region ®, + diagnostics::state_graphs::state_node_ref); + + bool + show_child_state_node_for_child_region_p (const region ®, + const concrete_bindings_t &conc_bindings, + bool create_all); + + std::unique_ptr<diagnostics::digraphs::node> + create_state_node_for_svalue (const svalue *sval); + + std::string make_node_id (const region ®); + std::string make_node_id (const char *prefix); + + tree_logical_location_manager m_logical_loc_mgr; + const program_state &m_state; + const extrinsic_state &m_ext_state; + region_model_manager &m_mgr; + std::map<const region *, diagnostics::digraphs::node *> m_region_to_state_node_map; + std::map<const region *, tree> m_types_for_untyped_regions; + unsigned m_next_id; + std::vector<pending_edge> m_pending_edges; +}; + +} // namespace ana + +#endif /* GCC_ANALYZER_ANA_STATE_TO_DIAGNOSTIC_STATE_H */ diff --git a/gcc/analyzer/analysis-plan.cc b/gcc/analyzer/analysis-plan.cc index 7ae01c0..c563554 100644 --- a/gcc/analyzer/analysis-plan.cc +++ b/gcc/analyzer/analysis-plan.cc @@ -18,27 +18,19 @@ 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/>. */ -#include "config.h" -#include "system.h" -#include "coretypes.h" -#include "tree.h" -#include "options.h" -#include "cgraph.h" +#include "analyzer/common.h" + #include "timevar.h" -#include "ipa-utils.h" -#include "function.h" -#include "analyzer/analyzer.h" -#include "diagnostic-core.h" -#include "analyzer/analyzer-logging.h" -#include "analyzer/analysis-plan.h" #include "ordered-hash-map.h" #include "options.h" #include "cgraph.h" #include "cfg.h" -#include "basic-block.h" -#include "gimple.h" #include "gimple-iterator.h" #include "digraph.h" +#include "ipa-utils.h" + +#include "analyzer/analyzer-logging.h" +#include "analyzer/analysis-plan.h" #include "analyzer/supergraph.h" #if ENABLE_ANALYZER diff --git a/gcc/analyzer/analyzer-language.cc b/gcc/analyzer/analyzer-language.cc index a0fa9f5..d4169f7 100644 --- a/gcc/analyzer/analyzer-language.cc +++ b/gcc/analyzer/analyzer-language.cc @@ -18,15 +18,13 @@ 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/>. */ -#include "config.h" -#include "system.h" -#include "coretypes.h" -#include "tree.h" +#include "analyzer/common.h" + +#include "diagnostic.h" #include "stringpool.h" -#include "analyzer/analyzer.h" + #include "analyzer/analyzer-language.h" #include "analyzer/analyzer-logging.h" -#include "diagnostic.h" /* Map from identifier to INTEGER_CST. */ static GTY (()) hash_map <tree, tree> *analyzer_stashed_constants; @@ -116,7 +114,7 @@ on_finish_translation_unit (const translation_unit &tu) return; FILE *logfile = get_or_create_any_logfile (); - log_user the_logger (NULL); + log_user the_logger (nullptr); if (logfile) the_logger.set_logger (new logger (logfile, 0, 0, *global_dc->get_reference_printer ())); diff --git a/gcc/analyzer/analyzer-language.h b/gcc/analyzer/analyzer-language.h index 1987ee0..a0c85a8 100644 --- a/gcc/analyzer/analyzer-language.h +++ b/gcc/analyzer/analyzer-language.h @@ -35,7 +35,7 @@ class translation_unit public: /* Attempt to look up an value for identifier ID (e.g. in the headers that have been seen). If it is defined and an integer (e.g. either as a - macro or enum), return the INTEGER_CST value, otherwise return NULL. */ + macro or enum), return the INTEGER_CST value, otherwise return NULL_TREE. */ virtual tree lookup_constant_by_id (tree id) const = 0; virtual tree lookup_type_by_id (tree id) const = 0; virtual tree lookup_global_var_by_id (tree id) const = 0; diff --git a/gcc/analyzer/analyzer-logging.h b/gcc/analyzer/analyzer-logging.h index 3208989..e85d293 100644 --- a/gcc/analyzer/analyzer-logging.h +++ b/gcc/analyzer/analyzer-logging.h @@ -91,7 +91,7 @@ public: /* The constructor for log_scope. - The normal case is that the logger is NULL, in which case this should + The normal case is that the logger is nullptr, in which case this should be largely a no-op. If we do have a logger, notify it that we're entering the given scope. @@ -139,7 +139,8 @@ log_scope::~log_scope () } } -/* A log_user is something that potentially uses a logger (which could be NULL). +/* A log_user is something that potentially uses a logger (which could be + nullptr). The log_user class keeps the reference-count of a logger up-to-date. */ @@ -169,8 +170,8 @@ class log_user FILE *get_logger_file () const { - if (m_logger == NULL) - return NULL; + if (m_logger == nullptr) + return nullptr; return m_logger->get_file (); } @@ -181,7 +182,7 @@ class log_user }; /* A shortcut for calling log from a log_user, handling the common - case where the underlying logger is NULL via a no-op. */ + case where the underlying logger is nullptr via a no-op. */ inline void log_user::log (const char *fmt, ...) const @@ -196,7 +197,7 @@ log_user::log (const char *fmt, ...) const } /* A shortcut for starting a log line from a log_user, - handling the common case where the underlying logger is NULL via + handling the common case where the underlying logger is nullptr via a no-op. */ inline void @@ -207,7 +208,7 @@ log_user::start_log_line () const } /* A shortcut for ending a log line from a log_user, - handling the common case where the underlying logger is NULL via + handling the common case where the underlying logger is nullptr via a no-op. */ inline void @@ -218,7 +219,7 @@ log_user::end_log_line () const } /* A shortcut for recording entry into a scope from a log_user, - handling the common case where the underlying logger is NULL via + handling the common case where the underlying logger is nullptr via a no-op. */ inline void @@ -229,7 +230,7 @@ log_user::enter_scope (const char *scope_name) } /* A shortcut for recording exit from a scope from a log_user, - handling the common case where the underlying logger is NULL via + handling the common case where the underlying logger is nullptr via a no-op. */ inline void diff --git a/gcc/analyzer/analyzer-pass.cc b/gcc/analyzer/analyzer-pass.cc index ab41de4..559fb94 100644 --- a/gcc/analyzer/analyzer-pass.cc +++ b/gcc/analyzer/analyzer-pass.cc @@ -18,15 +18,10 @@ 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/>. */ -#include "config.h" -#include "system.h" -#include "coretypes.h" -#include "context.h" +#include "analyzer/common.h" + #include "tree-pass.h" -#include "diagnostic.h" -#include "options.h" -#include "tree.h" -#include "analyzer/analyzer.h" + #include "analyzer/engine.h" namespace { @@ -53,15 +48,15 @@ class pass_analyzer : public ipa_opt_pass_d public: pass_analyzer(gcc::context *ctxt) : ipa_opt_pass_d (pass_data_analyzer, ctxt, - NULL, /* generate_summary */ - NULL, /* write_summary */ - NULL, /* read_summary */ - NULL, /* write_optimization_summary */ - NULL, /* read_optimization_summary */ - NULL, /* stmt_fixup */ + nullptr, /* generate_summary */ + nullptr, /* write_summary */ + nullptr, /* read_summary */ + nullptr, /* write_optimization_summary */ + nullptr, /* read_optimization_summary */ + nullptr, /* stmt_fixup */ 0, /* function_transform_todo_flags_start */ - NULL, /* function_transform */ - NULL) /* variable_transform */ + nullptr, /* function_transform */ + nullptr) /* variable_transform */ {} /* opt_pass methods: */ diff --git a/gcc/analyzer/analyzer-selftests.cc b/gcc/analyzer/analyzer-selftests.cc index 0f131f0..334c355 100644 --- a/gcc/analyzer/analyzer-selftests.cc +++ b/gcc/analyzer/analyzer-selftests.cc @@ -18,12 +18,10 @@ 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/>. */ -#include "config.h" -#include "system.h" -#include "coretypes.h" -#include "tree.h" +#include "analyzer/common.h" + #include "stringpool.h" -#include "analyzer/analyzer.h" + #include "analyzer/analyzer-selftests.h" #if CHECKING_P diff --git a/gcc/analyzer/analyzer.cc b/gcc/analyzer/analyzer.cc index da7491b..9f6f7b4 100644 --- a/gcc/analyzer/analyzer.cc +++ b/gcc/analyzer/analyzer.cc @@ -18,20 +18,12 @@ 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/>. */ -#include "config.h" -#include "system.h" -#include "coretypes.h" -#include "tree.h" -#include "function.h" -#include "basic-block.h" -#include "gimple.h" -#include "diagnostic.h" -#include "intl.h" -#include "analyzer/analyzer.h" +#include "analyzer/common.h" + #include "tree-pretty-print.h" -#include "diagnostic-event-id.h" +#include "diagnostics/event-id.h" #include "tree-dfa.h" -#include "make-unique.h" +#include "intl.h" #if ENABLE_ANALYZER @@ -227,11 +219,11 @@ std::unique_ptr<json::value> tree_to_json (tree node) { if (!node) - return ::make_unique<json::literal> (json::JSON_NULL); + return std::make_unique<json::literal> (json::JSON_NULL); pretty_printer pp; dump_generic_node (&pp, node, 0, TDF_VOPS|TDF_MEMSYMS, false); - return ::make_unique<json::string> (pp_formatted_text (&pp)); + return std::make_unique<json::string> (pp_formatted_text (&pp)); } /* Generate a JSON value for EVENT_ID. @@ -240,16 +232,16 @@ tree_to_json (tree node) for unknown). */ std::unique_ptr<json::value> -diagnostic_event_id_to_json (const diagnostic_event_id_t &event_id) +diagnostic_event_id_to_json (const diagnostics::paths::event_id_t &event_id) { if (event_id.known_p ()) { pretty_printer pp; pp_printf (&pp, "%@", &event_id); - return ::make_unique<json::string> (pp_formatted_text (&pp)); + return std::make_unique<json::string> (pp_formatted_text (&pp)); } else - return ::make_unique<json::literal> (json::JSON_NULL); + return std::make_unique<json::literal> (json::JSON_NULL); } /* Generate a JSON value for OFFSET. @@ -261,7 +253,7 @@ bit_offset_to_json (const bit_offset_t &offset) { pretty_printer pp; pp_wide_int_large (&pp, offset, SIGNED); - return ::make_unique<json::string> (pp_formatted_text (&pp)); + return std::make_unique<json::string> (pp_formatted_text (&pp)); } /* Generate a JSON value for OFFSET. @@ -273,7 +265,7 @@ byte_offset_to_json (const byte_offset_t &offset) { pretty_printer pp; pp_wide_int_large (&pp, offset, SIGNED); - return ::make_unique<json::string> (pp_formatted_text (&pp)); + return std::make_unique<json::string> (pp_formatted_text (&pp)); } /* Workaround for lack of const-correctness of ssa_default_def. */ @@ -298,12 +290,12 @@ get_ssa_default_def (const function &fun, tree var) If LOOK_IN_STD is true, then also look for within std:: for the name. */ bool -is_special_named_call_p (const gcall *call, const char *funcname, +is_special_named_call_p (const gcall &call, const char *funcname, unsigned int num_args, bool look_in_std) { gcc_assert (funcname); - tree fndecl = gimple_call_fndecl (call); + tree fndecl = gimple_call_fndecl (&call); if (!fndecl) return false; @@ -396,7 +388,7 @@ is_std_named_call_p (const_tree fndecl, const char *funcname) bool is_named_call_p (const_tree fndecl, const char *funcname, - const gcall *call, unsigned int num_args) + const gcall &call, unsigned int num_args) { gcc_assert (fndecl); gcc_assert (funcname); @@ -404,7 +396,7 @@ is_named_call_p (const_tree fndecl, const char *funcname, if (!is_named_call_p (fndecl, funcname)) return false; - if (gimple_call_num_args (call) != num_args) + if (gimple_call_num_args (&call) != num_args) return false; return true; @@ -414,7 +406,7 @@ is_named_call_p (const_tree fndecl, const char *funcname, bool is_std_named_call_p (const_tree fndecl, const char *funcname, - const gcall *call, unsigned int num_args) + const gcall &call, unsigned int num_args) { gcc_assert (fndecl); gcc_assert (funcname); @@ -422,7 +414,7 @@ is_std_named_call_p (const_tree fndecl, const char *funcname, if (!is_std_named_call_p (fndecl, funcname)) return false; - if (gimple_call_num_args (call) != num_args) + if (gimple_call_num_args (&call) != num_args) return false; return true; @@ -431,12 +423,12 @@ is_std_named_call_p (const_tree fndecl, const char *funcname, /* Return true if stmt is a setjmp or sigsetjmp call. */ bool -is_setjmp_call_p (const gcall *call) +is_setjmp_call_p (const gcall &call) { if (is_special_named_call_p (call, "setjmp", 1) || is_special_named_call_p (call, "sigsetjmp", 2)) /* region_model::on_setjmp requires a pointer. */ - if (POINTER_TYPE_P (TREE_TYPE (gimple_call_arg (call, 0)))) + if (POINTER_TYPE_P (TREE_TYPE (gimple_call_arg (&call, 0)))) return true; return false; @@ -445,26 +437,46 @@ is_setjmp_call_p (const gcall *call) /* Return true if stmt is a longjmp or siglongjmp call. */ bool -is_longjmp_call_p (const gcall *call) +is_longjmp_call_p (const gcall &call) { if (is_special_named_call_p (call, "longjmp", 2) || is_special_named_call_p (call, "siglongjmp", 2)) /* exploded_node::on_longjmp requires a pointer for the initial argument. */ - if (POINTER_TYPE_P (TREE_TYPE (gimple_call_arg (call, 0)))) + if (POINTER_TYPE_P (TREE_TYPE (gimple_call_arg (&call, 0)))) return true; return false; } +bool +is_cxa_throw_p (const gcall &call) +{ + tree fndecl = gimple_call_fndecl (&call); + if (!fndecl) + return false; + + return is_named_call_p (fndecl, "__cxa_throw"); +} + +bool +is_cxa_rethrow_p (const gcall &call) +{ + tree fndecl = gimple_call_fndecl (&call); + if (!fndecl) + return false; + + return is_named_call_p (fndecl, "__cxa_rethrow"); +} + /* For a CALL that matched is_special_named_call_p or is_named_call_p for some name, return a name for the called function suitable for use in diagnostics (stripping the leading underscores). */ const char * -get_user_facing_name (const gcall *call) +get_user_facing_name (const gcall &call) { - tree fndecl = gimple_call_fndecl (call); + tree fndecl = gimple_call_fndecl (&call); gcc_assert (fndecl); tree identifier = DECL_NAME (fndecl); @@ -506,7 +518,7 @@ make_label_text (bool can_colorize, const char *fmt, ...) va_start (ap, fmt); - text_info ti (_(fmt), &ap, 0, NULL, &rich_loc); + text_info ti (_(fmt), &ap, 0, nullptr, &rich_loc); pp_format (pp.get (), &ti); pp_output_formatted_text (pp.get ()); @@ -537,7 +549,7 @@ make_label_text_n (bool can_colorize, unsigned HOST_WIDE_INT n, const char *fmt = ngettext (singular_fmt, plural_fmt, n); - text_info ti (fmt, &ap, 0, NULL, &rich_loc); + text_info ti (fmt, &ap, 0, nullptr, &rich_loc); pp_format (pp.get (), &ti); pp_output_formatted_text (pp.get ()); diff --git a/gcc/analyzer/analyzer.opt b/gcc/analyzer/analyzer.opt index d5f82c6..2ca9058 100644 --- a/gcc/analyzer/analyzer.opt +++ b/gcc/analyzer/analyzer.opt @@ -222,6 +222,10 @@ Wanalyzer-tainted-size Common Var(warn_analyzer_tainted_size) Init(1) Warning Warn about code paths in which an unsanitized value is used as a size. +Wanalyzer-throw-of-unexpected-type +Common Var(warn_analyzer_throw_of_unexpected_type) Init(1) Warning +Warn about code paths in which an exception of unexpected type is thrown. + Wanalyzer-undefined-behavior-ptrdiff Common Var(warn_analyzer_undefined_behavior_ptrdiff) Init(1) Warning Warn about code paths in which pointer subtraction involves undefined behavior. diff --git a/gcc/analyzer/analyzer.opt.urls b/gcc/analyzer/analyzer.opt.urls index 18a0d69..e76e6e5 100644 --- a/gcc/analyzer/analyzer.opt.urls +++ b/gcc/analyzer/analyzer.opt.urls @@ -114,6 +114,9 @@ UrlSuffix(gcc/Static-Analyzer-Options.html#index-Wanalyzer-tainted-offset) Wanalyzer-tainted-size UrlSuffix(gcc/Static-Analyzer-Options.html#index-Wanalyzer-tainted-size) +Wanalyzer-throw-of-unexpected-type +UrlSuffix(gcc/Static-Analyzer-Options.html#index-Wanalyzer-throw-of-unexpected-type) + Wanalyzer-undefined-behavior-ptrdiff UrlSuffix(gcc/Static-Analyzer-Options.html#index-Wanalyzer-undefined-behavior-ptrdiff) diff --git a/gcc/analyzer/bounds-checking.cc b/gcc/analyzer/bounds-checking.cc index c83b419..921ad16 100644 --- a/gcc/analyzer/bounds-checking.cc +++ b/gcc/analyzer/bounds-checking.cc @@ -17,21 +17,12 @@ 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/>. */ -#include "config.h" -#define INCLUDE_VECTOR -#include "system.h" -#include "coretypes.h" -#include "make-unique.h" -#include "tree.h" -#include "function.h" -#include "basic-block.h" +#include "analyzer/common.h" + #include "intl.h" -#include "gimple.h" -#include "gimple-iterator.h" -#include "diagnostic-core.h" -#include "diagnostic-diagram.h" -#include "diagnostic-format-sarif.h" -#include "analyzer/analyzer.h" +#include "diagnostics/diagram.h" +#include "diagnostics/sarif-sink.h" + #include "analyzer/analyzer-logging.h" #include "analyzer/region-model.h" #include "analyzer/checker-event.h" @@ -60,7 +51,7 @@ public: } void prepare_for_emission (checker_path *path, pending_diagnostic *pd, - diagnostic_event_id_t emission_id) override + diagnostics::paths::event_id_t emission_id) override { region_creation_event_capacity::prepare_for_emission (path, pd, @@ -105,18 +96,19 @@ public: so we don't need an event for that. */ if (byte_capacity) emission_path.add_event - (make_unique<oob_region_creation_event_capacity> (byte_capacity, - loc_info, - *this)); + (std::make_unique<oob_region_creation_event_capacity> (byte_capacity, + loc_info, + *this)); } - void maybe_add_sarif_properties (sarif_object &result_obj) + void + maybe_add_sarif_properties (diagnostics::sarif_object &result_obj) const override { - sarif_property_bag &props = result_obj.get_or_create_properties (); + auto &props = result_obj.get_or_create_properties (); #define PROPERTY_PREFIX "gcc/analyzer/out_of_bounds/" props.set_string (PROPERTY_PREFIX "dir", - get_dir () == DIR_READ ? "read" : "write"); + get_dir () == access_direction::read ? "read" : "write"); props.set (PROPERTY_PREFIX "model", m_model.to_json ()); props.set (PROPERTY_PREFIX "region", m_reg->to_json ()); props.set (PROPERTY_PREFIX "diag_arg", tree_to_json (m_diag_arg)); @@ -190,7 +182,7 @@ protected: a problem. Give up if that's happened. */ return; } - diagnostic_diagram diagram + diagnostics::diagram diagram (canvas, /* Alt text. */ _("Diagram visualizing the predicted out-of-bounds access")); @@ -212,7 +204,7 @@ protected: const region *m_reg; tree m_diag_arg; const svalue *m_sval_hint; - diagnostic_event_id_t m_region_creation_event_id; + diagnostics::paths::event_id_t m_region_creation_event_id; }; /* Abstract base class for all out-of-bounds warnings where the @@ -237,11 +229,11 @@ public: && m_out_of_bounds_bits == other.m_out_of_bounds_bits); } - void maybe_add_sarif_properties (sarif_object &result_obj) + void maybe_add_sarif_properties (diagnostics::sarif_object &result_obj) const override { out_of_bounds::maybe_add_sarif_properties (result_obj); - sarif_property_bag &props = result_obj.get_or_create_properties (); + auto &props = result_obj.get_or_create_properties (); #define PROPERTY_PREFIX "gcc/analyzer/concrete_out_of_bounds/" props.set (PROPERTY_PREFIX "out_of_bounds_bits", m_out_of_bounds_bits.to_json ()); @@ -298,16 +290,16 @@ public: { if (m_byte_bound && TREE_CODE (m_byte_bound) == INTEGER_CST) emission_path.add_event - (make_unique<oob_region_creation_event_capacity> (m_byte_bound, - loc_info, - *this)); + (std::make_unique<oob_region_creation_event_capacity> (m_byte_bound, + loc_info, + *this)); } - void maybe_add_sarif_properties (sarif_object &result_obj) + void maybe_add_sarif_properties (diagnostics::sarif_object &result_obj) const final override { concrete_out_of_bounds::maybe_add_sarif_properties (result_obj); - sarif_property_bag &props = result_obj.get_or_create_properties (); + auto &props = result_obj.get_or_create_properties (); #define PROPERTY_PREFIX "gcc/analyzer/concrete_past_the_end/" props.set (PROPERTY_PREFIX "bit_bound", tree_to_json (m_bit_bound)); @@ -505,7 +497,7 @@ public: } } - enum access_direction get_dir () const final override { return DIR_WRITE; } + enum access_direction get_dir () const final override { return access_direction::write; } }; /* Concrete subclass to complain about buffer over-reads. */ @@ -516,7 +508,7 @@ public: concrete_buffer_over_read (const region_model &model, const region *reg, tree diag_arg, bit_range range, tree bit_bound) - : concrete_past_the_end (model, reg, diag_arg, range, bit_bound, NULL) + : concrete_past_the_end (model, reg, diag_arg, range, bit_bound, nullptr) {} const char *get_kind () const final override @@ -689,7 +681,7 @@ public: } } - enum access_direction get_dir () const final override { return DIR_READ; } + enum access_direction get_dir () const final override { return access_direction::read; } }; /* Concrete subclass to complain about buffer underwrites. */ @@ -817,7 +809,7 @@ public: } } - enum access_direction get_dir () const final override { return DIR_WRITE; } + enum access_direction get_dir () const final override { return access_direction::write; } }; /* Concrete subclass to complain about buffer under-reads. */ @@ -828,7 +820,7 @@ public: concrete_buffer_under_read (const region_model &model, const region *reg, tree diag_arg, bit_range range) - : concrete_out_of_bounds (model, reg, diag_arg, range, NULL) + : concrete_out_of_bounds (model, reg, diag_arg, range, nullptr) {} const char *get_kind () const final override @@ -945,7 +937,7 @@ public: } } - enum access_direction get_dir () const final override { return DIR_READ; } + enum access_direction get_dir () const final override { return access_direction::read; } }; /* Abstract class to complain about out-of-bounds read/writes where @@ -975,11 +967,12 @@ public: && pending_diagnostic::same_tree_p (m_capacity, other.m_capacity)); } - void maybe_add_sarif_properties (sarif_object &result_obj) + void + maybe_add_sarif_properties (diagnostics::sarif_object &result_obj) const final override { out_of_bounds::maybe_add_sarif_properties (result_obj); - sarif_property_bag &props = result_obj.get_or_create_properties (); + auto &props = result_obj.get_or_create_properties (); #define PROPERTY_PREFIX "gcc/analyzer/symbolic_past_the_end/" props.set (PROPERTY_PREFIX "offset", tree_to_json (m_offset)); props.set (PROPERTY_PREFIX "num_bytes", tree_to_json (m_num_bytes)); @@ -1116,7 +1109,7 @@ public: return true; } - enum access_direction get_dir () const final override { return DIR_WRITE; } + enum access_direction get_dir () const final override { return access_direction::write; } }; /* Concrete subclass to complain about over-reads with symbolic values. */ @@ -1128,7 +1121,7 @@ public: const region *reg, tree diag_arg, tree offset, tree num_bytes, tree capacity) : symbolic_past_the_end (model, reg, diag_arg, offset, num_bytes, capacity, - NULL) + nullptr) { } @@ -1243,7 +1236,7 @@ public: return true; } - enum access_direction get_dir () const final override { return DIR_READ; } + enum access_direction get_dir () const final override { return access_direction::read; } }; const svalue * @@ -1427,24 +1420,26 @@ region_model::check_symbolic_bounds (const region *base_reg, default: gcc_unreachable (); break; - case DIR_READ: + case access_direction::read: gcc_assert (sval_hint == nullptr); - ctxt->warn (make_unique<symbolic_buffer_over_read> (*this, - sized_offset_reg, - diag_arg, - offset_tree, - num_bytes_tree, - capacity_tree)); + ctxt->warn + (std::make_unique<symbolic_buffer_over_read> (*this, + sized_offset_reg, + diag_arg, + offset_tree, + num_bytes_tree, + capacity_tree)); return false; break; - case DIR_WRITE: - ctxt->warn (make_unique<symbolic_buffer_overflow> (*this, - sized_offset_reg, - diag_arg, - offset_tree, - num_bytes_tree, - capacity_tree, - sval_hint)); + case access_direction::write: + ctxt->warn + (std::make_unique<symbolic_buffer_overflow> (*this, + sized_offset_reg, + diag_arg, + offset_tree, + num_bytes_tree, + capacity_tree, + sval_hint)); return false; break; } @@ -1535,18 +1530,20 @@ region_model::check_region_bounds (const region *reg, default: gcc_unreachable (); break; - case DIR_READ: + case access_direction::read: gcc_assert (sval_hint == nullptr); - ctxt->warn (make_unique<concrete_buffer_under_read> (*this, reg, - diag_arg, - bits_outside)); + ctxt->warn + (std::make_unique<concrete_buffer_under_read> (*this, reg, + diag_arg, + bits_outside)); oob_safe = false; break; - case DIR_WRITE: - ctxt->warn (make_unique<concrete_buffer_underwrite> (*this, - reg, diag_arg, - bits_outside, - sval_hint)); + case access_direction::write: + ctxt->warn + (std::make_unique<concrete_buffer_underwrite> (*this, + reg, diag_arg, + bits_outside, + sval_hint)); oob_safe = false; break; } @@ -1571,20 +1568,22 @@ region_model::check_region_bounds (const region *reg, default: gcc_unreachable (); break; - case DIR_READ: + case access_direction::read: gcc_assert (sval_hint == nullptr); - ctxt->warn (make_unique<concrete_buffer_over_read> (*this, - reg, diag_arg, - bits_outside, - bit_bound)); + ctxt->warn + (std::make_unique<concrete_buffer_over_read> (*this, + reg, diag_arg, + bits_outside, + bit_bound)); oob_safe = false; break; - case DIR_WRITE: - ctxt->warn (make_unique<concrete_buffer_overflow> (*this, - reg, diag_arg, - bits_outside, - bit_bound, - sval_hint)); + case access_direction::write: + ctxt->warn + (std::make_unique<concrete_buffer_overflow> (*this, + reg, diag_arg, + bits_outside, + bit_bound, + sval_hint)); oob_safe = false; break; } diff --git a/gcc/analyzer/call-details.cc b/gcc/analyzer/call-details.cc index 4122f84..ede1229 100644 --- a/gcc/analyzer/call-details.cc +++ b/gcc/analyzer/call-details.cc @@ -18,27 +18,19 @@ 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/>. */ -#include "config.h" -#define INCLUDE_VECTOR -#include "system.h" -#include "coretypes.h" -#include "tree.h" -#include "function.h" -#include "basic-block.h" -#include "gimple.h" -#include "diagnostic-core.h" -#include "analyzer/analyzer.h" -#include "analyzer/analyzer-logging.h" +#include "analyzer/common.h" + #include "diagnostic.h" #include "tree-diagnostic.h" /* for default_tree_printer. */ #include "gimple-pretty-print.h" +#include "stringpool.h" +#include "attribs.h" +#include "diagnostics/sarif-sink.h" + +#include "analyzer/analyzer-logging.h" #include "analyzer/region-model.h" #include "analyzer/call-details.h" #include "analyzer/ranges.h" -#include "stringpool.h" -#include "attribs.h" -#include "make-unique.h" -#include "diagnostic-format-sarif.h" #if ENABLE_ANALYZER @@ -48,13 +40,13 @@ namespace ana { /* call_details's ctor. */ -call_details::call_details (const gcall *call, region_model *model, +call_details::call_details (const gcall &call, region_model *model, region_model_context *ctxt) : m_call (call), m_model (model), m_ctxt (ctxt), - m_lhs_type (NULL_TREE), m_lhs_region (NULL) + m_lhs_type (NULL_TREE), m_lhs_region (nullptr) { m_lhs_type = NULL_TREE; - if (tree lhs = gimple_call_lhs (call)) + if (tree lhs = gimple_call_lhs (&call)) { m_lhs_region = model->get_lvalue (lhs, ctxt); m_lhs_type = TREE_TYPE (lhs); @@ -66,9 +58,11 @@ call_details::call_details (const gcall *call, region_model *model, call_details::call_details (const call_details &cd, region_model_context *ctxt) +: m_call (cd.m_call), m_model (cd.m_model), + m_ctxt (ctxt), + m_lhs_type (cd.m_lhs_type), + m_lhs_region (cd.m_lhs_region) { - *this = cd; - m_ctxt = ctxt; } /* Get the manager from m_model. */ @@ -87,7 +81,7 @@ call_details::get_logger () const if (m_ctxt) return m_ctxt->get_logger (); else - return NULL; + return nullptr; } /* Get any uncertainty_t associated with the region_model_context. */ @@ -98,7 +92,7 @@ call_details::get_uncertainty () const if (m_ctxt) return m_ctxt->get_uncertainty (); else - return NULL; + return nullptr; } /* If the callsite has a left-hand-side region, set it to RESULT @@ -133,25 +127,25 @@ const_fn_p (const call_details &cd) /* If this CD is known to be a call to a function with __attribute__((const)), attempt to get a const_fn_result_svalue - based on the arguments, or return NULL otherwise. */ + based on the arguments, or return nullptr otherwise. */ static const svalue * maybe_get_const_fn_result (const call_details &cd) { if (!const_fn_p (cd)) - return NULL; + return nullptr; unsigned num_args = cd.num_args (); if (num_args > const_fn_result_svalue::MAX_INPUTS) /* Too many arguments. */ - return NULL; + return nullptr; auto_vec<const svalue *> inputs (num_args); for (unsigned arg_idx = 0; arg_idx < num_args; arg_idx++) { const svalue *arg_sval = cd.get_arg_svalue (arg_idx); if (!arg_sval->can_have_associated_state_p ()) - return NULL; + return nullptr; inputs.quick_push (arg_sval); } @@ -228,8 +222,8 @@ call_details::set_any_lhs_with_defaults () const if (lookup_function_attribute ("malloc")) { const region *new_reg - = m_model->get_or_create_region_for_heap_alloc (NULL, m_ctxt); - m_model->mark_region_as_unknown (new_reg, NULL); + = m_model->get_or_create_region_for_heap_alloc (nullptr, m_ctxt); + m_model->mark_region_as_unknown (new_reg, nullptr); sval = mgr->get_ptr_svalue (get_lhs_type (), new_reg); } else @@ -252,7 +246,7 @@ call_details::set_any_lhs_with_defaults () const unsigned call_details::num_args () const { - return gimple_call_num_args (m_call); + return gimple_call_num_args (&m_call); } /* Return true if argument IDX is a size_t (or compatible with it). */ @@ -268,7 +262,7 @@ call_details::arg_is_size_p (unsigned idx) const location_t call_details::get_location () const { - return m_call->location; + return m_call.location; } /* Get argument IDX at the callsite as a tree. */ @@ -276,7 +270,7 @@ call_details::get_location () const tree call_details::get_arg_tree (unsigned idx) const { - return gimple_call_arg (m_call, idx); + return gimple_call_arg (&m_call, idx); } /* Get the type of argument IDX. */ @@ -284,7 +278,7 @@ call_details::get_arg_tree (unsigned idx) const tree call_details::get_arg_type (unsigned idx) const { - return TREE_TYPE (gimple_call_arg (m_call, idx)); + return TREE_TYPE (gimple_call_arg (&m_call, idx)); } /* Get argument IDX at the callsite as an svalue. */ @@ -298,7 +292,7 @@ call_details::get_arg_svalue (unsigned idx) const /* If argument IDX's svalue at the callsite is of pointer type, return the region it points to. - Otherwise return NULL. */ + Otherwise return nullptr. */ const region * call_details::deref_ptr_arg (unsigned idx) const @@ -307,7 +301,7 @@ call_details::deref_ptr_arg (unsigned idx) const return m_model->deref_rvalue (ptr_sval, get_arg_tree (idx), m_ctxt); } -/* Attempt to get the string literal for argument IDX, or return NULL +/* Attempt to get the string literal for argument IDX, or return nullptr otherwise. For use when implementing "__analyzer_*" functions that take string literals. */ @@ -322,7 +316,7 @@ call_details::get_arg_string_literal (unsigned idx) const tree string_cst = string_reg->get_string_cst (); return TREE_STRING_POINTER (string_cst); } - return NULL; + return nullptr; } /* Attempt to get the fndecl used at this call, if known, or NULL_TREE @@ -340,7 +334,7 @@ void call_details::dump_to_pp (pretty_printer *pp, bool simple) const { pp_string (pp, "gcall: "); - pp_gimple_stmt_1 (pp, m_call, 0 /* spc */, TDF_NONE /* flags */); + pp_gimple_stmt_1 (pp, &m_call, 0 /* spc */, TDF_NONE /* flags */); pp_newline (pp); pp_string (pp, "return region: "); if (m_lhs_region) @@ -348,7 +342,7 @@ call_details::dump_to_pp (pretty_printer *pp, bool simple) const else pp_string (pp, "NULL"); pp_newline (pp); - for (unsigned i = 0; i < gimple_call_num_args (m_call); i++) + for (unsigned i = 0; i < gimple_call_num_args (&m_call); i++) { const svalue *arg_sval = get_arg_svalue (i); pp_printf (pp, "arg %i: ", i); @@ -366,6 +360,65 @@ call_details::dump (bool simple) const dump_to_pp (&pp, simple); } +/* Dump a tree-like representation of this call to stderr. */ + +DEBUG_FUNCTION void +call_details::dump () const +{ + text_art::dump (*this); +} + +std::unique_ptr<text_art::tree_widget> +call_details::make_dump_widget (const text_art::dump_widget_info &dwi) const +{ + using text_art::tree_widget; + std::unique_ptr<tree_widget> cd_widget + (tree_widget::from_fmt (dwi, nullptr, "Call Details")); + + { + pretty_printer the_pp; + pretty_printer * const pp = &the_pp; + pp_format_decoder (pp) = default_tree_printer; + pp_string (pp, "gcall: "); + pp_gimple_stmt_1 (pp, &m_call, 0 /* spc */, TDF_NONE /* flags */); + cd_widget->add_child (tree_widget::make (dwi, pp)); + } + { + pretty_printer the_pp; + pretty_printer * const pp = &the_pp; + pp_format_decoder (pp) = default_tree_printer; + pp_string (pp, "return region: "); + if (m_lhs_region) + m_lhs_region->dump_to_pp (pp, true); + else + pp_string (pp, "NULL"); + auto w = tree_widget::make (dwi, pp); + if (m_lhs_region) + w->add_child (m_lhs_region->make_dump_widget (dwi)); + cd_widget->add_child (std::move (w)); + } + if (gimple_call_num_args (&m_call) > 0) + { + std::unique_ptr<tree_widget> args_widget + (tree_widget::from_fmt (dwi, nullptr, "Arguments")); + for (unsigned i = 0; i < gimple_call_num_args (&m_call); i++) + { + pretty_printer the_pp; + pretty_printer * const pp = &the_pp; + pp_format_decoder (pp) = default_tree_printer; + const svalue *arg_sval = get_arg_svalue (i); + pp_printf (pp, "%i: ", i); + arg_sval->dump_to_pp (pp, true); + auto w = tree_widget::make (dwi, pp); + w->add_child (arg_sval->make_dump_widget (dwi)); + args_widget->add_child (std::move (w)); + } + cd_widget->add_child (std::move (args_widget)); + } + + return cd_widget; +} + /* Get a conjured_svalue for this call for REG, and purge any state already relating to that conjured_svalue. */ @@ -373,7 +426,7 @@ const svalue * call_details::get_or_create_conjured_svalue (const region *reg) const { region_model_manager *mgr = m_model->get_manager (); - return mgr->get_or_create_conjured_svalue (reg->get_type (), m_call, reg, + return mgr->get_or_create_conjured_svalue (reg->get_type (), &m_call, reg, conjured_purge (m_model, m_ctxt)); } @@ -388,7 +441,7 @@ call_details::lookup_function_attribute (const char *attr_name) const if (tree fndecl = get_fndecl_for_call ()) allocfntype = TREE_TYPE (fndecl); else - allocfntype = gimple_call_fntype (m_call); + allocfntype = gimple_call_fntype (&m_call); if (!allocfntype) return NULL_TREE; @@ -475,10 +528,10 @@ public: return true; } - void maybe_add_sarif_properties (sarif_object &result_obj) + void maybe_add_sarif_properties (diagnostics::sarif_object &result_obj) const final override { - sarif_property_bag &props = result_obj.get_or_create_properties (); + auto &props = result_obj.get_or_create_properties (); #define PROPERTY_PREFIX "gcc/analyzer/overlapping_buffers/" props.set (PROPERTY_PREFIX "bytes_range_a", m_byte_range_a.to_json ()); @@ -540,10 +593,10 @@ call_details::complain_about_overlap (unsigned arg_idx_a, if (!byte_range_a.intersection (byte_range_b, *model).is_true ()) return; - ctxt->warn (make_unique<overlapping_buffers> (get_fndecl_for_call (), - byte_range_a, - byte_range_b, - num_bytes_read_sval)); + ctxt->warn (std::make_unique<overlapping_buffers> (get_fndecl_for_call (), + byte_range_a, + byte_range_b, + num_bytes_read_sval)); } } // namespace ana diff --git a/gcc/analyzer/call-details.h b/gcc/analyzer/call-details.h index fee7ad4..c0a9118 100644 --- a/gcc/analyzer/call-details.h +++ b/gcc/analyzer/call-details.h @@ -28,7 +28,7 @@ namespace ana { class call_details { public: - call_details (const gcall *call, region_model *model, + call_details (const gcall &call, region_model *model, region_model_context *ctxt); call_details (const call_details &cd, region_model_context *ctxt); @@ -55,7 +55,7 @@ public: return INTEGRAL_TYPE_P (get_arg_type (idx)); } - const gcall *get_call_stmt () const { return m_call; } + const gcall &get_call_stmt () const { return m_call; } location_t get_location () const; tree get_arg_tree (unsigned idx) const; @@ -68,6 +68,10 @@ public: void dump_to_pp (pretty_printer *pp, bool simple) const; void dump (bool simple) const; + void dump () const; + + std::unique_ptr<text_art::tree_widget> + make_dump_widget (const text_art::dump_widget_info &dwi) const; const svalue *get_or_create_conjured_svalue (const region *) const; @@ -86,7 +90,7 @@ public: const svalue *num_bytes_read_sval) const; private: - const gcall *m_call; + const gcall &m_call; region_model *m_model; region_model_context *m_ctxt; tree m_lhs_type; @@ -110,13 +114,13 @@ public: bool operator== (const call_arg_details &other) const { - return (m_call == other.m_call + return (&m_call == &other.m_call && m_called_fndecl == other.m_called_fndecl && m_arg_idx == other.m_arg_idx && pending_diagnostic::same_tree_p (m_arg_expr, other.m_arg_expr)); } - const gcall *m_call; + const gcall &m_call; tree m_called_fndecl; unsigned m_arg_idx; // 0-based tree m_arg_expr; diff --git a/gcc/analyzer/call-info.cc b/gcc/analyzer/call-info.cc index 1adbf64..f431143 100644 --- a/gcc/analyzer/call-info.cc +++ b/gcc/analyzer/call-info.cc @@ -18,45 +18,31 @@ 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/>. */ -#include "config.h" -#define INCLUDE_VECTOR -#include "system.h" -#include "coretypes.h" -#include "tree.h" -#include "function.h" -#include "basic-block.h" -#include "gimple.h" -#include "gimple-iterator.h" -#include "diagnostic-core.h" -#include "options.h" -#include "cgraph.h" -#include "tree-pretty-print.h" -#include "bitmap.h" -#include "analyzer/analyzer.h" -#include "analyzer/analyzer-logging.h" +#include "analyzer/common.h" + #include "ordered-hash-map.h" #include "cfg.h" #include "digraph.h" -#include "analyzer/supergraph.h" #include "sbitmap.h" +#include "diagnostics/event-id.h" + +#include "analyzer/analyzer-logging.h" +#include "analyzer/supergraph.h" #include "analyzer/call-string.h" #include "analyzer/program-point.h" #include "analyzer/store.h" #include "analyzer/region-model.h" #include "analyzer/constraint-manager.h" -#include "diagnostic-event-id.h" #include "analyzer/sm.h" #include "analyzer/pending-diagnostic.h" #include "analyzer/region-model-reachability.h" #include "analyzer/analyzer-selftests.h" #include "analyzer/program-state.h" -#include "diagnostic-path.h" #include "analyzer/checker-path.h" #include "analyzer/diagnostic-manager.h" #include "analyzer/exploded-graph.h" #include "analyzer/call-details.h" #include "analyzer/call-info.h" -#include "make-unique.h" #if ENABLE_ANALYZER @@ -72,6 +58,18 @@ custom_edge_info::update_state (program_state *state, return update_model (state->m_region_model, eedge, ctxt); } +/* Base implementation of custom_edge_info::create_enode vfunc. */ + +exploded_node * +custom_edge_info::create_enode (exploded_graph &eg, + const program_point &point, + program_state &&state, + exploded_node *enode_for_diag, + region_model_context *) const +{ + return eg.get_or_create_node (point, state, enode_for_diag); +} + /* class call_info : public custom_edge_info. */ /* Implementation of custom_edge_info::print vfunc for call_info. */ @@ -115,7 +113,7 @@ call_info::add_events_to_path (checker_path *emission_path, const int stack_depth = src_point.get_stack_depth (); emission_path->add_event - (make_unique<call_event> (event_loc_info (get_call_stmt ()->location, + (std::make_unique<call_event> (event_loc_info (get_call_stmt ().location, caller_fndecl, stack_depth), this)); diff --git a/gcc/analyzer/call-info.h b/gcc/analyzer/call-info.h index ec304d8..6548d86 100644 --- a/gcc/analyzer/call-info.h +++ b/gcc/analyzer/call-info.h @@ -30,11 +30,11 @@ namespace ana { class call_info : public custom_edge_info { public: - void print (pretty_printer *pp) const final override; + void print (pretty_printer *pp) const override; void add_events_to_path (checker_path *emission_path, - const exploded_edge &eedge) const final override; + const exploded_edge &eedge) const override; - const gcall *get_call_stmt () const { return m_call_stmt; } + const gcall &get_call_stmt () const { return m_call_stmt; } tree get_fndecl () const { return m_fndecl; } virtual void print_desc (pretty_printer &pp) const = 0; @@ -47,7 +47,7 @@ protected: call_info (const call_details &cd, const function &called_fn); private: - const gcall *m_call_stmt; + const gcall &m_call_stmt; tree m_fndecl; }; diff --git a/gcc/analyzer/call-string.cc b/gcc/analyzer/call-string.cc index 5099986..0bac8b4 100644 --- a/gcc/analyzer/call-string.cc +++ b/gcc/analyzer/call-string.cc @@ -18,26 +18,11 @@ 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/>. */ -#include "config.h" -#include "system.h" -#include "coretypes.h" -#include "pretty-print.h" -#include "tree.h" -#include "options.h" -#include "ordered-hash-map.h" -#include "options.h" -#include "cgraph.h" -#include "function.h" -#include "cfg.h" -#include "basic-block.h" -#include "gimple.h" -#include "gimple-iterator.h" -#include "digraph.h" -#include "analyzer/analyzer.h" +#include "analyzer/common.h" + #include "analyzer/analyzer-logging.h" #include "analyzer/call-string.h" #include "analyzer/supergraph.h" -#include "make-unique.h" #if ENABLE_ANALYZER @@ -106,11 +91,11 @@ call_string::print (pretty_printer *pp) const std::unique_ptr<json::value> call_string::to_json () const { - auto arr = ::make_unique<json::array> (); + auto arr = std::make_unique<json::array> (); for (const call_string::element_t &e : m_elements) { - auto e_obj = ::make_unique<json::object> (); + auto e_obj = std::make_unique<json::object> (); e_obj->set_integer ("src_snode_idx", e.m_callee->m_index); e_obj->set_integer ("dst_snode_idx", e.m_caller->m_index); e_obj->set_string ("funcname", function_name (e.m_caller->m_fun)); @@ -238,22 +223,22 @@ call_string::cmp_ptr_ptr (const void *pa, const void *pb) } /* Return the pointer to callee of the topmost call in the stack, - or NULL if stack is empty. */ + or nullptr if stack is empty. */ const supernode * call_string::get_callee_node () const { if(m_elements.is_empty ()) - return NULL; + return nullptr; return m_elements[m_elements.length () - 1].m_callee; } /* Return the pointer to caller of the topmost call in the stack, - or NULL if stack is empty. */ + or nullptr if stack is empty. */ const supernode * call_string::get_caller_node () const { if(m_elements.is_empty ()) - return NULL; + return nullptr; return m_elements[m_elements.length () - 1].m_caller; } @@ -281,7 +266,7 @@ call_string::validate () const /* ctor for the root/empty call_string. */ call_string::call_string () -: m_parent (NULL), m_elements () +: m_parent (nullptr), m_elements () { } diff --git a/gcc/analyzer/call-string.h b/gcc/analyzer/call-string.h index 642cf76..f8c6a25 100644 --- a/gcc/analyzer/call-string.h +++ b/gcc/analyzer/call-string.h @@ -129,12 +129,12 @@ private: } template <typename T> static inline void remove (T &entry) { - entry.m_key = element_t (NULL, NULL); + entry.m_key = element_t (nullptr, nullptr); } static const bool empty_zero_p = true; template <typename T> static inline bool is_empty (const T &entry) { - return entry.m_key.m_caller == NULL; + return entry.m_key.m_caller == nullptr; } template <typename T> static inline bool is_deleted (const T &entry) { @@ -142,8 +142,8 @@ private: } template <typename T> static inline void mark_empty (T &entry) { - entry.m_key = element_t (NULL, NULL); - entry.m_value = NULL; + entry.m_key = element_t (nullptr, nullptr); + entry.m_value = nullptr; } template <typename T> static inline void mark_deleted (T &entry) { diff --git a/gcc/analyzer/call-summary.cc b/gcc/analyzer/call-summary.cc index 27fb575..a094cba 100644 --- a/gcc/analyzer/call-summary.cc +++ b/gcc/analyzer/call-summary.cc @@ -17,16 +17,8 @@ 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/>. */ -#include "config.h" -#define INCLUDE_VECTOR -#include "system.h" -#include "coretypes.h" -#include "tree.h" -#include "tree-dfa.h" -#include "diagnostic-core.h" -#include "diagnostic.h" -#include "tree-diagnostic.h" -#include "analyzer/analyzer.h" +#include "analyzer/common.h" + #include "analyzer/region-model.h" #include "analyzer/call-summary.h" #include "analyzer/exploded-graph.h" @@ -79,9 +71,9 @@ call_summary::get_user_facing_desc (pretty_printer *pp) const if (tree result = DECL_RESULT (fndecl)) { const region *result_reg - = get_state ().m_region_model->get_lvalue (result, NULL); + = get_state ().m_region_model->get_lvalue (result, nullptr); const svalue *result_sval - = get_state ().m_region_model->get_store_value (result_reg, NULL); + = get_state ().m_region_model->get_store_value (result_reg, nullptr); switch (result_sval->get_kind ()) { default: @@ -165,7 +157,7 @@ call_summary::dump (const extrinsic_state &ext_state, bool simple) const call_summary_replay::call_summary_replay (const call_details &cd, const function &called_fn, - call_summary *summary, + call_summary &summary, const extrinsic_state &ext_state) : m_cd (cd), m_summary (summary), @@ -180,7 +172,7 @@ call_summary_replay::call_summary_replay (const call_details &cd, This will be a top-level frame, since that's what's in the summary. */ const frame_region *summary_frame - = mgr->get_frame_region (NULL, called_fn); + = mgr->get_frame_region (nullptr, called_fn); unsigned idx = 0; for (tree iter_parm = DECL_ARGUMENTS (fndecl); iter_parm; @@ -218,7 +210,7 @@ call_summary_replay::call_summary_replay (const call_details &cd, /* Try to convert SUMMARY_SVAL in the summary to a corresponding svalue in the caller, caching the result. - Return NULL if the conversion is not possible. */ + Return nullptr if the conversion is not possible. */ const svalue * call_summary_replay::convert_svalue_from_summary (const svalue *summary_sval) @@ -260,7 +252,7 @@ call_summary_replay::convert_svalue_from_summary_1 (const svalue *summary_sval) const region *summary_reg = region_summary_sval->get_pointee (); const region *caller_reg = convert_region_from_summary (summary_reg); if (!caller_reg) - return NULL; + return nullptr; region_model_manager *mgr = get_manager (); const svalue *caller_ptr = mgr->get_ptr_svalue (summary_sval->get_type (), @@ -276,7 +268,7 @@ call_summary_replay::convert_svalue_from_summary_1 (const svalue *summary_sval) return summary_sval; case SK_SETJMP: - return NULL; // TODO + return nullptr; // TODO case SK_INITIAL: { @@ -290,7 +282,7 @@ call_summary_replay::convert_svalue_from_summary_1 (const svalue *summary_sval) const region *summary_reg = initial_summary_sval->get_region (); const region *caller_reg = convert_region_from_summary (summary_reg); if (!caller_reg) - return NULL; + return nullptr; const svalue *caller_sval = m_cd.get_model ()->get_store_value (caller_reg, m_cd.get_ctxt ()); return caller_sval; @@ -303,7 +295,7 @@ call_summary_replay::convert_svalue_from_summary_1 (const svalue *summary_sval) const svalue *summary_arg = unaryop_summary_sval->get_arg (); const svalue *caller_arg = convert_svalue_from_summary (summary_arg); if (!caller_arg) - return NULL; + return nullptr; region_model_manager *mgr = get_manager (); return mgr->get_or_create_unaryop (summary_sval->get_type (), unaryop_summary_sval->get_op (), @@ -317,11 +309,11 @@ call_summary_replay::convert_svalue_from_summary_1 (const svalue *summary_sval) const svalue *summary_arg0 = binop_summary_sval->get_arg0 (); const svalue *caller_arg0 = convert_svalue_from_summary (summary_arg0); if (!caller_arg0) - return NULL; + return nullptr; const svalue *summary_arg1 = binop_summary_sval->get_arg1 (); const svalue *caller_arg1 = convert_svalue_from_summary (summary_arg1); if (!caller_arg1) - return NULL; + return nullptr; region_model_manager *mgr = get_manager (); return mgr->get_or_create_binop (summary_sval->get_type (), binop_summary_sval->get_op (), @@ -336,10 +328,10 @@ call_summary_replay::convert_svalue_from_summary_1 (const svalue *summary_sval) region_model_manager *mgr = get_manager (); const svalue *summary_parent_sval = sub_summary_sval->get_parent (); if (!summary_parent_sval) - return NULL; + return nullptr; const region *summary_subregion = sub_summary_sval->get_subregion (); if (!summary_subregion) - return NULL; + return nullptr; return mgr->get_or_create_sub_svalue (summary_sval->get_type (), summary_parent_sval, summary_subregion); @@ -354,13 +346,13 @@ call_summary_replay::convert_svalue_from_summary_1 (const svalue *summary_sval) const svalue *caller_outer_size = convert_svalue_from_summary (summary_outer_size); if (!caller_outer_size) - return NULL; + return nullptr; const svalue *summary_inner_sval = repeated_summary_sval->get_inner_svalue (); const svalue *caller_inner_sval = convert_svalue_from_summary (summary_inner_sval); if (!caller_inner_sval) - return NULL; + return nullptr; region_model_manager *mgr = get_manager (); return mgr->get_or_create_repeated_svalue (summary_sval->get_type (), caller_outer_size, @@ -377,7 +369,7 @@ call_summary_replay::convert_svalue_from_summary_1 (const svalue *summary_sval) const svalue *caller_inner_sval = convert_svalue_from_summary (summary_inner_sval); if (!caller_inner_sval) - return NULL; + return nullptr; region_model_manager *mgr = get_manager (); return mgr->get_or_create_bits_within (summary_sval->get_type (), bits, @@ -392,7 +384,7 @@ call_summary_replay::convert_svalue_from_summary_1 (const svalue *summary_sval) const svalue *caller_arg_sval = convert_svalue_from_summary (summary_arg_sval); if (!caller_arg_sval) - return NULL; + return nullptr; region_model_manager *mgr = get_manager (); return mgr->get_or_create_unmergeable (caller_arg_sval); } @@ -408,14 +400,14 @@ call_summary_replay::convert_svalue_from_summary_1 (const svalue *summary_sval) = convert_svalue_from_summary (summary_base_sval); if (!(caller_base_sval && caller_base_sval->can_have_associated_state_p ())) - return NULL; + return nullptr; const svalue *summary_iter_sval = widening_summary_sval->get_iter_svalue (); const svalue *caller_iter_sval = convert_svalue_from_summary (summary_iter_sval); if (!(caller_iter_sval && caller_iter_sval->can_have_associated_state_p ())) - return NULL; + return nullptr; region_model_manager *mgr = get_manager (); return mgr->get_or_create_widening_svalue (summary_iter_sval->get_type (), @@ -499,7 +491,7 @@ call_summary_replay::convert_svalue_from_summary_1 (const svalue *summary_sval) const svalue *caller_input = convert_svalue_from_summary (summary_input); if (!caller_input) - return NULL; + return nullptr; inputs.safe_push (caller_input); } region_model_manager *mgr = get_manager (); @@ -524,7 +516,7 @@ call_summary_replay::convert_svalue_from_summary_1 (const svalue *summary_sval) const svalue *caller_input = convert_svalue_from_summary (summary_input); if (!caller_input) - return NULL; + return nullptr; inputs.safe_push (caller_input); } region_model_manager *mgr = get_manager (); @@ -540,7 +532,7 @@ call_summary_replay::convert_svalue_from_summary_1 (const svalue *summary_sval) /* Try to convert SUMMARY_REG in the summary to a corresponding region in the caller, caching the result. - Return NULL if the conversion is not possible. */ + Return nullptr if the conversion is not possible. */ const region * call_summary_replay::convert_region_from_summary (const region *summary_reg) @@ -604,7 +596,7 @@ call_summary_replay::convert_region_from_summary_1 (const region *summary_reg) const svalue *caller_ptr_sval = convert_svalue_from_summary (summary_ptr_sval); if (!caller_ptr_sval) - return NULL; + return nullptr; const region *caller_reg = get_caller_model ()->deref_rvalue (caller_ptr_sval, NULL_TREE, @@ -627,7 +619,7 @@ call_summary_replay::convert_region_from_summary_1 (const region *summary_reg) case SSA_NAME: /* We don't care about writes to locals within the summary. */ - return NULL; + return nullptr; case VAR_DECL: /* We don't care about writes to locals within the summary. */ @@ -636,12 +628,12 @@ call_summary_replay::convert_region_from_summary_1 (const region *summary_reg) return summary_reg; else /* Otherwise, we don't care about locals. */ - return NULL; + return nullptr; case RESULT_DECL: return m_cd.get_lhs_region (); case PARM_DECL: /* Writes (by value) to parms should be visible to the caller. */ - return NULL; + return nullptr; } } break; @@ -653,7 +645,7 @@ call_summary_replay::convert_region_from_summary_1 (const region *summary_reg) const region *caller_parent_reg = convert_region_from_summary (summary_parent_reg); if (!caller_parent_reg) - return NULL; + return nullptr; tree field = summary_field_reg->get_field (); return mgr->get_field_region (caller_parent_reg, field); } @@ -666,12 +658,12 @@ call_summary_replay::convert_region_from_summary_1 (const region *summary_reg) const region *caller_parent_reg = convert_region_from_summary (summary_parent_reg); if (!caller_parent_reg) - return NULL; + return nullptr; const svalue *summary_index = summary_element_reg->get_index (); const svalue *caller_index = convert_svalue_from_summary (summary_index); if (!caller_index) - return NULL; + return nullptr; return mgr->get_element_region (caller_parent_reg, summary_reg->get_type (), caller_index); @@ -685,13 +677,13 @@ call_summary_replay::convert_region_from_summary_1 (const region *summary_reg) const region *caller_parent_reg = convert_region_from_summary (summary_parent_reg); if (!caller_parent_reg) - return NULL; + return nullptr; const svalue *summary_byte_offset = summary_offset_reg->get_byte_offset (); const svalue *caller_byte_offset = convert_svalue_from_summary (summary_byte_offset); if (!caller_byte_offset) - return NULL; + return nullptr; return mgr->get_offset_region (caller_parent_reg, summary_reg->get_type (), caller_byte_offset); @@ -705,13 +697,13 @@ call_summary_replay::convert_region_from_summary_1 (const region *summary_reg) const region *caller_parent_reg = convert_region_from_summary (summary_parent_reg); if (!caller_parent_reg) - return NULL; + return nullptr; const svalue *summary_byte_size = summary_sized_reg->get_byte_size_sval (mgr); const svalue *caller_byte_size = convert_svalue_from_summary (summary_byte_size); if (!caller_byte_size) - return NULL; + return nullptr; return mgr->get_sized_region (caller_parent_reg, summary_reg->get_type (), caller_byte_size); @@ -723,7 +715,7 @@ call_summary_replay::convert_region_from_summary_1 (const region *summary_reg) const region *caller_parent_reg = convert_region_from_summary (summary_parent_reg); if (!caller_parent_reg) - return NULL; + return nullptr; return mgr->get_cast_region (caller_parent_reg, summary_reg->get_type ()); } @@ -739,7 +731,7 @@ call_summary_replay::convert_region_from_summary_1 (const region *summary_reg) } break; case RK_ALLOCA: - return NULL; + return nullptr; case RK_BIT_RANGE: { const bit_range_region *summary_bit_range_reg @@ -748,7 +740,7 @@ call_summary_replay::convert_region_from_summary_1 (const region *summary_reg) const region *caller_parent_reg = convert_region_from_summary (summary_parent_reg); if (!caller_parent_reg) - return NULL; + return nullptr; const bit_range &bits = summary_bit_range_reg->get_bits (); return mgr->get_bit_range (caller_parent_reg, summary_reg->get_type (), @@ -756,14 +748,14 @@ call_summary_replay::convert_region_from_summary_1 (const region *summary_reg) } break; case RK_VAR_ARG: - return NULL; + return nullptr; } } /* Try to convert SUMMARY_KEY in the summary to a corresponding binding key in the caller. - Return NULL if the conversion is not possible. */ + Return nullptr if the conversion is not possible. */ const binding_key * call_summary_replay::convert_key_from_summary (const binding_key *summary_key) @@ -775,7 +767,7 @@ call_summary_replay::convert_key_from_summary (const binding_key *summary_key) const region *summary_reg = symbolic_key->get_region (); const region *caller_reg = convert_region_from_summary (summary_reg); if (!caller_reg) - return NULL; + return nullptr; region_model_manager *mgr = get_manager (); store_manager *store_mgr = mgr->get_store_manager (); return store_mgr->get_symbolic_binding (caller_reg); @@ -788,7 +780,7 @@ call_summary_replay::add_svalue_mapping (const svalue *summary_sval, const svalue *caller_sval) { gcc_assert (summary_sval); - // CALLER_SVAL can be NULL + // CALLER_SVAL can be nullptr m_map_svalue_from_summary_to_caller.put (summary_sval, caller_sval); } @@ -799,7 +791,7 @@ call_summary_replay::add_region_mapping (const region *summary_reg, const region *caller_reg) { gcc_assert (summary_reg); - // CALLER_REG can be NULL + // CALLER_REG can be nullptr m_map_region_from_summary_to_caller.put (summary_reg, caller_reg); } @@ -816,7 +808,7 @@ call_summary_replay::dump_to_pp (pretty_printer *pp, bool simple) const pp_newline (pp); pp_string (pp, "CALLEE SUMMARY:"); pp_newline (pp); - m_summary->dump_to_pp (m_ext_state, pp, simple); + m_summary.dump_to_pp (m_ext_state, pp, simple); /* Current state of caller (could be in mid-update). */ pp_newline (pp); diff --git a/gcc/analyzer/call-summary.h b/gcc/analyzer/call-summary.h index 220dd083..7280cca 100644 --- a/gcc/analyzer/call-summary.h +++ b/gcc/analyzer/call-summary.h @@ -69,11 +69,11 @@ class call_summary_replay public: call_summary_replay (const call_details &cd, const function &called_fn, - call_summary *m_summary, + call_summary &summary, const extrinsic_state &ext_state); const call_details &get_call_details () const { return m_cd; } - const gcall *get_call_stmt () const { return m_cd.get_call_stmt (); } + const gcall &get_call_stmt () const { return m_cd.get_call_stmt (); } region_model_manager *get_manager () const { return m_cd.get_manager (); } store_manager *get_store_manager () const { @@ -102,7 +102,7 @@ private: const region *convert_region_from_summary_1 (const region *); const call_details &m_cd; - call_summary *m_summary; + call_summary &m_summary; const extrinsic_state &m_ext_state; // Mapping from svalues in summary to svalues for callsite: diff --git a/gcc/analyzer/checker-event.cc b/gcc/analyzer/checker-event.cc index 5d7647c..4eac945 100644 --- a/gcc/analyzer/checker-event.cc +++ b/gcc/analyzer/checker-event.cc @@ -1,4 +1,4 @@ -/* Subclasses of diagnostic_event for analyzer diagnostics. +/* Subclasses of diagnostics::paths::event for analyzer diagnostics. Copyright (C) 2019-2025 Free Software Foundation, Inc. Contributed by David Malcolm <dmalcolm@redhat.com>. @@ -18,45 +18,32 @@ 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/>. */ -#include "config.h" -#define INCLUDE_VECTOR -#include "system.h" -#include "coretypes.h" -#include "tree.h" -#include "function.h" -#include "basic-block.h" -#include "gimple.h" -#include "diagnostic-core.h" +#include "analyzer/common.h" + #include "gimple-pretty-print.h" +#include "sbitmap.h" +#include "ordered-hash-map.h" #include "fold-const.h" -#include "diagnostic-path.h" -#include "options.h" -#include "cgraph.h" -#include "cfg.h" -#include "digraph.h" -#include "diagnostic-event-id.h" -#include "analyzer/analyzer.h" +#include "gimple-iterator.h" +#include "inlining-iterator.h" +#include "tree-logical-location.h" +#include "diagnostics/sarif-sink.h" +#include "diagnostics/state-graphs.h" + #include "analyzer/analyzer-logging.h" #include "analyzer/sm.h" -#include "sbitmap.h" -#include "bitmap.h" -#include "ordered-hash-map.h" #include "analyzer/call-string.h" #include "analyzer/program-point.h" #include "analyzer/store.h" #include "analyzer/region-model.h" #include "analyzer/program-state.h" #include "analyzer/checker-path.h" -#include "gimple-iterator.h" -#include "inlining-iterator.h" #include "analyzer/supergraph.h" #include "analyzer/pending-diagnostic.h" #include "analyzer/diagnostic-manager.h" #include "analyzer/constraint-manager.h" #include "analyzer/checker-event.h" #include "analyzer/exploded-graph.h" -#include "diagnostic-format-sarif.h" -#include "tree-logical-location.h" #if ENABLE_ANALYZER @@ -71,56 +58,64 @@ event_kind_to_string (enum event_kind ek) { default: gcc_unreachable (); - case EK_DEBUG: - return "EK_DEBUG"; - case EK_CUSTOM: - return "EK_CUSTOM"; - case EK_STMT: - return "EK_STMT"; - case EK_REGION_CREATION: - return "EK_REGION_CREATION"; - case EK_FUNCTION_ENTRY: - return "EK_FUNCTION_ENTRY"; - case EK_STATE_CHANGE: - return "EK_STATE_CHANGE"; - case EK_START_CFG_EDGE: - return "EK_START_CFG_EDGE"; - case EK_END_CFG_EDGE: - return "EK_END_CFG_EDGE"; - case EK_CALL_EDGE: - return "EK_CALL_EDGE"; - case EK_RETURN_EDGE: - return "EK_RETURN_EDGE"; - case EK_START_CONSOLIDATED_CFG_EDGES: - return "EK_START_CONSOLIDATED_CFG_EDGES"; - case EK_END_CONSOLIDATED_CFG_EDGES: - return "EK_END_CONSOLIDATED_CFG_EDGES"; - case EK_INLINED_CALL: - return "EK_INLINED_CALL"; - case EK_SETJMP: - return "EK_SETJMP"; - case EK_REWIND_FROM_LONGJMP: - return "EK_REWIND_FROM_LONGJMP"; - case EK_REWIND_TO_SETJMP: - return "EK_REWIND_TO_SETJMP"; - case EK_WARNING: - return "EK_WARNING"; + case event_kind::debug: + return "debug"; + case event_kind::custom: + return "custom"; + case event_kind::stmt: + return "stmt"; + case event_kind::region_creation: + return "region_creation"; + case event_kind::function_entry: + return "function_entry"; + case event_kind::state_change: + return "state_change"; + case event_kind::start_cfg_edge: + return "start_cfg_edge"; + case event_kind::end_cfg_edge: + return "end_cfg_edge"; + case event_kind::catch_: + return "catch"; + case event_kind::call_edge: + return "call_edge"; + case event_kind::return_edge: + return "return_edge"; + case event_kind::start_consolidated_cfg_edges: + return "start_consolidated_cfg_edges"; + case event_kind::end_consolidated_cfg_edges: + return "end_consolidated_cfg_edges"; + case event_kind::inlined_call: + return "inlined_call"; + case event_kind::setjmp_: + return "setjmp"; + case event_kind::rewind_from_longjmp: + return "rewind_from_longjmp"; + case event_kind::rewind_to_setjmp: + return "rewind_to_setjmp"; + case event_kind::throw_: + return "throw"; + case event_kind::unwind: + return "unwind"; + case event_kind::warning: + return "warning"; } } -/* class checker_event : public diagnostic_event. */ +/* class checker_event : public diagnostics::paths::event. */ /* checker_event's ctor. */ checker_event::checker_event (enum event_kind kind, const event_loc_info &loc_info) -: m_kind (kind), m_loc (loc_info.m_loc), +: m_path (nullptr), + m_kind (kind), m_loc (loc_info.m_loc), m_original_fndecl (loc_info.m_fndecl), m_effective_fndecl (loc_info.m_fndecl), m_original_depth (loc_info.m_depth), m_effective_depth (loc_info.m_depth), - m_pending_diagnostic (NULL), m_emission_id (), - m_logical_loc (loc_info.m_fndecl) + m_pending_diagnostic (nullptr), m_emission_id (), + m_logical_loc + (tree_logical_location_manager::key_from_tree (loc_info.m_fndecl)) { /* Update effective fndecl and depth if inlining has been recorded. */ if (flag_analyzer_undo_inlining) @@ -130,40 +125,41 @@ checker_event::checker_event (enum event_kind kind, { m_effective_fndecl = info.get_inner_fndecl (); m_effective_depth += info.get_extra_frames (); - m_logical_loc = tree_logical_location (m_effective_fndecl); + m_logical_loc + = tree_logical_location_manager::key_from_tree (m_effective_fndecl); } } } -/* No-op implementation of diagnostic_event::get_meaning vfunc for +/* No-op implementation of diagnostics::paths::event::get_meaning vfunc for checker_event: checker events have no meaning by default. */ -diagnostic_event::meaning +diagnostics::paths::event::meaning checker_event::get_meaning () const { - return meaning (); + return diagnostics::paths::event::meaning (); } -/* Implementation of diagnostic_event::maybe_add_sarif_properties +/* Implementation of diagnostics::paths::event::maybe_add_sarif_properties for checker_event. */ void checker_event:: -maybe_add_sarif_properties (sarif_object &thread_flow_loc_obj) const +maybe_add_sarif_properties (diagnostics::sarif_builder &builder, + diagnostics::sarif_object &thread_flow_loc_obj) const { - sarif_property_bag &props = thread_flow_loc_obj.get_or_create_properties (); + auto &props = thread_flow_loc_obj.get_or_create_properties (); #define PROPERTY_PREFIX "gcc/analyzer/checker_event/" props.set (PROPERTY_PREFIX "emission_id", diagnostic_event_id_to_json (m_emission_id)); props.set_string (PROPERTY_PREFIX "kind", event_kind_to_string (m_kind)); if (m_original_fndecl != m_effective_fndecl) - { - tree_logical_location logical_loc (m_original_fndecl); - props.set<sarif_logical_location> - (PROPERTY_PREFIX "original_fndecl", - make_sarif_logical_location_object (logical_loc)); - } + props.set_logical_location + (PROPERTY_PREFIX "original_fndecl", + builder, + tree_logical_location_manager::key_from_tree (m_original_fndecl)); + if (m_original_depth != m_effective_depth) props.set_integer (PROPERTY_PREFIX "original_depth", m_original_depth); #undef PROPERTY_PREFIX @@ -217,10 +213,11 @@ checker_event::debug () const pertinent data within the sm-state). */ void -checker_event::prepare_for_emission (checker_path *, +checker_event::prepare_for_emission (checker_path *path, pending_diagnostic *pd, - diagnostic_event_id_t emission_id) + diagnostics::paths::event_id_t emission_id) { + m_path = path; m_pending_diagnostic = pd; m_emission_id = emission_id; @@ -228,9 +225,34 @@ checker_event::prepare_for_emission (checker_path *, print_desc (*pp.get ()); } +std::unique_ptr<diagnostics::digraphs::digraph> +checker_event::maybe_make_diagnostic_state_graph (bool debug) const +{ + const program_state *state = get_program_state (); + if (!state) + return nullptr; + + gcc_assert (m_path); + const extrinsic_state &ext_state = m_path->get_ext_state (); + + auto result = state->make_diagnostic_state_graph (ext_state); + + if (debug) + { + pretty_printer pp; + text_art::theme *theme = global_dc->get_diagram_theme (); + text_art::dump_to_pp (*state, theme, &pp); + result->set_attr (STATE_GRAPH_PREFIX, + "analyzer/program_state/", + pp_formatted_text (&pp)); + } + + return result; +} + /* class debug_event : public checker_event. */ -/* Implementation of diagnostic_event::print_desc vfunc for +/* Implementation of diagnostics::paths::event::print_desc vfunc for debug_event. Use the saved string as the event's description. */ @@ -242,7 +264,7 @@ debug_event::print_desc (pretty_printer &pp) const /* class precanned_custom_event : public custom_event. */ -/* Implementation of diagnostic_event::print_desc vfunc for +/* Implementation of diagnostics::paths::event::print_desc vfunc for precanned_custom_event. Use the saved string as the event's description. */ @@ -258,14 +280,14 @@ precanned_custom_event::print_desc (pretty_printer &pp) const statement_event::statement_event (const gimple *stmt, tree fndecl, int depth, const program_state &dst_state) -: checker_event (EK_STMT, +: checker_event (event_kind::stmt, event_loc_info (gimple_location (stmt), fndecl, depth)), m_stmt (stmt), m_dst_state (dst_state) { } -/* Implementation of diagnostic_event::print_desc vfunc for +/* Implementation of diagnostics::paths::event::print_desc vfunc for statement_event. Use the statement's dump form as the event's description. */ @@ -279,7 +301,7 @@ statement_event::print_desc (pretty_printer &pp) const /* class region_creation_event : public checker_event. */ region_creation_event::region_creation_event (const event_loc_info &loc_info) -: checker_event (EK_REGION_CREATION, loc_info) +: checker_event (event_kind::region_creation, loc_info) { } @@ -350,16 +372,18 @@ region_creation_event_debug::print_desc (pretty_printer &pp) const /* class function_entry_event : public checker_event. */ -function_entry_event::function_entry_event (const program_point &dst_point) -: checker_event (EK_FUNCTION_ENTRY, +function_entry_event::function_entry_event (const program_point &dst_point, + const program_state &state) +: checker_event (event_kind::function_entry, event_loc_info (dst_point.get_supernode ()->get_start_location (), dst_point.get_fndecl (), - dst_point.get_stack_depth ())) + dst_point.get_stack_depth ())), + m_state (state) { } -/* Implementation of diagnostic_event::print_desc vfunc for +/* Implementation of diagnostics::paths::event::print_desc vfunc for function_entry_event. Use a string such as "entry to 'foo'" as the event's description. */ @@ -370,13 +394,13 @@ function_entry_event::print_desc (pretty_printer &pp) const pp_printf (&pp, "entry to %qE", m_effective_fndecl); } -/* Implementation of diagnostic_event::get_meaning vfunc for +/* Implementation of diagnostics::paths::event::get_meaning vfunc for function entry. */ -diagnostic_event::meaning +diagnostics::paths::event::meaning function_entry_event::get_meaning () const { - return meaning (VERB_enter, NOUN_function); + return meaning (verb::enter, noun::function); } /* class state_change_event : public checker_event. */ @@ -393,7 +417,7 @@ state_change_event::state_change_event (const supernode *node, const svalue *origin, const program_state &dst_state, const exploded_node *enode) -: checker_event (EK_STATE_CHANGE, +: checker_event (event_kind::state_change, event_loc_info (stmt->location, node->m_fun->decl, stack_depth)), @@ -405,7 +429,7 @@ state_change_event::state_change_event (const supernode *node, { } -/* Implementation of diagnostic_event::print_desc vfunc for +/* Implementation of diagnostics::paths::event::print_desc vfunc for state_change_event. Attempt to generate a nicer human-readable description. @@ -447,7 +471,7 @@ state_change_event::print_desc (pretty_printer &pp) const pp_string (&pp, "NULL origin"); /* Get any "meaning" of event. */ - diagnostic_event::meaning meaning = get_meaning (); + diagnostics::paths::event::meaning meaning = get_meaning (); pp_string (&pp, ", meaning: "); meaning.dump_to_pp (&pp); pp_string (&pp, ")"); @@ -476,7 +500,7 @@ state_change_event::print_desc (pretty_printer &pp) const } else { - gcc_assert (m_origin == NULL); + gcc_assert (m_origin == nullptr); pp_printf (&pp, "global state: %qs -> %qs", m_from->get_name (), @@ -484,11 +508,11 @@ state_change_event::print_desc (pretty_printer &pp) const } } -/* Implementation of diagnostic_event::get_meaning vfunc for +/* Implementation of diagnostics::paths::event::get_meaning vfunc for state change events: delegate to the pending_diagnostic to get any meaning. */ -diagnostic_event::meaning +diagnostics::paths::event::meaning state_change_event::get_meaning () const { if (m_pending_diagnostic) @@ -506,15 +530,17 @@ state_change_event::get_meaning () const /* class superedge_event : public checker_event. */ -/* Implementation of diagnostic_event::maybe_add_sarif_properties +/* Implementation of diagnostics::paths::event::maybe_add_sarif_properties for superedge_event. */ void -superedge_event::maybe_add_sarif_properties (sarif_object &thread_flow_loc_obj) +superedge_event:: +maybe_add_sarif_properties (diagnostics::sarif_builder &builder, + diagnostics::sarif_object &thread_flow_loc_obj) const { - checker_event::maybe_add_sarif_properties (thread_flow_loc_obj); - sarif_property_bag &props = thread_flow_loc_obj.get_or_create_properties (); + checker_event::maybe_add_sarif_properties (builder, thread_flow_loc_obj); + auto &props = thread_flow_loc_obj.get_or_create_properties (); #define PROPERTY_PREFIX "gcc/analyzer/superedge_event/" if (m_sedge) props.set (PROPERTY_PREFIX "superedge", m_sedge->to_json ()); @@ -562,6 +588,12 @@ superedge_event::should_filter_p (int verbosity) const return false; } +const program_state * +superedge_event::get_program_state () const +{ + return &m_eedge.m_dest->get_state (); +} + /* superedge_event's ctor. */ superedge_event::superedge_event (enum event_kind kind, @@ -595,24 +627,24 @@ cfg_edge_event::cfg_edge_event (enum event_kind kind, gcc_assert (eedge.m_sedge->m_kind == SUPEREDGE_CFG_EDGE); } -/* Implementation of diagnostic_event::get_meaning vfunc for +/* Implementation of diagnostics::paths::event::get_meaning vfunc for CFG edge events. */ -diagnostic_event::meaning +diagnostics::paths::event::meaning cfg_edge_event::get_meaning () const { const cfg_superedge& cfg_sedge = get_cfg_superedge (); if (cfg_sedge.true_value_p ()) - return meaning (VERB_branch, PROPERTY_true); + return meaning (verb::branch, property::true_); else if (cfg_sedge.false_value_p ()) - return meaning (VERB_branch, PROPERTY_false); + return meaning (verb::branch, property::false_); else return meaning (); } /* class start_cfg_edge_event : public cfg_edge_event. */ -/* Implementation of diagnostic_event::print_desc vfunc for +/* Implementation of diagnostics::paths::event::print_desc vfunc for start_cfg_edge_event. If -fanalyzer-verbose-edges, then generate low-level descriptions, such @@ -715,7 +747,7 @@ start_cfg_edge_event::maybe_describe_condition (bool can_colorize) const lhs, op, rhs); } } - return label_text::borrow (NULL); + return label_text::borrow (nullptr); } /* Subroutine of maybe_describe_condition above. @@ -741,7 +773,7 @@ start_cfg_edge_event::maybe_describe_condition (bool can_colorize, && zerop (rhs)) { if (gcall *call = dyn_cast <gcall *> (SSA_NAME_DEF_STMT (lhs))) - if (is_special_named_call_p (call, "strcmp", 2)) + if (is_special_named_call_p (*call, "strcmp", 2)) { if (op == EQ_EXPR) return label_text::borrow ("when the strings are equal"); @@ -752,9 +784,9 @@ start_cfg_edge_event::maybe_describe_condition (bool can_colorize, /* Only attempt to generate text for sufficiently simple expressions. */ if (!should_print_expr_p (lhs)) - return label_text::borrow (NULL); + return label_text::borrow (nullptr); if (!should_print_expr_p (rhs)) - return label_text::borrow (NULL); + return label_text::borrow (nullptr); /* Special cases for pointer comparisons against NULL. */ if (POINTER_TYPE_P (TREE_TYPE (lhs)) @@ -804,7 +836,7 @@ start_cfg_edge_event::should_print_expr_p (tree expr) call_event::call_event (const exploded_edge &eedge, const event_loc_info &loc_info) -: superedge_event (EK_CALL_EDGE, eedge, loc_info) +: superedge_event (event_kind::call_edge, eedge, loc_info) { if (eedge.m_sedge) gcc_assert (eedge.m_sedge->m_kind == SUPEREDGE_CALL); @@ -813,7 +845,7 @@ call_event::call_event (const exploded_edge &eedge, m_dest_snode = eedge.m_dest->get_supernode (); } -/* Implementation of diagnostic_event::print_desc vfunc for +/* Implementation of diagnostics::paths::event::print_desc vfunc for call_event. If this call event passes critical state for an sm-based warning, @@ -845,13 +877,13 @@ call_event::print_desc (pretty_printer &pp) const get_caller_fndecl ()); } -/* Implementation of diagnostic_event::get_meaning vfunc for +/* Implementation of diagnostics::paths::event::get_meaning vfunc for function call events. */ -diagnostic_event::meaning +diagnostics::paths::event::meaning call_event::get_meaning () const { - return meaning (VERB_call, NOUN_function); + return meaning (verb::call, noun::function); } /* Override of checker_event::is_call_p for calls. */ @@ -874,13 +906,21 @@ call_event::get_callee_fndecl () const return m_dest_snode->m_fun->decl; } +const program_state * +call_event::get_program_state () const +{ + /* Use the state at the source (at the caller), + rather than the one at the dest, which has a frame for the callee. */ + return &m_eedge.m_src->get_state (); +} + /* class return_event : public superedge_event. */ /* return_event's ctor. */ return_event::return_event (const exploded_edge &eedge, const event_loc_info &loc_info) -: superedge_event (EK_RETURN_EDGE, eedge, loc_info) +: superedge_event (event_kind::return_edge, eedge, loc_info) { if (eedge.m_sedge) gcc_assert (eedge.m_sedge->m_kind == SUPEREDGE_RETURN); @@ -889,7 +929,7 @@ return_event::return_event (const exploded_edge &eedge, m_dest_snode = eedge.m_dest->get_supernode (); } -/* Implementation of diagnostic_event::print_desc vfunc for +/* Implementation of diagnostics::paths::event::print_desc vfunc for return_event. If this return event returns critical state for an sm-based warning, @@ -921,13 +961,13 @@ return_event::print_desc (pretty_printer &pp) const m_src_snode->m_fun->decl); } -/* Implementation of diagnostic_event::get_meaning vfunc for +/* Implementation of diagnostics::paths::event::get_meaning vfunc for function return events. */ -diagnostic_event::meaning +diagnostics::paths::event::meaning return_event::get_meaning () const { - return meaning (VERB_return, NOUN_function); + return meaning (verb::return_, noun::function); } /* Override of checker_event::is_return_p for returns. */ @@ -948,14 +988,14 @@ start_consolidated_cfg_edges_event::print_desc (pretty_printer &pp) const m_edge_sense ? "true" : "false"); } -/* Implementation of diagnostic_event::get_meaning vfunc for +/* Implementation of diagnostics::paths::event::get_meaning vfunc for start_consolidated_cfg_edges_event. */ -diagnostic_event::meaning +diagnostics::paths::event::meaning start_consolidated_cfg_edges_event::get_meaning () const { - return meaning (VERB_branch, - (m_edge_sense ? PROPERTY_true : PROPERTY_false)); + return meaning (verb::branch, + (m_edge_sense ? property::true_ : property::false_)); } /* class inlined_call_event : public checker_event. */ @@ -969,18 +1009,18 @@ inlined_call_event::print_desc (pretty_printer &pp) const m_apparent_caller_fndecl); } -/* Implementation of diagnostic_event::get_meaning vfunc for +/* Implementation of diagnostics::paths::event::get_meaning vfunc for reconstructed inlined function calls. */ -diagnostic_event::meaning +diagnostics::paths::event::meaning inlined_call_event::get_meaning () const { - return meaning (VERB_call, NOUN_function); + return meaning (verb::call, noun::function); } /* class setjmp_event : public checker_event. */ -/* Implementation of diagnostic_event::print_desc vfunc for +/* Implementation of diagnostics::paths::event::print_desc vfunc for setjmp_event. */ void @@ -999,7 +1039,7 @@ setjmp_event::print_desc (pretty_printer &pp) const void setjmp_event::prepare_for_emission (checker_path *path, pending_diagnostic *pd, - diagnostic_event_id_t emission_id) + diagnostics::paths::event_id_t emission_id) { checker_event::prepare_for_emission (path, pd, emission_id); path->record_setjmp_event (m_enode, emission_id); @@ -1038,7 +1078,7 @@ rewind_event::rewind_event (const exploded_edge *eedge, /* class rewind_from_longjmp_event : public rewind_event. */ -/* Implementation of diagnostic_event::print_desc vfunc for +/* Implementation of diagnostics::paths::event::print_desc vfunc for rewind_from_longjmp_event. */ void @@ -1062,7 +1102,7 @@ rewind_from_longjmp_event::print_desc (pretty_printer &pp) const /* class rewind_to_setjmp_event : public rewind_event. */ -/* Implementation of diagnostic_event::print_desc vfunc for +/* Implementation of diagnostics::paths::event::print_desc vfunc for rewind_to_setjmp_event. */ void @@ -1111,16 +1151,60 @@ rewind_to_setjmp_event::print_desc (pretty_printer &pp) const void rewind_to_setjmp_event::prepare_for_emission (checker_path *path, pending_diagnostic *pd, - diagnostic_event_id_t emission_id) + diagnostics::paths::event_id_t emission_id) { checker_event::prepare_for_emission (path, pd, emission_id); path->get_setjmp_event (m_rewind_info->get_enode_origin (), &m_original_setjmp_event_id); } +/* class throw_event : public checker_event. */ + +/* class explicit_throw_event : public throw_event. */ +void +explicit_throw_event::print_desc (pretty_printer &pp) const +{ + if (m_is_rethrow) + { + if (m_type) + pp_printf (&pp, "rethrowing exception of type %qT here...", m_type); + else + pp_printf (&pp, "rethrowing exception here..."); + } + else + { + if (m_type) + pp_printf (&pp, "throwing exception of type %qT here...", m_type); + else + pp_printf (&pp, "throwing exception here..."); + } +} + +/* class throw_from_call_to_external_fn_event : public throw_event. */ + +void +throw_from_call_to_external_fn_event::print_desc (pretty_printer &pp) const +{ + if (m_fndecl) + pp_printf (&pp, "if %qD throws an exception...", m_fndecl); + else + pp_printf (&pp, "if the called function throws an exception..."); +} + +// class unwind_event : public checker_event + +void +unwind_event::print_desc (pretty_printer &pp) const +{ + if (m_num_frames > 1) + pp_printf (&pp, "unwinding %i stack frames", m_num_frames); + else + pp_printf (&pp, "unwinding stack frame"); +} + /* class warning_event : public checker_event. */ -/* Implementation of diagnostic_event::print_desc vfunc for +/* Implementation of diagnostics::paths::event::print_desc vfunc for warning_event. If the pending diagnostic implements describe_final_event, use it, @@ -1165,13 +1249,22 @@ warning_event::print_desc (pretty_printer &pp) const pp_string (&pp, "here"); } -/* Implementation of diagnostic_event::get_meaning vfunc for +/* Implementation of diagnostics::paths::event::get_meaning vfunc for warning_event. */ -diagnostic_event::meaning +diagnostics::paths::event::meaning warning_event::get_meaning () const { - return meaning (VERB_danger, NOUN_unknown); + return meaning (verb::danger, noun::unknown); +} + +const program_state * +warning_event::get_program_state () const +{ + if (m_program_state) + return m_program_state.get (); + else + return &m_enode->get_state (); } } // namespace ana diff --git a/gcc/analyzer/checker-event.h b/gcc/analyzer/checker-event.h index f92f514..909e388 100644 --- a/gcc/analyzer/checker-event.h +++ b/gcc/analyzer/checker-event.h @@ -1,4 +1,4 @@ -/* Subclasses of diagnostic_event for analyzer diagnostics. +/* Subclasses of diagnostics::paths::event for analyzer diagnostics. Copyright (C) 2019-2025 Free Software Foundation, Inc. Contributed by David Malcolm <dmalcolm@redhat.com>. @@ -24,31 +24,35 @@ along with GCC; see the file COPYING3. If not see #include "tree-logical-location.h" #include "analyzer/program-state.h" #include "analyzer/event-loc-info.h" +#include "diagnostics/digraphs.h" namespace ana { /* An enum for discriminating between the concrete subclasses of checker_event. */ -enum event_kind -{ - EK_DEBUG, - EK_CUSTOM, - EK_STMT, - EK_REGION_CREATION, - EK_FUNCTION_ENTRY, - EK_STATE_CHANGE, - EK_START_CFG_EDGE, - EK_END_CFG_EDGE, - EK_CALL_EDGE, - EK_RETURN_EDGE, - EK_START_CONSOLIDATED_CFG_EDGES, - EK_END_CONSOLIDATED_CFG_EDGES, - EK_INLINED_CALL, - EK_SETJMP, - EK_REWIND_FROM_LONGJMP, - EK_REWIND_TO_SETJMP, - EK_WARNING +enum class event_kind +{ + debug, + custom, + stmt, + region_creation, + function_entry, + state_change, + start_cfg_edge, + end_cfg_edge, + catch_, + call_edge, + return_edge, + start_consolidated_cfg_edges, + end_consolidated_cfg_edges, + inlined_call, + setjmp_, + rewind_from_longjmp, + rewind_to_setjmp, + throw_, + unwind, + warning }; extern const char *event_kind_to_string (enum event_kind ek); @@ -58,71 +62,83 @@ extern const char *event_kind_to_string (enum event_kind ek); The class hierarchy looks like this (using indentation to show inheritance, and with event_kinds shown for the concrete subclasses): - diagnostic_event + diagnostics::paths::event checker_event - debug_event (EK_DEBUG) - custom_event (EK_CUSTOM) + debug_event (event_kind::debug) + custom_event (event_kind::custom) precanned_custom_event - statement_event (EK_STMT) - region_creation_event (EK_REGION_CREATION) - function_entry_event (EK_FUNCTION_ENTRY) - state_change_event (EK_STATE_CHANGE) + statement_event (event_kind::stmt) + region_creation_event (event_kind::region_creation) + function_entry_event (event_kind::function_entry) + state_change_event (event_kind::state_change) superedge_event cfg_edge_event - start_cfg_edge_event (EK_START_CFG_EDGE) - end_cfg_edge_event (EK_END_CFG_EDGE) - call_event (EK_CALL_EDGE) - return_edge (EK_RETURN_EDGE) - start_consolidated_cfg_edges_event (EK_START_CONSOLIDATED_CFG_EDGES) - end_consolidated_cfg_edges_event (EK_END_CONSOLIDATED_CFG_EDGES) - inlined_call_event (EK_INLINED_CALL) - setjmp_event (EK_SETJMP) + start_cfg_edge_event (event_kind::start_cfg_edge) + end_cfg_edge_event (event_kind::end_cfg_edge) + catch_cfg_edge_event (event_kind::catch_cfg_edge) + call_event (event_kind::call_edge) + return_edge (event_kind::return_edge) + start_consolidated_cfg_edges_event (event_kind::start_consolidated_cfg_edges) + end_consolidated_cfg_edges_event (event_kind::end_consolidated_cfg_edges) + inlined_call_event (event_kind::inlined_call) + setjmp_event (event_kind::setjmp_) rewind_event - rewind_from_longjmp_event (EK_REWIND_FROM_LONGJMP) - rewind_to_setjmp_event (EK_REWIND_TO_SETJMP) - warning_event (EK_WARNING). */ - -/* Abstract subclass of diagnostic_event; the base class for use in - checker_path (the analyzer's diagnostic_path subclass). */ - -class checker_event : public diagnostic_event + rewind_from_longjmp_event (event_kind::rewind_from_longjmp) + rewind_to_setjmp_event (event_kind::rewind_to_setjmp) + throw_event (event_kind:throw_) + explicit_throw_event + throw_from_call_to_external_fn_event + unwind_event (event_kind::unwind) + warning_event (event_kind::warning). */ + +/* Abstract subclass of diagnostics::paths::event; the base class for use in + checker_path (the analyzer's diagnostics::paths::path subclass). */ + +class checker_event : public diagnostics::paths::event { public: - /* Implementation of diagnostic_event. */ + /* Implementation of diagnostics::paths::event. */ location_t get_location () const final override { return m_loc; } int get_stack_depth () const final override { return m_effective_depth; } - const logical_location *get_logical_location () const final override + diagnostics::logical_locations::key + get_logical_location () const final override { - if (m_effective_fndecl) - return &m_logical_loc; - else - return NULL; + return m_logical_loc; } meaning get_meaning () const override; bool connect_to_next_event_p () const override { return false; } - diagnostic_thread_id_t get_thread_id () const final override + diagnostics::paths::thread_id_t get_thread_id () const final override { return 0; } void - maybe_add_sarif_properties (sarif_object &thread_flow_loc_obj) const override; + maybe_add_sarif_properties (diagnostics::sarif_builder &, + diagnostics::sarif_object &thread_flow_loc_obj) + const override; /* Additional functionality. */ + enum event_kind get_kind () const { return m_kind; } tree get_fndecl () const { return m_effective_fndecl; } int get_original_stack_depth () const { return m_original_depth; } virtual void prepare_for_emission (checker_path *, pending_diagnostic *pd, - diagnostic_event_id_t emission_id); + diagnostics::paths::event_id_t emission_id); virtual bool is_call_p () const { return false; } virtual bool is_function_entry_p () const { return false; } virtual bool is_return_p () const { return false; } + std::unique_ptr<diagnostics::digraphs::digraph> + maybe_make_diagnostic_state_graph (bool debug) const final override; + + virtual const program_state * + get_program_state () const { return nullptr; } + /* For use with %@. */ - const diagnostic_event_id_t *get_id_ptr () const + const diagnostics::paths::event_id_t *get_id_ptr () const { return &m_emission_id; } @@ -136,7 +152,8 @@ protected: checker_event (enum event_kind kind, const event_loc_info &loc_info); - public: + private: + const checker_path *m_path; const enum event_kind m_kind; protected: location_t m_loc; @@ -145,8 +162,8 @@ protected: int m_original_depth; int m_effective_depth; pending_diagnostic *m_pending_diagnostic; - diagnostic_event_id_t m_emission_id; // only set once all pruning has occurred - tree_logical_location m_logical_loc; + diagnostics::paths::event_id_t m_emission_id; // only set once all pruning has occurred + diagnostics::logical_locations::key m_logical_loc; }; /* A concrete event subclass for a purely textual event, for use in @@ -158,7 +175,7 @@ public: debug_event (const event_loc_info &loc_info, const char *desc) - : checker_event (EK_DEBUG, loc_info), + : checker_event (event_kind::debug, loc_info), m_desc (xstrdup (desc)) { } @@ -180,7 +197,7 @@ class custom_event : public checker_event { protected: custom_event (const event_loc_info &loc_info) - : checker_event (EK_CUSTOM, loc_info) + : checker_event (event_kind::custom, loc_info) { } }; @@ -218,6 +235,12 @@ public: void print_desc (pretty_printer &) const final override; + const program_state * + get_program_state () const final override + { + return &m_dst_state; + } + const gimple * const m_stmt; const program_state m_dst_state; }; @@ -328,17 +351,29 @@ private: class function_entry_event : public checker_event { public: - function_entry_event (const event_loc_info &loc_info) - : checker_event (EK_FUNCTION_ENTRY, loc_info) + function_entry_event (const event_loc_info &loc_info, + const program_state &state) + : checker_event (event_kind::function_entry, loc_info), + m_state (state) { } - function_entry_event (const program_point &dst_point); + function_entry_event (const program_point &dst_point, + const program_state &state); void print_desc (pretty_printer &pp) const override; meaning get_meaning () const override; bool is_function_entry_p () const final override { return true; } + + const program_state * + get_program_state () const final override + { + return &m_state; + } + +private: + const program_state &m_state; }; /* Subclass of checker_event describing a state change. */ @@ -359,6 +394,12 @@ public: void print_desc (pretty_printer &pp) const final override; meaning get_meaning () const override; + const program_state * + get_program_state () const final override + { + return &m_dst_state; + } + const function *get_dest_function () const { return m_dst_state.get_current_function (); @@ -383,7 +424,9 @@ public: class superedge_event : public checker_event { public: - void maybe_add_sarif_properties (sarif_object &thread_flow_loc_obj) + void + maybe_add_sarif_properties (diagnostics::sarif_builder &, + diagnostics::sarif_object &thread_flow_loc_obj) const override; /* Mark this edge event as being either an interprocedural call or @@ -400,6 +443,9 @@ public: bool should_filter_p (int verbosity) const; + const program_state * + get_program_state () const override; + protected: superedge_event (enum event_kind kind, const exploded_edge &eedge, const event_loc_info &loc_info); @@ -435,7 +481,7 @@ class start_cfg_edge_event : public cfg_edge_event public: start_cfg_edge_event (const exploded_edge &eedge, const event_loc_info &loc_info) - : cfg_edge_event (EK_START_CFG_EDGE, eedge, loc_info) + : cfg_edge_event (event_kind::start_cfg_edge, eedge, loc_info) { } @@ -461,7 +507,7 @@ class end_cfg_edge_event : public cfg_edge_event public: end_cfg_edge_event (const exploded_edge &eedge, const event_loc_info &loc_info) - : cfg_edge_event (EK_END_CFG_EDGE, eedge, loc_info) + : cfg_edge_event (event_kind::end_cfg_edge, eedge, loc_info) { } @@ -471,6 +517,32 @@ public: } }; +/* A concrete event subclass for catching an exception + e.g. "...catching 'struct io_error' here". */ + +class catch_cfg_edge_event : public cfg_edge_event +{ +public: + catch_cfg_edge_event (const exploded_edge &eedge, + const event_loc_info &loc_info, + tree type) + : cfg_edge_event (event_kind::catch_, eedge, loc_info), + m_type (type) + { + } + + void print_desc (pretty_printer &pp) const final override + { + if (m_type) + pp_printf (&pp, "...catching exception of type %qT here", m_type); + else + pp_string (&pp, "...catching exception here"); + } + +private: + tree m_type; +}; + /* A concrete event subclass for an interprocedural call. */ class call_event : public superedge_event @@ -484,6 +556,9 @@ public: bool is_call_p () const final override; + const program_state * + get_program_state () const final override; + protected: tree get_caller_fndecl () const; tree get_callee_fndecl () const; @@ -517,7 +592,7 @@ class start_consolidated_cfg_edges_event : public checker_event public: start_consolidated_cfg_edges_event (const event_loc_info &loc_info, bool edge_sense) - : checker_event (EK_START_CONSOLIDATED_CFG_EDGES, loc_info), + : checker_event (event_kind::start_consolidated_cfg_edges, loc_info), m_edge_sense (edge_sense) { } @@ -537,7 +612,7 @@ class end_consolidated_cfg_edges_event : public checker_event { public: end_consolidated_cfg_edges_event (const event_loc_info &loc_info) - : checker_event (EK_END_CONSOLIDATED_CFG_EDGES, loc_info) + : checker_event (event_kind::end_consolidated_cfg_edges, loc_info) { } @@ -558,7 +633,7 @@ public: tree apparent_caller_fndecl, int actual_depth, int stack_depth_adjustment) - : checker_event (EK_INLINED_CALL, + : checker_event (event_kind::inlined_call, event_loc_info (loc, apparent_caller_fndecl, actual_depth + stack_depth_adjustment)), @@ -583,8 +658,8 @@ class setjmp_event : public checker_event public: setjmp_event (const event_loc_info &loc_info, const exploded_node *enode, - const gcall *setjmp_call) - : checker_event (EK_SETJMP, loc_info), + const gcall &setjmp_call) + : checker_event (event_kind::setjmp_, loc_info), m_enode (enode), m_setjmp_call (setjmp_call) { } @@ -593,11 +668,11 @@ public: void prepare_for_emission (checker_path *path, pending_diagnostic *pd, - diagnostic_event_id_t emission_id) final override; + diagnostics::paths::event_id_t emission_id) final override; private: const exploded_node *m_enode; - const gcall *m_setjmp_call; + const gcall &m_setjmp_call; }; /* An abstract event subclass for rewinding from a longjmp to a setjmp @@ -633,7 +708,7 @@ public: rewind_from_longjmp_event (const exploded_edge *eedge, const event_loc_info &loc_info, const rewind_info_t *rewind_info) - : rewind_event (eedge, EK_REWIND_FROM_LONGJMP, loc_info, + : rewind_event (eedge, event_kind::rewind_from_longjmp, loc_info, rewind_info) { } @@ -650,7 +725,7 @@ public: rewind_to_setjmp_event (const exploded_edge *eedge, const event_loc_info &loc_info, const rewind_info_t *rewind_info) - : rewind_event (eedge, EK_REWIND_TO_SETJMP, loc_info, + : rewind_event (eedge, event_kind::rewind_to_setjmp, loc_info, rewind_info) { } @@ -659,10 +734,92 @@ public: void prepare_for_emission (checker_path *path, pending_diagnostic *pd, - diagnostic_event_id_t emission_id) final override; + diagnostics::paths::event_id_t emission_id) final override; private: - diagnostic_event_id_t m_original_setjmp_event_id; + diagnostics::paths::event_id_t m_original_setjmp_event_id; +}; + +/* An abstract subclass for throwing/rethrowing an exception. */ + +class throw_event : public checker_event +{ +public: + throw_event (const event_loc_info &loc_info, + const exploded_node *enode, + const gcall &throw_call) + : checker_event (event_kind::throw_, loc_info), + m_enode (enode), + m_throw_call (throw_call) + { + } + +protected: + const exploded_node *m_enode; + const gcall &m_throw_call; +}; + +/* A concrete event subclass for an explicit "throw EXC;" + or "throw;" (actually, a call to __cxa_throw or __cxa_rethrow). */ + +class explicit_throw_event : public throw_event +{ +public: + explicit_throw_event (const event_loc_info &loc_info, + const exploded_node *enode, + const gcall &throw_call, + tree type, + bool is_rethrow) + : throw_event (loc_info, enode, throw_call), + m_type (type), + m_is_rethrow (is_rethrow) + { + } + + void print_desc (pretty_printer &pp) const final override; + +private: + tree m_type; + bool m_is_rethrow; +}; + +/* A concrete event subclass for an exception being thrown + from within a call to a function we don't have the body of, + or where we don't know what function was called. */ + +class throw_from_call_to_external_fn_event : public throw_event +{ +public: + throw_from_call_to_external_fn_event (const event_loc_info &loc_info, + const exploded_node *enode, + const gcall &throw_call, + tree fndecl) + : throw_event (loc_info, enode, throw_call), + m_fndecl (fndecl) + { + } + + void print_desc (pretty_printer &pp) const final override; + +private: + tree m_fndecl; +}; + +/* A concrete event subclass for unwinding a stack frame when + processing an exception. */ + +class unwind_event : public checker_event +{ +public: + unwind_event (const event_loc_info &loc_info) + : checker_event (event_kind::unwind, loc_info), + m_num_frames (1) + { + } + + void print_desc (pretty_printer &pp) const final override; + + int m_num_frames; }; /* Concrete subclass of checker_event for use at the end of a path: @@ -676,16 +833,22 @@ public: warning_event (const event_loc_info &loc_info, const exploded_node *enode, const state_machine *sm, - tree var, state_machine::state_t state) - : checker_event (EK_WARNING, loc_info), + tree var, state_machine::state_t state, + const program_state *program_state_ = nullptr) + : checker_event (event_kind::warning, loc_info), m_enode (enode), m_sm (sm), m_var (var), m_state (state) { + if (program_state_) + m_program_state = std::make_unique<program_state> (*program_state_); } void print_desc (pretty_printer &pp) const final override; meaning get_meaning () const override; + const program_state * + get_program_state () const final override; + const exploded_node *get_exploded_node () const { return m_enode; } private: @@ -693,6 +856,9 @@ private: const state_machine *m_sm; tree m_var; state_machine::state_t m_state; + /* Optional copy of program state, for when this is different from + m_enode's state: */ + std::unique_ptr<program_state> m_program_state; }; } // namespace ana diff --git a/gcc/analyzer/checker-path.cc b/gcc/analyzer/checker-path.cc index d97378e..646d72c 100644 --- a/gcc/analyzer/checker-path.cc +++ b/gcc/analyzer/checker-path.cc @@ -1,4 +1,4 @@ -/* Subclass of diagnostic_path for analyzer diagnostics. +/* Subclass of diagnostics::paths::path for analyzer diagnostics. Copyright (C) 2019-2025 Free Software Foundation, Inc. Contributed by David Malcolm <dmalcolm@redhat.com>. @@ -18,37 +18,22 @@ 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/>. */ -#include "config.h" -#define INCLUDE_VECTOR -#include "system.h" -#include "coretypes.h" -#include "tree.h" -#include "function.h" -#include "basic-block.h" -#include "gimple.h" -#include "diagnostic-core.h" -#include "gimple-pretty-print.h" -#include "fold-const.h" -#include "diagnostic-path.h" -#include "options.h" -#include "cgraph.h" -#include "cfg.h" -#include "digraph.h" -#include "diagnostic-event-id.h" -#include "analyzer/analyzer.h" -#include "analyzer/analyzer-logging.h" -#include "analyzer/sm.h" +#include "analyzer/common.h" + +#include "tree-pretty-print.h" #include "sbitmap.h" -#include "bitmap.h" #include "ordered-hash-map.h" +#include "gimple-iterator.h" +#include "inlining-iterator.h" + +#include "analyzer/analyzer-logging.h" +#include "analyzer/sm.h" #include "analyzer/call-string.h" #include "analyzer/program-point.h" #include "analyzer/store.h" #include "analyzer/region-model.h" #include "analyzer/program-state.h" #include "analyzer/checker-path.h" -#include "gimple-iterator.h" -#include "inlining-iterator.h" #include "analyzer/supergraph.h" #include "analyzer/pending-diagnostic.h" #include "analyzer/diagnostic-manager.h" @@ -56,7 +41,6 @@ along with GCC; see the file COPYING3. If not see #include "analyzer/diagnostic-manager.h" #include "analyzer/checker-path.h" #include "analyzer/exploded-graph.h" -#include "make-unique.h" #if ENABLE_ANALYZER @@ -105,7 +89,7 @@ checker_path::maybe_log (logger *logger, const char *desc) const { logger->start_log_line (); logger->log_partial ("%s[%i]: %s ", desc, i, - event_kind_to_string (m_events[i]->m_kind)); + event_kind_to_string (m_events[i]->get_kind ())); m_events[i]->dump (logger->get_printer ()); logger->end_log_line (); } @@ -119,7 +103,7 @@ checker_path::add_event (std::unique_ptr<checker_event> event) m_logger->start_log_line (); m_logger->log_partial ("added event[%i]: %s ", m_events.length (), - event_kind_to_string (event.get ()->m_kind)); + event_kind_to_string (event.get ()->get_kind ())); event.get ()->dump (m_logger->get_printer ()); m_logger->end_log_line (); } @@ -139,7 +123,7 @@ checker_path::debug () const fprintf (stderr, "[%i]: %s \"%s\"\n", i, - event_kind_to_string (m_events[i]->m_kind), + event_kind_to_string (m_events[i]->get_kind ()), event_desc.get ()); } } @@ -164,8 +148,8 @@ checker_path::add_region_creation_events (pending_diagnostic *pd, pd->add_region_creation_events (reg, capacity, loc_info, *this); if (debug) - add_event (make_unique<region_creation_event_debug> (reg, capacity, - loc_info)); + add_event (std::make_unique<region_creation_event_debug> (reg, capacity, + loc_info)); } void @@ -183,8 +167,8 @@ checker_path::cfg_edge_pair_at_p (unsigned idx) const { if (m_events.length () < idx + 1) return false; - return (m_events[idx]->m_kind == EK_START_CFG_EDGE - && m_events[idx + 1]->m_kind == EK_END_CFG_EDGE); + return (m_events[idx]->get_kind () == event_kind::start_cfg_edge + && m_events[idx + 1]->get_kind () == event_kind::end_cfg_edge); } /* Consider a call from "outer" to "middle" which calls "inner", @@ -202,38 +186,38 @@ checker_path::cfg_edge_pair_at_p (unsigned idx) const (for gcc.dg/analyzer/inlining-4.c): before[0]: - EK_FUNCTION_ENTRY "entry to ‘outer’" + event_kind::function_entry "entry to ‘outer’" (depth 1, fndecl ‘outer’, m_loc=511c4) before[1]: - EK_START_CFG_EDGE "following ‘true’ branch (when ‘flag != 0’)..." + event_kind::start_cfg_edge "following ‘true’ branch (when ‘flag != 0’)..." (depth 3 corrected from 1, fndecl ‘inner’ corrected from ‘outer’, m_loc=8000000f) before[2]: - EK_END_CFG_EDGE "...to here" + event_kind::end_cfg_edge "...to here" (depth 1, fndecl ‘outer’, m_loc=0) before[3]: - EK_WARNING "here (‘<unknown>’ is in state ‘null’)" + event_kind::warning "here (‘<unknown>’ is in state ‘null’)" (depth 1, fndecl ‘outer’, m_loc=80000004) We want to add inlined_call_events showing the calls, so that the above becomes: after[0]: - EK_FUNCTION_ENTRY "entry to ‘outer’" + event_kind::function_entry "entry to ‘outer’" (depth 1, fndecl ‘outer’, m_loc=511c4) after[1]: - EK_INLINED_CALL "inlined call to ‘middle’ from ‘outer’" + event_kind::inlined_call "inlined call to ‘middle’ from ‘outer’" (depth 1, fndecl ‘outer’, m_loc=53300) after[2]: - EK_INLINED_CALL "inlined call to ‘inner’ from ‘middle’" + event_kind::inlined_call "inlined call to ‘inner’ from ‘middle’" (depth 2, fndecl ‘middle’, m_loc=4d2e0) after[3]: - EK_START_CFG_EDGE "following ‘true’ branch (when ‘flag != 0’)..." + event_kind::start_cfg_edge "following ‘true’ branch (when ‘flag != 0’)..." (depth 3 corrected from 1, fndecl ‘inner’ corrected from ‘outer’, m_loc=8000000f) - after[4]: EK_END_CFG_EDGE "...to here" + after[4]: event_kind::end_cfg_edge "...to here" (depth 1, fndecl ‘outer’, m_loc=0) - after[5]: EK_WARNING "here (‘<unknown>’ is in state ‘null’)" + after[5]: event_kind::warning "here (‘<unknown>’ is in state ‘null’)" (depth 1, fndecl ‘outer’, m_loc=80000004) where we've added events between before[0] and before[1] to show @@ -270,7 +254,7 @@ checker_path::inject_any_inlined_call_events (logger *logger) { logger->start_log_line (); logger->log_partial ("event[%i]: %s ", ev_idx, - event_kind_to_string (curr_event->m_kind)); + event_kind_to_string (curr_event->get_kind ())); curr_event->dump (logger->get_printer ()); logger->end_log_line (); for (inlining_iterator iter (curr_event->get_location ()); diff --git a/gcc/analyzer/checker-path.h b/gcc/analyzer/checker-path.h index dfc782d..490d21b 100644 --- a/gcc/analyzer/checker-path.h +++ b/gcc/analyzer/checker-path.h @@ -1,4 +1,4 @@ -/* Subclass of diagnostic_path for analyzer diagnostics. +/* Subclass of diagnostics::paths::path for analyzer diagnostics. Copyright (C) 2019-2025 Free Software Foundation, Inc. Contributed by David Malcolm <dmalcolm@redhat.com>. @@ -26,25 +26,29 @@ along with GCC; see the file COPYING3. If not see namespace ana { -/* Subclass of diagnostic_path for analyzer diagnostics. */ +/* Subclass of diagnostic path for analyzer diagnostics. */ -class checker_path : public diagnostic_path +class checker_path : public diagnostics::paths::path { public: - checker_path (logger *logger) - : diagnostic_path (), + checker_path (const diagnostics::logical_locations::manager &logical_loc_mgr, + const extrinsic_state &ext_state, + logger *logger) + : diagnostics::paths::path (logical_loc_mgr), + m_ext_state (ext_state), m_thread ("main"), m_logger (logger) {} - /* Implementation of diagnostic_path vfuncs. */ + /* Implementation of diagnostics::paths::path vfuncs. */ unsigned num_events () const final override { return m_events.length (); } - const diagnostic_event & get_event (int idx) const final override + const diagnostics::paths::event & + get_event (int idx) const final override { return *m_events[idx]; } @@ -52,12 +56,14 @@ public: { return 1; } - const diagnostic_thread & - get_thread (diagnostic_thread_id_t) const final override + const diagnostics::paths::thread & + get_thread (diagnostics::paths::thread_id_t) const final override { return m_thread; } + const extrinsic_state &get_ext_state () const { return m_ext_state; } + checker_event *get_checker_event (int idx) { return m_events[idx]; @@ -110,21 +116,22 @@ public: checker_event *e; int i; FOR_EACH_VEC_ELT (m_events, i, e) - e->prepare_for_emission (this, pd, diagnostic_event_id_t (i)); + e->prepare_for_emission (this, pd, diagnostics::paths::event_id_t (i)); } void fixup_locations (pending_diagnostic *pd); void record_setjmp_event (const exploded_node *enode, - diagnostic_event_id_t setjmp_emission_id) + diagnostics::paths::event_id_t setjmp_emission_id) { m_setjmp_event_ids.put (enode, setjmp_emission_id); } bool get_setjmp_event (const exploded_node *enode, - diagnostic_event_id_t *out_emission_id) + diagnostics::paths::event_id_t *out_emission_id) { - if (diagnostic_event_id_t *emission_id = m_setjmp_event_ids.get (enode)) + if (diagnostics::paths::event_id_t *emission_id + = m_setjmp_event_ids.get (enode)) { *out_emission_id = *emission_id; return true; @@ -139,6 +146,8 @@ public: private: DISABLE_COPY_AND_ASSIGN(checker_path); + const extrinsic_state &m_ext_state; + simple_diagnostic_thread m_thread; /* The events that have occurred along this path. */ @@ -147,7 +156,7 @@ private: /* During prepare_for_emission (and after), the setjmp_event for each exploded_node *, so that rewind events can refer to them in their descriptions. */ - hash_map <const exploded_node *, diagnostic_event_id_t> m_setjmp_event_ids; + hash_map <const exploded_node *, diagnostics::paths::event_id_t> m_setjmp_event_ids; logger *m_logger; }; diff --git a/gcc/analyzer/analyzer.h b/gcc/analyzer/common.h index 4843d99..ac3048c 100644 --- a/gcc/analyzer/analyzer.h +++ b/gcc/analyzer/common.h @@ -1,4 +1,4 @@ -/* Utility functions for the analyzer. +/* Base header for the analyzer, plus utility functions. Copyright (C) 2019-2025 Free Software Foundation, Inc. Contributed by David Malcolm <dmalcolm@redhat.com>. @@ -18,9 +18,23 @@ 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_ANALYZER_H -#define GCC_ANALYZER_ANALYZER_H +#ifndef GCC_ANALYZER_COMMON_H +#define GCC_ANALYZER_COMMON_H +#include "config.h" +#define INCLUDE_MAP +#define INCLUDE_STRING +#define INCLUDE_VECTOR +#include "system.h" +#include "coretypes.h" +#include "tree.h" +#include "function.h" +#include "basic-block.h" +#include "gimple.h" +#include "options.h" +#include "bitmap.h" +#include "diagnostic-core.h" +#include "diagnostics/paths.h" #include "rich-location.h" #include "function.h" #include "json.h" @@ -37,6 +51,9 @@ class supernode; class superedge; class cfg_superedge; class switch_cfg_superedge; + class eh_dispatch_cfg_superedge; + class eh_dispatch_try_cfg_superedge; + class eh_dispatch_allowed_cfg_superedge; class callgraph_superedge; class call_superedge; class return_superedge; @@ -192,14 +209,14 @@ class region_offset { public: region_offset () - : m_base_region (NULL), m_offset (0), m_sym_offset (NULL) + : m_base_region (nullptr), m_offset (0), m_sym_offset (nullptr) { } static region_offset make_concrete (const region *base_region, bit_offset_t offset) { - return region_offset (base_region, offset, NULL); + return region_offset (base_region, offset, nullptr); } static region_offset make_symbolic (const region *base_region, const svalue *sym_offset) @@ -211,8 +228,8 @@ public: const region *get_base_region () const { return m_base_region; } - bool concrete_p () const { return m_sym_offset == NULL; } - bool symbolic_p () const { return m_sym_offset != NULL; } + bool concrete_p () const { return m_sym_offset == nullptr; } + bool symbolic_p () const { return m_sym_offset != nullptr; } bit_offset_t get_bit_offset () const { @@ -289,7 +306,7 @@ public: } virtual const builtin_known_function * - dyn_cast_builtin_kf () const { return NULL; } + dyn_cast_builtin_kf () const { return nullptr; } }; /* Subclass of known_function for builtin functions. */ @@ -350,10 +367,10 @@ public: /* An enum for describing the direction of an access to memory. */ -enum access_direction +enum class access_direction { - DIR_READ, - DIR_WRITE + read, + write }; /* Abstract base class for associating custom data with an @@ -384,6 +401,12 @@ public: virtual void add_events_to_path (checker_path *emission_path, const exploded_edge &eedge) const = 0; + + virtual exploded_node *create_enode (exploded_graph &eg, + const program_point &point, + program_state &&state, + exploded_node *enode_for_diag, + region_model_context *ctxt) const; }; /* Abstract base class for splitting state. @@ -420,7 +443,7 @@ extern std::unique_ptr<json::value> tree_to_json (tree node); extern std::unique_ptr<json::value> -diagnostic_event_id_to_json (const diagnostic_event_id_t &); +diagnostic_event_id_to_json (const diagnostics::paths::event_id_t &); extern std::unique_ptr<json::value> bit_offset_to_json (const bit_offset_t &offset); @@ -447,21 +470,23 @@ extern tree remove_ssa_names (tree expr); } // namespace ana -extern bool is_special_named_call_p (const gcall *call, const char *funcname, +extern bool is_special_named_call_p (const gcall &call, const char *funcname, unsigned int num_args, bool look_in_std = false); extern bool is_named_call_p (const_tree fndecl, const char *funcname); extern bool is_named_call_p (const_tree fndecl, const char *funcname, - const gcall *call, unsigned int num_args); + const gcall &call, unsigned int num_args); extern bool is_std_function_p (const_tree fndecl); extern bool is_std_named_call_p (const_tree fndecl, const char *funcname); extern bool is_std_named_call_p (const_tree fndecl, const char *funcname, - const gcall *call, unsigned int num_args); -extern bool is_setjmp_call_p (const gcall *call); -extern bool is_longjmp_call_p (const gcall *call); -extern bool is_placement_new_p (const gcall *call); + const gcall &call, unsigned int num_args); +extern bool is_setjmp_call_p (const gcall &call); +extern bool is_longjmp_call_p (const gcall &call); +extern bool is_placement_new_p (const gcall &call); +extern bool is_cxa_throw_p (const gcall &call); +extern bool is_cxa_rethrow_p (const gcall &call); -extern const char *get_user_facing_name (const gcall *call); +extern const char *get_user_facing_name (const gcall &call); extern void register_analyzer_pass (); @@ -542,13 +567,13 @@ public: delete (*iter).second; } - /* Get the instance of T for K if one exists, or NULL. */ + /* Get the instance of T for K if one exists, or nullptr. */ T *get (const key_t &k) const { if (instance_t **slot = const_cast<inner_map_t &> (m_inner_map).get (k)) return *slot; - return NULL; + return nullptr; } /* Take ownership of INSTANCE. */ @@ -577,4 +602,4 @@ private: extern void sorry_no_analyzer (); #endif /* #if !ENABLE_ANALYZER */ -#endif /* GCC_ANALYZER_ANALYZER_H */ +#endif /* GCC_ANALYZER_COMMON_H */ diff --git a/gcc/analyzer/complexity.cc b/gcc/analyzer/complexity.cc index cb88f60..4a0a156 100644 --- a/gcc/analyzer/complexity.cc +++ b/gcc/analyzer/complexity.cc @@ -18,34 +18,14 @@ 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/>. */ -#include "config.h" -#define INCLUDE_VECTOR -#include "system.h" -#include "coretypes.h" -#include "tree.h" -#include "diagnostic-core.h" -#include "gimple-pretty-print.h" -#include "function.h" -#include "basic-block.h" -#include "gimple.h" -#include "gimple-iterator.h" -#include "diagnostic-core.h" -#include "graphviz.h" -#include "options.h" -#include "cgraph.h" -#include "tree-dfa.h" -#include "stringpool.h" -#include "convert.h" -#include "target.h" -#include "fold-const.h" -#include "tree-pretty-print.h" -#include "bitmap.h" -#include "analyzer/analyzer.h" -#include "analyzer/analyzer-logging.h" +#include "analyzer/common.h" + #include "options.h" #include "cgraph.h" #include "cfg.h" #include "digraph.h" + +#include "analyzer/analyzer-logging.h" #include "analyzer/call-string.h" #include "analyzer/program-point.h" #include "analyzer/store.h" diff --git a/gcc/analyzer/constraint-manager.cc b/gcc/analyzer/constraint-manager.cc index 55d8996..58c60fe 100644 --- a/gcc/analyzer/constraint-manager.cc +++ b/gcc/analyzer/constraint-manager.cc @@ -18,28 +18,17 @@ 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/>. */ -#include "config.h" -#define INCLUDE_VECTOR -#include "system.h" -#include "coretypes.h" -#include "tree.h" -#include "function.h" -#include "basic-block.h" -#include "gimple.h" -#include "gimple-iterator.h" +#include "analyzer/common.h" + #include "fold-const.h" -#include "selftest.h" -#include "diagnostic-core.h" -#include "graphviz.h" -#include "analyzer/analyzer.h" #include "ordered-hash-map.h" -#include "options.h" #include "cgraph.h" #include "cfg.h" #include "digraph.h" -#include "analyzer/supergraph.h" #include "sbitmap.h" -#include "bitmap.h" +#include "tree-pretty-print.h" + +#include "analyzer/supergraph.h" #include "analyzer/analyzer-logging.h" #include "analyzer/call-string.h" #include "analyzer/program-point.h" @@ -48,8 +37,6 @@ along with GCC; see the file COPYING3. If not see #include "analyzer/constraint-manager.h" #include "analyzer/call-summary.h" #include "analyzer/analyzer-selftests.h" -#include "tree-pretty-print.h" -#include "make-unique.h" #if ENABLE_ANALYZER @@ -117,7 +104,7 @@ minus_one (tree cst) closed one. */ void -bound::ensure_closed (enum bound_kind bound_kind) +bound::ensure_closed (enum bound_kind bnd_kind) { if (!m_closed) { @@ -126,7 +113,7 @@ bound::ensure_closed (enum bound_kind bound_kind) and convert x < 5 into x <= 4. */ gcc_assert (CONSTANT_CLASS_P (m_constant)); gcc_assert (INTEGRAL_TYPE_P (TREE_TYPE (m_constant))); - m_constant = fold_build2 (bound_kind == BK_UPPER ? MINUS_EXPR : PLUS_EXPR, + m_constant = fold_build2 (bnd_kind == bound_kind::upper ? MINUS_EXPR : PLUS_EXPR, TREE_TYPE (m_constant), m_constant, integer_one_node); gcc_assert (CONSTANT_CLASS_P (m_constant)); @@ -203,8 +190,8 @@ range::constrained_to_single_element () return NULL_TREE; /* Convert any open bounds to closed bounds. */ - m_lower_bound.ensure_closed (BK_LOWER); - m_upper_bound.ensure_closed (BK_UPPER); + m_lower_bound.ensure_closed (bound_kind::lower); + m_upper_bound.ensure_closed (bound_kind::upper); // Are they equal? tree comparison = fold_binary (EQ_EXPR, boolean_type_node, @@ -303,30 +290,30 @@ range::above_upper_bound (tree rhs_const) const Return true if feasible; false if infeasible. */ bool -range::add_bound (bound b, enum bound_kind bound_kind) +range::add_bound (bound b, enum bound_kind bnd_kind) { /* Bail out on floating point constants. */ if (!INTEGRAL_TYPE_P (TREE_TYPE (b.m_constant))) return true; - b.ensure_closed (bound_kind); + b.ensure_closed (bnd_kind); - switch (bound_kind) + switch (bnd_kind) { default: gcc_unreachable (); - case BK_LOWER: + case bound_kind::lower: /* Discard redundant bounds. */ if (m_lower_bound.m_constant) { - m_lower_bound.ensure_closed (BK_LOWER); + m_lower_bound.ensure_closed (bound_kind::lower); if (tree_int_cst_le (b.m_constant, m_lower_bound.m_constant)) return true; } if (m_upper_bound.m_constant) { - m_upper_bound.ensure_closed (BK_UPPER); + m_upper_bound.ensure_closed (bound_kind::upper); /* Reject B <= V <= UPPER when B > UPPER. */ if (!tree_int_cst_le (b.m_constant, m_upper_bound.m_constant)) @@ -335,18 +322,18 @@ range::add_bound (bound b, enum bound_kind bound_kind) m_lower_bound = b; break; - case BK_UPPER: + case bound_kind::upper: /* Discard redundant bounds. */ if (m_upper_bound.m_constant) { - m_upper_bound.ensure_closed (BK_UPPER); + m_upper_bound.ensure_closed (bound_kind::upper); if (!tree_int_cst_lt (b.m_constant, m_upper_bound.m_constant)) return true; } if (m_lower_bound.m_constant) { - m_lower_bound.ensure_closed (BK_LOWER); + m_lower_bound.ensure_closed (bound_kind::lower); /* Reject LOWER <= V <= B when LOWER > B. */ if (!tree_int_cst_le (m_lower_bound.m_constant, b.m_constant)) @@ -371,16 +358,16 @@ range::add_bound (enum tree_code op, tree rhs_const) return true; case LT_EXPR: /* "V < RHS_CONST" */ - return add_bound (bound (rhs_const, false), BK_UPPER); + return add_bound (bound (rhs_const, false), bound_kind::upper); case LE_EXPR: /* "V <= RHS_CONST" */ - return add_bound (bound (rhs_const, true), BK_UPPER); + return add_bound (bound (rhs_const, true), bound_kind::upper); case GE_EXPR: /* "V >= RHS_CONST" */ - return add_bound (bound (rhs_const, true), BK_LOWER); + return add_bound (bound (rhs_const, true), bound_kind::lower); case GT_EXPR: /* "V > RHS_CONST" */ - return add_bound (bound (rhs_const, false), BK_LOWER); + return add_bound (bound (rhs_const, false), bound_kind::lower); } } @@ -449,7 +436,7 @@ bounded_range::dump (bool show_types) const std::unique_ptr<json::object> bounded_range::to_json () const { - auto range_obj = ::make_unique<json::object> (); + auto range_obj = std::make_unique<json::object> (); set_json_attr (*range_obj, "lower", m_lower); set_json_attr (*range_obj, "upper", m_upper); return range_obj; @@ -622,7 +609,7 @@ bounded_ranges::canonicalize () { bounded_range *prev = &m_ranges[i - 1]; const bounded_range *next = &m_ranges[i]; - if (prev->intersects_p (*next, NULL) + if (prev->intersects_p (*next, nullptr) || (can_plus_one_p (prev->m_upper) && tree_int_cst_equal (plus_one (prev->m_upper), next->m_lower))) @@ -718,7 +705,7 @@ bounded_ranges::dump (bool show_types) const std::unique_ptr<json::value> bounded_ranges::to_json () const { - auto arr_obj = ::make_unique<json::array> (); + auto arr_obj = std::make_unique<json::array> (); for (unsigned i = 0; i < m_ranges.length (); ++i) arr_obj->append (m_ranges[i].to_json ()); @@ -1071,7 +1058,7 @@ bounded_ranges_manager::log_stats (logger *logger, bool show_objs) const /* equiv_class's default ctor. */ equiv_class::equiv_class () -: m_constant (NULL_TREE), m_cst_sval (NULL), m_vars () +: m_constant (NULL_TREE), m_cst_sval (nullptr), m_vars () { } @@ -1116,9 +1103,9 @@ equiv_class::print (pretty_printer *pp) const std::unique_ptr<json::object> equiv_class::to_json () const { - auto ec_obj = ::make_unique<json::object> (); + auto ec_obj = std::make_unique<json::object> (); - auto sval_arr = ::make_unique<json::array> (); + auto sval_arr = std::make_unique<json::array> (); for (const svalue *sval : m_vars) sval_arr->append (sval->to_json ()); ec_obj->set ("svals", std::move (sval_arr)); @@ -1383,7 +1370,7 @@ constraint::print (pretty_printer *pp, const constraint_manager &cm) const std::unique_ptr<json::object> constraint::to_json () const { - auto con_obj = ::make_unique<json::object> (); + auto con_obj = std::make_unique<json::object> (); con_obj->set_integer ("lhs", m_lhs.as_int ()); con_obj->set_string ("op", constraint_op_code (m_op)); @@ -1471,7 +1458,7 @@ bounded_ranges_constraint::print (pretty_printer *pp, std::unique_ptr<json::object> bounded_ranges_constraint::to_json () const { - auto con_obj = ::make_unique<json::object> (); + auto con_obj = std::make_unique<json::object> (); con_obj->set_integer ("ec", m_ec_id.as_int ()); con_obj->set ("ranges", m_ranges->to_json ()); @@ -1784,11 +1771,11 @@ debug (const constraint_manager &cm) std::unique_ptr<json::object> constraint_manager::to_json () const { - auto cm_obj = ::make_unique<json::object> (); + auto cm_obj = std::make_unique<json::object> (); /* Equivalence classes. */ { - auto ec_arr = ::make_unique<json::array> (); + auto ec_arr = std::make_unique<json::array> (); for (const equiv_class *ec : m_equiv_classes) ec_arr->append (ec->to_json ()); cm_obj->set ("ecs", std::move (ec_arr)); @@ -1796,7 +1783,7 @@ constraint_manager::to_json () const /* Constraints. */ { - auto con_arr = ::make_unique<json::array> (); + auto con_arr = std::make_unique<json::array> (); for (const constraint &c : m_constraints) con_arr->append (c.to_json ()); cm_obj->set ("constraints", std::move (con_arr)); @@ -1804,7 +1791,7 @@ constraint_manager::to_json () const /* m_bounded_ranges_constraints. */ { - auto con_arr = ::make_unique<json::array> (); + auto con_arr = std::make_unique<json::array> (); for (const auto &c : m_bounded_ranges_constraints) con_arr->append (c.to_json ()); cm_obj->set ("bounded_ranges_constraints", std::move (con_arr)); @@ -2578,12 +2565,12 @@ constraint_manager::get_ec_bounds (equiv_class_id ec_id) const case CONSTRAINT_LT: /* We have "EC_ID < OTHER_CST". */ - result.add_bound (bound (other_cst, false), BK_UPPER); + result.add_bound (bound (other_cst, false), bound_kind::upper); break; case CONSTRAINT_LE: /* We have "EC_ID <= OTHER_CST". */ - result.add_bound (bound (other_cst, true), BK_UPPER); + result.add_bound (bound (other_cst, true), bound_kind::upper); break; } } @@ -2600,13 +2587,13 @@ constraint_manager::get_ec_bounds (equiv_class_id ec_id) const case CONSTRAINT_LT: /* We have "OTHER_CST < EC_ID" i.e. "EC_ID > OTHER_CST". */ - result.add_bound (bound (other_cst, false), BK_LOWER); + result.add_bound (bound (other_cst, false), bound_kind::lower); break; case CONSTRAINT_LE: /* We have "OTHER_CST <= EC_ID" i.e. "EC_ID >= OTHER_CST". */ - result.add_bound (bound (other_cst, true), BK_LOWER); + result.add_bound (bound (other_cst, true), bound_kind::lower); break; } } @@ -3037,7 +3024,7 @@ on_liveness_change (const svalue_set &live_svalues, const region_model *model) { dead_svalue_purger p (live_svalues, model); - purge (p, NULL); + purge (p, nullptr); } class svalue_purger @@ -3060,7 +3047,7 @@ void constraint_manager::purge_state_involving (const svalue *sval) { svalue_purger p (sval); - purge (p, NULL); + purge (p, nullptr); } /* Comparator for use by constraint_manager::canonicalize. @@ -3204,7 +3191,7 @@ public: { /* Special-case for widening. */ if (lhs->get_kind () == SK_WIDENING) - if (!m_cm_b->get_equiv_class_by_svalue (lhs, NULL)) + if (!m_cm_b->get_equiv_class_by_svalue (lhs, nullptr)) { /* LHS isn't constrained within m_cm_b. */ bool sat = m_out->add_constraint (lhs, code, rhs); @@ -3528,7 +3515,7 @@ test_constraint_conditions () ADD_SAT_CONSTRAINT (model, x, EQ_EXPR, x); ADD_SAT_CONSTRAINT (model, int_42, EQ_EXPR, int_42); /* ...even when done directly via svalues: */ - const svalue *sval_int_42 = model.get_rvalue (int_42, NULL); + const svalue *sval_int_42 = model.get_rvalue (int_42, nullptr); bool sat = model.get_constraints ()->add_constraint (sval_int_42, EQ_EXPR, sval_int_42); @@ -4317,8 +4304,8 @@ test_purging (void) ASSERT_EQ (model.get_constraints ()->m_constraints.length (), 1); /* Purge state for "a". */ - const svalue *sval_a = model.get_rvalue (a, NULL); - model.purge_state_involving (sval_a, NULL); + const svalue *sval_a = model.get_rvalue (a, nullptr); + model.purge_state_involving (sval_a, nullptr); model.canonicalize (); /* We should have an empty constraint_manager. */ ASSERT_EQ (model.get_constraints ()->m_equiv_classes.length (), 0); @@ -4335,8 +4322,8 @@ test_purging (void) ASSERT_EQ (model.get_constraints ()->m_constraints.length (), 2); /* Purge state for "a". */ - const svalue *sval_a = model.get_rvalue (a, NULL); - model.purge_state_involving (sval_a, NULL); + const svalue *sval_a = model.get_rvalue (a, nullptr); + model.purge_state_involving (sval_a, nullptr); model.canonicalize (); /* We should just have the constraint/ECs involving b != 0. */ ASSERT_EQ (model.get_constraints ()->m_equiv_classes.length (), 2); @@ -4354,8 +4341,8 @@ test_purging (void) ASSERT_EQ (model.get_constraints ()->m_constraints.length (), 1); /* Purge state for "a". */ - const svalue *sval_a = model.get_rvalue (a, NULL); - model.purge_state_involving (sval_a, NULL); + const svalue *sval_a = model.get_rvalue (a, nullptr); + model.purge_state_involving (sval_a, nullptr); model.canonicalize (); /* We should just have the EC involving b == 0. */ ASSERT_EQ (model.get_constraints ()->m_equiv_classes.length (), 1); @@ -4372,8 +4359,8 @@ test_purging (void) ASSERT_EQ (model.get_constraints ()->m_constraints.length (), 0); /* Purge state for "a". */ - const svalue *sval_a = model.get_rvalue (a, NULL); - model.purge_state_involving (sval_a, NULL); + const svalue *sval_a = model.get_rvalue (a, nullptr); + model.purge_state_involving (sval_a, nullptr); model.canonicalize (); /* We should have an empty constraint_manager. */ ASSERT_EQ (model.get_constraints ()->m_equiv_classes.length (), 0); @@ -4390,8 +4377,8 @@ test_purging (void) ASSERT_EQ (model.get_constraints ()->m_constraints.length (), 1); /* Purge state for "a". */ - const svalue *sval_a = model.get_rvalue (a, NULL); - model.purge_state_involving (sval_a, NULL); + const svalue *sval_a = model.get_rvalue (a, nullptr); + model.purge_state_involving (sval_a, nullptr); model.canonicalize (); /* We should just have the constraint/ECs involving b != 0. */ ASSERT_EQ (model.get_constraints ()->m_equiv_classes.length (), 2); @@ -4409,8 +4396,8 @@ test_purging (void) ASSERT_EQ (model.get_constraints ()->m_constraints.length (), 0); /* Purge state for "a". */ - const svalue *sval_a = model.get_rvalue (a, NULL); - model.purge_state_involving (sval_a, NULL); + const svalue *sval_a = model.get_rvalue (a, nullptr); + model.purge_state_involving (sval_a, nullptr); model.canonicalize (); /* We should just have the EC involving b == 0. */ ASSERT_EQ (model.get_constraints ()->m_equiv_classes.length (), 1); @@ -4472,8 +4459,8 @@ test_bounded_range () bounded_range br_u8_64_128 (u8_64, u8_128); ASSERT_DUMP_BOUNDED_RANGE_EQ (br_u8_64_128, "[64, 128]"); - ASSERT_FALSE (br_u8_0.intersects_p (br_u8_64_128, NULL)); - ASSERT_FALSE (br_u8_64_128.intersects_p (br_u8_0, NULL)); + ASSERT_FALSE (br_u8_0.intersects_p (br_u8_64_128, nullptr)); + ASSERT_FALSE (br_u8_64_128.intersects_p (br_u8_0, nullptr)); bounded_range br_u8_128_255 (u8_128, u8_255); ASSERT_DUMP_BOUNDED_RANGE_EQ (br_u8_128_255, "[128, 255]"); diff --git a/gcc/analyzer/constraint-manager.h b/gcc/analyzer/constraint-manager.h index c22b99e..4339ea6 100644 --- a/gcc/analyzer/constraint-manager.h +++ b/gcc/analyzer/constraint-manager.h @@ -25,10 +25,10 @@ namespace ana { class constraint_manager; -enum bound_kind +enum class bound_kind { - BK_LOWER, - BK_UPPER + lower, + upper }; /* One of the end-points of a range. */ @@ -226,8 +226,8 @@ private: { return k->get_hash (); } - static inline bool is_empty (key_type k) { return k == NULL; } - static inline void mark_empty (key_type &k) { k = NULL; } + static inline bool is_empty (key_type k) { return k == nullptr; } + static inline void mark_empty (key_type &k) { k = nullptr; } static inline bool is_deleted (key_type k) { return k == reinterpret_cast<key_type> (1); diff --git a/gcc/analyzer/diagnostic-manager.cc b/gcc/analyzer/diagnostic-manager.cc index 4bf1dce..88f72d1 100644 --- a/gcc/analyzer/diagnostic-manager.cc +++ b/gcc/analyzer/diagnostic-manager.cc @@ -18,22 +18,19 @@ 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/>. */ -#include "config.h" -#define INCLUDE_VECTOR -#include "system.h" -#include "coretypes.h" -#include "tree.h" -#include "input.h" -#include "diagnostic-core.h" -#include "pretty-print.h" -#include "gcc-rich-location.h" +#include "analyzer/common.h" + +#include "cfg.h" +#include "basic-block.h" +#include "gimple.h" #include "gimple-pretty-print.h" -#include "function.h" -#include "diagnostic-event-id.h" -#include "diagnostic-path.h" -#include "bitmap.h" -#include "ordered-hash-map.h" -#include "analyzer/analyzer.h" +#include "gimple-iterator.h" +#include "inlining-iterator.h" +#include "cgraph.h" +#include "digraph.h" +#include "gcc-rich-location.h" +#include "diagnostics/sarif-sink.h" + #include "analyzer/analyzer-logging.h" #include "analyzer/sm.h" #include "analyzer/pending-diagnostic.h" @@ -43,13 +40,6 @@ along with GCC; see the file COPYING3. If not see #include "analyzer/store.h" #include "analyzer/region-model.h" #include "analyzer/constraint-manager.h" -#include "cfg.h" -#include "basic-block.h" -#include "gimple.h" -#include "gimple-iterator.h" -#include "inlining-iterator.h" -#include "cgraph.h" -#include "digraph.h" #include "analyzer/supergraph.h" #include "analyzer/program-state.h" #include "analyzer/exploded-graph.h" @@ -57,8 +47,6 @@ along with GCC; see the file COPYING3. If not see #include "analyzer/feasible-graph.h" #include "analyzer/checker-path.h" #include "analyzer/reachability.h" -#include "make-unique.h" -#include "diagnostic-format-sarif.h" #if ENABLE_ANALYZER @@ -75,7 +63,7 @@ class epath_finder public: epath_finder (const exploded_graph &eg) : m_eg (eg), - m_sep (NULL) + m_sep (nullptr) { /* This is shared by all diagnostics, but only needed if !flag_analyzer_feasibility. */ @@ -137,7 +125,7 @@ private: within ENODE. Ideally we want to report the shortest feasible path. - Return NULL if we could not find a feasible path + Return nullptr if we could not find a feasible path (when flag_analyzer_feasibility is true). If flag_analyzer_feasibility is false, then simply return the @@ -189,7 +177,7 @@ epath_finder::get_best_epath (const exploded_node *enode, logger->log ("rejecting %qs at EN: %i, SN: %i (sd: %i)" " due to not finding feasible path", desc, enode->m_index, snode_idx, diag_idx); - return NULL; + return nullptr; } } else @@ -203,7 +191,7 @@ epath_finder::get_best_epath (const exploded_node *enode, logger->log ("trying to find shortest path ignoring feasibility"); gcc_assert (m_sep); std::unique_ptr<exploded_path> epath - = make_unique<exploded_path> (m_sep->get_shortest_path (enode)); + = std::make_unique<exploded_path> (m_sep->get_shortest_path (enode)); if (epath->feasible_p (logger, out_problem, m_eg.get_engine (), &m_eg)) { if (logger) @@ -233,7 +221,7 @@ class feasible_worklist { public: feasible_worklist (const shortest_paths<eg_traits, exploded_path> &sep) - : m_queue (key_t (*this, NULL)), + : m_queue (key_t (*this, nullptr)), m_sep (sep) { } @@ -428,7 +416,7 @@ epath_finder::explore_feasible_paths (const exploded_node *target_enode, a limit. */ /* Set this if we find a feasible path to TARGET_ENODE. */ - std::unique_ptr<exploded_path> best_path = NULL; + std::unique_ptr<exploded_path> best_path = nullptr; { auto_checking_feasibility sentinel (mgr); @@ -520,7 +508,7 @@ process_worklist_item (feasible_worklist *worklist, std::unique_ptr<rejected_constraint> rc; if (succ_state.maybe_update_for_edge (logger, succ_eedge, nullptr, &rc)) { - gcc_assert (rc == NULL); + gcc_assert (rc == nullptr); feasible_node *succ_fnode = fg->add_node (succ_eedge->m_dest, succ_state, @@ -624,7 +612,7 @@ dump_trimmed_graph (const exploded_node *target_enode, pp_printf (&pp, "%s.%s.%i.to-en%i.tg.dot", dump_base_name, desc, diag_idx, target_enode->m_index); char *filename = xstrdup (pp_formatted_text (&pp)); - tg.dump_dot (filename, NULL, args); + tg.dump_dot (filename, nullptr, args); free (filename); } @@ -642,7 +630,7 @@ epath_finder::dump_feasible_graph (const exploded_node *target_enode, pp_printf (&pp, "%s.%s.%i.to-en%i.fg.dot", dump_base_name, desc, diag_idx, target_enode->m_index); char *filename = xstrdup (pp_formatted_text (&pp)); - fg.dump_dot (filename, NULL, args); + fg.dump_dot (filename, nullptr, args); free (filename); } @@ -699,6 +687,11 @@ saved_diagnostic::operator== (const saved_diagnostic &other) const for (unsigned i = 0; i < m_notes.length (); i++) if (!m_notes[i]->equal_p (*other.m_notes[i])) return false; + + // Don't deduplicate dump_path_diagnostic instances + if (!strcmp (m_d->get_kind (), "dump_path_diagnostic")) + return this == &other; + return (m_sm == other.m_sm /* We don't compare m_enode. */ && m_snode == other.m_snode @@ -742,7 +735,7 @@ saved_diagnostic::add_event (std::unique_ptr<checker_event> event) std::unique_ptr<json::object> saved_diagnostic::to_json () const { - auto sd_obj = ::make_unique<json::object> (); + auto sd_obj = std::make_unique<json::object> (); if (m_sm) sd_obj->set_string ("sm", m_sm->get_name ()); @@ -834,7 +827,7 @@ saved_diagnostic::dump_as_dot_node (pretty_printer *pp) const /* Use PF to find the best exploded_path for this saved_diagnostic, and store it in m_best_epath. - If we don't have a specific location in m_loc and m_stmt is still NULL, + If we don't have a specific location in m_loc and m_stmt is still nullptr, use m_stmt_finder on the epath to populate m_stmt. Return true if a best path was found. */ @@ -843,20 +836,20 @@ saved_diagnostic::calc_best_epath (epath_finder *pf) { logger *logger = pf->get_logger (); LOG_SCOPE (logger); - m_problem = NULL; + m_problem = nullptr; m_best_epath = pf->get_best_epath (m_enode, m_stmt, *m_d, m_d->get_kind (), m_idx, &m_problem); /* Handle failure to find a feasible path. */ - if (m_best_epath == NULL) + if (m_best_epath == nullptr) return false; gcc_assert (m_best_epath); if (m_loc == UNKNOWN_LOCATION) { - if (m_stmt == NULL) + if (m_stmt == nullptr) { gcc_assert (m_stmt_finder); m_stmt = m_stmt_finder->find_stmt (*m_best_epath); @@ -1024,9 +1017,10 @@ saved_diagnostic::emit_any_notes () const This extra data is intended for use when debugging the analyzer. */ void -saved_diagnostic::maybe_add_sarif_properties (sarif_object &result_obj) const +saved_diagnostic:: +maybe_add_sarif_properties (diagnostics::sarif_object &result_obj) const { - sarif_property_bag &props = result_obj.get_or_create_properties (); + auto &props = result_obj.get_or_create_properties (); #define PROPERTY_PREFIX "gcc/analyzer/saved_diagnostic/" if (m_sm) props.set_string (PROPERTY_PREFIX "sm", m_sm->get_name ()); @@ -1048,10 +1042,10 @@ saved_diagnostic::maybe_add_sarif_properties (sarif_object &result_obj) const props.set_integer (PROPERTY_PREFIX "idx", m_idx); if (m_duplicates.length () > 0) { - auto duplicates_arr = ::make_unique<json::array> (); + auto duplicates_arr = std::make_unique<json::array> (); for (auto iter : m_duplicates) { - auto sd_obj = ::make_unique<sarif_object> (); + auto sd_obj = std::make_unique<diagnostics::sarif_object> (); iter->maybe_add_sarif_properties (*sd_obj); duplicates_arr->append (std::move (sd_obj)); } @@ -1205,7 +1199,7 @@ diagnostic_manager::add_diagnostic (const pending_location &ploc, std::unique_ptr<pending_diagnostic> d) { gcc_assert (ploc.m_enode); - return add_diagnostic (NULL, ploc, NULL_TREE, NULL, 0, std::move (d)); + return add_diagnostic (nullptr, ploc, NULL_TREE, nullptr, 0, std::move (d)); } /* Add PN to the most recent saved_diagnostic. */ @@ -1242,10 +1236,10 @@ diagnostic_manager::add_event (std::unique_ptr<checker_event> event) std::unique_ptr<json::object> diagnostic_manager::to_json () const { - auto dm_obj = ::make_unique<json::object> (); + auto dm_obj = std::make_unique<json::object> (); { - auto sd_arr = ::make_unique<json::array> (); + auto sd_arr = std::make_unique<json::array> (); int i; saved_diagnostic *sd; FOR_EACH_VEC_ELT (m_saved_diagnostics, i, sd) @@ -1350,7 +1344,7 @@ public: template <typename T> static inline void mark_empty (T &entry) { - entry.m_key = NULL; + entry.m_key = nullptr; } template <typename T> static inline bool is_deleted (const T &entry) @@ -1360,7 +1354,7 @@ public: template <typename T> static inline bool is_empty (const T &entry) { - return entry.m_key == NULL; + return entry.m_key == nullptr; } static const bool empty_zero_p = true; }; @@ -1548,12 +1542,12 @@ diagnostic_manager::emit_saved_diagnostics (const exploded_graph &eg) best_candidates.emit_best (this, eg); } -/* Custom subclass of diagnostic_metadata which, for SARIF output, +/* Custom subclass of diagnostics::metadata which, for SARIF output, populates the property bag of the diagnostic's "result" object with information from the saved_diagnostic and the pending_diagnostic. */ -class pending_diagnostic_metadata : public diagnostic_metadata +class pending_diagnostic_metadata : public diagnostics::metadata { public: pending_diagnostic_metadata (const saved_diagnostic &sd) @@ -1562,7 +1556,8 @@ public: } void - maybe_add_sarif_properties (sarif_object &result_obj) const override + maybe_add_sarif_properties (diagnostics::sarif_object &result_obj) + const override { m_sd.maybe_add_sarif_properties (result_obj); } @@ -1590,9 +1585,11 @@ diagnostic_manager::emit_saved_diagnostic (const exploded_graph &eg, /* Precompute all enodes from which the diagnostic is reachable. */ path_builder pb (eg, *epath, sd.get_feasibility_problem (), sd); - /* This is the diagnostic_path subclass that will be built for + /* This is the diagnostics::paths::path subclass that will be built for the diagnostic. */ - checker_path emission_path (get_logger ()); + checker_path emission_path (get_logical_location_manager (), + eg.get_ext_state (), + get_logger ()); /* Populate emission_path with a full description of EPATH. */ build_emission_path (pb, *epath, &emission_path); @@ -1625,7 +1622,7 @@ diagnostic_manager::emit_saved_diagnostic (const exploded_graph &eg, trailing eedge stashed, add any events for it. This is for use in handling longjmp, to show where a longjmp is rewinding to. */ if (sd.m_trailing_eedge) - add_events_for_eedge (pb, *sd.m_trailing_eedge, &emission_path, NULL); + add_events_for_eedge (pb, *sd.m_trailing_eedge, &emission_path, nullptr); emission_path.inject_any_inlined_call_events (get_logger ()); @@ -1668,6 +1665,15 @@ diagnostic_manager::emit_saved_diagnostic (const exploded_graph &eg, } } +const diagnostics::logical_locations::manager & +diagnostic_manager::get_logical_location_manager () const +{ + gcc_assert (global_dc); + auto mgr = global_dc->get_logical_location_manager (); + gcc_assert (mgr); + return *mgr; +} + /* Emit a "path" of events to EMISSION_PATH describing the exploded path EPATH within EG. */ @@ -1700,7 +1706,7 @@ diagnostic_manager::build_emission_path (const path_builder &pb, { emission_path->add_region_creation_events (pb.get_pending_diagnostic (), - reg, NULL, + reg, nullptr, event_loc_info (DECL_SOURCE_LOCATION (decl), NULL_TREE, 0), @@ -1759,7 +1765,7 @@ diagnostic_manager::add_event_on_final_node (const path_builder &pb, = src_model->get_dynamic_extents (base_reg); const svalue *new_extents = dst_model->get_dynamic_extents (base_reg); - if (old_extents == NULL && new_extents != NULL) + if (old_extents == nullptr && new_extents != nullptr) switch (base_reg->get_kind ()) { default: @@ -1816,16 +1822,16 @@ public: int stack_depth = src_stack_depth; m_emission_path->add_event - (make_unique<state_change_event> (supernode, - stmt, - stack_depth, - sm, - nullptr, - src_sm_val, - dst_sm_val, - nullptr, - dst_state, - src_node)); + (std::make_unique<state_change_event> (supernode, + stmt, + stack_depth, + sm, + nullptr, + src_sm_val, + dst_sm_val, + nullptr, + dst_state, + src_node)); return false; } @@ -1861,16 +1867,16 @@ public: return false; m_emission_path->add_event - (make_unique<state_change_event> (supernode, - stmt, - stack_depth, - sm, - sval, - src_sm_val, - dst_sm_val, - dst_origin_sval, - dst_state, - src_node)); + (std::make_unique<state_change_event> (supernode, + stmt, + stack_depth, + sm, + sval, + src_sm_val, + dst_sm_val, + dst_origin_sval, + dst_state, + src_node)); return false; } @@ -1963,7 +1969,7 @@ struct null_assignment_sm_context : public sm_context { } - tree get_fndecl_for_call (const gcall */*call*/) final override + tree get_fndecl_for_call (const gcall &/*call*/) final override { return NULL_TREE; } @@ -1972,7 +1978,7 @@ struct null_assignment_sm_context : public sm_context tree var) final override { const svalue *var_old_sval - = m_old_state->m_region_model->get_rvalue (var, NULL); + = m_old_state->m_region_model->get_rvalue (var, nullptr); const sm_state_map *old_smap = m_old_state->m_checker_states[m_sm_idx]; state_machine::state_t current @@ -2001,21 +2007,21 @@ struct null_assignment_sm_context : public sm_context return; const svalue *var_new_sval - = m_new_state->m_region_model->get_rvalue (var, NULL); + = m_new_state->m_region_model->get_rvalue (var, nullptr); const supernode *supernode = m_point->get_supernode (); int stack_depth = m_point->get_stack_depth (); m_emission_path->add_event - (make_unique<state_change_event> (supernode, - m_stmt, - stack_depth, - m_sm, - var_new_sval, - from, to, - nullptr, - *m_new_state, - nullptr)); + (std::make_unique<state_change_event> (supernode, + m_stmt, + stack_depth, + m_sm, + var_new_sval, + from, to, + nullptr, + *m_new_state, + nullptr)); } void set_next_state (const gimple *stmt, @@ -2033,15 +2039,15 @@ struct null_assignment_sm_context : public sm_context int stack_depth = m_point->get_stack_depth (); m_emission_path->add_event - (make_unique<state_change_event> (supernode, - m_stmt, - stack_depth, - m_sm, - sval, - from, to, - nullptr, - *m_new_state, - nullptr)); + (std::make_unique<state_change_event> (supernode, + m_stmt, + stack_depth, + m_sm, + sval, + from, to, + nullptr, + *m_new_state, + nullptr)); } void warn (const supernode *, const gimple *, @@ -2088,7 +2094,7 @@ struct null_assignment_sm_context : public sm_context if (!assign_stmt) return NULL_TREE; if (const svalue *sval - = m_new_state->m_region_model->get_gassign_result (assign_stmt, NULL)) + = m_new_state->m_region_model->get_gassign_result (assign_stmt, nullptr)) if (tree cst = sval->maybe_get_constant ()) if (::zerop(cst)) return gimple_assign_lhs (assign_stmt); @@ -2220,18 +2226,19 @@ diagnostic_manager::add_events_for_eedge (const path_builder &pb, { const gimple *stmt = dst_point.get_stmt (); const gcall *call = dyn_cast <const gcall *> (stmt); - if (call && is_setjmp_call_p (call)) + if (call && is_setjmp_call_p (*call)) emission_path->add_event - (make_unique<setjmp_event> (event_loc_info (stmt->location, - dst_point.get_fndecl (), - dst_stack_depth), - dst_node, - call)); + (std::make_unique<setjmp_event> + (event_loc_info (stmt->location, + dst_point.get_fndecl (), + dst_stack_depth), + dst_node, + *call)); else emission_path->add_event - (make_unique<statement_event> (stmt, - dst_point.get_fndecl (), - dst_stack_depth, dst_state)); + (std::make_unique<statement_event> (stmt, + dst_point.get_fndecl (), + dst_stack_depth, dst_state)); /* Create state change events for assignment to NULL. Iterate through the stmts in dst_enode, adding state change @@ -2248,7 +2255,7 @@ diagnostic_manager::add_events_for_eedge (const path_builder &pb, { const extrinsic_state &ext_state = pb.get_ext_state (); program_state old_state (iter_state); - iter_state.m_region_model->on_assignment (assign, NULL); + iter_state.m_region_model->on_assignment (assign, nullptr); for (unsigned i = 0; i < ext_state.get_num_checkers (); i++) { const state_machine &sm = ext_state.get_sm (i); @@ -2294,7 +2301,7 @@ diagnostic_manager::add_events_for_eedge (const path_builder &pb, = src_model->get_dynamic_extents (base_reg); const svalue *new_extents = dst_model->get_dynamic_extents (base_reg); - if (old_extents == NULL && new_extents != NULL) + if (old_extents == nullptr && new_extents != nullptr) switch (base_reg->get_kind ()) { default: @@ -2324,11 +2331,11 @@ diagnostic_manager::add_events_for_eedge (const path_builder &pb, " at this edge: "); pb.get_feasibility_problem ()->dump_to_pp (&pp); emission_path->add_event - (make_unique<precanned_custom_event> - (event_loc_info (dst_point.get_location (), - dst_point.get_fndecl (), - dst_stack_depth), - pp_formatted_text (&pp))); + (std::make_unique<precanned_custom_event> + (event_loc_info (dst_point.get_location (), + dst_point.get_fndecl (), + dst_stack_depth), + pp_formatted_text (&pp))); } } @@ -2438,18 +2445,48 @@ diagnostic_manager::add_events_for_superedge (const path_builder &pb, { case SUPEREDGE_CFG_EDGE: { + if (auto eh_dispatch_try_sedge + = eedge.m_sedge->dyn_cast_eh_dispatch_try_cfg_superedge ()) + { + if (eh_dispatch_try_sedge->get_eh_catch ()) + { + const region_model *model = src_node->get_state ().m_region_model; + auto curr_thrown_exception_node + = model->get_current_thrown_exception (); + gcc_assert (curr_thrown_exception_node); + tree type = curr_thrown_exception_node->maybe_get_type (); + emission_path->add_event + (std::make_unique<catch_cfg_edge_event> + (eedge, + event_loc_info (dst_point.get_supernode ()->get_start_location (), + dst_point.get_fndecl (), + dst_stack_depth), + type)); + return; + } + else + { + /* We have the "uncaught exception" sedge, from eh_dispatch + to a block containing resx. + Don't add any events for this, so that we can consolidate + adjacent stack unwinding events. */ + return; + } + } + emission_path->add_event - (make_unique<start_cfg_edge_event> - (eedge, - event_loc_info (last_stmt ? last_stmt->location : UNKNOWN_LOCATION, - src_point.get_fndecl (), - src_stack_depth))); + (std::make_unique<start_cfg_edge_event> + (eedge, + event_loc_info + (last_stmt ? last_stmt->location : UNKNOWN_LOCATION, + src_point.get_fndecl (), + src_stack_depth))); emission_path->add_event - (make_unique<end_cfg_edge_event> - (eedge, - event_loc_info (dst_point.get_supernode ()->get_start_location (), - dst_point.get_fndecl (), - dst_stack_depth))); + (std::make_unique<end_cfg_edge_event> + (eedge, + event_loc_info (dst_point.get_supernode ()->get_start_location (), + dst_point.get_fndecl (), + dst_stack_depth))); } break; @@ -2462,12 +2499,13 @@ diagnostic_manager::add_events_for_superedge (const path_builder &pb, /* TODO: add a subclass for this, or generate events for the summary. */ emission_path->add_event - (make_unique<debug_event> (event_loc_info (last_stmt - ? last_stmt->location - : UNKNOWN_LOCATION, - src_point.get_fndecl (), - src_stack_depth), - "call summary")); + (std::make_unique<debug_event> + (event_loc_info (last_stmt + ? last_stmt->location + : UNKNOWN_LOCATION, + src_point.get_fndecl (), + src_stack_depth), + "call summary")); } break; @@ -2476,14 +2514,13 @@ diagnostic_manager::add_events_for_superedge (const path_builder &pb, const return_superedge *return_edge = as_a <const return_superedge *> (eedge.m_sedge); - const gcall *call_stmt = return_edge->get_call_stmt (); + const gcall &call_stmt = return_edge->get_call_stmt (); emission_path->add_event - (make_unique<return_event> (eedge, - event_loc_info (call_stmt - ? call_stmt->location - : UNKNOWN_LOCATION, - dst_point.get_fndecl (), - dst_stack_depth))); + (std::make_unique<return_event> + (eedge, + event_loc_info (call_stmt.location, + dst_point.get_fndecl (), + dst_stack_depth))); } break; } @@ -2512,6 +2549,7 @@ diagnostic_manager::prune_path (checker_path *path, if (! flag_analyzer_show_events_in_system_headers) prune_system_headers (path); consolidate_conditions (path); + consolidate_unwind_events (path); finish_pruning (path); path->maybe_log (get_logger (), "pruned"); } @@ -2569,24 +2607,24 @@ diagnostic_manager::prune_for_sm_diagnostic (checker_path *path, { label_text sval_desc = sval->get_desc (); log ("considering event %i (%s), with sval: %qs, state: %qs", - idx, event_kind_to_string (base_event->m_kind), + idx, event_kind_to_string (base_event->get_kind ()), sval_desc.get (), state->get_name ()); } else log ("considering event %i (%s), with global state: %qs", - idx, event_kind_to_string (base_event->m_kind), + idx, event_kind_to_string (base_event->get_kind ()), state->get_name ()); } else log ("considering event %i", idx); } - switch (base_event->m_kind) + switch (base_event->get_kind ()) { default: gcc_unreachable (); - case EK_DEBUG: + case event_kind::debug: if (m_verbosity < 4) { log ("filtering event %i: debug event", idx); @@ -2594,11 +2632,11 @@ diagnostic_manager::prune_for_sm_diagnostic (checker_path *path, } break; - case EK_CUSTOM: + case event_kind::custom: /* Don't filter custom events. */ break; - case EK_STMT: + case event_kind::stmt: { if (m_verbosity < 4) { @@ -2608,11 +2646,11 @@ diagnostic_manager::prune_for_sm_diagnostic (checker_path *path, } break; - case EK_REGION_CREATION: + case event_kind::region_creation: /* Don't filter these. */ break; - case EK_FUNCTION_ENTRY: + case event_kind::function_entry: if (m_verbosity < 1) { log ("filtering event %i: function entry", idx); @@ -2620,7 +2658,7 @@ diagnostic_manager::prune_for_sm_diagnostic (checker_path *path, } break; - case EK_STATE_CHANGE: + case event_kind::state_change: { state_change_event *state_change = (state_change_event *)base_event; gcc_assert (state_change->m_dst_state.m_region_model); @@ -2674,7 +2712,7 @@ diagnostic_manager::prune_for_sm_diagnostic (checker_path *path, } break; - case EK_START_CFG_EDGE: + case event_kind::start_cfg_edge: { cfg_edge_event *event = (cfg_edge_event *)base_event; @@ -2687,20 +2725,26 @@ diagnostic_manager::prune_for_sm_diagnostic (checker_path *path, { log ("filtering events %i and %i: CFG edge", idx, idx + 1); path->delete_event (idx); - /* Also delete the corresponding EK_END_CFG_EDGE. */ - gcc_assert (path->get_checker_event (idx)->m_kind - == EK_END_CFG_EDGE); + /* Also delete the corresponding event_kind::end_cfg_edge. */ + gcc_assert (path->get_checker_event (idx)->get_kind () + == event_kind::end_cfg_edge); path->delete_event (idx); } } break; - case EK_END_CFG_EDGE: - /* These come in pairs with EK_START_CFG_EDGE events and are + case event_kind::end_cfg_edge: + /* These come in pairs with event_kind::start_cfg_edge events and are filtered when their start event is filtered. */ break; - case EK_CALL_EDGE: + case event_kind::catch_: + case event_kind::throw_: + case event_kind::unwind: + /* Don't filter these. */ + break; + + case event_kind::call_edge: { call_event *event = (call_event *)base_event; const region_model *callee_model @@ -2741,7 +2785,7 @@ diagnostic_manager::prune_for_sm_diagnostic (checker_path *path, } break; - case EK_RETURN_EDGE: + case event_kind::return_edge: { if (sval) { @@ -2785,19 +2829,19 @@ diagnostic_manager::prune_for_sm_diagnostic (checker_path *path, } break; - case EK_INLINED_CALL: + case event_kind::inlined_call: /* We don't expect to see these yet, as they're added later. We'd want to keep them around. */ break; - case EK_SETJMP: + case event_kind::setjmp_: /* TODO: only show setjmp_events that matter i.e. those for which there is a later rewind event using them. */ - case EK_REWIND_FROM_LONGJMP: - case EK_REWIND_TO_SETJMP: + case event_kind::rewind_from_longjmp: + case event_kind::rewind_to_setjmp: break; - case EK_WARNING: + case event_kind::warning: /* Always show the final "warning" event in the path. */ break; } @@ -3002,7 +3046,7 @@ same_line_as_p (const expanded_location &ref_exp_loc, const checker_event *ev = path->get_checker_event (idx); expanded_location idx_exp_loc = expand_location (ev->get_location ()); gcc_assert (ref_exp_loc.file); - if (idx_exp_loc.file == NULL) + if (idx_exp_loc.file == nullptr) return false; if (strcmp (ref_exp_loc.file, idx_exp_loc.file)) return false; @@ -3067,13 +3111,13 @@ diagnostic_manager::consolidate_conditions (checker_path *path) const = path->get_checker_event (start_idx); expanded_location start_exp_loc = expand_location (old_start_ev->get_location ()); - if (start_exp_loc.file == NULL) + if (start_exp_loc.file == nullptr) continue; if (!same_line_as_p (start_exp_loc, path, start_idx + 1)) continue; /* Are we looking for a run of all TRUE edges, or all FALSE edges? */ - gcc_assert (old_start_ev->m_kind == EK_START_CFG_EDGE); + gcc_assert (old_start_ev->get_kind () == event_kind::start_cfg_edge); const start_cfg_edge_event *old_start_cfg_ev = (const start_cfg_edge_event *)old_start_ev; const cfg_superedge& first_cfg_sedge @@ -3096,7 +3140,7 @@ diagnostic_manager::consolidate_conditions (checker_path *path) const { const checker_event *iter_ev = path->get_checker_event (next_idx); - gcc_assert (iter_ev->m_kind == EK_START_CFG_EDGE); + gcc_assert (iter_ev->get_kind () == event_kind::start_cfg_edge); const start_cfg_edge_event *iter_cfg_ev = (const start_cfg_edge_event *)iter_ev; const cfg_superedge& iter_cfg_sedge @@ -3140,6 +3184,50 @@ diagnostic_manager::consolidate_conditions (checker_path *path) const } } +/* Consolidate runs of consecutive unwind_event. */ + +void +diagnostic_manager::consolidate_unwind_events (checker_path *path) const +{ + /* Don't simplify edges if we're debugging them. */ + if (flag_analyzer_verbose_edges) + return; + + for (int start_idx = 0; + start_idx < (signed)path->num_events () - 1; + start_idx++) + { + /* Find a run of consecutive unwind_event instances. */ + if (path->get_checker_event (start_idx)->get_kind () + != event_kind::unwind) + continue; + int iter_idx = start_idx + 1; + while (iter_idx < (int)path->num_events ()) + if (path->get_checker_event (iter_idx)->get_kind () + == event_kind::unwind) + ++iter_idx; + else + break; + + /* iter_idx should now be one after the last unwind_event in the run. */ + const int last_idx = iter_idx - 1; + if (last_idx == start_idx) + continue; + + gcc_assert (last_idx > start_idx); + + log ("consolidating unwind events %i-%i into %i", + start_idx, last_idx, start_idx); + + unwind_event *first_event + = (unwind_event *)path->get_checker_event (start_idx); + const unwind_event *last_event + = (const unwind_event *)path->get_checker_event (last_idx); + first_event->m_num_frames += last_event->m_num_frames; + path->delete_events (start_idx + 1, last_idx - start_idx); + } +} + /* Final pass of diagnostic_manager::prune_path. If all we're left with is in one function, then filter function entry @@ -3154,7 +3242,7 @@ diagnostic_manager::finish_pruning (checker_path *path) const while (idx >= 0 && idx < (signed)path->num_events ()) { checker_event *base_event = path->get_checker_event (idx); - if (base_event->m_kind == EK_FUNCTION_ENTRY) + if (base_event->get_kind () == event_kind::function_entry) { log ("filtering event %i:" " function entry for purely intraprocedural path", idx); diff --git a/gcc/analyzer/diagnostic-manager.h b/gcc/analyzer/diagnostic-manager.h index 06a8233..d9cf109 100644 --- a/gcc/analyzer/diagnostic-manager.h +++ b/gcc/analyzer/diagnostic-manager.h @@ -67,7 +67,8 @@ public: void emit_any_notes () const; - void maybe_add_sarif_properties (sarif_object &result_obj) const; + void + maybe_add_sarif_properties (diagnostics::sarif_object &result_obj) const; //private: const state_machine *m_sm; @@ -191,6 +192,9 @@ public: } private: + const diagnostics::logical_locations::manager & + get_logical_location_manager () const; + void build_emission_path (const path_builder &pb, const exploded_path &epath, checker_path *emission_path) const; @@ -229,6 +233,7 @@ private: void prune_interproc_events (checker_path *path) const; void prune_system_headers (checker_path *path) const; void consolidate_conditions (checker_path *path) const; + void consolidate_unwind_events (checker_path *path) const; void finish_pruning (checker_path *path) const; engine *m_eng; diff --git a/gcc/analyzer/engine.cc b/gcc/analyzer/engine.cc index 71d7ed7..745ef7e 100644 --- a/gcc/analyzer/engine.cc +++ b/gcc/analyzer/engine.cc @@ -18,23 +18,27 @@ 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/>. */ -#include "config.h" -#define INCLUDE_VECTOR -#include "system.h" -#include "coretypes.h" -#include "make-unique.h" -#include "tree.h" -#include "fold-const.h" +#include "analyzer/common.h" + +#include <zlib.h> + +#include "cfg.h" +#include "basic-block.h" #include "gcc-rich-location.h" -#include "diagnostic-core.h" -#include "diagnostic-event-id.h" -#include "diagnostic-path.h" -#include "function.h" -#include "pretty-print.h" -#include "sbitmap.h" -#include "bitmap.h" -#include "ordered-hash-map.h" -#include "analyzer/analyzer.h" +#include "gimple.h" +#include "gimple-iterator.h" +#include "gimple-pretty-print.h" +#include "cgraph.h" +#include "fold-const.h" +#include "digraph.h" +#include "plugin.h" +#include "target.h" +#include "stringpool.h" +#include "attribs.h" +#include "tree-dfa.h" + +#include "text-art/dump.h" + #include "analyzer/analyzer-logging.h" #include "analyzer/call-string.h" #include "analyzer/program-point.h" @@ -44,13 +48,6 @@ along with GCC; see the file COPYING3. If not see #include "analyzer/sm.h" #include "analyzer/pending-diagnostic.h" #include "analyzer/diagnostic-manager.h" -#include "cfg.h" -#include "basic-block.h" -#include "gimple.h" -#include "gimple-iterator.h" -#include "gimple-pretty-print.h" -#include "cgraph.h" -#include "digraph.h" #include "analyzer/supergraph.h" #include "analyzer/program-state.h" #include "analyzer/exploded-graph.h" @@ -59,16 +56,8 @@ along with GCC; see the file COPYING3. If not see #include "analyzer/state-purge.h" #include "analyzer/bar-chart.h" #include "analyzer/call-info.h" -#include <zlib.h> -#include "plugin.h" -#include "target.h" -#include <memory> -#include "stringpool.h" -#include "attribs.h" -#include "tree-dfa.h" #include "analyzer/known-function-manager.h" #include "analyzer/call-summary.h" -#include "text-art/dump.h" /* For an overview, see gcc/doc/analyzer.texi. */ @@ -106,14 +95,14 @@ impl_region_model_context (program_state *state, const extrinsic_state &ext_state, uncertainty_t *uncertainty, logger *logger) -: m_eg (NULL), m_logger (logger), m_enode_for_diag (NULL), - m_old_state (NULL), +: m_eg (nullptr), m_logger (logger), m_enode_for_diag (nullptr), + m_old_state (nullptr), m_new_state (state), - m_stmt (NULL), - m_stmt_finder (NULL), + m_stmt (nullptr), + m_stmt_finder (nullptr), m_ext_state (ext_state), m_uncertainty (uncertainty), - m_path_ctxt (NULL), + m_path_ctxt (nullptr), m_out_could_have_done_work (nullptr) { } @@ -124,7 +113,7 @@ impl_region_model_context::warn (std::unique_ptr<pending_diagnostic> d, { LOG_FUNC (get_logger ()); auto curr_stmt_finder = custom_finder ? custom_finder : m_stmt_finder; - if (m_stmt == NULL && curr_stmt_finder == NULL) + if (m_stmt == nullptr && curr_stmt_finder == nullptr) { if (get_logger ()) get_logger ()->log ("rejecting diagnostic: no stmt"); @@ -307,7 +296,7 @@ public: const sm_state_map *old_smap, sm_state_map *new_smap, path_context *path_ctxt, - const stmt_finder *stmt_finder = NULL, + const stmt_finder *stmt_finder = nullptr, bool unknown_side_effects = false) : sm_context (sm_idx, sm), m_logger (eg.get_logger ()), @@ -322,11 +311,11 @@ public: logger *get_logger () const { return m_logger.get_logger (); } - tree get_fndecl_for_call (const gcall *call) final override + tree get_fndecl_for_call (const gcall &call) final override { impl_region_model_context old_ctxt - (m_eg, m_enode_for_diag, NULL, NULL, NULL/*m_enode->get_state ()*/, - NULL, call); + (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); } @@ -336,10 +325,10 @@ public: { logger * const logger = get_logger (); LOG_FUNC (logger); - /* Use NULL ctxt on this get_rvalue call to avoid triggering + /* 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, NULL); + = 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 ()); @@ -364,9 +353,9 @@ public: logger * const logger = get_logger (); LOG_FUNC (logger); const svalue *var_new_sval - = m_new_state->m_region_model->get_rvalue (var, NULL); + = m_new_state->m_region_model->get_rvalue (var, nullptr); const svalue *origin_new_sval - = m_new_state->m_region_model->get_rvalue (origin, NULL); + = 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 @@ -389,11 +378,11 @@ public: logger * const logger = get_logger (); LOG_FUNC (logger); impl_region_model_context old_ctxt - (m_eg, m_enode_for_diag, NULL, NULL, NULL/*m_enode->get_state ()*/, - NULL, stmt); + (m_eg, m_enode_for_diag, nullptr, nullptr, nullptr/*m_enode->get_state ()*/, + nullptr, stmt); const svalue *origin_new_sval - = m_new_state->m_region_model->get_rvalue (origin, NULL); + = 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 ()); @@ -419,7 +408,7 @@ public: LOG_FUNC (get_logger ()); gcc_assert (d); const svalue *var_old_sval - = m_old_state->m_region_model->get_rvalue (var, NULL); + = 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 ()) @@ -472,7 +461,7 @@ public: return expr; gcc_assert (m_new_state); - const svalue *sval = m_new_state->m_region_model->get_rvalue (expr, NULL); + 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; @@ -513,7 +502,7 @@ public: if (!assign_stmt) return NULL_TREE; impl_region_model_context old_ctxt - (m_eg, m_enode_for_diag, m_old_state, m_new_state, NULL, NULL, stmt); + (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)) @@ -583,17 +572,17 @@ get_state_map_by_name (const char *name, { const sm_state_map *old_smap = m_old_state->m_checker_states[sm_idx]; *out_sm_context - = make_unique<impl_sm_context> (*m_eg, - sm_idx, - *sm, - m_enode_for_diag, - m_old_state, - m_new_state, - old_smap, - new_smap, - m_path_ctxt, - m_stmt_finder, - false); + = std::make_unique<impl_sm_context> (*m_eg, + sm_idx, + *sm, + m_enode_for_diag, + m_old_state, + m_new_state, + old_smap, + new_smap, + m_path_ctxt, + m_stmt_finder, + false); } return true; } @@ -609,7 +598,7 @@ public: std::unique_ptr<stmt_finder> clone () const final override { - return make_unique<leak_stmt_finder> (m_eg, m_var); + return std::make_unique<leak_stmt_finder> (m_eg, m_var); } const gimple *find_stmt (const exploded_path &epath) @@ -677,7 +666,7 @@ public: } gcc_unreachable (); - return NULL; + return nullptr; } void update_event_loc_info (event_loc_info &) final override @@ -936,7 +925,9 @@ impl_region_model_context::on_state_leak (const state_machine &sm, } tree leaked_tree_for_diag = fixup_tree_for_diagnostic (leaked_tree); - std::unique_ptr<pending_diagnostic> pd = sm.on_leak (leaked_tree_for_diag); + std::unique_ptr<pending_diagnostic> pd = sm.on_leak (leaked_tree_for_diag, + m_old_state, + m_new_state); if (pd) { pending_location ploc (m_enode_for_diag, @@ -971,7 +962,7 @@ impl_region_model_context::on_condition (const svalue *lhs, sm.on_condition (sm_ctxt, (m_enode_for_diag ? m_enode_for_diag->get_supernode () - : NULL), + : nullptr), m_stmt, lhs, op, rhs); } @@ -998,7 +989,7 @@ impl_region_model_context::on_bounded_ranges (const svalue &sval, sm.on_bounded_ranges (sm_ctxt, (m_enode_for_diag ? m_enode_for_diag->get_supernode () - : NULL), + : nullptr), m_stmt, sval, ranges); } } @@ -1207,10 +1198,10 @@ exploded_node::status_to_str (enum status s) switch (s) { default: gcc_unreachable (); - case STATUS_WORKLIST: return "WORKLIST"; - case STATUS_PROCESSED: return "PROCESSED"; - case STATUS_MERGER: return "MERGER"; - case STATUS_BULK_MERGED: return "BULK_MERGED"; + case status::worklist: return "worklist"; + case status::processed: return "processed"; + case status::merger: return "merger"; + case status::bulk_merged: return "bulk_merged"; } } @@ -1218,7 +1209,7 @@ exploded_node::status_to_str (enum status s) exploded_node::exploded_node (const point_and_state &ps, int index) -: m_ps (ps), m_status (STATUS_WORKLIST), m_index (index), +: m_ps (ps), m_status (status::worklist), m_index (index), m_num_processed_stmts (0) { gcc_checking_assert (ps.get_state ().m_region_model->canonicalized_p ()); @@ -1296,9 +1287,9 @@ exploded_node::dump_dot (graphviz_out *gv, const dump_args_t &args) const pp_write_text_to_stream (pp); pp_printf (pp, "EN: %i", m_index); - if (m_status == STATUS_MERGER) + if (m_status == status::merger) pp_string (pp, " (merger)"); - else if (m_status == STATUS_BULK_MERGED) + else if (m_status == status::bulk_merged) pp_string (pp, " (bulk merged)"); pp_newline (pp); @@ -1440,7 +1431,7 @@ exploded_node::dump (const extrinsic_state &ext_state) const std::unique_ptr<json::object> exploded_node::to_json (const extrinsic_state &ext_state) const { - auto enode_obj = ::make_unique<json::object> (); + auto enode_obj = std::make_unique<json::object> (); enode_obj->set ("point", get_point ().to_json ()); enode_obj->set ("state", get_state ().to_json (ext_state)); @@ -1522,7 +1513,7 @@ exploded_node::on_stmt (exploded_graph &eg, gcc_assert (called_fn); return replay_call_summaries (eg, snode, - as_a <const gcall *> (stmt), + *as_a <const gcall *> (stmt), state, path_ctxt, *called_fn, @@ -1549,7 +1540,7 @@ exploded_node::on_stmt (exploded_graph &eg, = old_state.m_checker_states[sm_idx]; sm_state_map *new_smap = state->m_checker_states[sm_idx]; impl_sm_context sm_ctxt (eg, sm_idx, sm, this, &old_state, state, - old_smap, new_smap, path_ctxt, NULL, + old_smap, new_smap, path_ctxt, nullptr, unknown_side_effects); /* Allow the state_machine to handle the stmt. */ @@ -1579,8 +1570,9 @@ exploded_node::on_stmt_pre (exploded_graph &eg, region_model_context *ctxt) { /* Handle special-case calls that require the full program_state. */ - if (const gcall *call = dyn_cast <const gcall *> (stmt)) + if (const gcall *call_stmt = dyn_cast <const gcall *> (stmt)) { + const gcall &call = *call_stmt; if (is_special_named_call_p (call, "__analyzer_dump", 0)) { /* Handle the builtin "__analyzer_dump" by dumping state @@ -1588,6 +1580,16 @@ exploded_node::on_stmt_pre (exploded_graph &eg, state->dump (eg.get_ext_state (), true); return; } + else if (is_special_named_call_p (call, "__analyzer_dump_sarif", 0)) + { + state->dump_sarif (eg.get_ext_state ()); + return; + } + else if (is_special_named_call_p (call, "__analyzer_dump_dot", 0)) + { + state->dump_dot (eg.get_ext_state ()); + return; + } else if (is_special_named_call_p (call, "__analyzer_dump_state", 2)) { state->impl_call_analyzer_dump_state (call, eg.get_ext_state (), @@ -1609,6 +1611,24 @@ exploded_node::on_stmt_pre (exploded_graph &eg, ctxt->maybe_did_work (); return; } + else if (is_cxa_throw_p (call)) + { + on_throw (eg, call, state, false, ctxt); + *out_terminate_path = true; + return; + } + else if (is_cxa_rethrow_p (call)) + { + on_throw (eg, call, state, true, ctxt); + *out_terminate_path = true; + return; + } + } + else if (const gresx *resx = dyn_cast <const gresx *> (stmt)) + { + on_resx (eg, *resx, state, ctxt); + *out_terminate_path = true; + return; } /* Otherwise, defer to m_region_model. */ @@ -1626,7 +1646,7 @@ exploded_node::on_stmt_post (const gimple *stmt, region_model_context *ctxt) { if (const gcall *call = dyn_cast <const gcall *> (stmt)) - state->m_region_model->on_call_post (call, unknown_side_effects, ctxt); + state->m_region_model->on_call_post (*call, unknown_side_effects, ctxt); } /* A concrete call_info subclass representing a replay of a call summary. */ @@ -1636,7 +1656,7 @@ class call_summary_edge_info : public call_info public: call_summary_edge_info (const call_details &cd, const function &called_fn, - call_summary *summary, + call_summary &summary, const extrinsic_state &ext_state) : call_info (cd, called_fn), m_called_fn (called_fn), @@ -1651,7 +1671,7 @@ public: /* Update STATE based on summary_end_state. */ call_details cd (get_call_details (state->m_region_model, ctxt)); call_summary_replay r (cd, m_called_fn, m_summary, m_ext_state); - const program_state &summary_end_state = m_summary->get_state (); + const program_state &summary_end_state = m_summary.get_state (); return state->replay_call_summary (r, summary_end_state); } @@ -1662,19 +1682,19 @@ public: /* Update STATE based on summary_end_state. */ call_details cd (get_call_details (model, ctxt)); call_summary_replay r (cd, m_called_fn, m_summary, m_ext_state); - const program_state &summary_end_state = m_summary->get_state (); + const program_state &summary_end_state = m_summary.get_state (); model->replay_call_summary (r, *summary_end_state.m_region_model); return true; } void print_desc (pretty_printer &pp) const final override { - pp_string (&pp, m_summary->get_desc ().get ()); + pp_string (&pp, m_summary.get_desc ().get ()); } private: const function &m_called_fn; - call_summary *m_summary; + call_summary &m_summary; const extrinsic_state &m_ext_state; }; @@ -1684,7 +1704,7 @@ private: exploded_node::on_stmt_flags exploded_node::replay_call_summaries (exploded_graph &eg, const supernode *snode, - const gcall *call_stmt, + const gcall &call_stmt, program_state *state, path_context *path_ctxt, const function &called_fn, @@ -1696,8 +1716,11 @@ exploded_node::replay_call_summaries (exploded_graph &eg, /* Each summary will call bifurcate on the PATH_CTXT. */ for (auto summary : called_fn_data.m_summaries) - replay_call_summary (eg, snode, call_stmt, state, - path_ctxt, called_fn, summary, ctxt); + { + gcc_assert (summary); + replay_call_summary (eg, snode, call_stmt, state, + path_ctxt, called_fn, *summary, ctxt); + } path_ctxt->terminate_path (); return on_stmt_flags (); @@ -1710,27 +1733,25 @@ exploded_node::replay_call_summaries (exploded_graph &eg, void exploded_node::replay_call_summary (exploded_graph &eg, const supernode *snode, - const gcall *call_stmt, + const gcall &call_stmt, program_state *old_state, path_context *path_ctxt, const function &called_fn, - call_summary *summary, + call_summary &summary, region_model_context *ctxt) { logger *logger = eg.get_logger (); LOG_SCOPE (logger); gcc_assert (snode); - gcc_assert (call_stmt); gcc_assert (old_state); - gcc_assert (summary); if (logger) logger->log ("using %s as summary for call to %qE from %qE", - summary->get_desc ().get (), + summary.get_desc ().get (), called_fn.decl, snode->get_function ()->decl); const extrinsic_state &ext_state = eg.get_ext_state (); - const program_state &summary_end_state = summary->get_state (); + const program_state &summary_end_state = summary.get_state (); if (logger) { pretty_printer *pp = logger->get_printer (); @@ -1752,10 +1773,11 @@ exploded_node::replay_call_summary (exploded_graph &eg, call_summary_replay r (cd, called_fn, summary, ext_state); if (path_ctxt) - path_ctxt->bifurcate (make_unique<call_summary_edge_info> (cd, - called_fn, - summary, - ext_state)); + path_ctxt->bifurcate + (std::make_unique<call_summary_edge_info> (cd, + called_fn, + summary, + ext_state)); } @@ -1826,10 +1848,10 @@ valid_longjmp_stack_p (const program_point &longjmp_point, class stale_jmp_buf : public pending_diagnostic_subclass<stale_jmp_buf> { public: - stale_jmp_buf (const gcall *setjmp_call, const gcall *longjmp_call, + stale_jmp_buf (const gcall &setjmp_call, const gcall &longjmp_call, const program_point &setjmp_point) : m_setjmp_call (setjmp_call), m_longjmp_call (longjmp_call), - m_setjmp_point (setjmp_point), m_stack_pop_event (NULL) + m_setjmp_point (setjmp_point), m_stack_pop_event (nullptr) {} int get_controlling_option () const final override @@ -1849,8 +1871,8 @@ public: bool operator== (const stale_jmp_buf &other) const { - return (m_setjmp_call == other.m_setjmp_call - && m_longjmp_call == other.m_longjmp_call); + return (&m_setjmp_call == &other.m_setjmp_call + && &m_longjmp_call == &other.m_longjmp_call); } bool @@ -1903,8 +1925,8 @@ public: private: - const gcall *m_setjmp_call; - const gcall *m_longjmp_call; + const gcall &m_setjmp_call; + const gcall &m_longjmp_call; program_point m_setjmp_point; custom_event *m_stack_pop_event; }; @@ -1917,11 +1939,11 @@ private: void exploded_node::on_longjmp (exploded_graph &eg, - const gcall *longjmp_call, + const gcall &longjmp_call, program_state *new_state, region_model_context *ctxt) { - tree buf_ptr = gimple_call_arg (longjmp_call, 0); + tree buf_ptr = gimple_call_arg (&longjmp_call, 0); gcc_assert (POINTER_TYPE_P (TREE_TYPE (buf_ptr))); region_model *new_region_model = new_state->m_region_model; @@ -1942,7 +1964,7 @@ exploded_node::on_longjmp (exploded_graph &eg, call back to the setjmp/sigsetjmp. */ rewind_info_t rewind_info (tmp_setjmp_record, longjmp_call); - const gcall *setjmp_call = rewind_info.get_setjmp_call (); + const gcall &setjmp_call = rewind_info.get_setjmp_call (); const program_point &setjmp_point = rewind_info.get_setjmp_point (); const program_point &longjmp_point = get_point (); @@ -1950,9 +1972,9 @@ exploded_node::on_longjmp (exploded_graph &eg, /* Verify that the setjmp's call_stack hasn't been popped. */ if (!valid_longjmp_stack_p (longjmp_point, setjmp_point)) { - ctxt->warn (make_unique<stale_jmp_buf> (setjmp_call, - longjmp_call, - setjmp_point)); + ctxt->warn (std::make_unique<stale_jmp_buf> (setjmp_call, + longjmp_call, + setjmp_point)); return; } @@ -1971,7 +1993,7 @@ exploded_node::on_longjmp (exploded_graph &eg, setjmp_point.get_stack_depth (), ctxt); /* Detect leaks in the new state relative to the old state. */ - program_state::detect_leaks (get_state (), *new_state, NULL, + program_state::detect_leaks (get_state (), *new_state, nullptr, eg.get_ext_state (), ctxt); program_point next_point @@ -1985,9 +2007,9 @@ exploded_node::on_longjmp (exploded_graph &eg, if (next) { exploded_edge *eedge - = eg.add_edge (const_cast<exploded_node *> (this), next, NULL, true, - make_unique<rewind_info_t> (tmp_setjmp_record, - longjmp_call)); + = eg.add_edge (const_cast<exploded_node *> (this), next, nullptr, true, + std::make_unique<rewind_info_t> (tmp_setjmp_record, + longjmp_call)); /* For any diagnostics that were queued here (such as leaks) we want the checker_path to show the rewinding events after the "final event" @@ -2025,6 +2047,332 @@ exploded_node::on_longjmp (exploded_graph &eg, } } +/* Subclass of call_info for exploded edges that express + a throw or rethrow of an exception (actually a call + to __cxa_throw or __cxa_rethrow). */ + +class throw_custom_edge : public call_info +{ +public: + throw_custom_edge (const call_details &cd, + tree type, + bool is_rethrow) + : call_info (cd), + m_type (type), + m_is_rethrow (is_rethrow) + { + } + + void print (pretty_printer *pp) const final override + { + if (m_is_rethrow) + { + if (m_type) + pp_printf (pp, "rethrowing %qT", m_type); + else + pp_printf (pp, "rethrowing"); + } + else + { + if (m_type) + pp_printf (pp, "throwing %qT", m_type); + else + pp_printf (pp, "throwing"); + } + } + + void print_desc (pretty_printer &pp) const final override + { + print (&pp); + } + + bool update_model (region_model *model, + const exploded_edge *, + region_model_context *ctxt) const final override + { + if (m_is_rethrow) + { + auto eh_node = model->get_current_caught_exception (); + gcc_assert (eh_node); + model->push_thrown_exception (*eh_node); + } + else + { + call_details cd (get_call_details (model, ctxt)); + + const svalue *exception_sval = cd.get_arg_svalue (0); + const svalue *tinfo_sval = cd.get_arg_svalue (1); + const svalue *destructor_sval = cd.get_arg_svalue (2); + + /* Push a new exception_node on the model's m_exception_stack. */ + exception_node eh_node (exception_sval, tinfo_sval, destructor_sval); + model->push_thrown_exception (eh_node); + } + + return true; + } + + void add_events_to_path (checker_path *emission_path, + const exploded_edge &eedge) const final override + { + const exploded_node *dst_node = eedge.m_dest; + const program_point &dst_point = dst_node->get_point (); + const int dst_stack_depth = dst_point.get_stack_depth (); + + const gcall &call = get_call_stmt (); + + emission_path->add_event + (std::make_unique<explicit_throw_event> + (event_loc_info (call.location, + dst_point.get_fndecl (), + dst_stack_depth), + dst_node, + call, + m_type, + m_is_rethrow)); + } + +private: + tree m_type; + bool m_is_rethrow; +}; + +/* Subclass of custom_edge_info for an exploded edge that expresses + unwinding one stack frame during exception handling. */ + +class unwind_custom_edge : public custom_edge_info +{ +public: + unwind_custom_edge (location_t loc) + : m_loc (loc) + { + } + + void print (pretty_printer *pp) const final override + { + pp_printf (pp, "unwinding frame"); + } + + bool update_model (region_model *model, + const exploded_edge *, + region_model_context *ctxt) const final override + { + model->pop_frame (NULL_TREE, nullptr, ctxt, nullptr, false); + return true; + } + + void add_events_to_path (checker_path *emission_path, + const exploded_edge &eedge) const final override + { + const exploded_node *src_node = eedge.m_src; + const program_point &src_point = src_node->get_point (); + const int src_stack_depth = src_point.get_stack_depth (); + emission_path->add_event + (std::make_unique<unwind_event> (event_loc_info (m_loc, + src_point.get_fndecl (), + src_stack_depth))); + } + +private: + location_t m_loc; +}; + +/* Locate an SNODE that's a CFG edge with the EH flag, + or return nullptr. */ + +static const superedge * +get_eh_outedge (const supernode &snode) +{ + for (auto out_sedge : snode.m_succs) + if (::edge cfg_edge = out_sedge->get_any_cfg_edge ()) + if (cfg_edge->flags & EDGE_EH) + return out_sedge; + + // Not found + return nullptr; +} + +/* Given THROWN_ENODE, which expreses a throw or rethrow occurring at + THROW_STMT, unwind intraprocedurally and interprocedurally to find + the next eh_dispatch statement to handle exceptions, if any. + + Add eedges and enodes to this graph expressing the actions taken + to reach an enode containing the eh_dispatch stmt, if any. + Only the final enode is added to this graph's worklist. + + Use CTXT to warn about problems e.g. memory leaks due to stack frames + being unwound. */ + +void +exploded_graph::unwind_from_exception (exploded_node &thrown_enode, + const gimple *throw_stmt, + region_model_context *ctxt) +{ + logger * const logger = get_logger (); + LOG_FUNC_1 (logger, "thrown EN: %i", thrown_enode.m_index); + + /* Iteratively unwind the stack looking for an out-cfg-edge + flagged EH. */ + exploded_node *iter_enode = &thrown_enode; + while (iter_enode) + { + /* If we have an out-cfg-edge flagged EH, follow that, + presumably to a bb with a label and an eh_dispatch stmt. + Otherwise assume no out-cfgs-edges, and we are unwinding to the + caller. */ + if (auto sedge = get_eh_outedge (*iter_enode->get_supernode ())) + { + /* Intraprocedural case. + Assume we have an out-edge flagged with EH leading to + code for dispatch to catch handlers. */ + const program_point next_point + = program_point::before_supernode (sedge->m_dest, + sedge, + iter_enode->get_point ().get_call_string ()); + exploded_node *next_enode + = get_or_create_node (next_point, + iter_enode->get_state (), + iter_enode, + /* Add this enode to the worklist. */ + true); + if (!next_enode) + return; + + add_edge (iter_enode, next_enode, nullptr, false, nullptr); + return; + } + else + { + /* Interprocedural case. + No out-cfg-edge. Unwind one stack frame. */ + program_state unwound_state (iter_enode->get_state ()); + location_t loc = throw_stmt ? throw_stmt->location : UNKNOWN_LOCATION; + auto unwind_edge_info + = std::make_unique<unwind_custom_edge> (loc); + unwind_edge_info->update_model (unwound_state.m_region_model, nullptr, + ctxt); + + /* Detect leaks in the new state relative to the old state. + Use an alternate ctxt that uses the original enode and the stmt + (if any) for the location of any diagnostics. */ + { + uncertainty_t uncertainty; + impl_region_model_context ctxt (*this, + &thrown_enode, + &iter_enode->get_state (), + &unwound_state, + &uncertainty, + nullptr, + throw_stmt); + program_state::detect_leaks (iter_enode->get_state (), + unwound_state, + nullptr, + get_ext_state (), &ctxt); + } + const call_string &cs = iter_enode->get_point ().get_call_string (); + if (cs.empty_p ()) + { + /* Top-level stack frame in analysis: unwinding + to the outside world that called us. */ + return; + } + else + { + /* Nested function in analysis: unwinding to + the callsite in the analysis (or beyond). */ + program_point unwound_point + = program_point::after_supernode (cs.get_caller_node (), cs); + unwound_point.pop_from_call_stack (); + + exploded_node *after_unwind_enode + = get_or_create_node (unwound_point, + std::move (unwound_state), + iter_enode, + /* Don't add this enode to the + worklist; we will process it + on the next iteration. */ + false); + + if (!after_unwind_enode) + return; + + add_edge (iter_enode, after_unwind_enode, nullptr, true, + std::move (unwind_edge_info)); + iter_enode = after_unwind_enode; + } + } + } +} + +/* Handle THROW_CALL, a call to __cxa_throw or __cxa_rethrow. + + Create an eedge and destination enode for the throw/rethrow, adding + them to this egraph. The new enode isn't added to the worklist, but + instead exploded_graph::unwind_from_exception is immediately called + on it, potentially creating more eedges and enodes leading to an + eh_handler stmt. */ + +void +exploded_node::on_throw (exploded_graph &eg, + const gcall &throw_call, + program_state *new_state, + bool is_rethrow, + region_model_context *ctxt) +{ + region_model *model = new_state->m_region_model; + call_details cd (throw_call, model, ctxt); + + /* Create an enode and eedge for the "throw". */ + tree type = NULL_TREE; + if (is_rethrow) + { + const exception_node *eh_node = model->get_current_caught_exception (); + gcc_assert (eh_node); + type = eh_node->maybe_get_type (); + } + else + { + const svalue *tinfo_sval = cd.get_arg_svalue (1); + type = tinfo_sval->maybe_get_type_from_typeinfo (); + } + auto throw_edge_info + = std::make_unique<throw_custom_edge> (cd, type, is_rethrow); + throw_edge_info->update_model (model, nullptr, ctxt); + + program_point after_throw_point = get_point ().get_next (); + + exploded_node *after_throw_enode + = eg.get_or_create_node (after_throw_point, *new_state, this, + /* Don't add to worklist; we process + this immediately below. */ + false); + + if (!after_throw_enode) + return; + + /* Create custom exploded_edge for a throw. */ + eg.add_edge (this, after_throw_enode, nullptr, true, + std::move (throw_edge_info)); + + eg.unwind_from_exception (*after_throw_enode, &throw_call, ctxt); +} + +/* Handle a gimple "resx" statement by adding eedges and enode. + that unwind to the next eh_dispatch statement, if any. Only + the final enode is added to the worklist. */ + +void +exploded_node::on_resx (exploded_graph &eg, + const gresx &/*resx*/, + program_state */*new_state*/, + region_model_context *ctxt) +{ + eg.unwind_from_exception (*this, + nullptr, + ctxt); +} + + /* Subroutine of exploded_graph::process_node for finding the successors of the supernode for a function exit basic block. @@ -2056,10 +2404,10 @@ exploded_node::detect_leaks (exploded_graph &eg) uncertainty_t uncertainty; impl_region_model_context ctxt (eg, this, - &old_state, &new_state, &uncertainty, NULL, + &old_state, &new_state, &uncertainty, nullptr, get_stmt ()); - const svalue *result = NULL; - new_state.m_region_model->pop_frame (NULL, &result, &ctxt, nullptr); + const svalue *result = nullptr; + new_state.m_region_model->pop_frame (nullptr, &result, &ctxt, nullptr); program_state::detect_leaks (old_state, new_state, result, eg.get_ext_state (), &ctxt); } @@ -2131,20 +2479,16 @@ dynamic_call_info_t::add_events_to_path (checker_path *emission_path, if (m_is_returning_call) emission_path->add_event - (make_unique<return_event> (eedge, - event_loc_info (m_dynamic_call - ? m_dynamic_call->location - : UNKNOWN_LOCATION, - dest_point.get_fndecl (), - dest_stack_depth))); + (std::make_unique<return_event> (eedge, + event_loc_info (m_dynamic_call.location, + dest_point.get_fndecl (), + dest_stack_depth))); else emission_path->add_event - (make_unique<call_event> (eedge, - event_loc_info (m_dynamic_call - ? m_dynamic_call->location - : UNKNOWN_LOCATION, - src_point.get_fndecl (), - src_stack_depth))); + (std::make_unique<call_event> (eedge, + event_loc_info (m_dynamic_call.location, + src_point.get_fndecl (), + src_stack_depth))); } /* class rewind_info_t : public custom_edge_info. */ @@ -2170,7 +2514,7 @@ rewind_info_t::update_model (region_model *model, model->on_longjmp (get_longjmp_call (), get_setjmp_call (), - setjmp_point.get_stack_depth (), NULL); + setjmp_point.get_stack_depth (), nullptr); return true; } @@ -2189,19 +2533,19 @@ rewind_info_t::add_events_to_path (checker_path *emission_path, const int dst_stack_depth = dst_point.get_stack_depth (); emission_path->add_event - (make_unique<rewind_from_longjmp_event> - (&eedge, - event_loc_info (get_longjmp_call ()->location, - src_point.get_fndecl (), - src_stack_depth), - this)); + (std::make_unique<rewind_from_longjmp_event> + (&eedge, + event_loc_info (get_longjmp_call ().location, + src_point.get_fndecl (), + src_stack_depth), + this)); emission_path->add_event - (make_unique<rewind_to_setjmp_event> - (&eedge, - event_loc_info (get_setjmp_call ()->location, - dst_point.get_fndecl (), - dst_stack_depth), - this)); + (std::make_unique<rewind_to_setjmp_event> + (&eedge, + event_loc_info (get_setjmp_call ().location, + dst_point.get_fndecl (), + dst_stack_depth), + this)); } /* class exploded_edge : public dedge<eg_traits>. */ @@ -2294,7 +2638,7 @@ exploded_edge::dump_dot_label (pretty_printer *pp) const std::unique_ptr<json::object> exploded_edge::to_json () const { - auto eedge_obj = ::make_unique<json::object> (); + auto eedge_obj = std::make_unique<json::object> (); eedge_obj->set_integer ("src_idx", m_src->m_index); eedge_obj->set_integer ("dst_idx", m_dest->m_index); if (m_sedge) @@ -2420,9 +2764,9 @@ strongly_connected_components::dump () const std::unique_ptr<json::array> strongly_connected_components::to_json () const { - auto scc_arr = ::make_unique<json::array> (); + auto scc_arr = std::make_unique<json::array> (); for (int i = 0; i < m_sg.num_nodes (); i++) - scc_arr->append (::make_unique<json::integer_number> (get_scc_id (i))); + scc_arr->append (std::make_unique<json::integer_number> (get_scc_id (i))); return scc_arr; } @@ -2485,7 +2829,7 @@ strongly_connected_components::strong_connect (unsigned index) worklist::worklist (const exploded_graph &eg, const analysis_plan &plan) : m_scc (eg.get_supergraph (), eg.get_logger ()), m_plan (plan), - m_queue (key_t (*this, NULL)) + m_queue (key_t (*this, nullptr)) { } @@ -2518,7 +2862,7 @@ worklist::peek_next () void worklist::add_node (exploded_node *enode) { - gcc_assert (enode->get_status () == exploded_node::STATUS_WORKLIST); + gcc_assert (enode->get_status () == exploded_node::status::worklist); m_queue.insert (key_t (*this, enode), enode); } @@ -2547,8 +2891,8 @@ worklist::key_t::cmp (const worklist::key_t &ka, const worklist::key_t &kb) if (flag_analyzer_call_summaries && call_string_a.empty_p () && call_string_b.empty_p () - && point_a.get_function () != NULL - && point_b.get_function () != NULL + && point_a.get_function () != nullptr + && point_b.get_function () != nullptr && point_a.get_function () != point_b.get_function ()) { if (int cmp = ka.m_worklist.m_plan.cmp_function (point_a.get_function (), @@ -2584,19 +2928,19 @@ worklist::key_t::cmp (const worklist::key_t &ka, const worklist::key_t &kb) ordering). */ const supernode *snode_a = ka.m_enode->get_supernode (); const supernode *snode_b = kb.m_enode->get_supernode (); - if (snode_a == NULL) + if (snode_a == nullptr) { - if (snode_b != NULL) - /* One is NULL. */ + if (snode_b != nullptr) + /* One is nullptr. */ return -1; else - /* Both are NULL. */ + /* Both are nullptr. */ return 0; } - if (snode_b == NULL) - /* One is NULL. */ + if (snode_b == nullptr) + /* One is nullptr. */ return 1; - /* Neither are NULL. */ + /* Neither are nullptr. */ gcc_assert (snode_a && snode_b); if (snode_a->m_index != snode_b->m_index) return snode_a->m_index - snode_b->m_index; @@ -2641,7 +2985,7 @@ worklist::key_t::cmp (const worklist::key_t &ka, const worklist::key_t &kb) std::unique_ptr<json::object> worklist::to_json () const { - auto worklist_obj = ::make_unique<json::object> (); + auto worklist_obj = std::make_unique<json::object> (); worklist_obj->set ("scc", m_scc.to_json ()); @@ -2670,7 +3014,7 @@ exploded_graph::exploded_graph (const supergraph &sg, logger *logger, { m_origin = get_or_create_node (program_point::origin (*ext_state.get_model_manager ()), - program_state (ext_state), NULL); + program_state (ext_state), nullptr); for (int i = 0; i < m_sg.num_nodes (); i++) m_PK_AFTER_SUPERNODE_per_snode.quick_push (i); } @@ -2722,10 +3066,10 @@ mark_params_as_tainted (program_state *state, tree fndecl, tree param = iter_parm; if (tree parm_default_ssa = ssa_default_def (fun, iter_parm)) param = parm_default_ssa; - const region *param_reg = state->m_region_model->get_lvalue (param, NULL); + const region *param_reg = state->m_region_model->get_lvalue (param, nullptr); const svalue *init_sval = mgr->get_or_create_initial_value (param_reg); smap->set_state (state->m_region_model, init_sval, - tainted, NULL /*origin_new_sval*/, ext_state); + tainted, nullptr /*origin_new_sval*/, ext_state); if (POINTER_TYPE_P (TREE_TYPE (param))) { const region *pointee_reg = mgr->get_symbolic_region (init_sval); @@ -2733,7 +3077,7 @@ mark_params_as_tainted (program_state *state, tree fndecl, const svalue *init_pointee_sval = mgr->get_or_create_initial_value (pointee_reg); smap->set_state (state->m_region_model, init_pointee_sval, - tainted, NULL /*origin_new_sval*/, ext_state); + tainted, nullptr /*origin_new_sval*/, ext_state); } } @@ -2791,8 +3135,8 @@ public: const exploded_edge &) const final override { emission_path->add_event - (make_unique<tainted_args_function_custom_event> - (event_loc_info (DECL_SOURCE_LOCATION (m_fndecl), m_fndecl, 0))); + (std::make_unique<tainted_args_function_custom_event> + (event_loc_info (DECL_SOURCE_LOCATION (m_fndecl), m_fndecl, 0))); } private: @@ -2819,7 +3163,7 @@ exploded_graph::add_function_entry (const function &fun) logger * const logger = get_logger (); if (logger) logger->log ("entrypoint for %qE already exists", fun.decl); - return NULL; + return nullptr; } program_point point @@ -2828,22 +3172,22 @@ exploded_graph::add_function_entry (const function &fun) program_state state (m_ext_state); state.push_frame (m_ext_state, fun); - std::unique_ptr<custom_edge_info> edge_info = NULL; + std::unique_ptr<custom_edge_info> edge_info = nullptr; if (lookup_attribute ("tainted_args", DECL_ATTRIBUTES (fun.decl))) { if (mark_params_as_tainted (&state, fun.decl, m_ext_state)) - edge_info = make_unique<tainted_args_function_info> (fun.decl); + edge_info = std::make_unique<tainted_args_function_info> (fun.decl); } if (!state.m_valid) - return NULL; + return nullptr; - exploded_node *enode = get_or_create_node (point, state, NULL); + exploded_node *enode = get_or_create_node (point, state, nullptr); if (!enode) - return NULL; + return nullptr; - add_edge (m_origin, enode, NULL, false, std::move (edge_info)); + add_edge (m_origin, enode, nullptr, false, std::move (edge_info)); m_functions_with_enodes.add (key); @@ -2851,7 +3195,8 @@ exploded_graph::add_function_entry (const function &fun) } /* Get or create an exploded_node for (POINT, STATE). - If a new node is created, it is added to the worklist. + If a new node is created and ADD_TO_WORKLIST is true, + it is added to the worklist. Use ENODE_FOR_DIAG, a pre-existing enode, for any diagnostics that need to be emitted (e.g. when purging state *before* we have @@ -2860,7 +3205,8 @@ exploded_graph::add_function_entry (const function &fun) exploded_node * exploded_graph::get_or_create_node (const program_point &point, const program_state &state, - exploded_node *enode_for_diag) + exploded_node *enode_for_diag, + bool add_to_worklist) { logger * const logger = get_logger (); LOG_FUNC (logger); @@ -2884,7 +3230,7 @@ exploded_graph::get_or_create_node (const program_point &point, { if (logger) logger->log ("invalid state; not creating node"); - return NULL; + return nullptr; } auto_cfun sentinel (point.get_function ()); @@ -2998,7 +3344,7 @@ exploded_graph::get_or_create_node (const program_point &point, "terminating analysis for this program point: %s", pp_formatted_text (&pp)); per_point_data->m_excess_enodes++; - return NULL; + return nullptr; } ps.validate (m_ext_state); @@ -3035,7 +3381,10 @@ exploded_graph::get_or_create_node (const program_point &point, } /* Add the new node to the worlist. */ - m_worklist.add_node (node); + if (add_to_worklist) + m_worklist.add_node (node); + else + node->set_status (exploded_node::status::special); return node; } @@ -3075,7 +3424,7 @@ get_or_create_per_program_point_data (const program_point &point) } /* Get this graph's per-program-point-data for POINT if there is any, - otherwise NULL. */ + otherwise nullptr. */ per_program_point_data * exploded_graph::get_per_program_point_data (const program_point &point) const @@ -3084,7 +3433,7 @@ exploded_graph::get_per_program_point_data (const program_point &point) const = const_cast <point_map_t &> (m_per_point_data).get (&point)) return *slot; - return NULL; + return nullptr; } /* Ensure that this graph has per-call_string-data for CS; @@ -3117,7 +3466,7 @@ exploded_graph::get_or_create_per_function_data (function *fun) } /* Get this graph's per-function-data for FUN if there is any, - otherwise NULL. */ + otherwise nullptr. */ per_function_data * exploded_graph::get_per_function_data (function *fun) const @@ -3126,7 +3475,7 @@ exploded_graph::get_per_function_data (function *fun) const = const_cast <per_function_data_t &> (m_per_function_data).get (fun)) return *slot; - return NULL; + return nullptr; } /* Return true if FUN should be traversed directly, rather than only as @@ -3238,16 +3587,16 @@ public: /* Show the field in the struct declaration, e.g. "(1) field 'store' is marked with '__attribute__((tainted_args))'" */ emission_path->add_event - (make_unique<tainted_args_field_custom_event> (m_field)); + (std::make_unique<tainted_args_field_custom_event> (m_field)); /* Show the callback in the initializer e.g. "(2) function 'gadget_dev_desc_UDC_store' used as initializer for field 'store' marked with '__attribute__((tainted_args))'". */ emission_path->add_event - (make_unique<tainted_args_callback_custom_event> - (event_loc_info (m_loc, m_fndecl, 0), - m_field)); + (std::make_unique<tainted_args_callback_custom_event> + (event_loc_info (m_loc, m_fndecl, 0), + m_field)); } private: @@ -3289,7 +3638,7 @@ add_tainted_args_callback (exploded_graph *eg, tree field, tree fndecl, if (!state.m_valid) return; - exploded_node *enode = eg->get_or_create_node (point, state, NULL); + exploded_node *enode = eg->get_or_create_node (point, state, nullptr); if (logger) { if (enode) @@ -3303,8 +3652,8 @@ add_tainted_args_callback (exploded_graph *eg, tree field, tree fndecl, } } - eg->add_edge (eg->get_origin (), enode, NULL, false, - make_unique<tainted_args_call_info> (field, fndecl, loc)); + eg->add_edge (eg->get_origin (), enode, nullptr, false, + std::make_unique<tainted_args_call_info> (field, fndecl, loc)); } /* Callback for walk_tree for finding callbacks within initializers; @@ -3377,7 +3726,7 @@ exploded_graph::build_initial_worklist () tree init = DECL_INITIAL (decl); if (!init) continue; - walk_tree (&init, add_any_callbacks, this, NULL); + walk_tree (&init, add_any_callbacks, this, nullptr); } } @@ -3397,7 +3746,7 @@ exploded_graph::process_worklist () while (m_worklist.length () > 0) { exploded_node *node = m_worklist.take_next (); - gcc_assert (node->get_status () == exploded_node::STATUS_WORKLIST); + gcc_assert (node->get_status () == exploded_node::status::worklist); gcc_assert (node->m_succs.length () == 0 || node == m_origin); @@ -3417,7 +3766,7 @@ exploded_graph::process_worklist () if (exploded_node *node_2 = m_worklist.peek_next ()) { gcc_assert (node_2->get_status () - == exploded_node::STATUS_WORKLIST); + == exploded_node::status::worklist); gcc_assert (node->m_succs.length () == 0); gcc_assert (node_2->m_succs.length () == 0); @@ -3458,11 +3807,11 @@ exploded_graph::process_worklist () if (merged_state == state) { /* Then merge node_2 into node by adding an edge. */ - add_edge (node_2, node, NULL, false); + add_edge (node_2, node, nullptr, false); /* Remove node_2 from the worklist. */ m_worklist.take_next (); - node_2->set_status (exploded_node::STATUS_MERGER); + node_2->set_status (exploded_node::status::merger); /* Continue processing "node" below. */ } @@ -3471,8 +3820,8 @@ exploded_graph::process_worklist () /* Then merge node into node_2, and leave node_2 in the worklist, to be processed on the next iteration. */ - add_edge (node, node_2, NULL, false); - node->set_status (exploded_node::STATUS_MERGER); + add_edge (node, node_2, nullptr, false); + node->set_status (exploded_node::status::merger); continue; } else @@ -3488,7 +3837,7 @@ exploded_graph::process_worklist () exploded_node *merged_enode = get_or_create_node (node->get_point (), merged_state, node); - if (merged_enode == NULL) + if (merged_enode == nullptr) continue; if (logger) @@ -3516,16 +3865,16 @@ exploded_graph::process_worklist () m_worklist.add_node (merged_enode); else { - add_edge (node, merged_enode, NULL, false); - node->set_status (exploded_node::STATUS_MERGER); + add_edge (node, merged_enode, nullptr, false); + node->set_status (exploded_node::status::merger); } if (merged_enode == node_2) m_worklist.add_node (merged_enode); else { - add_edge (node_2, merged_enode, NULL, false); - node_2->set_status (exploded_node::STATUS_MERGER); + add_edge (node_2, merged_enode, nullptr, false); + node_2->set_status (exploded_node::status::merger); } continue; @@ -3575,7 +3924,7 @@ exploded_graph::process_worklist () If ENODE's point is of the form (before-supernode, SNODE) and the next nodes in the worklist are a consecutive run of enodes of the same form, for the same supernode as ENODE (but potentially from different in-edges), - process them all together, setting their status to STATUS_BULK_MERGED, + process them all together, setting their status to status::bulk_merged, and return true. Otherwise, return false, in which case ENODE must be processed in the normal way. @@ -3614,7 +3963,7 @@ maybe_process_run_of_before_supernode_enodes (exploded_node *enode) int m_merger_idx; }; - gcc_assert (enode->get_status () == exploded_node::STATUS_WORKLIST); + gcc_assert (enode->get_status () == exploded_node::status::worklist); gcc_assert (enode->m_succs.length () == 0); const program_point &point = enode->get_point (); @@ -3634,7 +3983,7 @@ maybe_process_run_of_before_supernode_enodes (exploded_node *enode) while (exploded_node *enode_2 = m_worklist.peek_next ()) { gcc_assert (enode_2->get_status () - == exploded_node::STATUS_WORKLIST); + == exploded_node::status::worklist); gcc_assert (enode_2->m_succs.length () == 0); const program_point &point_2 = enode_2->get_point (); @@ -3679,7 +4028,7 @@ maybe_process_run_of_before_supernode_enodes (exploded_node *enode) uncertainty_t uncertainty; impl_region_model_context ctxt (*this, iter_enode, &state, next_state, - &uncertainty, NULL, NULL); + &uncertainty, nullptr, nullptr); const cfg_superedge *last_cfg_superedge = iter_sedge->dyn_cast_cfg_superedge (); if (last_cfg_superedge) @@ -3742,7 +4091,7 @@ maybe_process_run_of_before_supernode_enodes (exploded_node *enode) = first_item_for_each_merged_state[i]->m_input_enode; exploded_node *next = get_or_create_node (next_point, *merged_state, src_enode); - /* "next" could be NULL; we handle that when adding the edges below. */ + /* "next" could be nullptr; we handle that when adding the edges below. */ next_enodes.quick_push (next); if (logger) { @@ -3759,9 +4108,9 @@ maybe_process_run_of_before_supernode_enodes (exploded_node *enode) { exploded_node *next = next_enodes[it->m_merger_idx]; if (next) - add_edge (it->m_input_enode, next, NULL, + add_edge (it->m_input_enode, next, nullptr, false); /* no "work" is done during merger. */ - it->m_input_enode->set_status (exploded_node::STATUS_BULK_MERGED); + it->m_input_enode->set_status (exploded_node::status::bulk_merged); } if (logger) @@ -3779,8 +4128,9 @@ static bool stmt_requires_new_enode_p (const gimple *stmt, const gimple *prev_stmt) { - if (const gcall *call = dyn_cast <const gcall *> (stmt)) + if (const gcall *call_stmt = dyn_cast <const gcall *> (stmt)) { + const gcall &call = *call_stmt; /* Stop consolidating at calls to "__analyzer_dump_exploded_nodes", so they always appear at the start of an exploded_node. */ @@ -3849,7 +4199,7 @@ state_change_requires_new_enode_p (const program_state &old_state, functions or calls that happen via function pointer. */ bool -exploded_graph::maybe_create_dynamic_call (const gcall *call, +exploded_graph::maybe_create_dynamic_call (const gcall &call, tree fn_decl, exploded_node *node, program_state next_state, @@ -3869,7 +4219,7 @@ exploded_graph::maybe_create_dynamic_call (const gcall *call, program_point new_point = program_point::before_supernode (sn_entry, - NULL, + nullptr, this_point->get_call_string ()); new_point.push_to_call_stack (sn_exit, @@ -3902,9 +4252,9 @@ exploded_graph::maybe_create_dynamic_call (const gcall *call, next_state, node); if (enode) - add_edge (node,enode, NULL, + add_edge (node,enode, nullptr, false, /* No work is done by the call itself. */ - make_unique<dynamic_call_info_t> (call)); + std::make_unique<dynamic_call_info_t> (call)); return true; } } @@ -3992,7 +4342,7 @@ private: class jump_through_null : public pending_diagnostic_subclass<jump_through_null> { public: - jump_through_null (const gcall *call) + jump_through_null (const gcall &call) : m_call (call) {} @@ -4003,7 +4353,7 @@ public: bool operator== (const jump_through_null &other) const { - return m_call == other.m_call; + return &m_call == &other.m_call; } int get_controlling_option () const final override @@ -4024,7 +4374,7 @@ public: } private: - const gcall *m_call; + const gcall &m_call; }; /* The core of exploded_graph::process_worklist (the main analysis loop), @@ -4041,7 +4391,7 @@ exploded_graph::process_node (exploded_node *node) logger * const logger = get_logger (); LOG_FUNC_1 (logger, "EN: %i", node->m_index); - node->set_status (exploded_node::STATUS_PROCESSED); + node->set_status (exploded_node::status::processed); const program_point &point = node->get_point (); @@ -4082,7 +4432,7 @@ exploded_graph::process_node (exploded_node *node) { impl_region_model_context ctxt (*this, node, &state, &next_state, - &uncertainty, NULL, NULL); + &uncertainty, nullptr, nullptr); const cfg_superedge *last_cfg_superedge = point.get_from_edge ()->dyn_cast_cfg_superedge (); if (last_cfg_superedge) @@ -4090,14 +4440,14 @@ exploded_graph::process_node (exploded_node *node) (node->get_supernode (), last_cfg_superedge, &ctxt); - program_state::detect_leaks (state, next_state, NULL, + program_state::detect_leaks (state, next_state, nullptr, get_ext_state (), &ctxt); } program_point next_point (point.get_next ()); exploded_node *next = get_or_create_node (next_point, next_state, node); if (next) - add_edge (node, next, NULL, + add_edge (node, next, nullptr, false); /* Assume no work is done at phi nodes. */ } break; @@ -4130,7 +4480,7 @@ exploded_graph::process_node (exploded_node *node) uncertainty_t uncertainty; const supernode *snode = point.get_supernode (); unsigned stmt_idx; - const gimple *prev_stmt = NULL; + const gimple *prev_stmt = nullptr; for (stmt_idx = point.get_stmt_idx (); stmt_idx < snode->m_stmts.length (); stmt_idx++) @@ -4162,8 +4512,8 @@ exploded_graph::process_node (exploded_node *node) { impl_region_model_context ctxt (*this, node, &old_state, &next_state, - &uncertainty, NULL, stmt); - program_state::detect_leaks (old_state, next_state, NULL, + &uncertainty, nullptr, stmt); + program_state::detect_leaks (old_state, next_state, nullptr, get_ext_state (), &ctxt); } @@ -4207,7 +4557,7 @@ exploded_graph::process_node (exploded_node *node) node->m_num_processed_stmts--; if (logger) logger->log ("creating edge to split_enode"); - add_edge (node, split_enode, NULL, could_have_done_work); + add_edge (node, split_enode, nullptr, could_have_done_work); return; } else @@ -4234,7 +4584,7 @@ exploded_graph::process_node (exploded_node *node) exploded_node *next = get_or_create_node (next_point, next_state, node); if (next) - add_edge (node, next, NULL, could_have_done_work); + add_edge (node, next, nullptr, could_have_done_work); } /* If we have custom edge infos, "bifurcate" the state @@ -4262,19 +4612,25 @@ exploded_graph::process_node (exploded_node *node) node, // enode_for_diag &path_ctxt.get_state_at_bifurcation (), &bifurcated_new_state, - NULL, // uncertainty_t *uncertainty - NULL, // path_context *path_ctxt + nullptr, // uncertainty_t *uncertainty + nullptr, // path_context *path_ctxt stmt); if (edge_info->update_state (&bifurcated_new_state, - NULL, /* no exploded_edge yet. */ + nullptr, /* no exploded_edge yet. */ &bifurcation_ctxt)) { - exploded_node *next2 - = get_or_create_node (next_point, bifurcated_new_state, node); - if (next2) - add_edge (node, next2, NULL, - true /* assume that work could be done */, - std::move (edge_info)); + if (exploded_node *next2 + = edge_info->create_enode + (*this, + next_point, + std::move (bifurcated_new_state), + node, + &bifurcation_ctxt)) + { + add_edge (node, next2, nullptr, + true /* assume that work could be done */, + std::move (edge_info)); + } } else { @@ -4344,15 +4700,15 @@ exploded_graph::process_node (exploded_node *node) if (succ->m_kind == SUPEREDGE_INTRAPROCEDURAL_CALL && !(succ->get_any_callgraph_edge ())) { - const gcall *call - = point.get_supernode ()->get_final_call (); + const gcall &call + = *point.get_supernode ()->get_final_call (); impl_region_model_context ctxt (*this, node, &state, &next_state, &uncertainty, - NULL, + nullptr, point.get_stmt()); region_model *model = state.m_region_model; @@ -4368,13 +4724,14 @@ exploded_graph::process_node (exploded_node *node) logger); if (!call_discovered) { - /* Check for jump through NULL. */ - if (tree fn_ptr = gimple_call_fn (call)) + /* Check for jump through nullptr. */ + if (tree fn_ptr = gimple_call_fn (&call)) { const svalue *fn_ptr_sval = model->get_rvalue (fn_ptr, &ctxt); if (fn_ptr_sval->all_zeroes_p ()) - ctxt.warn (make_unique<jump_through_null> (call)); + ctxt.warn + (std::make_unique<jump_through_null> (call)); } /* An unknown function or a special function was called @@ -4394,6 +4751,18 @@ exploded_graph::process_node (exploded_node *node) } } + /* Ignore CFG edges in the sgraph flagged with EH whilst + we're exploring the egraph. + We only use these sedges in special-case logic for + dealing with exception-handling. */ + if (auto cfg_sedge = succ->dyn_cast_cfg_superedge ()) + if (cfg_sedge->get_flags () & EDGE_EH) + { + if (logger) + logger->log ("rejecting EH edge"); + continue; + } + if (!node->on_edge (*this, succ, &next_point, &next_state, &uncertainty)) { @@ -4422,7 +4791,7 @@ exploded_graph::process_node (exploded_node *node) const call_string &cs = point.get_call_string (); program_point next_point = program_point::before_supernode (cs.get_caller_node (), - NULL, + nullptr, cs); program_state next_state (state); uncertainty_t uncertainty; @@ -4431,7 +4800,7 @@ exploded_graph::process_node (exploded_node *node) = next_point.get_supernode ()->get_returning_call (); if (call) - next_state.returning_call (*this, node, call, &uncertainty); + next_state.returning_call (*this, node, *call, &uncertainty); if (next_state.m_valid) { @@ -4440,8 +4809,8 @@ exploded_graph::process_node (exploded_node *node) next_state, node); if (enode) - add_edge (node, enode, NULL, false, - make_unique<dynamic_call_info_t> (call, true)); + add_edge (node, enode, nullptr, false, + std::make_unique<dynamic_call_info_t> (*call, true)); } } } @@ -4450,7 +4819,7 @@ exploded_graph::process_node (exploded_node *node) } /* Ensure that this graph has a stats instance for FN, return it. - FN can be NULL, in which case a stats instances is returned covering + FN can be nullptr, in which case a stats instances is returned covering "functionless" parts of the graph (the origin node). */ stats * @@ -4660,11 +5029,11 @@ exploded_graph::dump_states_for_supernode (FILE *out, std::unique_ptr<json::object> exploded_graph::to_json () const { - auto egraph_obj = ::make_unique<json::object> (); + auto egraph_obj = std::make_unique<json::object> (); /* Nodes. */ { - auto nodes_arr = ::make_unique<json::array> (); + auto nodes_arr = std::make_unique<json::array> (); unsigned i; exploded_node *n; FOR_EACH_VEC_ELT (m_nodes, i, n) @@ -4674,7 +5043,7 @@ exploded_graph::to_json () const /* Edges. */ { - auto edges_arr = ::make_unique<json::array> (); + auto edges_arr = std::make_unique<json::array> (); unsigned i; exploded_edge *n; FOR_EACH_VEC_ELT (m_edges, i, n) @@ -4780,9 +5149,9 @@ exploded_path::feasible_p (logger *logger, const program_point &src_point = src_enode.get_point (); const gimple *last_stmt = src_point.get_supernode ()->get_last_stmt (); - *out = ::make_unique<feasibility_problem> (edge_idx, *eedge, - last_stmt, - std::move (rc)); + *out = std::make_unique<feasibility_problem> (edge_idx, *eedge, + last_stmt, + std::move (rc)); } return false; } @@ -4986,7 +5355,7 @@ maybe_update_for_edge (logger *logger, == PK_BEFORE_SUPERNODE); function *fun = eedge->m_dest->get_function (); gcc_assert (fun); - m_model.push_frame (*fun, NULL, ctxt); + m_model.push_frame (*fun, nullptr, nullptr, ctxt); if (logger) logger->log (" pushing frame for %qD", fun->decl); } @@ -5032,16 +5401,16 @@ void feasibility_state::update_for_stmt (const gimple *stmt) { if (const gassign *assign = dyn_cast <const gassign *> (stmt)) - m_model.on_assignment (assign, NULL); + m_model.on_assignment (assign, nullptr); else if (const gasm *asm_stmt = dyn_cast <const gasm *> (stmt)) - m_model.on_asm_stmt (asm_stmt, NULL); + m_model.on_asm_stmt (asm_stmt, nullptr); else if (const gcall *call = dyn_cast <const gcall *> (stmt)) { - bool unknown_side_effects = m_model.on_call_pre (call, NULL); - m_model.on_call_post (call, unknown_side_effects, NULL); + bool unknown_side_effects = m_model.on_call_pre (*call, nullptr); + m_model.on_call_post (*call, unknown_side_effects, nullptr); } else if (const greturn *return_ = dyn_cast <const greturn *> (stmt)) - m_model.on_return (return_, NULL); + m_model.on_return (return_, nullptr); } /* Dump this object to PP. */ @@ -5256,7 +5625,7 @@ template <> inline void pod_hash_traits<function_call_string>::mark_empty (value_type &v) { - v.m_fun = NULL; + v.m_fun = nullptr; } template <> inline bool @@ -5268,7 +5637,7 @@ template <> inline bool pod_hash_traits<function_call_string>::is_empty (value_type v) { - return v.m_fun == NULL; + return v.m_fun == nullptr; } namespace ana { @@ -5424,7 +5793,7 @@ exploded_graph::dump_exploded_nodes () const { auto_timevar tv (TV_ANALYZER_DUMP); char *filename - = concat (dump_base_name, ".eg.txt", NULL); + = concat (dump_base_name, ".eg.txt", nullptr); FILE *outf = fopen (filename, "w"); if (!outf) error_at (UNKNOWN_LOCATION, "unable to open %qs for writing", filename); @@ -5499,7 +5868,7 @@ exploded_graph::dump_exploded_nodes () const if (const gimple *stmt = enode->get_stmt ()) if (const gcall *call = dyn_cast <const gcall *> (stmt)) - if (is_special_named_call_p (call, "__analyzer_dump_exploded_nodes", + if (is_special_named_call_p (*call, "__analyzer_dump_exploded_nodes", 1)) { if (seen.contains (stmt)) @@ -5520,13 +5889,13 @@ exploded_graph::dump_exploded_nodes () const { default: gcc_unreachable (); - case exploded_node::STATUS_WORKLIST: + case exploded_node::status::worklist: worklist_enodes.safe_push (other_enode); break; - case exploded_node::STATUS_PROCESSED: + case exploded_node::status::processed: processed_enodes.safe_push (other_enode); break; - case exploded_node::STATUS_MERGER: + case exploded_node::status::merger: merger_enodes.safe_push (other_enode); break; } @@ -5851,7 +6220,7 @@ dump_callgraph (const supergraph &sg, const char *filename, // TODO viz_callgraph vcg (sg); - vcg.dump_dot (filename, NULL, viz_callgraph_traits::dump_args_t (eg)); + vcg.dump_dot (filename, nullptr, viz_callgraph_traits::dump_args_t (eg)); fclose (outf); } @@ -5862,7 +6231,7 @@ static void dump_callgraph (const supergraph &sg, const exploded_graph *eg) { auto_timevar tv (TV_ANALYZER_DUMP); - char *filename = concat (dump_base_name, ".callgraph.dot", NULL); + char *filename = concat (dump_base_name, ".callgraph.dot", nullptr); dump_callgraph (sg, filename, eg); free (filename); } @@ -6007,15 +6376,18 @@ private: { default: gcc_unreachable (); - case exploded_node::STATUS_WORKLIST: + case exploded_node::status::worklist: pp_string (pp, "(W)"); break; - case exploded_node::STATUS_PROCESSED: + case exploded_node::status::processed: + break; + case exploded_node::status::special: + pp_string (pp, "(S)"); break; - case exploded_node::STATUS_MERGER: + case exploded_node::status::merger: pp_string (pp, "(M)"); break; - case exploded_node::STATUS_BULK_MERGED: + case exploded_node::status::bulk_merged: pp_string (pp, "(BM)"); break; } @@ -6085,7 +6457,7 @@ dump_analyzer_json (const supergraph &sg, const exploded_graph &eg) { auto_timevar tv (TV_ANALYZER_DUMP); - char *filename = concat (dump_base_name, ".analyzer.json.gz", NULL); + char *filename = concat (dump_base_name, ".analyzer.json.gz", nullptr); gzFile output = gzopen (filename, "w"); if (!output) { @@ -6094,7 +6466,7 @@ dump_analyzer_json (const supergraph &sg, return; } - auto toplev_obj = ::make_unique<json::object> (); + auto toplev_obj = std::make_unique<json::object> (); toplev_obj->set ("sgraph", sg.to_json ()); toplev_obj->set ("egraph", eg.to_json ()); @@ -6115,8 +6487,8 @@ dump_analyzer_json (const supergraph &sg, class plugin_analyzer_init_impl : public plugin_analyzer_init_iface { public: - plugin_analyzer_init_impl (auto_delete_vec <state_machine> *checkers, - known_function_manager *known_fn_mgr, + plugin_analyzer_init_impl (std::vector<std::unique_ptr<state_machine>> &checkers, + known_function_manager &known_fn_mgr, logger *logger) : m_checkers (checkers), m_known_fn_mgr (known_fn_mgr), @@ -6126,14 +6498,14 @@ public: void register_state_machine (std::unique_ptr<state_machine> sm) final override { LOG_SCOPE (m_logger); - m_checkers->safe_push (sm.release ()); + m_checkers.push_back (std::move (sm)); } void register_known_function (const char *name, std::unique_ptr<known_function> kf) final override { LOG_SCOPE (m_logger); - m_known_fn_mgr->add (name, std::move (kf)); + m_known_fn_mgr.add (name, std::move (kf)); } logger *get_logger () const final override @@ -6142,8 +6514,8 @@ public: } private: - auto_delete_vec <state_machine> *m_checkers; - known_function_manager *m_known_fn_mgr; + std::vector<std::unique_ptr<state_machine>> &m_checkers; + known_function_manager &m_known_fn_mgr; logger *m_logger; }; @@ -6172,7 +6544,7 @@ impl_run_checkers (logger *logger) engine eng (&sg, logger); - state_purge_map *purge_map = NULL; + state_purge_map *purge_map = nullptr; if (flag_analyzer_state_purge) purge_map = new state_purge_map (sg, eng.get_model_manager (), logger); @@ -6181,8 +6553,8 @@ impl_run_checkers (logger *logger) { /* Dump supergraph pre-analysis. */ auto_timevar tv (TV_ANALYZER_DUMP); - char *filename = concat (dump_base_name, ".supergraph.dot", NULL); - supergraph::dump_args_t args ((enum supergraph_dot_flags)0, NULL); + char *filename = concat (dump_base_name, ".supergraph.dot", nullptr); + supergraph::dump_args_t args ((enum supergraph_dot_flags)0, nullptr); sg.dump_dot (filename, args); free (filename); } @@ -6191,33 +6563,31 @@ impl_run_checkers (logger *logger) { auto_timevar tv (TV_ANALYZER_DUMP); state_purge_annotator a (purge_map); - char *filename = concat (dump_base_name, ".state-purge.dot", NULL); + char *filename = concat (dump_base_name, ".state-purge.dot", nullptr); supergraph::dump_args_t args ((enum supergraph_dot_flags)0, &a); sg.dump_dot (filename, args); free (filename); } - auto_delete_vec <state_machine> checkers; - make_checkers (checkers, logger); + auto checkers = make_checkers (logger); register_known_functions (*eng.get_known_function_manager (), *eng.get_model_manager ()); - plugin_analyzer_init_impl data (&checkers, - eng.get_known_function_manager (), + plugin_analyzer_init_impl data (checkers, + *eng.get_known_function_manager (), logger); invoke_plugin_callbacks (PLUGIN_ANALYZER_INIT, &data); if (logger) { - int i; - state_machine *sm; - FOR_EACH_VEC_ELT (checkers, i, sm) - logger->log ("checkers[%i]: %s", i, sm->get_name ()); + int i = 0; + for (auto &sm : checkers) + logger->log ("checkers[%i]: %s", ++i, sm->get_name ()); } /* Extrinsic state shared by nodes in the graph. */ - const extrinsic_state ext_state (checkers, &eng, logger); + const extrinsic_state ext_state (std::move (checkers), &eng, logger); const analysis_plan plan (sg, logger); @@ -6238,7 +6608,7 @@ impl_run_checkers (logger *logger) { auto_timevar tv (TV_ANALYZER_DUMP); char *filename - = concat (dump_base_name, ".eg.dot", NULL); + = concat (dump_base_name, ".eg.dot", nullptr); exploded_graph::dump_args_t args (eg); root_cluster c; eg.dump_dot (filename, &c, args); @@ -6259,7 +6629,7 @@ impl_run_checkers (logger *logger) { /* Dump post-analysis form of supergraph. */ auto_timevar tv (TV_ANALYZER_DUMP); - char *filename = concat (dump_base_name, ".supergraph-eg.dot", NULL); + char *filename = concat (dump_base_name, ".supergraph-eg.dot", nullptr); exploded_graph_annotator a (eg); supergraph::dump_args_t args ((enum supergraph_dot_flags)0, &a); sg.dump_dot (filename, args); @@ -6283,7 +6653,7 @@ impl_run_checkers (logger *logger) } /* Handle -fdump-analyzer and -fdump-analyzer-stderr. */ -static FILE *dump_fout = NULL; +static FILE *dump_fout = nullptr; /* Track if we're responsible for closing dump_fout. */ static bool owns_dump_fout = false; @@ -6300,7 +6670,7 @@ get_or_create_any_logfile () dump_fout = stderr; else if (flag_dump_analyzer) { - char *dump_filename = concat (dump_base_name, ".analyzer.txt", NULL); + char *dump_filename = concat (dump_base_name, ".analyzer.txt", nullptr); dump_fout = fopen (dump_filename, "w"); free (dump_filename); if (dump_fout) @@ -6320,7 +6690,7 @@ run_checkers () location_t saved_input_location = input_location; { - log_user the_logger (NULL); + log_user the_logger (nullptr); get_or_create_any_logfile (); if (dump_fout) the_logger.set_logger (new logger (dump_fout, 0, 0, @@ -6337,7 +6707,7 @@ run_checkers () { fclose (dump_fout); owns_dump_fout = false; - dump_fout = NULL; + dump_fout = nullptr; } /* Restore input_location. Subsequent passes may assume that input_location diff --git a/gcc/analyzer/exploded-graph.h b/gcc/analyzer/exploded-graph.h index 6148ed7..1d31097 100644 --- a/gcc/analyzer/exploded-graph.h +++ b/gcc/analyzer/exploded-graph.h @@ -49,17 +49,17 @@ class impl_region_model_context : public region_model_context path_context *path_ctxt, const gimple *stmt, - stmt_finder *stmt_finder = NULL, + stmt_finder *stmt_finder = nullptr, bool *out_could_have_done_work = nullptr); impl_region_model_context (program_state *state, const extrinsic_state &ext_state, uncertainty_t *uncertainty, - logger *logger = NULL); + logger *logger = nullptr); bool warn (std::unique_ptr<pending_diagnostic> d, - const stmt_finder *custom_finder = NULL) final override; + const stmt_finder *custom_finder = nullptr) final override; void add_note (std::unique_ptr<pending_note> pn) final override; void add_event (std::unique_ptr<checker_event> event) final override; void on_svalue_leak (const svalue *) override; @@ -112,6 +112,8 @@ class impl_region_model_context : public region_model_context const gimple *get_stmt () const override { return m_stmt; } const exploded_graph *get_eg () const override { return m_eg; } + const program_state *get_state () const override { return m_new_state; } + void maybe_did_work () override; bool checking_for_infinite_loop_p () const override { return false; } void on_unusable_in_infinite_loop () override {} @@ -206,20 +208,24 @@ class exploded_node : public dnode<eg_traits> This allows us to distinguish enodes that were merged during worklist-handling, and thus never had process_node called on them (in favor of processing the merged node). */ - enum status + enum class status { /* Node is in the worklist. */ - STATUS_WORKLIST, + worklist, /* Node has had exploded_graph::process_node called on it. */ - STATUS_PROCESSED, + processed, + + /* Node was excluded from worklist on creation. + e.g. for handling exception-unwinding. */ + special, /* Node was left unprocessed due to merger; it won't have had exploded_graph::process_node called on it. */ - STATUS_MERGER, + merger, /* Node was processed by maybe_process_run_of_before_supernode_enodes. */ - STATUS_BULK_MERGED + bulk_merged }; static const char * status_to_str (enum status s); @@ -282,7 +288,7 @@ class exploded_node : public dnode<eg_traits> on_stmt_flags replay_call_summaries (exploded_graph &eg, const supernode *snode, - const gcall *call_stmt, + const gcall &call_stmt, program_state *state, path_context *path_ctxt, const function &called_fn, @@ -290,11 +296,11 @@ class exploded_node : public dnode<eg_traits> region_model_context *ctxt); void replay_call_summary (exploded_graph &eg, const supernode *snode, - const gcall *call_stmt, + const gcall &call_stmt, program_state *state, path_context *path_ctxt, const function &called_fn, - call_summary *summary, + call_summary &summary, region_model_context *ctxt); bool on_edge (exploded_graph &eg, @@ -303,9 +309,18 @@ class exploded_node : public dnode<eg_traits> program_state *next_state, uncertainty_t *uncertainty); void on_longjmp (exploded_graph &eg, - const gcall *call, + const gcall &call, program_state *new_state, region_model_context *ctxt); + void on_throw (exploded_graph &eg, + const gcall &call, + program_state *new_state, + bool is_rethrow, + region_model_context *ctxt); + void on_resx (exploded_graph &eg, + const gresx &resx, + program_state *new_state, + region_model_context *ctxt); void detect_leaks (exploded_graph &eg); @@ -333,10 +348,10 @@ class exploded_node : public dnode<eg_traits> void dump_succs_and_preds (FILE *outf) const; enum status get_status () const { return m_status; } - void set_status (enum status status) + void set_status (enum status s) { - gcc_assert (m_status == STATUS_WORKLIST); - m_status = status; + gcc_assert (m_status == status::worklist); + m_status = s; } void add_diagnostic (const saved_diagnostic *sd) @@ -392,7 +407,7 @@ class exploded_edge : public dedge<eg_traits> //private: const superedge *const m_sedge; - /* NULL for most edges; will be non-NULL for special cases + /* nullptr for most edges; will be non-NULL for special cases such as an unwind from a longjmp to a setjmp, or when a signal is delivered to a signal-handler. */ std::unique_ptr<custom_edge_info> m_custom_info; @@ -424,7 +439,7 @@ private: class dynamic_call_info_t : public custom_edge_info { public: - dynamic_call_info_t (const gcall *dynamic_call, + dynamic_call_info_t (const gcall &dynamic_call, const bool is_returning_call = false) : m_dynamic_call (dynamic_call), m_is_returning_call (is_returning_call) @@ -445,7 +460,7 @@ public: void add_events_to_path (checker_path *emission_path, const exploded_edge &eedge) const final override; private: - const gcall *m_dynamic_call; + const gcall &m_dynamic_call; const bool m_is_returning_call; }; @@ -457,7 +472,7 @@ class rewind_info_t : public custom_edge_info { public: rewind_info_t (const setjmp_record &setjmp_record, - const gcall *longjmp_call) + const gcall &longjmp_call) : m_setjmp_record (setjmp_record), m_longjmp_call (longjmp_call) {} @@ -486,12 +501,12 @@ public: return origin_point; } - const gcall *get_setjmp_call () const + const gcall &get_setjmp_call () const { - return m_setjmp_record.m_setjmp_call; + return *m_setjmp_record.m_setjmp_call; } - const gcall *get_longjmp_call () const + const gcall &get_longjmp_call () const { return m_longjmp_call; } @@ -503,7 +518,7 @@ public: private: setjmp_record m_setjmp_record; - const gcall *m_longjmp_call; + const gcall &m_longjmp_call; }; /* Statistics about aspects of an exploded_graph. */ @@ -533,14 +548,14 @@ struct eg_hash_map_traits static inline hashval_t hash (const key_type &k) { - gcc_assert (k != NULL); + gcc_assert (k != nullptr); gcc_assert (k != reinterpret_cast<key_type> (1)); return k->hash (); } static inline bool equal_keys (const key_type &k1, const key_type &k2) { - gcc_assert (k1 != NULL); - gcc_assert (k2 != NULL); + gcc_assert (k1 != nullptr); + gcc_assert (k2 != nullptr); gcc_assert (k1 != reinterpret_cast<key_type> (1)); gcc_assert (k2 != reinterpret_cast<key_type> (1)); if (k1 && k2) @@ -562,7 +577,7 @@ struct eg_hash_map_traits template <typename T> static inline void mark_empty (T &entry) { - entry.m_key = NULL; + entry.m_key = nullptr; } template <typename T> static inline bool is_deleted (const T &entry) @@ -572,7 +587,7 @@ struct eg_hash_map_traits template <typename T> static inline bool is_empty (const T &entry) { - return entry.m_key == NULL; + return entry.m_key == nullptr; } static const bool empty_zero_p = false; }; @@ -603,14 +618,14 @@ struct eg_point_hash_map_traits static inline hashval_t hash (const key_type &k) { - gcc_assert (k != NULL); + gcc_assert (k != nullptr); gcc_assert (k != reinterpret_cast<key_type> (1)); return k->hash (); } static inline bool equal_keys (const key_type &k1, const key_type &k2) { - gcc_assert (k1 != NULL); - gcc_assert (k2 != NULL); + gcc_assert (k1 != nullptr); + gcc_assert (k2 != nullptr); gcc_assert (k1 != reinterpret_cast<key_type> (1)); gcc_assert (k2 != reinterpret_cast<key_type> (1)); if (k1 && k2) @@ -632,7 +647,7 @@ struct eg_point_hash_map_traits template <typename T> static inline void mark_empty (T &entry) { - entry.m_key = NULL; + entry.m_key = nullptr; } template <typename T> static inline bool is_deleted (const T &entry) @@ -642,7 +657,7 @@ struct eg_point_hash_map_traits template <typename T> static inline bool is_empty (const T &entry) { - return entry.m_key == NULL; + return entry.m_key == nullptr; } static const bool empty_zero_p = false; }; @@ -763,7 +778,7 @@ private: int get_scc_id (const exploded_node *enode) const { const supernode *snode = enode->get_supernode (); - if (snode == NULL) + if (snode == nullptr) return 0; return m_worklist.m_scc.get_scc_id (snode->m_index); } @@ -817,7 +832,7 @@ public: bool maybe_process_run_of_before_supernode_enodes (exploded_node *node); void process_node (exploded_node *node); - bool maybe_create_dynamic_call (const gcall *call, + bool maybe_create_dynamic_call (const gcall &call, tree fn_decl, exploded_node *node, program_state next_state, @@ -827,10 +842,11 @@ public: exploded_node *get_or_create_node (const program_point &point, const program_state &state, - exploded_node *enode_for_diag); + exploded_node *enode_for_diag, + bool add_to_worklist = true); exploded_edge *add_edge (exploded_node *src, exploded_node *dest, const superedge *sedge, bool could_do_work, - std::unique_ptr<custom_edge_info> custom = NULL); + std::unique_ptr<custom_edge_info> custom = nullptr); per_program_point_data * get_or_create_per_program_point_data (const program_point &); @@ -881,6 +897,10 @@ public: void on_escaped_function (tree fndecl); + void unwind_from_exception (exploded_node &enode, + const gimple *stmt, + region_model_context *ctxt); + /* In infinite-loop.cc */ void detect_infinite_loops (); @@ -959,7 +979,7 @@ public: void dump_to_pp (pretty_printer *pp, const extrinsic_state *ext_state) const; void dump (FILE *fp, const extrinsic_state *ext_state) const; - void dump (const extrinsic_state *ext_state = NULL) const; + void dump (const extrinsic_state *ext_state = nullptr) const; void dump_to_file (const char *filename, const extrinsic_state &ext_state) const; @@ -1024,7 +1044,7 @@ private: typedef shortest_paths<eg_traits, exploded_path> shortest_exploded_paths; -/* Abstract base class for use when passing NULL as the stmt for +/* Abstract base class for use when passing nullptr as the stmt for a possible warning, allowing the choice of stmt to be deferred until after we have an emission path (and know we're emitting a warning). */ diff --git a/gcc/analyzer/feasible-graph.cc b/gcc/analyzer/feasible-graph.cc index f8d28ac..25a97e7 100644 --- a/gcc/analyzer/feasible-graph.cc +++ b/gcc/analyzer/feasible-graph.cc @@ -18,21 +18,13 @@ 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/>. */ -#include "config.h" -#define INCLUDE_VECTOR -#include "system.h" -#include "coretypes.h" -#include "tree.h" -#include "pretty-print.h" -#include "gcc-rich-location.h" -#include "gimple-pretty-print.h" -#include "function.h" -#include "diagnostic-core.h" -#include "diagnostic-event-id.h" -#include "diagnostic-path.h" -#include "bitmap.h" -#include "ordered-hash-map.h" -#include "analyzer/analyzer.h" +#include "analyzer/common.h" + +#include "cfg.h" +#include "gimple-iterator.h" +#include "cgraph.h" +#include "digraph.h" + #include "analyzer/analyzer-logging.h" #include "analyzer/sm.h" #include "analyzer/pending-diagnostic.h" @@ -42,12 +34,6 @@ along with GCC; see the file COPYING3. If not see #include "analyzer/store.h" #include "analyzer/region-model.h" #include "analyzer/constraint-manager.h" -#include "cfg.h" -#include "basic-block.h" -#include "gimple.h" -#include "gimple-iterator.h" -#include "cgraph.h" -#include "digraph.h" #include "analyzer/supergraph.h" #include "analyzer/program-state.h" #include "analyzer/exploded-graph.h" diff --git a/gcc/analyzer/function-set.cc b/gcc/analyzer/function-set.cc index 817a512..530e0ad 100644 --- a/gcc/analyzer/function-set.cc +++ b/gcc/analyzer/function-set.cc @@ -95,7 +95,7 @@ namespace selftest { static void test_empty () { - function_set fs (NULL, 0); + function_set fs (nullptr, 0); fs.assert_sorted (); fs.assert_sane (); ASSERT_FALSE (fs.contains_name_p ("")); diff --git a/gcc/analyzer/infinite-loop.cc b/gcc/analyzer/infinite-loop.cc index d6f05d8..a53807c 100644 --- a/gcc/analyzer/infinite-loop.cc +++ b/gcc/analyzer/infinite-loop.cc @@ -18,28 +18,15 @@ 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/>. */ -#include "config.h" -#define INCLUDE_VECTOR -#include "system.h" -#include "coretypes.h" -#include "tree.h" -#include "fold-const.h" -#include "gcc-rich-location.h" -#include "alloc-pool.h" -#include "fibonacci_heap.h" -#include "shortest-paths.h" -#include "diagnostic-core.h" -#include "diagnostic-event-id.h" -#include "diagnostic-path.h" -#include "function.h" -#include "pretty-print.h" -#include "sbitmap.h" -#include "bitmap.h" -#include "tristate.h" -#include "ordered-hash-map.h" -#include "selftest.h" -#include "json.h" -#include "analyzer/analyzer.h" +#include "analyzer/common.h" + +#include "cfg.h" +#include "gimple-iterator.h" +#include "gimple-pretty-print.h" +#include "cgraph.h" +#include "digraph.h" +#include "diagnostics/sarif-sink.h" + #include "analyzer/analyzer-logging.h" #include "analyzer/call-string.h" #include "analyzer/program-point.h" @@ -49,20 +36,11 @@ along with GCC; see the file COPYING3. If not see #include "analyzer/sm.h" #include "analyzer/pending-diagnostic.h" #include "analyzer/diagnostic-manager.h" -#include "cfg.h" -#include "basic-block.h" -#include "gimple.h" -#include "gimple-iterator.h" -#include "gimple-pretty-print.h" -#include "cgraph.h" -#include "digraph.h" #include "analyzer/supergraph.h" #include "analyzer/program-state.h" #include "analyzer/exploded-graph.h" #include "analyzer/checker-path.h" #include "analyzer/feasible-graph.h" -#include "make-unique.h" -#include "diagnostic-format-sarif.h" /* A bundle of data characterizing a particular infinite loop identified within the exploded graph. */ @@ -108,9 +86,9 @@ struct infinite_loop std::unique_ptr<json::object> to_json () const { - auto loop_obj = ::make_unique<json::object> (); + auto loop_obj = std::make_unique<json::object> (); loop_obj->set_integer ("enode", m_enode.m_index); - auto edge_arr = ::make_unique<json::array> (); + auto edge_arr = std::make_unique<json::array> (); for (auto eedge : m_eedge_vec) edge_arr->append (eedge->to_json ()); loop_obj->set ("eedges", std::move (edge_arr)); @@ -237,7 +215,7 @@ public: checker_path *emission_path) final override { emission_path->add_event - (make_unique<warning_event> + (std::make_unique<warning_event> (event_loc_info (m_inf_loop->m_loc, enode->get_function ()->decl, enode->get_stack_depth ()), @@ -285,51 +263,55 @@ public: if (switch_cfg_sedge->implicitly_created_default_p ()) { emission_path->add_event - (make_unique<perpetual_start_cfg_edge_event> (*eedge, - loc_info_from)); + (std::make_unique<perpetual_start_cfg_edge_event> + (*eedge, + loc_info_from)); emission_path->add_event - (make_unique<end_cfg_edge_event> - (*eedge, - loc_info_to)); + (std::make_unique<end_cfg_edge_event> + (*eedge, + loc_info_to)); } } if (cfg_sedge->true_value_p ()) { emission_path->add_event - (make_unique<perpetual_start_cfg_edge_event> (*eedge, - loc_info_from)); + (std::make_unique<perpetual_start_cfg_edge_event> + (*eedge, + loc_info_from)); emission_path->add_event - (make_unique<end_cfg_edge_event> - (*eedge, - loc_info_to)); + (std::make_unique<end_cfg_edge_event> + (*eedge, + loc_info_to)); } else if (cfg_sedge->false_value_p ()) { emission_path->add_event - (make_unique<perpetual_start_cfg_edge_event> (*eedge, - loc_info_from)); + (std::make_unique<perpetual_start_cfg_edge_event> + (*eedge, + loc_info_from)); emission_path->add_event - (make_unique<end_cfg_edge_event> - (*eedge, - loc_info_to)); + (std::make_unique<end_cfg_edge_event> + (*eedge, + loc_info_to)); } else if (cfg_sedge->back_edge_p ()) { emission_path->add_event - (make_unique<looping_back_event> (*eedge, loc_info_from)); + (std::make_unique<looping_back_event> (*eedge, loc_info_from)); emission_path->add_event - (make_unique<end_cfg_edge_event> - (*eedge, - loc_info_to)); + (std::make_unique<end_cfg_edge_event> + (*eedge, + loc_info_to)); } } } - void maybe_add_sarif_properties (sarif_object &result_obj) + void + maybe_add_sarif_properties (diagnostics::sarif_object &result_obj) const final override { - sarif_property_bag &props = result_obj.get_or_create_properties (); + auto &props = result_obj.get_or_create_properties (); #define PROPERTY_PREFIX "gcc/analyzer/infinite_loop_diagnostic/" props.set (PROPERTY_PREFIX "inf_loop", m_inf_loop->to_json ()); #undef PROPERTY_PREFIX @@ -415,7 +397,7 @@ starts_infinite_loop_p (const exploded_node &enode, feasible_node *curr_fnode = nullptr; if (flag_dump_analyzer_infinite_loop) - fg = ::make_unique<feasible_graph> (); + fg = std::make_unique<feasible_graph> (); location_t first_loc = UNKNOWN_LOCATION; const exploded_node *iter = &enode; @@ -432,7 +414,7 @@ starts_infinite_loop_p (const exploded_node &enode, if (logger) logger->log ("iter: EN: %i", iter->m_index); /* Analysis bailed out before processing this node. */ - if (iter->get_status () == exploded_node::STATUS_WORKLIST) + if (iter->get_status () == exploded_node::status::worklist) { if (logger) logger->log ("rejecting: EN: %i is still in worklist", @@ -460,10 +442,10 @@ starts_infinite_loop_p (const exploded_node &enode, fg->dump_dot (filename, nullptr, dump_args); free (filename); } - return ::make_unique<infinite_loop> (enode, - first_loc, - std::move (eedges), - logger); + return std::make_unique<infinite_loop> (enode, + first_loc, + std::move (eedges), + logger); } else { @@ -593,7 +575,7 @@ exploded_graph::detect_infinite_loops () pending_location ploc (enode, snode, inf_loop->m_loc); auto d - = ::make_unique<infinite_loop_diagnostic> (std::move (inf_loop)); + = std::make_unique<infinite_loop_diagnostic> (std::move (inf_loop)); get_diagnostic_manager ().add_diagnostic (ploc, std::move (d)); } } diff --git a/gcc/analyzer/infinite-recursion.cc b/gcc/analyzer/infinite-recursion.cc index 42f87ed..960b487 100644 --- a/gcc/analyzer/infinite-recursion.cc +++ b/gcc/analyzer/infinite-recursion.cc @@ -18,28 +18,15 @@ 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/>. */ -#include "config.h" -#define INCLUDE_VECTOR -#include "system.h" -#include "coretypes.h" -#include "tree.h" -#include "fold-const.h" -#include "gcc-rich-location.h" -#include "alloc-pool.h" -#include "fibonacci_heap.h" -#include "shortest-paths.h" -#include "diagnostic-core.h" -#include "diagnostic-event-id.h" -#include "diagnostic-path.h" -#include "function.h" -#include "pretty-print.h" -#include "sbitmap.h" -#include "bitmap.h" -#include "tristate.h" -#include "ordered-hash-map.h" -#include "selftest.h" -#include "json.h" -#include "analyzer/analyzer.h" +#include "analyzer/common.h" + +#include "cfg.h" +#include "gimple-iterator.h" +#include "gimple-pretty-print.h" +#include "cgraph.h" +#include "digraph.h" +#include "diagnostics/sarif-sink.h" + #include "analyzer/analyzer-logging.h" #include "analyzer/call-string.h" #include "analyzer/program-point.h" @@ -49,20 +36,11 @@ along with GCC; see the file COPYING3. If not see #include "analyzer/sm.h" #include "analyzer/pending-diagnostic.h" #include "analyzer/diagnostic-manager.h" -#include "cfg.h" -#include "basic-block.h" -#include "gimple.h" -#include "gimple-iterator.h" -#include "gimple-pretty-print.h" -#include "cgraph.h" -#include "digraph.h" #include "analyzer/supergraph.h" #include "analyzer/program-state.h" #include "analyzer/exploded-graph.h" -#include "make-unique.h" #include "analyzer/checker-path.h" #include "analyzer/feasible-graph.h" -#include "diagnostic-format-sarif.h" /* A subclass of pending_diagnostic for complaining about suspected infinite recursion. */ @@ -77,7 +55,7 @@ public: : m_prev_entry_enode (prev_entry_enode), m_new_entry_enode (new_entry_enode), m_callee_fndecl (callee_fndecl), - m_prev_entry_event (NULL) + m_prev_entry_event (nullptr) {} const char *get_kind () const final override @@ -130,9 +108,10 @@ public: { public: recursive_function_entry_event (const program_point &dst_point, + const program_state &dst_state, const infinite_recursion_diagnostic &pd, bool topmost) - : function_entry_event (dst_point), + : function_entry_event (dst_point, dst_state), m_pd (pd), m_topmost (topmost) { @@ -168,16 +147,19 @@ public: const program_point &dst_point = dst_node->get_point (); if (eedge.m_dest == m_prev_entry_enode) { - gcc_assert (m_prev_entry_event == NULL); + gcc_assert (m_prev_entry_event == nullptr); std::unique_ptr<checker_event> prev_entry_event - = make_unique <recursive_function_entry_event> (dst_point, - *this, false); + = std::make_unique <recursive_function_entry_event> + (dst_point, + dst_node->get_state (), + *this, false); m_prev_entry_event = prev_entry_event.get (); emission_path->add_event (std::move (prev_entry_event)); } else if (eedge.m_dest == m_new_entry_enode) emission_path->add_event - (make_unique<recursive_function_entry_event> (dst_point, *this, true)); + (std::make_unique<recursive_function_entry_event> + (dst_point, dst_node->get_state (), *this, true)); else pending_diagnostic::add_function_entry_event (eedge, emission_path); } @@ -193,7 +175,7 @@ public: { gcc_assert (m_new_entry_enode); emission_path->add_event - (make_unique<warning_event> + (std::make_unique<warning_event> (event_loc_info (m_new_entry_enode->get_supernode ()->get_start_location (), m_callee_fndecl, @@ -241,10 +223,11 @@ public: return false; } - void maybe_add_sarif_properties (sarif_object &result_obj) + void + maybe_add_sarif_properties (diagnostics::sarif_object &result_obj) const final override { - sarif_property_bag &props = result_obj.get_or_create_properties (); + auto &props = result_obj.get_or_create_properties (); #define PROPERTY_PREFIX "gcc/analyzer/infinite_recursion_diagnostic/" props.set_integer (PROPERTY_PREFIX "prev_entry_enode", m_prev_entry_enode->m_index); @@ -309,7 +292,7 @@ private: bool m_found_conjured_svalues; }; - const svalue *sval = model.get_rvalue (expr, NULL); + const svalue *sval = model.get_rvalue (expr, nullptr); conjured_svalue_finder v; sval->accept (&v); return v.m_found_conjured_svalues; @@ -369,7 +352,7 @@ exploded_graph::find_previous_entry_to (function *top_of_stack_fun, } /* Not found. */ - return NULL; + return nullptr; } /* Given BASE_REG within ENCLOSING_FRAME (such as a function parameter), @@ -403,7 +386,7 @@ remap_enclosing_frame (const region *base_reg, const decl_region *decl_reg = (const decl_region *)base_reg; return equiv_prev_frame->get_region_for_local (mgr, decl_reg->get_decl (), - NULL); + nullptr); } } } @@ -445,7 +428,7 @@ sufficiently_different_region_binding_p (exploded_node *new_entry_enode, /* Get the value within the new frame. */ const svalue *new_sval - = new_model.get_store_value (base_reg, NULL); + = new_model.get_store_value (base_reg, nullptr); /* If any part of the value is UNKNOWN (e.g. due to hitting complexity limits) assume that it differs from the previous @@ -465,7 +448,7 @@ sufficiently_different_region_binding_p (exploded_node *new_entry_enode, to the recursion. */ const int old_stack_depth = prev_entry_enode->get_stack_depth (); if (enclosing_frame->get_stack_depth () < old_stack_depth) - prev_sval = prev_model.get_store_value (base_reg, NULL); + prev_sval = prev_model.get_store_value (base_reg, nullptr); else { /* Ignore bindings within frames below the new entry node. */ @@ -487,11 +470,11 @@ sufficiently_different_region_binding_p (exploded_node *new_entry_enode, equiv_prev_frame, new_model.get_manager ()); prev_sval - = prev_model.get_store_value (equiv_prev_base_reg, NULL); + = prev_model.get_store_value (equiv_prev_base_reg, nullptr); } } else - prev_sval = prev_model.get_store_value (base_reg, NULL); + prev_sval = prev_model.get_store_value (base_reg, nullptr); /* If the prev_sval contains UNKNOWN (e.g. due to hitting complexity limits) assume that it will differ from any new value. */ @@ -645,7 +628,7 @@ exploded_graph::detect_infinite_recursion (exploded_node *enode) nullptr); get_diagnostic_manager ().add_diagnostic (ploc, - make_unique<infinite_recursion_diagnostic> (prev_entry_enode, - enode, - fndecl)); + std::make_unique<infinite_recursion_diagnostic> (prev_entry_enode, + enode, + fndecl)); } diff --git a/gcc/analyzer/inlining-iterator.h b/gcc/analyzer/inlining-iterator.h index ac1463d..20d1d2b 100644 --- a/gcc/analyzer/inlining-iterator.h +++ b/gcc/analyzer/inlining-iterator.h @@ -46,7 +46,7 @@ class inlining_iterator public: inlining_iterator (location_t loc) : m_abstract_origin (LOCATION_BLOCK (loc)), - m_callsite (UNKNOWN_LOCATION), m_fndecl (NULL), + m_callsite (UNKNOWN_LOCATION), m_fndecl (NULL_TREE), m_next_abstract_origin (NULL) { prepare_iteration (); @@ -71,7 +71,7 @@ private: return; tree block = m_abstract_origin; m_callsite = BLOCK_SOURCE_LOCATION (block); - m_fndecl = NULL; + m_fndecl = NULL_TREE; block = BLOCK_SUPERCONTEXT (block); while (block && TREE_CODE (block) == BLOCK && BLOCK_ABSTRACT_ORIGIN (block)) diff --git a/gcc/analyzer/kf-analyzer.cc b/gcc/analyzer/kf-analyzer.cc index 54f85a4..13476de 100644 --- a/gcc/analyzer/kf-analyzer.cc +++ b/gcc/analyzer/kf-analyzer.cc @@ -18,24 +18,17 @@ 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/>. */ -#include "config.h" -#define INCLUDE_VECTOR -#include "system.h" -#include "coretypes.h" -#include "tree.h" -#include "function.h" -#include "basic-block.h" -#include "gimple.h" -#include "diagnostic-core.h" -#include "analyzer/analyzer.h" -#include "analyzer/analyzer-logging.h" +#include "analyzer/common.h" + #include "diagnostic.h" #include "tree-diagnostic.h" /* for default_tree_printer. */ +#include "pretty-print-markup.h" + +#include "analyzer/analyzer-logging.h" #include "analyzer/region-model.h" #include "analyzer/pending-diagnostic.h" #include "analyzer/call-details.h" -#include "make-unique.h" -#include "pretty-print-markup.h" +#include "analyzer/program-state.h" #if ENABLE_ANALYZER @@ -110,7 +103,7 @@ public: const region *base_reg = reg->get_base_region (); const svalue *capacity = model->get_capacity (base_reg); label_text desc = capacity->get_desc (true); - warning_at (cd.get_call_stmt ()->location, 0, + warning_at (cd.get_call_stmt ().location, 0, "capacity: %qs", desc.get ()); } }; @@ -268,6 +261,11 @@ class dump_path_diagnostic : public pending_diagnostic_subclass<dump_path_diagnostic> { public: + dump_path_diagnostic (const program_state &state) + : m_state (state) + { + } + int get_controlling_option () const final override { return 0; @@ -288,6 +286,15 @@ public: { return true; } + + const program_state * + get_final_state () const final override + { + return &m_state; + } + +private: + program_state m_state; }; /* Handle calls to "__analyzer_dump_path" by queuing a diagnostic at this @@ -305,7 +312,8 @@ public: region_model_context *ctxt = cd.get_ctxt (); if (!ctxt) return; - ctxt->warn (make_unique<dump_path_diagnostic> ()); + if (const program_state *state = ctxt->get_state ()) + ctxt->warn (std::make_unique<dump_path_diagnostic> (*state)); } }; @@ -382,22 +390,28 @@ public: void register_known_analyzer_functions (known_function_manager &kfm) { - kfm.add ("__analyzer_break", make_unique<kf_analyzer_break> ()); - kfm.add ("__analyzer_describe", make_unique<kf_analyzer_describe> ()); + kfm.add ("__analyzer_break", + std::make_unique<kf_analyzer_break> ()); + kfm.add ("__analyzer_describe", + std::make_unique<kf_analyzer_describe> ()); kfm.add ("__analyzer_dump_capacity", - make_unique<kf_analyzer_dump_capacity> ()); - kfm.add ("__analyzer_dump_escaped", make_unique<kf_analyzer_dump_escaped> ()); + std::make_unique<kf_analyzer_dump_capacity> ()); + kfm.add ("__analyzer_dump_escaped", + std::make_unique<kf_analyzer_dump_escaped> ()); kfm.add ("__analyzer_dump_exploded_nodes", - make_unique<kf_analyzer_dump_exploded_nodes> ()); + std::make_unique<kf_analyzer_dump_exploded_nodes> ()); kfm.add ("__analyzer_dump_named_constant", - make_unique<kf_analyzer_dump_named_constant> ()); - kfm.add ("__analyzer_dump_path", make_unique<kf_analyzer_dump_path> ()); + std::make_unique<kf_analyzer_dump_named_constant> ()); + kfm.add ("__analyzer_dump_path", + std::make_unique<kf_analyzer_dump_path> ()); kfm.add ("__analyzer_dump_region_model", - make_unique<kf_analyzer_dump_region_model> ()); - kfm.add ("__analyzer_eval", make_unique<kf_analyzer_eval> ()); + std::make_unique<kf_analyzer_dump_region_model> ()); + kfm.add ("__analyzer_eval", + std::make_unique<kf_analyzer_eval> ()); kfm.add ("__analyzer_get_unknown_ptr", - make_unique<kf_analyzer_get_unknown_ptr> ()); - kfm.add ("__analyzer_get_strlen", make_kf_strlen ()); + std::make_unique<kf_analyzer_get_unknown_ptr> ()); + kfm.add ("__analyzer_get_strlen", + make_kf_strlen ()); } } // namespace ana diff --git a/gcc/analyzer/kf-lang-cp.cc b/gcc/analyzer/kf-lang-cp.cc index b2b0c9b..01a98b0 100644 --- a/gcc/analyzer/kf-lang-cp.cc +++ b/gcc/analyzer/kf-lang-cp.cc @@ -18,20 +18,13 @@ 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/>. */ -#include "config.h" -#define INCLUDE_VECTOR -#include "system.h" -#include "coretypes.h" -#include "tree.h" -#include "function.h" -#include "basic-block.h" -#include "gimple.h" -#include "analyzer/analyzer.h" -#include "analyzer/analyzer-logging.h" +#include "analyzer/common.h" + #include "diagnostic.h" + +#include "analyzer/analyzer-logging.h" #include "analyzer/region-model.h" #include "analyzer/call-details.h" -#include "make-unique.h" #if ENABLE_ANALYZER @@ -43,10 +36,9 @@ along with GCC; see the file COPYING3. If not see See https://en.cppreference.com/w/cpp/memory/new/operator_new. */ -bool is_placement_new_p (const gcall *call) +bool is_placement_new_p (const gcall &call) { - gcc_assert (call); - tree fndecl = gimple_call_fndecl (call); + tree fndecl = gimple_call_fndecl (&call); if (!fndecl || TREE_CODE (TREE_TYPE (fndecl)) == METHOD_TYPE) /* Give up on overloaded operator new. */ @@ -91,7 +83,7 @@ public: region_model_manager *mgr = cd.get_manager (); const svalue *size_sval = cd.get_arg_svalue (0); region_model_context *ctxt = cd.get_ctxt (); - const gcall *call = cd.get_call_stmt (); + const gcall &call = cd.get_call_stmt (); /* If the call was actually a placement new, check that accessing the buffer lhs is placed into does not result in out-of-bounds. */ @@ -169,10 +161,165 @@ public: /* If the ptr points to an underlying heap region, delete it, poisoning pointers. */ model->unbind_region_and_descendents (freed_reg, - POISON_KIND_DELETED); + poison_kind::deleted); + } + } + +}; + +class kf_cxa_allocate_exception : public known_function +{ +public: + bool matches_call_types_p (const call_details &cd) const final override + { + return cd.num_args () == 1 && cd.arg_is_size_p (0); + } + + void impl_call_pre (const call_details &cd) const final override + { + region_model *model = cd.get_model (); + region_model_manager *mgr = cd.get_manager (); + const svalue *size_sval = cd.get_arg_svalue (0); + region_model_context *ctxt = cd.get_ctxt (); + + /* Create a heap allocated region. */ + const region *new_reg + = model->get_or_create_region_for_heap_alloc (size_sval, ctxt); + if (cd.get_lhs_type ()) + { + const svalue *ptr_sval + = mgr->get_ptr_svalue (cd.get_lhs_type (), new_reg); + cd.maybe_set_lhs (ptr_sval); + } + } +}; + +class kf_cxa_begin_catch : public known_function +{ +public: + bool matches_call_types_p (const call_details &cd) const final override + { + return (cd.num_args () == 1 + && POINTER_TYPE_P (cd.get_arg_type (0))); + } + + void impl_call_pre (const call_details &cd) const final override + { + region_model *model = cd.get_model (); + + auto node = model->pop_thrown_exception (); + model->push_caught_exception (node); + cd.maybe_set_lhs (node.m_exception_sval); + } +}; + +class kf_cxa_end_catch : public known_function +{ +public: + bool matches_call_types_p (const call_details &cd) const final override + { + return cd.num_args () == 0; + } + + void impl_call_pre (const call_details &cd) const final override + { + region_model *model = cd.get_model (); + model->pop_caught_exception (); + } +}; + +/* A subclass of pending_diagnostic for complaining about an exception + of an unexpected type being thrown (due to a call to + __cxa_call_unexpected). + See https://en.cppreference.com/w/cpp/language/except_spec */ + +class throw_of_unexpected_type +: public pending_diagnostic_subclass<throw_of_unexpected_type> +{ +public: + throw_of_unexpected_type (tree exception_type, + tree thrown_from_fndecl) + : m_exception_type (exception_type), + m_thrown_from_fndecl (thrown_from_fndecl) + { + gcc_assert (m_exception_type); + gcc_assert (m_thrown_from_fndecl); + } + + const char *get_kind () const final override + { + return "throw_of_unexpected_type"; + } + + bool operator== (const throw_of_unexpected_type &other) const + { + return (m_exception_type == other.m_exception_type + && m_thrown_from_fndecl == other.m_thrown_from_fndecl); + } + + int get_controlling_option () const final override + { + return OPT_Wanalyzer_throw_of_unexpected_type; + } + + bool emit (diagnostic_emission_context &ctxt) final override + { + auto_diagnostic_group d; + + bool warned + = ctxt.warn ("throwing exception of unexpected type %qT from %qE", + m_exception_type, m_thrown_from_fndecl); + if (warned) + { + inform (DECL_SOURCE_LOCATION (m_thrown_from_fndecl), + "%qE declared here", m_thrown_from_fndecl); + // TODO: show specified types? } + return warned; + } + + bool + describe_final_event (pretty_printer &pp, + const evdesc::final_event &) final override + { + pp_printf (&pp, + "exception of unexpected type %qT thrown from %qE", + m_exception_type, m_thrown_from_fndecl); + return true; + } + +private: + tree m_exception_type; + tree m_thrown_from_fndecl; +}; + +/* See https://en.cppreference.com/w/cpp/language/except_spec */ + +class kf_cxa_call_unexpected : public known_function +{ +public: + bool matches_call_types_p (const call_details &cd) const final override + { + return (cd.num_args () == 1 + && POINTER_TYPE_P (cd.get_arg_type (0))); } + void impl_call_pre (const call_details &cd) const final override + { + if (region_model_context *ctxt = cd.get_ctxt ()) + { + region_model *model = cd.get_model (); + tree thrown_from_fndecl = model->get_current_function ()->decl; + /* We must have a thrown exception. */ + auto eh_node = model->get_current_thrown_exception (); + gcc_assert (eh_node); + tree exception_type = eh_node->maybe_get_type (); + ctxt->warn + (std::make_unique<throw_of_unexpected_type> (exception_type, + thrown_from_fndecl)); + ctxt->terminate_path (); + } + } }; /* Populate KFM with instances of known functions relating to C++. */ @@ -180,10 +327,21 @@ public: void register_known_functions_lang_cp (known_function_manager &kfm) { - kfm.add ("operator new", make_unique<kf_operator_new> ()); - kfm.add ("operator new []", make_unique<kf_operator_new> ()); - kfm.add ("operator delete", make_unique<kf_operator_delete> ()); - kfm.add ("operator delete []", make_unique<kf_operator_delete> ()); + kfm.add ("operator new", std::make_unique<kf_operator_new> ()); + kfm.add ("operator new []", std::make_unique<kf_operator_new> ()); + kfm.add ("operator delete", std::make_unique<kf_operator_delete> ()); + kfm.add ("operator delete []", std::make_unique<kf_operator_delete> ()); + + /* Functions mentioned in "Itanium C++ ABI: Exception Handling"'s + "Level II: C++ ABI" + https://itanium-cxx-abi.github.io/cxx-abi/abi-eh.html#cxx-abi */ + kfm.add ("__cxa_allocate_exception", + std::make_unique<kf_cxa_allocate_exception> ()); + // We treat __cxa_throw and __cxa_rethrow as special cases + kfm.add ("__cxa_begin_catch", std::make_unique<kf_cxa_begin_catch> ()); + kfm.add ("__cxa_end_catch", std::make_unique<kf_cxa_end_catch> ()); + kfm.add ("__cxa_call_unexpected", + std::make_unique<kf_cxa_call_unexpected> ()); } } // namespace ana diff --git a/gcc/analyzer/kf.cc b/gcc/analyzer/kf.cc index dceedd4..2a7c357 100644 --- a/gcc/analyzer/kf.cc +++ b/gcc/analyzer/kf.cc @@ -18,23 +18,14 @@ 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/>. */ -#include "config.h" -#define INCLUDE_VECTOR -#include "system.h" -#include "coretypes.h" -#include "tree.h" -#include "function.h" -#include "basic-block.h" -#include "gimple.h" -#include "diagnostic-core.h" -#include "diagnostic-metadata.h" -#include "analyzer/analyzer.h" -#include "analyzer/analyzer-logging.h" +#include "analyzer/common.h" + #include "diagnostic.h" + +#include "analyzer/analyzer-logging.h" #include "analyzer/region-model.h" #include "analyzer/call-details.h" #include "analyzer/call-info.h" -#include "make-unique.h" #if ENABLE_ANALYZER @@ -50,7 +41,6 @@ public: : m_call_stmt (cd.get_call_stmt ()), m_callee_fndecl (cd.get_fndecl_for_call ()) { - gcc_assert (m_call_stmt); gcc_assert (m_callee_fndecl); } @@ -61,7 +51,7 @@ public: bool operator== (const undefined_function_behavior &other) const { - return (m_call_stmt == other.m_call_stmt + return (&m_call_stmt == &other.m_call_stmt && m_callee_fndecl == other.m_callee_fndecl); } @@ -70,7 +60,7 @@ public: tree get_callee_fndecl () const { return m_callee_fndecl; } private: - const gimple *m_call_stmt; + const gimple &m_call_stmt; tree m_callee_fndecl; }; @@ -596,7 +586,7 @@ kf_free::impl_call_post (const call_details &cd) const /* If the ptr points to an underlying heap region, delete it, poisoning pointers. */ region_model *model = cd.get_model (); - model->unbind_region_and_descendents (freed_reg, POISON_KIND_FREED); + model->unbind_region_and_descendents (freed_reg, poison_kind::freed); model->unset_dynamic_extents (freed_reg); } } @@ -793,7 +783,7 @@ public: /* SEI CERT C Coding Standard: "POS34-C. Do not call putenv() with a pointer to an automatic variable as the argument". */ - diagnostic_metadata::precanned_rule + diagnostics::metadata::precanned_rule rule ("POS34-C", "https://wiki.sei.cmu.edu/confluence/x/6NYxBQ"); ctxt.add_rule (rule); @@ -881,7 +871,7 @@ public: break; case MEMSPACE_STACK: if (ctxt) - ctxt->warn (make_unique<putenv_of_auto_var> (fndecl, reg)); + ctxt->warn (std::make_unique<putenv_of_auto_var> (fndecl, reg)); break; } cd.set_any_lhs_with_defaults (); @@ -1063,11 +1053,11 @@ kf_realloc::impl_call_post (const call_details &cd) const const svalue *copied_size_sval = get_copied_size (model, old_size_sval, new_size_sval); const region *copied_old_reg - = mgr->get_sized_region (freed_reg, NULL, copied_size_sval); + = mgr->get_sized_region (freed_reg, nullptr, copied_size_sval); const svalue *buffer_content_sval = model->get_store_value (copied_old_reg, cd.get_ctxt ()); const region *copied_new_reg - = mgr->get_sized_region (new_reg, NULL, copied_size_sval); + = mgr->get_sized_region (new_reg, nullptr, copied_size_sval); model->set_value (copied_new_reg, buffer_content_sval, cd.get_ctxt ()); } @@ -1084,7 +1074,7 @@ kf_realloc::impl_call_post (const call_details &cd) const /* If the ptr points to an underlying heap region, delete it, poisoning pointers. */ - model->unbind_region_and_descendents (freed_reg, POISON_KIND_FREED); + model->unbind_region_and_descendents (freed_reg, poison_kind::freed); model->unset_dynamic_extents (freed_reg); } @@ -1129,9 +1119,9 @@ kf_realloc::impl_call_post (const call_details &cd) const if (cd.get_ctxt ()) { - cd.get_ctxt ()->bifurcate (make_unique<failure> (cd)); - cd.get_ctxt ()->bifurcate (make_unique<success_no_move> (cd)); - cd.get_ctxt ()->bifurcate (make_unique<success_with_move> (cd)); + cd.get_ctxt ()->bifurcate (std::make_unique<failure> (cd)); + cd.get_ctxt ()->bifurcate (std::make_unique<success_no_move> (cd)); + cd.get_ctxt ()->bifurcate (std::make_unique<success_with_move> (cd)); cd.get_ctxt ()->terminate_path (); } } @@ -1200,7 +1190,7 @@ kf_strchr::impl_call_post (const call_details &cd) const using the str_reg as the id of the conjured_svalue. */ const svalue *offset = mgr->get_or_create_conjured_svalue (size_type_node, - cd.get_call_stmt (), + &cd.get_call_stmt (), str_reg, conjured_purge (model, ctxt)); @@ -1220,8 +1210,8 @@ kf_strchr::impl_call_post (const call_details &cd) const /* Body of kf_strchr::impl_call_post. */ if (cd.get_ctxt ()) { - cd.get_ctxt ()->bifurcate (make_unique<strchr_call_info> (cd, false)); - cd.get_ctxt ()->bifurcate (make_unique<strchr_call_info> (cd, true)); + cd.get_ctxt ()->bifurcate (std::make_unique<strchr_call_info> (cd, false)); + cd.get_ctxt ()->bifurcate (std::make_unique<strchr_call_info> (cd, true)); cd.get_ctxt ()->terminate_path (); } } @@ -1286,6 +1276,27 @@ public: /* Currently a no-op. */ }; +/* Handler for "__builtin_eh_pointer". */ + +class kf_eh_pointer : public builtin_known_function +{ +public: + bool matches_call_types_p (const call_details &) const final override + { + return true; + } + + enum built_in_function builtin_code () const final override + { + return BUILT_IN_EH_POINTER; + } + + void impl_call_pre (const call_details &cd) const final override + { + cd.set_any_lhs_with_defaults (); + } +}; + /* Handler for "strcat" and "__builtin_strcat_chk". */ class kf_strcat : public builtin_known_function @@ -1476,7 +1487,7 @@ public: std::unique_ptr<known_function> make_kf_strlen () { - return make_unique<kf_strlen> (); + return std::make_unique<kf_strlen> (); } /* Handler for "strncpy" and "__builtin_strncpy". @@ -1625,7 +1636,7 @@ kf_strncpy::impl_call_post (const call_details &cd) const } private: /* (strlen + 1) of the source string if it has a terminator, - or NULL for the case where UB would happen before + or nullptr for the case where UB would happen before finding any terminator. */ const svalue *m_num_bytes_with_terminator_sval; @@ -1650,11 +1661,13 @@ kf_strncpy::impl_call_post (const call_details &cd) const nullptr, nullptr); cd.get_ctxt ()->bifurcate - (make_unique<strncpy_call_info> (cd, num_bytes_with_terminator_sval, - false)); + (std::make_unique<strncpy_call_info> + (cd, num_bytes_with_terminator_sval, + false)); cd.get_ctxt ()->bifurcate - (make_unique<strncpy_call_info> (cd, num_bytes_with_terminator_sval, - true)); + (std::make_unique<strncpy_call_info> + (cd, num_bytes_with_terminator_sval, + true)); cd.get_ctxt ()->terminate_path (); } }; @@ -1678,8 +1691,8 @@ public: region_model_manager *mgr = cd.get_manager (); /* Ideally we'd get the size here, and simulate copying the bytes. */ const region *new_reg - = model->get_or_create_region_for_heap_alloc (NULL, cd.get_ctxt ()); - model->mark_region_as_unknown (new_reg, NULL); + = model->get_or_create_region_for_heap_alloc (nullptr, cd.get_ctxt ()); + model->mark_region_as_unknown (new_reg, nullptr); if (cd.get_lhs_type ()) { const svalue *ptr_sval @@ -1757,7 +1770,7 @@ kf_strstr::impl_call_post (const call_details &cd) const using the str_reg as the id of the conjured_svalue. */ const svalue *offset = mgr->get_or_create_conjured_svalue (size_type_node, - cd.get_call_stmt (), + &cd.get_call_stmt (), str_reg, conjured_purge (model, ctxt)); @@ -1777,8 +1790,8 @@ kf_strstr::impl_call_post (const call_details &cd) const /* Body of kf_strstr::impl_call_post. */ if (cd.get_ctxt ()) { - cd.get_ctxt ()->bifurcate (make_unique<strstr_call_info> (cd, false)); - cd.get_ctxt ()->bifurcate (make_unique<strstr_call_info> (cd, true)); + cd.get_ctxt ()->bifurcate (std::make_unique<strstr_call_info> (cd, false)); + cd.get_ctxt ()->bifurcate (std::make_unique<strstr_call_info> (cd, true)); cd.get_ctxt ()->terminate_path (); } } @@ -1924,7 +1937,7 @@ public: if (cd.get_arg_svalue (0)->all_zeroes_p ()) { if (ctxt) - ctxt->warn (::make_unique<undefined_behavior> (cd)); + ctxt->warn (::std::make_unique<undefined_behavior> (cd)); } /* Assume that "str" was actually non-null; terminate @@ -1958,14 +1971,14 @@ public: using the str_reg as the id of the conjured_svalue. */ const svalue *start_offset = mgr->get_or_create_conjured_svalue (size_type_node, - cd.get_call_stmt (), + &cd.get_call_stmt (), str_reg, conjured_purge (model, ctxt), 0); const svalue *nul_offset = mgr->get_or_create_conjured_svalue (size_type_node, - cd.get_call_stmt (), + &cd.get_call_stmt (), str_reg, conjured_purge (model, ctxt), @@ -2042,13 +2055,13 @@ public: Typically the str is either null or non-null at a particular site, so hopefully this will generally just lead to two out-edges. */ cd.get_ctxt ()->bifurcate - (make_unique<strtok_call_info> (cd, m_private_reg, false, false)); + (std::make_unique<strtok_call_info> (cd, m_private_reg, false, false)); cd.get_ctxt ()->bifurcate - (make_unique<strtok_call_info> (cd, m_private_reg, false, true)); + (std::make_unique<strtok_call_info> (cd, m_private_reg, false, true)); cd.get_ctxt ()->bifurcate - (make_unique<strtok_call_info> (cd, m_private_reg, true, false)); + (std::make_unique<strtok_call_info> (cd, m_private_reg, true, false)); cd.get_ctxt ()->bifurcate - (make_unique<strtok_call_info> (cd, m_private_reg, true, true)); + (std::make_unique<strtok_call_info> (cd, m_private_reg, true, true)); cd.get_ctxt ()->terminate_path (); } } @@ -2074,127 +2087,127 @@ region_model::impl_deallocation_call (const call_details &cd) static void register_atomic_builtins (known_function_manager &kfm) { - kfm.add (BUILT_IN_ATOMIC_EXCHANGE, make_unique<kf_atomic_exchange> ()); - kfm.add (BUILT_IN_ATOMIC_EXCHANGE_N, make_unique<kf_atomic_exchange_n> ()); - kfm.add (BUILT_IN_ATOMIC_EXCHANGE_1, make_unique<kf_atomic_exchange_n> ()); - kfm.add (BUILT_IN_ATOMIC_EXCHANGE_2, make_unique<kf_atomic_exchange_n> ()); - kfm.add (BUILT_IN_ATOMIC_EXCHANGE_4, make_unique<kf_atomic_exchange_n> ()); - kfm.add (BUILT_IN_ATOMIC_EXCHANGE_8, make_unique<kf_atomic_exchange_n> ()); - kfm.add (BUILT_IN_ATOMIC_EXCHANGE_16, make_unique<kf_atomic_exchange_n> ()); - kfm.add (BUILT_IN_ATOMIC_LOAD, make_unique<kf_atomic_load> ()); - kfm.add (BUILT_IN_ATOMIC_LOAD_N, make_unique<kf_atomic_load_n> ()); - kfm.add (BUILT_IN_ATOMIC_LOAD_1, make_unique<kf_atomic_load_n> ()); - kfm.add (BUILT_IN_ATOMIC_LOAD_2, make_unique<kf_atomic_load_n> ()); - kfm.add (BUILT_IN_ATOMIC_LOAD_4, make_unique<kf_atomic_load_n> ()); - kfm.add (BUILT_IN_ATOMIC_LOAD_8, make_unique<kf_atomic_load_n> ()); - kfm.add (BUILT_IN_ATOMIC_LOAD_16, make_unique<kf_atomic_load_n> ()); - kfm.add (BUILT_IN_ATOMIC_STORE, make_unique<kf_atomic_store> ()); - kfm.add (BUILT_IN_ATOMIC_STORE_N, make_unique<kf_atomic_store_n> ()); - kfm.add (BUILT_IN_ATOMIC_STORE_1, make_unique<kf_atomic_store_n> ()); - kfm.add (BUILT_IN_ATOMIC_STORE_2, make_unique<kf_atomic_store_n> ()); - kfm.add (BUILT_IN_ATOMIC_STORE_4, make_unique<kf_atomic_store_n> ()); - kfm.add (BUILT_IN_ATOMIC_STORE_8, make_unique<kf_atomic_store_n> ()); - kfm.add (BUILT_IN_ATOMIC_STORE_16, make_unique<kf_atomic_store_n> ()); + kfm.add (BUILT_IN_ATOMIC_EXCHANGE, std::make_unique<kf_atomic_exchange> ()); + kfm.add (BUILT_IN_ATOMIC_EXCHANGE_N, std::make_unique<kf_atomic_exchange_n> ()); + kfm.add (BUILT_IN_ATOMIC_EXCHANGE_1, std::make_unique<kf_atomic_exchange_n> ()); + kfm.add (BUILT_IN_ATOMIC_EXCHANGE_2, std::make_unique<kf_atomic_exchange_n> ()); + kfm.add (BUILT_IN_ATOMIC_EXCHANGE_4, std::make_unique<kf_atomic_exchange_n> ()); + kfm.add (BUILT_IN_ATOMIC_EXCHANGE_8, std::make_unique<kf_atomic_exchange_n> ()); + kfm.add (BUILT_IN_ATOMIC_EXCHANGE_16, std::make_unique<kf_atomic_exchange_n> ()); + kfm.add (BUILT_IN_ATOMIC_LOAD, std::make_unique<kf_atomic_load> ()); + kfm.add (BUILT_IN_ATOMIC_LOAD_N, std::make_unique<kf_atomic_load_n> ()); + kfm.add (BUILT_IN_ATOMIC_LOAD_1, std::make_unique<kf_atomic_load_n> ()); + kfm.add (BUILT_IN_ATOMIC_LOAD_2, std::make_unique<kf_atomic_load_n> ()); + kfm.add (BUILT_IN_ATOMIC_LOAD_4, std::make_unique<kf_atomic_load_n> ()); + kfm.add (BUILT_IN_ATOMIC_LOAD_8, std::make_unique<kf_atomic_load_n> ()); + kfm.add (BUILT_IN_ATOMIC_LOAD_16, std::make_unique<kf_atomic_load_n> ()); + kfm.add (BUILT_IN_ATOMIC_STORE, std::make_unique<kf_atomic_store> ()); + kfm.add (BUILT_IN_ATOMIC_STORE_N, std::make_unique<kf_atomic_store_n> ()); + kfm.add (BUILT_IN_ATOMIC_STORE_1, std::make_unique<kf_atomic_store_n> ()); + kfm.add (BUILT_IN_ATOMIC_STORE_2, std::make_unique<kf_atomic_store_n> ()); + kfm.add (BUILT_IN_ATOMIC_STORE_4, std::make_unique<kf_atomic_store_n> ()); + kfm.add (BUILT_IN_ATOMIC_STORE_8, std::make_unique<kf_atomic_store_n> ()); + kfm.add (BUILT_IN_ATOMIC_STORE_16, std::make_unique<kf_atomic_store_n> ()); kfm.add (BUILT_IN_ATOMIC_ADD_FETCH_1, - make_unique<kf_atomic_op_fetch> (PLUS_EXPR)); + std::make_unique<kf_atomic_op_fetch> (PLUS_EXPR)); kfm.add (BUILT_IN_ATOMIC_ADD_FETCH_2, - make_unique<kf_atomic_op_fetch> (PLUS_EXPR)); + std::make_unique<kf_atomic_op_fetch> (PLUS_EXPR)); kfm.add (BUILT_IN_ATOMIC_ADD_FETCH_4, - make_unique<kf_atomic_op_fetch> (PLUS_EXPR)); + std::make_unique<kf_atomic_op_fetch> (PLUS_EXPR)); kfm.add (BUILT_IN_ATOMIC_ADD_FETCH_8, - make_unique<kf_atomic_op_fetch> (PLUS_EXPR)); + std::make_unique<kf_atomic_op_fetch> (PLUS_EXPR)); kfm.add (BUILT_IN_ATOMIC_ADD_FETCH_16, - make_unique<kf_atomic_op_fetch> (PLUS_EXPR)); + std::make_unique<kf_atomic_op_fetch> (PLUS_EXPR)); kfm.add (BUILT_IN_ATOMIC_SUB_FETCH_1, - make_unique<kf_atomic_op_fetch> (MINUS_EXPR)); + std::make_unique<kf_atomic_op_fetch> (MINUS_EXPR)); kfm.add (BUILT_IN_ATOMIC_SUB_FETCH_2, - make_unique<kf_atomic_op_fetch> (MINUS_EXPR)); + std::make_unique<kf_atomic_op_fetch> (MINUS_EXPR)); kfm.add (BUILT_IN_ATOMIC_SUB_FETCH_4, - make_unique<kf_atomic_op_fetch> (MINUS_EXPR)); + std::make_unique<kf_atomic_op_fetch> (MINUS_EXPR)); kfm.add (BUILT_IN_ATOMIC_SUB_FETCH_8, - make_unique<kf_atomic_op_fetch> (MINUS_EXPR)); + std::make_unique<kf_atomic_op_fetch> (MINUS_EXPR)); kfm.add (BUILT_IN_ATOMIC_SUB_FETCH_16, - make_unique<kf_atomic_op_fetch> (MINUS_EXPR)); + std::make_unique<kf_atomic_op_fetch> (MINUS_EXPR)); kfm.add (BUILT_IN_ATOMIC_AND_FETCH_1, - make_unique<kf_atomic_op_fetch> (BIT_AND_EXPR)); + std::make_unique<kf_atomic_op_fetch> (BIT_AND_EXPR)); kfm.add (BUILT_IN_ATOMIC_AND_FETCH_2, - make_unique<kf_atomic_op_fetch> (BIT_AND_EXPR)); + std::make_unique<kf_atomic_op_fetch> (BIT_AND_EXPR)); kfm.add (BUILT_IN_ATOMIC_AND_FETCH_4, - make_unique<kf_atomic_op_fetch> (BIT_AND_EXPR)); + std::make_unique<kf_atomic_op_fetch> (BIT_AND_EXPR)); kfm.add (BUILT_IN_ATOMIC_AND_FETCH_8, - make_unique<kf_atomic_op_fetch> (BIT_AND_EXPR)); + std::make_unique<kf_atomic_op_fetch> (BIT_AND_EXPR)); kfm.add (BUILT_IN_ATOMIC_AND_FETCH_16, - make_unique<kf_atomic_op_fetch> (BIT_AND_EXPR)); + std::make_unique<kf_atomic_op_fetch> (BIT_AND_EXPR)); kfm.add (BUILT_IN_ATOMIC_XOR_FETCH_1, - make_unique<kf_atomic_op_fetch> (BIT_XOR_EXPR)); + std::make_unique<kf_atomic_op_fetch> (BIT_XOR_EXPR)); kfm.add (BUILT_IN_ATOMIC_XOR_FETCH_2, - make_unique<kf_atomic_op_fetch> (BIT_XOR_EXPR)); + std::make_unique<kf_atomic_op_fetch> (BIT_XOR_EXPR)); kfm.add (BUILT_IN_ATOMIC_XOR_FETCH_4, - make_unique<kf_atomic_op_fetch> (BIT_XOR_EXPR)); + std::make_unique<kf_atomic_op_fetch> (BIT_XOR_EXPR)); kfm.add (BUILT_IN_ATOMIC_XOR_FETCH_8, - make_unique<kf_atomic_op_fetch> (BIT_XOR_EXPR)); + std::make_unique<kf_atomic_op_fetch> (BIT_XOR_EXPR)); kfm.add (BUILT_IN_ATOMIC_XOR_FETCH_16, - make_unique<kf_atomic_op_fetch> (BIT_XOR_EXPR)); + std::make_unique<kf_atomic_op_fetch> (BIT_XOR_EXPR)); kfm.add (BUILT_IN_ATOMIC_OR_FETCH_1, - make_unique<kf_atomic_op_fetch> (BIT_IOR_EXPR)); + std::make_unique<kf_atomic_op_fetch> (BIT_IOR_EXPR)); kfm.add (BUILT_IN_ATOMIC_OR_FETCH_2, - make_unique<kf_atomic_op_fetch> (BIT_IOR_EXPR)); + std::make_unique<kf_atomic_op_fetch> (BIT_IOR_EXPR)); kfm.add (BUILT_IN_ATOMIC_OR_FETCH_4, - make_unique<kf_atomic_op_fetch> (BIT_IOR_EXPR)); + std::make_unique<kf_atomic_op_fetch> (BIT_IOR_EXPR)); kfm.add (BUILT_IN_ATOMIC_OR_FETCH_8, - make_unique<kf_atomic_op_fetch> (BIT_IOR_EXPR)); + std::make_unique<kf_atomic_op_fetch> (BIT_IOR_EXPR)); kfm.add (BUILT_IN_ATOMIC_OR_FETCH_16, - make_unique<kf_atomic_op_fetch> (BIT_IOR_EXPR)); + std::make_unique<kf_atomic_op_fetch> (BIT_IOR_EXPR)); kfm.add (BUILT_IN_ATOMIC_FETCH_ADD_1, - make_unique<kf_atomic_fetch_op> (PLUS_EXPR)); + std::make_unique<kf_atomic_fetch_op> (PLUS_EXPR)); kfm.add (BUILT_IN_ATOMIC_FETCH_ADD_2, - make_unique<kf_atomic_fetch_op> (PLUS_EXPR)); + std::make_unique<kf_atomic_fetch_op> (PLUS_EXPR)); kfm.add (BUILT_IN_ATOMIC_FETCH_ADD_4, - make_unique<kf_atomic_fetch_op> (PLUS_EXPR)); + std::make_unique<kf_atomic_fetch_op> (PLUS_EXPR)); kfm.add (BUILT_IN_ATOMIC_FETCH_ADD_8, - make_unique<kf_atomic_fetch_op> (PLUS_EXPR)); + std::make_unique<kf_atomic_fetch_op> (PLUS_EXPR)); kfm.add (BUILT_IN_ATOMIC_FETCH_ADD_16, - make_unique<kf_atomic_fetch_op> (PLUS_EXPR)); + std::make_unique<kf_atomic_fetch_op> (PLUS_EXPR)); kfm.add (BUILT_IN_ATOMIC_FETCH_SUB_1, - make_unique<kf_atomic_fetch_op> (MINUS_EXPR)); + std::make_unique<kf_atomic_fetch_op> (MINUS_EXPR)); kfm.add (BUILT_IN_ATOMIC_FETCH_SUB_2, - make_unique<kf_atomic_fetch_op> (MINUS_EXPR)); + std::make_unique<kf_atomic_fetch_op> (MINUS_EXPR)); kfm.add (BUILT_IN_ATOMIC_FETCH_SUB_4, - make_unique<kf_atomic_fetch_op> (MINUS_EXPR)); + std::make_unique<kf_atomic_fetch_op> (MINUS_EXPR)); kfm.add (BUILT_IN_ATOMIC_FETCH_SUB_8, - make_unique<kf_atomic_fetch_op> (MINUS_EXPR)); + std::make_unique<kf_atomic_fetch_op> (MINUS_EXPR)); kfm.add (BUILT_IN_ATOMIC_FETCH_SUB_16, - make_unique<kf_atomic_fetch_op> (MINUS_EXPR)); + std::make_unique<kf_atomic_fetch_op> (MINUS_EXPR)); kfm.add (BUILT_IN_ATOMIC_FETCH_AND_1, - make_unique<kf_atomic_fetch_op> (BIT_AND_EXPR)); + std::make_unique<kf_atomic_fetch_op> (BIT_AND_EXPR)); kfm.add (BUILT_IN_ATOMIC_FETCH_AND_2, - make_unique<kf_atomic_fetch_op> (BIT_AND_EXPR)); + std::make_unique<kf_atomic_fetch_op> (BIT_AND_EXPR)); kfm.add (BUILT_IN_ATOMIC_FETCH_AND_4, - make_unique<kf_atomic_fetch_op> (BIT_AND_EXPR)); + std::make_unique<kf_atomic_fetch_op> (BIT_AND_EXPR)); kfm.add (BUILT_IN_ATOMIC_FETCH_AND_8, - make_unique<kf_atomic_fetch_op> (BIT_AND_EXPR)); + std::make_unique<kf_atomic_fetch_op> (BIT_AND_EXPR)); kfm.add (BUILT_IN_ATOMIC_FETCH_AND_16, - make_unique<kf_atomic_fetch_op> (BIT_AND_EXPR)); + std::make_unique<kf_atomic_fetch_op> (BIT_AND_EXPR)); kfm.add (BUILT_IN_ATOMIC_FETCH_XOR_1, - make_unique<kf_atomic_fetch_op> (BIT_XOR_EXPR)); + std::make_unique<kf_atomic_fetch_op> (BIT_XOR_EXPR)); kfm.add (BUILT_IN_ATOMIC_FETCH_XOR_2, - make_unique<kf_atomic_fetch_op> (BIT_XOR_EXPR)); + std::make_unique<kf_atomic_fetch_op> (BIT_XOR_EXPR)); kfm.add (BUILT_IN_ATOMIC_FETCH_XOR_4, - make_unique<kf_atomic_fetch_op> (BIT_XOR_EXPR)); + std::make_unique<kf_atomic_fetch_op> (BIT_XOR_EXPR)); kfm.add (BUILT_IN_ATOMIC_FETCH_XOR_8, - make_unique<kf_atomic_fetch_op> (BIT_XOR_EXPR)); + std::make_unique<kf_atomic_fetch_op> (BIT_XOR_EXPR)); kfm.add (BUILT_IN_ATOMIC_FETCH_XOR_16, - make_unique<kf_atomic_fetch_op> (BIT_XOR_EXPR)); + std::make_unique<kf_atomic_fetch_op> (BIT_XOR_EXPR)); kfm.add (BUILT_IN_ATOMIC_FETCH_OR_1, - make_unique<kf_atomic_fetch_op> (BIT_IOR_EXPR)); + std::make_unique<kf_atomic_fetch_op> (BIT_IOR_EXPR)); kfm.add (BUILT_IN_ATOMIC_FETCH_OR_2, - make_unique<kf_atomic_fetch_op> (BIT_IOR_EXPR)); + std::make_unique<kf_atomic_fetch_op> (BIT_IOR_EXPR)); kfm.add (BUILT_IN_ATOMIC_FETCH_OR_4, - make_unique<kf_atomic_fetch_op> (BIT_IOR_EXPR)); + std::make_unique<kf_atomic_fetch_op> (BIT_IOR_EXPR)); kfm.add (BUILT_IN_ATOMIC_FETCH_OR_8, - make_unique<kf_atomic_fetch_op> (BIT_IOR_EXPR)); + std::make_unique<kf_atomic_fetch_op> (BIT_IOR_EXPR)); kfm.add (BUILT_IN_ATOMIC_FETCH_OR_16, - make_unique<kf_atomic_fetch_op> (BIT_IOR_EXPR)); + std::make_unique<kf_atomic_fetch_op> (BIT_IOR_EXPR)); } /* Handle calls to the various IFN_UBSAN_* with no return value. @@ -2224,14 +2237,14 @@ register_sanitizer_builtins (known_function_manager &kfm) /* Handle calls to the various IFN_UBSAN_* with no return value. For now, treat these as no-ops. */ kfm.add (IFN_UBSAN_NULL, - make_unique<kf_ubsan_noop> ()); + std::make_unique<kf_ubsan_noop> ()); kfm.add (IFN_UBSAN_BOUNDS, - make_unique<kf_ubsan_noop> ()); + std::make_unique<kf_ubsan_noop> ()); kfm.add (IFN_UBSAN_PTR, - make_unique<kf_ubsan_noop> ()); + std::make_unique<kf_ubsan_noop> ()); kfm.add (BUILT_IN_UBSAN_HANDLE_NONNULL_ARG, - make_unique<kf_ubsan_handler> ()); + std::make_unique<kf_ubsan_handler> ()); } /* Populate KFM with instances of known functions supported by the core of the @@ -2246,17 +2259,19 @@ register_known_functions (known_function_manager &kfm, /* Internal fns the analyzer has known_functions for. */ { - kfm.add (IFN_BUILTIN_EXPECT, make_unique<kf_expect> ()); + kfm.add (IFN_BUILTIN_EXPECT, std::make_unique<kf_expect> ()); } /* GCC built-ins that do not correspond to a function in the standard library. */ { - kfm.add (BUILT_IN_EXPECT, make_unique<kf_expect> ()); - kfm.add (BUILT_IN_EXPECT_WITH_PROBABILITY, make_unique<kf_expect> ()); - kfm.add (BUILT_IN_ALLOCA_WITH_ALIGN, make_unique<kf_alloca> ()); - kfm.add (BUILT_IN_STACK_RESTORE, make_unique<kf_stack_restore> ()); - kfm.add (BUILT_IN_STACK_SAVE, make_unique<kf_stack_save> ()); + kfm.add (BUILT_IN_EXPECT, std::make_unique<kf_expect> ()); + kfm.add (BUILT_IN_EXPECT_WITH_PROBABILITY, std::make_unique<kf_expect> ()); + kfm.add (BUILT_IN_ALLOCA_WITH_ALIGN, std::make_unique<kf_alloca> ()); + kfm.add (BUILT_IN_STACK_RESTORE, std::make_unique<kf_stack_restore> ()); + kfm.add (BUILT_IN_STACK_SAVE, std::make_unique<kf_stack_save> ()); + + kfm.add (BUILT_IN_EH_POINTER, std::make_unique<kf_eh_pointer> ()); register_atomic_builtins (kfm); register_sanitizer_builtins (kfm); @@ -2266,58 +2281,58 @@ register_known_functions (known_function_manager &kfm, /* Known builtins and C standard library functions the analyzer has known functions for. */ { - kfm.add ("alloca", make_unique<kf_alloca> ()); - kfm.add ("__builtin_alloca", make_unique<kf_alloca> ()); - kfm.add ("calloc", make_unique<kf_calloc> ()); - kfm.add ("__builtin_calloc", make_unique<kf_calloc> ()); - kfm.add ("free", make_unique<kf_free> ()); - kfm.add ("__builtin_free", make_unique<kf_free> ()); - kfm.add ("malloc", make_unique<kf_malloc> ()); - kfm.add ("__builtin_malloc", make_unique<kf_malloc> ()); + kfm.add ("alloca", std::make_unique<kf_alloca> ()); + kfm.add ("__builtin_alloca", std::make_unique<kf_alloca> ()); + kfm.add ("calloc", std::make_unique<kf_calloc> ()); + kfm.add ("__builtin_calloc", std::make_unique<kf_calloc> ()); + kfm.add ("free", std::make_unique<kf_free> ()); + kfm.add ("__builtin_free", std::make_unique<kf_free> ()); + kfm.add ("malloc", std::make_unique<kf_malloc> ()); + kfm.add ("__builtin_malloc", std::make_unique<kf_malloc> ()); kfm.add ("memcpy", - make_unique<kf_memcpy_memmove> (kf_memcpy_memmove::KF_MEMCPY)); + std::make_unique<kf_memcpy_memmove> (kf_memcpy_memmove::KF_MEMCPY)); kfm.add ("__builtin_memcpy", - make_unique<kf_memcpy_memmove> (kf_memcpy_memmove::KF_MEMCPY)); - kfm.add ("__memcpy_chk", make_unique<kf_memcpy_memmove> + std::make_unique<kf_memcpy_memmove> (kf_memcpy_memmove::KF_MEMCPY)); + kfm.add ("__memcpy_chk", std::make_unique<kf_memcpy_memmove> (kf_memcpy_memmove::KF_MEMCPY_CHK)); - kfm.add ("__builtin___memcpy_chk", make_unique<kf_memcpy_memmove> + kfm.add ("__builtin___memcpy_chk", std::make_unique<kf_memcpy_memmove> (kf_memcpy_memmove::KF_MEMCPY_CHK)); kfm.add ("memmove", - make_unique<kf_memcpy_memmove> (kf_memcpy_memmove::KF_MEMMOVE)); + std::make_unique<kf_memcpy_memmove> (kf_memcpy_memmove::KF_MEMMOVE)); kfm.add ("__builtin_memmove", - make_unique<kf_memcpy_memmove> (kf_memcpy_memmove::KF_MEMMOVE)); - kfm.add ("__memmove_chk", make_unique<kf_memcpy_memmove> + std::make_unique<kf_memcpy_memmove> (kf_memcpy_memmove::KF_MEMMOVE)); + kfm.add ("__memmove_chk", std::make_unique<kf_memcpy_memmove> (kf_memcpy_memmove::KF_MEMMOVE_CHK)); - kfm.add ("__builtin___memmove_chk", make_unique<kf_memcpy_memmove> + kfm.add ("__builtin___memmove_chk", std::make_unique<kf_memcpy_memmove> (kf_memcpy_memmove::KF_MEMMOVE_CHK)); - kfm.add ("memset", make_unique<kf_memset> (false)); - kfm.add ("__builtin_memset", make_unique<kf_memset> (false)); - kfm.add ("__memset_chk", make_unique<kf_memset> (true)); - kfm.add ("__builtin___memset_chk", make_unique<kf_memset> (true)); - kfm.add ("realloc", make_unique<kf_realloc> ()); - kfm.add ("__builtin_realloc", make_unique<kf_realloc> ()); - kfm.add ("sprintf", make_unique<kf_sprintf> ()); - kfm.add ("__builtin_sprintf", make_unique<kf_sprintf> ()); - kfm.add ("strchr", make_unique<kf_strchr> ()); - kfm.add ("__builtin_strchr", make_unique<kf_strchr> ()); - kfm.add ("strcpy", make_unique<kf_strcpy> (2, false)); - kfm.add ("__builtin_strcpy", make_unique<kf_strcpy> (2, false)); - kfm.add ("__strcpy_chk", make_unique<kf_strcpy> (3, true)); - kfm.add ("__builtin___strcpy_chk", make_unique<kf_strcpy> (3, true)); - kfm.add ("strcat", make_unique<kf_strcat> (2, false)); - kfm.add ("__builtin_strcat", make_unique<kf_strcat> (2, false)); - kfm.add ("__strcat_chk", make_unique<kf_strcat> (3, true)); - kfm.add ("__builtin___strcat_chk", make_unique<kf_strcat> (3, true)); - kfm.add ("strdup", make_unique<kf_strdup> ()); - kfm.add ("__builtin_strdup", make_unique<kf_strdup> ()); - kfm.add ("strncpy", make_unique<kf_strncpy> ()); - kfm.add ("__builtin_strncpy", make_unique<kf_strncpy> ()); - kfm.add ("strndup", make_unique<kf_strndup> ()); - kfm.add ("__builtin_strndup", make_unique<kf_strndup> ()); - kfm.add ("strlen", make_unique<kf_strlen> ()); - kfm.add ("__builtin_strlen", make_unique<kf_strlen> ()); - kfm.add ("strstr", make_unique<kf_strstr> ()); - kfm.add ("__builtin_strstr", make_unique<kf_strstr> ()); + kfm.add ("memset", std::make_unique<kf_memset> (false)); + kfm.add ("__builtin_memset", std::make_unique<kf_memset> (false)); + kfm.add ("__memset_chk", std::make_unique<kf_memset> (true)); + kfm.add ("__builtin___memset_chk", std::make_unique<kf_memset> (true)); + kfm.add ("realloc", std::make_unique<kf_realloc> ()); + kfm.add ("__builtin_realloc", std::make_unique<kf_realloc> ()); + kfm.add ("sprintf", std::make_unique<kf_sprintf> ()); + kfm.add ("__builtin_sprintf", std::make_unique<kf_sprintf> ()); + kfm.add ("strchr", std::make_unique<kf_strchr> ()); + kfm.add ("__builtin_strchr", std::make_unique<kf_strchr> ()); + kfm.add ("strcpy", std::make_unique<kf_strcpy> (2, false)); + kfm.add ("__builtin_strcpy", std::make_unique<kf_strcpy> (2, false)); + kfm.add ("__strcpy_chk", std::make_unique<kf_strcpy> (3, true)); + kfm.add ("__builtin___strcpy_chk", std::make_unique<kf_strcpy> (3, true)); + kfm.add ("strcat", std::make_unique<kf_strcat> (2, false)); + kfm.add ("__builtin_strcat", std::make_unique<kf_strcat> (2, false)); + kfm.add ("__strcat_chk", std::make_unique<kf_strcat> (3, true)); + kfm.add ("__builtin___strcat_chk", std::make_unique<kf_strcat> (3, true)); + kfm.add ("strdup", std::make_unique<kf_strdup> ()); + kfm.add ("__builtin_strdup", std::make_unique<kf_strdup> ()); + kfm.add ("strncpy", std::make_unique<kf_strncpy> ()); + kfm.add ("__builtin_strncpy", std::make_unique<kf_strncpy> ()); + kfm.add ("strndup", std::make_unique<kf_strndup> ()); + kfm.add ("__builtin_strndup", std::make_unique<kf_strndup> ()); + kfm.add ("strlen", std::make_unique<kf_strlen> ()); + kfm.add ("__builtin_strlen", std::make_unique<kf_strlen> ()); + kfm.add ("strstr", std::make_unique<kf_strstr> ()); + kfm.add ("__builtin_strstr", std::make_unique<kf_strstr> ()); register_atomic_builtins (kfm); register_varargs_builtins (kfm); @@ -2325,9 +2340,9 @@ register_known_functions (known_function_manager &kfm, /* Known POSIX functions, and some non-standard extensions. */ { - kfm.add ("fopen", make_unique<kf_fopen> ()); - kfm.add ("putenv", make_unique<kf_putenv> ()); - kfm.add ("strtok", make_unique<kf_strtok> (rmm)); + kfm.add ("fopen", std::make_unique<kf_fopen> ()); + kfm.add ("putenv", std::make_unique<kf_putenv> ()); + kfm.add ("strtok", std::make_unique<kf_strtok> (rmm)); register_known_fd_functions (kfm); register_known_file_functions (kfm); @@ -2335,13 +2350,13 @@ register_known_functions (known_function_manager &kfm, /* glibc functions. */ { - kfm.add ("__errno_location", make_unique<kf_errno_location> ()); - kfm.add ("error", make_unique<kf_error> (3)); - kfm.add ("error_at_line", make_unique<kf_error> (5)); + kfm.add ("__errno_location", std::make_unique<kf_errno_location> ()); + kfm.add ("error", std::make_unique<kf_error> (3)); + kfm.add ("error_at_line", std::make_unique<kf_error> (5)); /* Variants of "error" and "error_at_line" seen by the analyzer at -O0 (PR analyzer/115724). */ - kfm.add ("__error_alias", make_unique<kf_error> (3)); - kfm.add ("__error_at_line_alias", make_unique<kf_error> (5)); + kfm.add ("__error_alias", std::make_unique<kf_error> (3)); + kfm.add ("__error_at_line_alias", std::make_unique<kf_error> (5)); } /* Other implementations of C standard library. */ @@ -2355,9 +2370,9 @@ register_known_functions (known_function_manager &kfm, #define errno (*__error()) and similarly __errno for newlib. Add these as synonyms for "__errno_location". */ - kfm.add ("___errno", make_unique<kf_errno_location> ()); - kfm.add ("__error", make_unique<kf_errno_location> ()); - kfm.add ("__errno", make_unique<kf_errno_location> ()); + kfm.add ("___errno", std::make_unique<kf_errno_location> ()); + kfm.add ("__error", std::make_unique<kf_errno_location> ()); + kfm.add ("__errno", std::make_unique<kf_errno_location> ()); } /* Language-specific support functions. */ @@ -2367,22 +2382,22 @@ register_known_functions (known_function_manager &kfm, from <cstdlib> etc for the C spellings of these headers (e.g. <stdlib.h>), so we must match against these too. */ { - kfm.add_std_ns ("malloc", make_unique<kf_malloc> ()); - kfm.add_std_ns ("free", make_unique<kf_free> ()); - kfm.add_std_ns ("realloc", make_unique<kf_realloc> ()); - kfm.add_std_ns ("calloc", make_unique<kf_calloc> ()); + kfm.add_std_ns ("malloc", std::make_unique<kf_malloc> ()); + kfm.add_std_ns ("free", std::make_unique<kf_free> ()); + kfm.add_std_ns ("realloc", std::make_unique<kf_realloc> ()); + kfm.add_std_ns ("calloc", std::make_unique<kf_calloc> ()); kfm.add_std_ns ("memcpy", - make_unique<kf_memcpy_memmove> (kf_memcpy_memmove::KF_MEMCPY)); + std::make_unique<kf_memcpy_memmove> (kf_memcpy_memmove::KF_MEMCPY)); kfm.add_std_ns ("memmove", - make_unique<kf_memcpy_memmove> (kf_memcpy_memmove::KF_MEMMOVE)); - kfm.add_std_ns ("memset", make_unique<kf_memset> (false)); - kfm.add_std_ns ("strcat", make_unique<kf_strcat> (2, false)); - kfm.add_std_ns ("strcpy", make_unique<kf_strcpy> (2, false)); - kfm.add_std_ns ("strlen", make_unique<kf_strlen> ()); - kfm.add_std_ns ("strncpy", make_unique<kf_strncpy> ()); - kfm.add_std_ns ("strtok", make_unique<kf_strtok> (rmm)); + std::make_unique<kf_memcpy_memmove> (kf_memcpy_memmove::KF_MEMMOVE)); + kfm.add_std_ns ("memset", std::make_unique<kf_memset> (false)); + kfm.add_std_ns ("strcat", std::make_unique<kf_strcat> (2, false)); + kfm.add_std_ns ("strcpy", std::make_unique<kf_strcpy> (2, false)); + kfm.add_std_ns ("strlen", std::make_unique<kf_strlen> ()); + kfm.add_std_ns ("strncpy", std::make_unique<kf_strncpy> ()); + kfm.add_std_ns ("strtok", std::make_unique<kf_strtok> (rmm)); } } diff --git a/gcc/analyzer/known-function-manager.cc b/gcc/analyzer/known-function-manager.cc index db670b8..3b645a8 100644 --- a/gcc/analyzer/known-function-manager.cc +++ b/gcc/analyzer/known-function-manager.cc @@ -18,17 +18,12 @@ 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/>. */ -#include "config.h" -#define INCLUDE_VECTOR -#include "system.h" -#include "coretypes.h" -#include "tree.h" -#include "analyzer/analyzer.h" +#include "analyzer/common.h" + #include "diagnostic-core.h" -#include "analyzer/analyzer-logging.h" #include "stringpool.h" -#include "basic-block.h" -#include "gimple.h" + +#include "analyzer/analyzer-logging.h" #include "analyzer/known-function-manager.h" #include "analyzer/region-model.h" #include "analyzer/call-details.h" @@ -97,7 +92,7 @@ known_function_manager::add (enum internal_fn ifn, The call must match all assumptions made by the known_function (such as e.g. "argument 1's type must be a pointer type"). - Return NULL if no known_function is found, or it does not match the + Return nullptr if no known_function is found, or it does not match the assumption(s). */ const known_function * @@ -108,7 +103,7 @@ known_function_manager::get_match (tree fndecl, const call_details &cd) const { if (const known_function *candidate = get_normal_builtin (DECL_FUNCTION_CODE (fndecl))) - if (gimple_builtin_call_types_compatible_p (cd.get_call_stmt (), + if (gimple_builtin_call_types_compatible_p (&cd.get_call_stmt (), fndecl)) return candidate; } @@ -127,16 +122,16 @@ known_function_manager::get_match (tree fndecl, const call_details &cd) const if (DECL_CONTEXT (fndecl) && TREE_CODE (DECL_CONTEXT (fndecl)) != TRANSLATION_UNIT_DECL) - return NULL; + return nullptr; if (tree identifier = DECL_NAME (fndecl)) if (const known_function *candidate = get_by_identifier (identifier)) if (candidate->matches_call_types_p (cd)) return candidate; - return NULL; + return nullptr; } -/* Get any known_function for IFN, or NULL. */ +/* Get any known_function for IFN, or nullptr. */ const known_function * known_function_manager::get_internal_fn (enum internal_fn ifn) const @@ -146,7 +141,7 @@ known_function_manager::get_internal_fn (enum internal_fn ifn) const } /* Get any known_function for NAME, without type-checking. - Return NULL if there isn't one. */ + Return nullptr if there isn't one. */ const known_function * known_function_manager::get_normal_builtin (enum built_in_function name) const @@ -165,7 +160,7 @@ get_normal_builtin (const builtin_known_function *builtin_kf) const } /* Get any known_function matching IDENTIFIER, without type-checking. - Return NULL if there isn't one. */ + Return nullptr if there isn't one. */ const known_function * known_function_manager::get_by_identifier (tree identifier) const @@ -175,7 +170,7 @@ known_function_manager::get_by_identifier (tree identifier) const if (slot) return *slot; else - return NULL; + return nullptr; } /* Get any known_function in C++ std:: namespace matching IDENTIFIER, without diff --git a/gcc/analyzer/pending-diagnostic.cc b/gcc/analyzer/pending-diagnostic.cc index 0f69d02..14d8f9f 100644 --- a/gcc/analyzer/pending-diagnostic.cc +++ b/gcc/analyzer/pending-diagnostic.cc @@ -18,18 +18,19 @@ 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/>. */ -#include "config.h" -#define INCLUDE_VECTOR -#include "system.h" -#include "coretypes.h" -#include "tree.h" -#include "intl.h" -#include "diagnostic.h" -#include "analyzer/analyzer.h" -#include "diagnostic-event-id.h" +#include "analyzer/common.h" + +#include "diagnostics/event-id.h" +#include "diagnostics/logging.h" +#include "cpplib.h" +#include "digraph.h" +#include "ordered-hash-map.h" +#include "cfg.h" +#include "gimple-iterator.h" +#include "cgraph.h" + #include "analyzer/analyzer-logging.h" #include "analyzer/sm.h" -#include "diagnostic-event-id.h" #include "analyzer/sm.h" #include "analyzer/pending-diagnostic.h" #include "analyzer/diagnostic-manager.h" @@ -37,20 +38,10 @@ along with GCC; see the file COPYING3. If not see #include "analyzer/program-point.h" #include "analyzer/store.h" #include "analyzer/region-model.h" -#include "cpplib.h" -#include "digraph.h" -#include "ordered-hash-map.h" -#include "cfg.h" -#include "basic-block.h" -#include "gimple.h" -#include "gimple-iterator.h" -#include "cgraph.h" #include "analyzer/supergraph.h" #include "analyzer/program-state.h" #include "analyzer/exploded-graph.h" -#include "diagnostic-path.h" #include "analyzer/checker-path.h" -#include "make-unique.h" #if ENABLE_ANALYZER @@ -98,15 +89,26 @@ diagnostic_emission_context::get_pending_diagnostic () const bool diagnostic_emission_context::warn (const char *gmsgid, ...) { + auto dc_logger = global_dc->get_logger (); + diagnostics::logging::log_function_params + (dc_logger, "ana::diagnostic_emission_context::warn") + .log_param_string ("gmsgid", gmsgid); + diagnostics::logging::auto_inc_depth depth_sentinel (dc_logger); + const pending_diagnostic &pd = get_pending_diagnostic (); auto_diagnostic_group d; va_list ap; va_start (ap, gmsgid); - const bool result = emit_diagnostic_valist_meta (DK_WARNING, + const bool result = emit_diagnostic_valist_meta (diagnostics::kind::warning, &m_rich_loc, &m_metadata, pd.get_controlling_option (), gmsgid, &ap); va_end (ap); + + if (dc_logger) + dc_logger->log_bool_return ("ana::diagnostic_emission_context::warn", + result); + return result; } @@ -116,11 +118,17 @@ diagnostic_emission_context::warn (const char *gmsgid, ...) void diagnostic_emission_context::inform (const char *gmsgid, ...) { + auto dc_logger = global_dc->get_logger (); + diagnostics::logging::log_function_params + (dc_logger, "ana::diagnostic_emission_context::inform") + .log_param_string ("gmsgid", gmsgid); + diagnostics::logging::auto_inc_depth depth_sentinel (dc_logger); + const pending_diagnostic &pd = get_pending_diagnostic (); auto_diagnostic_group d; va_list ap; va_start (ap, gmsgid); - emit_diagnostic_valist_meta (DK_NOTE, + emit_diagnostic_valist_meta (diagnostics::kind::note, &m_rich_loc, &m_metadata, pd.get_controlling_option (), gmsgid, &ap); @@ -181,7 +189,7 @@ pending_diagnostic::fixup_location (location_t loc, bool) const const line_map_macro *macro_map = linemap_check_macro (map); if (fixup_location_in_macro_p (macro_map->macro)) loc = linemap_resolve_location (line_table, loc, - LRK_MACRO_EXPANSION_POINT, NULL); + LRK_MACRO_EXPANSION_POINT, nullptr); } return loc; } @@ -195,7 +203,10 @@ pending_diagnostic::add_function_entry_event (const exploded_edge &eedge, { const exploded_node *dst_node = eedge.m_dest; const program_point &dst_point = dst_node->get_point (); - emission_path->add_event (make_unique<function_entry_event> (dst_point)); + const program_state &dst_state = dst_node->get_state (); + emission_path->add_event + (std::make_unique<function_entry_event> (dst_point, + dst_state)); } /* Base implementation of pending_diagnostic::add_call_event. @@ -210,12 +221,12 @@ pending_diagnostic::add_call_event (const exploded_edge &eedge, const int src_stack_depth = src_point.get_stack_depth (); const gimple *last_stmt = src_point.get_supernode ()->get_last_stmt (); emission_path->add_event - (make_unique<call_event> (eedge, - event_loc_info (last_stmt - ? last_stmt->location - : UNKNOWN_LOCATION, - src_point.get_fndecl (), - src_stack_depth))); + (std::make_unique<call_event> (eedge, + event_loc_info (last_stmt + ? last_stmt->location + : UNKNOWN_LOCATION, + src_point.get_fndecl (), + src_stack_depth))); } /* Base implementation of pending_diagnostic::add_region_creation_events. @@ -228,12 +239,13 @@ pending_diagnostic::add_region_creation_events (const region *reg, checker_path &emission_path) { emission_path.add_event - (make_unique<region_creation_event_memory_space> (reg->get_memory_space (), - loc_info)); + (std::make_unique<region_creation_event_memory_space> + (reg->get_memory_space (), + loc_info)); if (capacity) emission_path.add_event - (make_unique<region_creation_event_capacity> (capacity, loc_info)); + (std::make_unique<region_creation_event_capacity> (capacity, loc_info)); } /* Base implementation of pending_diagnostic::add_final_event. @@ -247,10 +259,11 @@ pending_diagnostic::add_final_event (const state_machine *sm, checker_path *emission_path) { emission_path->add_event - (make_unique<warning_event> + (std::make_unique<warning_event> (loc_info, enode, - sm, var, state)); + sm, var, state, + get_final_state ())); } } // namespace ana diff --git a/gcc/analyzer/pending-diagnostic.h b/gcc/analyzer/pending-diagnostic.h index 6a0289e..b5d90a2 100644 --- a/gcc/analyzer/pending-diagnostic.h +++ b/gcc/analyzer/pending-diagnostic.h @@ -21,8 +21,7 @@ along with GCC; see the file COPYING3. If not see #ifndef GCC_ANALYZER_PENDING_DIAGNOSTIC_H #define GCC_ANALYZER_PENDING_DIAGNOSTIC_H -#include "diagnostic-metadata.h" -#include "diagnostic-path.h" +#include "diagnostics/metadata.h" #include "analyzer/sm.h" namespace ana { @@ -44,7 +43,7 @@ struct interesting_t }; /* Various bundles of information used for generating more precise - messages for events within a diagnostic_path, for passing to the + messages for events within a diagnostic path, for passing to the various "describe_*" vfuncs of pending_diagnostic. See those for more information. */ @@ -58,7 +57,7 @@ struct state_change tree origin, state_machine::state_t old_state, state_machine::state_t new_state, - diagnostic_event_id_t event_id, + diagnostics::paths::event_id_t event_id, const state_change_event &event) : m_expr (expr), m_origin (origin), m_old_state (old_state), m_new_state (new_state), @@ -71,7 +70,7 @@ struct state_change tree m_origin; state_machine::state_t m_old_state; state_machine::state_t m_new_state; - diagnostic_event_id_t m_event_id; + diagnostics::paths::event_id_t m_event_id; const state_change_event &m_event; }; @@ -131,14 +130,14 @@ struct final_event pending_diagnostic::emit vfunc. The rich_location will have already been populated with a - diagnostic_path. */ + diagnostics::paths::path. */ class diagnostic_emission_context { public: diagnostic_emission_context (const saved_diagnostic &sd, rich_location &rich_loc, - diagnostic_metadata &metadata, + diagnostics::metadata &metadata, logger *logger) : m_sd (sd), m_rich_loc (rich_loc), @@ -156,7 +155,7 @@ public: logger *get_logger () const { return m_logger; } void add_cwe (int cwe) { m_metadata.add_cwe (cwe); } - void add_rule (const diagnostic_metadata::rule &r) + void add_rule (const diagnostics::metadata::rule &r) { m_metadata.add_rule (r); } @@ -164,7 +163,7 @@ public: private: const saved_diagnostic &m_sd; rich_location &m_rich_loc; - diagnostic_metadata &m_metadata; + diagnostics::metadata &m_metadata; logger *m_logger; }; @@ -182,7 +181,7 @@ private: As well as emitting a diagnostic, the class has various "precision of wording" virtual functions, for generating descriptions for events - within a diagnostic_path. These are optional, but implementing these + within a diagnostic path. These are optional, but implementing these allows for more precise wordings than the more generic implementation. */ @@ -238,7 +237,7 @@ class pending_diagnostic virtual location_t fixup_location (location_t loc, bool primary) const; /* Precision-of-wording vfunc for describing a critical state change - within the diagnostic_path. + within the diagnostic path. For example, a double-free diagnostic might use the descriptions: - "first 'free' happens here" @@ -260,13 +259,13 @@ class pending_diagnostic return false; } - /* Vfunc for implementing diagnostic_event::get_meaning for + /* Vfunc for implementing event::get_meaning for state_change_event. */ - virtual diagnostic_event::meaning + virtual diagnostics::paths::event::meaning get_meaning_for_state_change (const evdesc::state_change &) const { /* Default no-op implementation. */ - return diagnostic_event::meaning (); + return diagnostics::paths::event::meaning (); } /* Precision-of-wording vfunc for describing an interprocedural call @@ -285,7 +284,7 @@ class pending_diagnostic } /* Precision-of-wording vfunc for describing an interprocedural return - within the diagnostic_path that carries critial state for the + within the diagnostic path that carries critial state for the diagnostic, from callee back to caller. For example, a deref-of-unchecked-malloc diagnostic might use: @@ -301,7 +300,7 @@ class pending_diagnostic } /* Precision-of-wording vfunc for describing the final event within a - diagnostic_path. + diagnostic path. For example a double-free diagnostic might use: - "second 'free' here; first 'free' was at (3)" @@ -364,6 +363,12 @@ class pending_diagnostic tree var, state_machine::state_t state, checker_path *emission_path); + virtual const program_state * + get_final_state () const + { + return nullptr; + } + /* Vfunc for determining that this pending_diagnostic supercedes OTHER, and that OTHER should therefore not be emitted. They have already been tested for being at the same stmt. */ @@ -396,7 +401,8 @@ class pending_diagnostic the opportunity to add diagnostic-specific properties to the SARIF "result" object for the diagnostic. This is intended for use when debugging a diagnostic. */ - virtual void maybe_add_sarif_properties (sarif_object &/*result_obj*/) const + virtual void + maybe_add_sarif_properties (diagnostics::sarif_object &/*result_obj*/) const { /* Default no-op implementation. */ } diff --git a/gcc/analyzer/program-point.cc b/gcc/analyzer/program-point.cc index 473c102..9baa007 100644 --- a/gcc/analyzer/program-point.cc +++ b/gcc/analyzer/program-point.cc @@ -18,42 +18,28 @@ 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/>. */ -#include "config.h" -#define INCLUDE_VECTOR -#include "system.h" -#include "coretypes.h" -#include "tree.h" -#include "gimple-pretty-print.h" +#include "analyzer/common.h" + +#include "diagnostics/event-id.h" #include "gcc-rich-location.h" -#include "ordered-hash-map.h" -#include "options.h" -#include "cgraph.h" -#include "function.h" -#include "cfg.h" -#include "basic-block.h" -#include "gimple.h" -#include "gimple-iterator.h" -#include "digraph.h" -#include "analyzer/analyzer.h" +#include "gimple-pretty-print.h" +#include "sbitmap.h" +#include "selftest.h" +#include "shortest-paths.h" + #include "analyzer/analyzer-logging.h" #include "analyzer/call-string.h" #include "analyzer/supergraph.h" #include "analyzer/program-point.h" -#include "sbitmap.h" -#include "bitmap.h" -#include "selftest.h" #include "analyzer/store.h" #include "analyzer/region-model.h" #include "analyzer/sm.h" #include "analyzer/program-state.h" -#include "diagnostic-event-id.h" #include "analyzer/pending-diagnostic.h" #include "analyzer/diagnostic-manager.h" -#include "shortest-paths.h" #include "analyzer/exploded-graph.h" #include "analyzer/analysis-plan.h" #include "analyzer/inlining-iterator.h" -#include "make-unique.h" #if ENABLE_ANALYZER @@ -185,7 +171,7 @@ function_point::get_function () const if (m_supernode) return m_supernode->m_fun; else - return NULL; + return nullptr; } /* Get the gimple stmt for this function_point, if any. */ @@ -198,7 +184,7 @@ function_point::get_stmt () const else if (m_kind == PK_AFTER_SUPERNODE) return m_supernode->get_last_stmt (); else - return NULL; + return nullptr; } /* Get a location for this function_point, if any. */ @@ -233,32 +219,33 @@ function_point::final_stmt_p () const function_point function_point::from_function_entry (const supergraph &sg, const function &fun) { - return before_supernode (sg.get_node_for_function_entry (fun), NULL); + return before_supernode (sg.get_node_for_function_entry (fun), nullptr); } /* Create a function_point representing entering supernode SUPERNODE, - having reached it via FROM_EDGE (which could be NULL). */ + having reached it via FROM_EDGE (which could be nullptr). */ function_point function_point::before_supernode (const supernode *supernode, const superedge *from_edge) { if (from_edge && from_edge->get_kind () != SUPEREDGE_CFG_EDGE) - from_edge = NULL; + from_edge = nullptr; return function_point (supernode, from_edge, 0, PK_BEFORE_SUPERNODE); } -/* A subclass of diagnostic_context for use by +/* A subclass of diagnostics::context for use by program_point::print_source_line. */ -class debug_diagnostic_context : public diagnostic_context +class debug_diagnostic_context : public diagnostics::context { public: debug_diagnostic_context () { diagnostic_initialize (this, 0); - m_source_printing.show_line_numbers_p = true; - m_source_printing.enabled = true; + auto &source_printing_opts = get_source_printing_options (); + source_printing_opts.show_line_numbers_p = true; + source_printing_opts.enabled = true; } ~debug_diagnostic_context () { @@ -277,9 +264,9 @@ function_point::print_source_line (pretty_printer *pp) const // TODO: monospace font debug_diagnostic_context tmp_dc; gcc_rich_location richloc (stmt->location); - diagnostic_source_print_policy source_policy (tmp_dc); + diagnostics::source_print_policy source_policy (tmp_dc); gcc_assert (pp); - source_policy.print (*pp, richloc, DK_ERROR, nullptr); + source_policy.print (*pp, richloc, diagnostics::kind::error, nullptr); pp_string (pp, pp_formatted_text (tmp_dc.get_reference_printer ())); } @@ -316,7 +303,7 @@ program_point::dump () const std::unique_ptr<json::object> program_point::to_json () const { - auto point_obj = ::make_unique<json::object> (); + auto point_obj = std::make_unique<json::object> (); point_obj->set_string ("kind", point_kind_to_string (get_kind ())); @@ -687,7 +674,7 @@ function_point::get_next () const program_point program_point::origin (const region_model_manager &mgr) { - return program_point (function_point (NULL, NULL, + return program_point (function_point (nullptr, nullptr, 0, PK_ORIGIN), mgr.get_empty_call_string ()); } @@ -780,11 +767,11 @@ namespace selftest { static void test_function_point_equality () { - const supernode *snode = NULL; + const supernode *snode = nullptr; - function_point a = function_point (snode, NULL, 0, + function_point a = function_point (snode, nullptr, 0, PK_BEFORE_SUPERNODE); - function_point b = function_point::before_supernode (snode, NULL); + function_point b = function_point::before_supernode (snode, nullptr); ASSERT_EQ (a, b); } @@ -793,12 +780,12 @@ test_function_point_equality () static void test_function_point_ordering () { - const supernode *snode = NULL; + const supernode *snode = nullptr; /* Populate an array with various points within the same snode, in order. */ auto_vec<function_point> points; - points.safe_push (function_point::before_supernode (snode, NULL)); + points.safe_push (function_point::before_supernode (snode, nullptr)); points.safe_push (function_point::before_stmt (snode, 0)); points.safe_push (function_point::before_stmt (snode, 1)); points.safe_push (function_point::after_supernode (snode)); @@ -830,14 +817,14 @@ test_program_point_equality () { region_model_manager mgr; - const supernode *snode = NULL; + const supernode *snode = nullptr; const call_string &cs = mgr.get_empty_call_string (); - program_point a = program_point::before_supernode (snode, NULL, + program_point a = program_point::before_supernode (snode, nullptr, cs); - program_point b = program_point::before_supernode (snode, NULL, + program_point b = program_point::before_supernode (snode, nullptr, cs); ASSERT_EQ (a, b); diff --git a/gcc/analyzer/program-point.h b/gcc/analyzer/program-point.h index 38d83197..fc543c0 100644 --- a/gcc/analyzer/program-point.h +++ b/gcc/analyzer/program-point.h @@ -120,23 +120,23 @@ public: static function_point before_stmt (const supernode *supernode, unsigned stmt_idx) { - return function_point (supernode, NULL, stmt_idx, PK_BEFORE_STMT); + return function_point (supernode, nullptr, stmt_idx, PK_BEFORE_STMT); } static function_point after_supernode (const supernode *supernode) { - return function_point (supernode, NULL, 0, PK_AFTER_SUPERNODE); + return function_point (supernode, nullptr, 0, PK_AFTER_SUPERNODE); } /* Support for hash_map. */ static function_point empty () { - return function_point (NULL, NULL, 0, PK_EMPTY); + return function_point (nullptr, nullptr, 0, PK_EMPTY); } static function_point deleted () { - return function_point (NULL, NULL, 0, PK_DELETED); + return function_point (nullptr, nullptr, 0, PK_DELETED); } static int cmp_within_supernode_1 (const function_point &point_a, @@ -305,7 +305,7 @@ public: private: program_point (const function_point &fn_point) : m_function_point (fn_point), - m_call_string (NULL) + m_call_string (nullptr) { } diff --git a/gcc/analyzer/program-state.cc b/gcc/analyzer/program-state.cc index ec96900..e16a50f 100644 --- a/gcc/analyzer/program-state.cc +++ b/gcc/analyzer/program-state.cc @@ -18,44 +18,39 @@ 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/>. */ -#include "config.h" -#define INCLUDE_VECTOR -#include "system.h" -#include "coretypes.h" -#include "tree.h" -#include "diagnostic-core.h" -#include "diagnostic.h" -#include "analyzer/analyzer.h" -#include "analyzer/analyzer-logging.h" -#include "analyzer/sm.h" +#include "analyzer/common.h" + #include "sbitmap.h" -#include "bitmap.h" #include "ordered-hash-map.h" #include "selftest.h" +#include "cfg.h" +#include "gimple-iterator.h" +#include "cgraph.h" +#include "digraph.h" +#include "diagnostics/event-id.h" +#include "diagnostics/state-graphs.h" +#include "graphviz.h" + +#include "text-art/tree-widget.h" +#include "text-art/dump.h" + +#include "analyzer/analyzer-logging.h" +#include "analyzer/sm.h" #include "analyzer/call-string.h" #include "analyzer/program-point.h" #include "analyzer/store.h" #include "analyzer/region-model.h" #include "analyzer/program-state.h" #include "analyzer/constraint-manager.h" -#include "diagnostic-event-id.h" #include "analyzer/pending-diagnostic.h" #include "analyzer/diagnostic-manager.h" -#include "cfg.h" -#include "basic-block.h" -#include "gimple.h" -#include "gimple-iterator.h" -#include "cgraph.h" -#include "digraph.h" #include "analyzer/supergraph.h" #include "analyzer/program-state.h" #include "analyzer/exploded-graph.h" #include "analyzer/state-purge.h" #include "analyzer/call-summary.h" #include "analyzer/analyzer-selftests.h" -#include "text-art/tree-widget.h" -#include "text-art/dump.h" -#include "make-unique.h" +#include "analyzer/ana-state-to-diagnostic-state.h" #if ENABLE_ANALYZER @@ -69,11 +64,10 @@ void extrinsic_state::dump_to_pp (pretty_printer *pp) const { pp_printf (pp, "extrinsic_state: %i checker(s)\n", get_num_checkers ()); - unsigned i; - state_machine *checker; - FOR_EACH_VEC_ELT (m_checkers, i, checker) + unsigned i = 0; + for (auto &checker : m_checkers) { - pp_printf (pp, "m_checkers[%i]: %qs\n", i, checker->get_name ()); + pp_printf (pp, "m_checkers[%i]: %qs\n", ++i, checker->get_name ()); checker->dump_to_pp (pp); } } @@ -101,13 +95,11 @@ extrinsic_state::dump () const std::unique_ptr<json::object> extrinsic_state::to_json () const { - auto ext_state_obj = ::make_unique<json::object> (); + auto ext_state_obj = std::make_unique<json::object> (); { - auto checkers_arr = ::make_unique<json::array> (); - unsigned i; - state_machine *sm; - FOR_EACH_VEC_ELT (m_checkers, i, sm) + auto checkers_arr = std::make_unique<json::array> (); + for (auto &sm : m_checkers) checkers_arr->append (sm->to_json ()); ext_state_obj->set ("checkers", std::move (checkers_arr)); } @@ -123,7 +115,7 @@ extrinsic_state::get_model_manager () const if (m_engine) return m_engine->get_model_manager (); else - return NULL; /* for selftests. */ + return nullptr; /* for selftests. */ } /* Try to find a state machine named NAME. @@ -133,10 +125,8 @@ extrinsic_state::get_model_manager () const bool extrinsic_state::get_sm_idx_by_name (const char *name, unsigned *out) const { - unsigned i; - state_machine *sm; - FOR_EACH_VEC_ELT (m_checkers, i, sm) - if (0 == strcmp (name, sm->get_name ())) + for (size_t i = 0; i < m_checkers.size (); ++i) + if (0 == strcmp (name, m_checkers[i]->get_name ())) { /* Found NAME. */ *out = i; @@ -268,7 +258,7 @@ DEBUG_FUNCTION void sm_state_map::dump (bool simple) const { tree_dump_pretty_printer pp (stderr); - print (NULL, simple, true, &pp); + print (nullptr, simple, true, &pp); pp_newline (&pp); } @@ -279,7 +269,7 @@ sm_state_map::dump (bool simple) const std::unique_ptr<json::object> sm_state_map::to_json () const { - auto map_obj = ::make_unique<json::object> (); + auto map_obj = std::make_unique<json::object> (); if (m_global_state != m_sm.get_start_state ()) map_obj->set ("global", m_global_state->to_json ()); @@ -430,7 +420,7 @@ sm_state_map::operator== (const sm_state_map &other) const const svalue *sval = (*iter).first; entry_t e = (*iter).second; entry_t *other_slot = const_cast <map_t &> (other.m_map).get (sval); - if (other_slot == NULL) + if (other_slot == nullptr) return false; if (e != *other_slot) return false; @@ -511,7 +501,7 @@ sm_state_map::get_origin (const svalue *sval, if (slot) return slot->m_origin; else - return NULL; + return nullptr; } /* Set the state of SID within MODEL to STATE, recording that @@ -524,7 +514,7 @@ sm_state_map::set_state (region_model *model, const svalue *origin, const extrinsic_state &ext_state) { - if (model == NULL) + if (model == nullptr) return; /* Reject attempts to set state on UNKNOWN/POISONED. */ @@ -781,7 +771,7 @@ sm_state_map::on_unknown_change (const svalue *sval, for (svalue_set::iterator iter = svals_to_unset.begin (); iter != svals_to_unset.end (); ++iter) - impl_set_state (*iter, (state_machine::state_t)0, NULL, ext_state); + impl_set_state (*iter, (state_machine::state_t)0, nullptr, ext_state); } /* Purge state for things involving SVAL. @@ -812,7 +802,7 @@ sm_state_map::purge_state_involving (const svalue *sval, for (svalue_set::iterator iter = svals_to_unset.begin (); iter != svals_to_unset.end (); ++iter) - impl_set_state (*iter, (state_machine::state_t)0, NULL, ext_state); + impl_set_state (*iter, (state_machine::state_t)0, nullptr, ext_state); } /* Comparator for imposing an order on sm_state_map instances. */ @@ -922,7 +912,7 @@ sm_state_map::can_merge_with_p (const sm_state_map &other, state_machine::state_t other_state = other.get_state (sval, ext_state); if (state_machine::state_t merged_state = sm.maybe_get_merged_state (this_state, other_state)) - (*out)->impl_set_state (sval, merged_state, NULL, ext_state); + (*out)->impl_set_state (sval, merged_state, nullptr, ext_state); else return false; } @@ -936,7 +926,7 @@ sm_state_map::can_merge_with_p (const sm_state_map &other, /* program_state's ctor. */ program_state::program_state (const extrinsic_state &ext_state) -: m_region_model (NULL), +: m_region_model (nullptr), m_checker_states (ext_state.get_num_checkers ()), m_valid (true) { @@ -970,8 +960,8 @@ sm_state_map::replay_call_summary (call_summary_replay &r, const svalue *caller_origin = (summary_origin ? r.convert_svalue_from_summary (summary_origin) - : NULL); - // caller_origin can be NULL. + : nullptr); + // caller_origin can be nullptr. m_map.put (caller_sval, entry_t (kv.second.m_state, caller_origin)); } m_global_state = summary.m_global_state; @@ -1019,7 +1009,7 @@ program_state::program_state (program_state &&other) : m_region_model (other.m_region_model), m_checker_states (other.m_checker_states.length ()) { - other.m_region_model = NULL; + other.m_region_model = nullptr; int i; sm_state_map *smap; @@ -1188,7 +1178,7 @@ program_state::dump () const std::unique_ptr<json::object> program_state::to_json (const extrinsic_state &ext_state) const { - auto state_obj = ::make_unique<json::object> (); + auto state_obj = std::make_unique<json::object> (); state_obj->set ("store", m_region_model->get_store ()->to_json ()); state_obj->set ("constraints", @@ -1199,7 +1189,7 @@ program_state::to_json (const extrinsic_state &ext_state) const /* Provide m_checker_states as an object, using names as keys. */ { - auto checkers_obj = ::make_unique<json::object> (); + auto checkers_obj = std::make_unique<json::object> (); int i; sm_state_map *smap; @@ -1237,6 +1227,24 @@ program_state::make_dump_widget (const text_art::dump_widget_info &dwi) const return state_widget; } +void +program_state::dump_dot (const extrinsic_state &ext_state) const +{ + auto state_graph = make_diagnostic_state_graph (ext_state); + + gcc_assert (global_dc); + auto logical_loc_mgr = global_dc->get_logical_location_manager (); + gcc_assert (logical_loc_mgr); + + auto graph = diagnostics::state_graphs::make_dot_graph (*state_graph, + *logical_loc_mgr); + + pretty_printer pp; + dot::writer w (pp); + graph->print (w); + pp_flush (&pp); +} + /* Update this program_state to reflect a top-level call to FUN. The params will have initial_svalues. */ @@ -1244,7 +1252,7 @@ void program_state::push_frame (const extrinsic_state &ext_state ATTRIBUTE_UNUSED, const function &fun) { - m_region_model->push_frame (fun, NULL, NULL); + m_region_model->push_frame (fun, nullptr, nullptr, nullptr); } /* Get the current function of this state. */ @@ -1335,7 +1343,7 @@ program_state::on_edge (exploded_graph &eg, return false; program_state::detect_leaks (enode->get_state (), *this, - NULL, eg.get_ext_state (), + nullptr, eg.get_ext_state (), &ctxt); return true; @@ -1348,7 +1356,7 @@ program_state::on_edge (exploded_graph &eg, void program_state::push_call (exploded_graph &eg, exploded_node *enode, - const gcall *call_stmt, + const gcall &call_stmt, uncertainty_t *uncertainty) { /* Update state. */ @@ -1359,7 +1367,7 @@ program_state::push_call (exploded_graph &eg, &enode->get_state (), this, uncertainty, - NULL, + nullptr, last_stmt); m_region_model->update_for_gcall (call_stmt, &ctxt); } @@ -1371,7 +1379,7 @@ program_state::push_call (exploded_graph &eg, void program_state::returning_call (exploded_graph &eg, exploded_node *enode, - const gcall *call_stmt, + const gcall &call_stmt, uncertainty_t *uncertainty) { /* Update state. */ @@ -1382,7 +1390,7 @@ program_state::returning_call (exploded_graph &eg, &enode->get_state (), this, uncertainty, - NULL, + nullptr, last_stmt); m_region_model->update_for_return_gcall (call_stmt, &ctxt); } @@ -1440,7 +1448,7 @@ program_state::prune_for_point (exploded_graph &eg, temporaries keep the value reachable until the frame is popped. */ const svalue *sval - = new_state.m_region_model->get_store_value (reg, NULL); + = new_state.m_region_model->get_store_value (reg, nullptr); if (!new_state.can_purge_p (eg.get_ext_state (), sval) && SSA_NAME_VAR (ssa_name)) { @@ -1499,9 +1507,9 @@ program_state::prune_for_point (exploded_graph &eg, impl_region_model_context ctxt (eg, enode_for_diag, this, &new_state, - uncertainty, NULL, + uncertainty, nullptr, point.get_stmt ()); - detect_leaks (*this, new_state, NULL, eg.get_ext_state (), &ctxt); + detect_leaks (*this, new_state, nullptr, eg.get_ext_state (), &ctxt); } } @@ -1673,7 +1681,7 @@ program_state::detect_leaks (const program_state &src_state, *might* still be reachable in dst_state. */ svalue_set known_src_svalues; src_state.m_region_model->get_reachable_svalues (&known_src_svalues, - NULL, NULL); + nullptr, nullptr); svalue_set maybe_dest_svalues; dest_state.m_region_model->get_reachable_svalues (&maybe_dest_svalues, extra_sval, uncertainty); @@ -1749,7 +1757,7 @@ program_state::replay_call_summary (call_summary_replay &r, /* Handle calls to "__analyzer_dump_state". */ void -program_state::impl_call_analyzer_dump_state (const gcall *call, +program_state::impl_call_analyzer_dump_state (const gcall &call, const extrinsic_state &ext_state, region_model_context *ctxt) { @@ -1757,13 +1765,13 @@ program_state::impl_call_analyzer_dump_state (const gcall *call, const char *sm_name = cd.get_arg_string_literal (0); if (!sm_name) { - error_at (call->location, "cannot determine state machine"); + error_at (call.location, "cannot determine state machine"); return; } unsigned sm_idx; if (!ext_state.get_sm_idx_by_name (sm_name, &sm_idx)) { - error_at (call->location, "unrecognized state machine %qs", sm_name); + error_at (call.location, "unrecognized state machine %qs", sm_name); return; } const sm_state_map *smap = m_checker_states[sm_idx]; @@ -1775,7 +1783,7 @@ program_state::impl_call_analyzer_dump_state (const gcall *call, sval = cast; state_machine::state_t state = smap->get_state (sval, ext_state); - warning_at (call->location, 0, "state: %qs", state->get_name ()); + warning_at (call.location, 0, "state: %qs", state->get_name ()); } #if CHECKING_P @@ -1791,12 +1799,13 @@ test_sm_state_map () tree y = build_global_decl ("y", integer_type_node); tree z = build_global_decl ("z", integer_type_node); - state_machine *sm = make_malloc_state_machine (NULL); - auto_delete_vec <state_machine> checkers; - checkers.safe_push (sm); - engine eng; - extrinsic_state ext_state (checkers, &eng); + std::unique_ptr<state_machine> sm = make_malloc_state_machine (nullptr); state_machine::state_t start = sm->get_start_state (); + std::vector<std::unique_ptr<state_machine>> checkers; + const state_machine &borrowed_sm = *sm.get (); + checkers.push_back (std::move (sm)); + engine eng; + extrinsic_state ext_state (std::move (checkers), &eng); /* Test setting states on svalue_id instances directly. */ { @@ -1804,11 +1813,11 @@ test_sm_state_map () const state_machine::state_t TEST_STATE_42 = &test_state_42; region_model_manager mgr; region_model model (&mgr); - const svalue *x_sval = model.get_rvalue (x, NULL); - const svalue *y_sval = model.get_rvalue (y, NULL); - const svalue *z_sval = model.get_rvalue (z, NULL); + const svalue *x_sval = model.get_rvalue (x, nullptr); + const svalue *y_sval = model.get_rvalue (y, nullptr); + const svalue *z_sval = model.get_rvalue (z, nullptr); - sm_state_map map (*sm); + sm_state_map map (borrowed_sm); ASSERT_TRUE (map.is_empty_p ()); ASSERT_EQ (map.get_state (x_sval, ext_state), start); @@ -1833,16 +1842,16 @@ test_sm_state_map () { region_model_manager mgr; region_model model (&mgr); - const svalue *x_sval = model.get_rvalue (x, NULL); - const svalue *y_sval = model.get_rvalue (y, NULL); - const svalue *z_sval = model.get_rvalue (z, NULL); + const svalue *x_sval = model.get_rvalue (x, nullptr); + const svalue *y_sval = model.get_rvalue (y, nullptr); + const svalue *z_sval = model.get_rvalue (z, nullptr); - sm_state_map map (*sm); + sm_state_map map (borrowed_sm); ASSERT_TRUE (map.is_empty_p ()); ASSERT_EQ (map.get_state (x_sval, ext_state), start); ASSERT_EQ (map.get_state (y_sval, ext_state), start); - model.add_constraint (x, EQ_EXPR, y, NULL); + model.add_constraint (x, EQ_EXPR, y, nullptr); /* Setting x to a state should also update y, as they are in the same equivalence class. */ @@ -1857,12 +1866,12 @@ test_sm_state_map () { region_model_manager mgr; region_model model (&mgr); - const svalue *y_sval = model.get_rvalue (y, NULL); - const svalue *z_sval = model.get_rvalue (z, NULL); + const svalue *y_sval = model.get_rvalue (y, nullptr); + const svalue *z_sval = model.get_rvalue (z, nullptr); - sm_state_map map0 (*sm); - sm_state_map map1 (*sm); - sm_state_map map2 (*sm); + sm_state_map map0 (borrowed_sm); + sm_state_map map1 (borrowed_sm); + sm_state_map map2 (borrowed_sm); ASSERT_EQ (map0.hash (), map1.hash ()); ASSERT_EQ (map0, map1); @@ -1883,26 +1892,26 @@ test_sm_state_map () const state_machine::state_t TEST_STATE_2 = &test_state_2; const state_machine::state test_state_3 ("test state 3", 3); const state_machine::state_t TEST_STATE_3 = &test_state_3; - sm_state_map map0 (*sm); - sm_state_map map1 (*sm); - sm_state_map map2 (*sm); + sm_state_map map0 (borrowed_sm); + sm_state_map map1 (borrowed_sm); + sm_state_map map2 (borrowed_sm); ASSERT_EQ (map0.hash (), map1.hash ()); ASSERT_EQ (map0, map1); region_model_manager mgr; region_model model (&mgr); - const svalue *x_sval = model.get_rvalue (x, NULL); - const svalue *y_sval = model.get_rvalue (y, NULL); - const svalue *z_sval = model.get_rvalue (z, NULL); + const svalue *x_sval = model.get_rvalue (x, nullptr); + const svalue *y_sval = model.get_rvalue (y, nullptr); + const svalue *z_sval = model.get_rvalue (z, nullptr); - map1.impl_set_state (x_sval, TEST_STATE_2, NULL, ext_state); - map1.impl_set_state (y_sval, TEST_STATE_3, NULL, ext_state); - map1.impl_set_state (z_sval, TEST_STATE_2, NULL, ext_state); + map1.impl_set_state (x_sval, TEST_STATE_2, nullptr, ext_state); + map1.impl_set_state (y_sval, TEST_STATE_3, nullptr, ext_state); + map1.impl_set_state (z_sval, TEST_STATE_2, nullptr, ext_state); - map2.impl_set_state (z_sval, TEST_STATE_2, NULL, ext_state); - map2.impl_set_state (y_sval, TEST_STATE_3, NULL, ext_state); - map2.impl_set_state (x_sval, TEST_STATE_2, NULL, ext_state); + map2.impl_set_state (z_sval, TEST_STATE_2, nullptr, ext_state); + map2.impl_set_state (y_sval, TEST_STATE_3, nullptr, ext_state); + map2.impl_set_state (x_sval, TEST_STATE_2, nullptr, ext_state); ASSERT_EQ (map1.hash (), map2.hash ()); ASSERT_EQ (map1, map2); @@ -1920,27 +1929,25 @@ test_program_state_1 () malloc sm-state, pointing to a region on the heap. */ tree p = build_global_decl ("p", ptr_type_node); - state_machine *sm = make_malloc_state_machine (NULL); + std::unique_ptr<state_machine> sm = make_malloc_state_machine (nullptr); const state_machine::state_t UNCHECKED_STATE = sm->get_state_by_name ("unchecked"); - auto_delete_vec <state_machine> checkers; - checkers.safe_push (sm); engine eng; - extrinsic_state ext_state (checkers, &eng); + extrinsic_state ext_state (std::move (sm), &eng); region_model_manager *mgr = eng.get_model_manager (); program_state s (ext_state); region_model *model = s.m_region_model; const svalue *size_in_bytes = mgr->get_or_create_unknown_svalue (size_type_node); const region *new_reg - = model->get_or_create_region_for_heap_alloc (size_in_bytes, NULL); + = model->get_or_create_region_for_heap_alloc (size_in_bytes, nullptr); const svalue *ptr_sval = mgr->get_ptr_svalue (ptr_type_node, new_reg); - model->set_value (model->get_lvalue (p, NULL), - ptr_sval, NULL); + model->set_value (model->get_lvalue (p, nullptr), + ptr_sval, nullptr); sm_state_map *smap = s.m_checker_states[0]; - smap->impl_set_state (ptr_sval, UNCHECKED_STATE, NULL, ext_state); + smap->impl_set_state (ptr_sval, UNCHECKED_STATE, nullptr, ext_state); ASSERT_EQ (smap->get_state (ptr_sval, ext_state), UNCHECKED_STATE); } @@ -1955,15 +1962,15 @@ test_program_state_2 () tree string_cst_ptr = build_string_literal (4, "foo"); - auto_delete_vec <state_machine> checkers; + std::vector<std::unique_ptr<state_machine>> checkers; engine eng; - extrinsic_state ext_state (checkers, &eng); + extrinsic_state ext_state (std::move (checkers), &eng); program_state s (ext_state); region_model *model = s.m_region_model; - const region *p_reg = model->get_lvalue (p, NULL); - const svalue *str_sval = model->get_rvalue (string_cst_ptr, NULL); - model->set_value (p_reg, str_sval, NULL); + const region *p_reg = model->get_lvalue (p, nullptr); + const svalue *str_sval = model->get_rvalue (string_cst_ptr, nullptr); + model->set_value (p_reg, str_sval, nullptr); } /* Verify that program_states with identical sm-state can be merged, @@ -1979,9 +1986,8 @@ test_program_state_merging () engine eng; region_model_manager *mgr = eng.get_model_manager (); program_point point (program_point::origin (*mgr)); - auto_delete_vec <state_machine> checkers; - checkers.safe_push (make_malloc_state_machine (NULL)); - extrinsic_state ext_state (checkers, &eng); + extrinsic_state ext_state (make_malloc_state_machine (nullptr), + &eng); program_state s0 (ext_state); uncertainty_t uncertainty; @@ -1991,20 +1997,20 @@ test_program_state_merging () const svalue *size_in_bytes = mgr->get_or_create_unknown_svalue (size_type_node); const region *new_reg - = model0->get_or_create_region_for_heap_alloc (size_in_bytes, NULL); + = model0->get_or_create_region_for_heap_alloc (size_in_bytes, nullptr); const svalue *ptr_sval = mgr->get_ptr_svalue (ptr_type_node, new_reg); model0->set_value (model0->get_lvalue (p, &ctxt), ptr_sval, &ctxt); sm_state_map *smap = s0.m_checker_states[0]; const state_machine::state test_state ("test state", 0); const state_machine::state_t TEST_STATE = &test_state; - smap->impl_set_state (ptr_sval, TEST_STATE, NULL, ext_state); + smap->impl_set_state (ptr_sval, TEST_STATE, nullptr, ext_state); ASSERT_EQ (smap->get_state (ptr_sval, ext_state), TEST_STATE); model0->canonicalize (); /* Verify that canonicalization preserves sm-state. */ - ASSERT_EQ (smap->get_state (model0->get_rvalue (p, NULL), ext_state), + ASSERT_EQ (smap->get_state (model0->get_rvalue (p, nullptr), ext_state), TEST_STATE); /* Make a copy of the program_state. */ @@ -2021,7 +2027,7 @@ test_program_state_merging () /* Verify that the merged state has the sm-state for "p". */ region_model *merged_model = merged.m_region_model; sm_state_map *merged_smap = merged.m_checker_states[0]; - ASSERT_EQ (merged_smap->get_state (merged_model->get_rvalue (p, NULL), + ASSERT_EQ (merged_smap->get_state (merged_model->get_rvalue (p, nullptr), ext_state), TEST_STATE); @@ -2030,7 +2036,7 @@ test_program_state_merging () merged.validate (ext_state); /* Verify that the merged state still has the sm-state for "p". */ - ASSERT_EQ (merged_smap->get_state (merged_model->get_rvalue (p, NULL), + ASSERT_EQ (merged_smap->get_state (merged_model->get_rvalue (p, nullptr), ext_state), TEST_STATE); @@ -2047,9 +2053,7 @@ test_program_state_merging_2 () engine eng; region_model_manager *mgr = eng.get_model_manager (); program_point point (program_point::origin (*mgr)); - auto_delete_vec <state_machine> checkers; - checkers.safe_push (make_signal_state_machine (NULL)); - extrinsic_state ext_state (checkers, &eng); + extrinsic_state ext_state (make_signal_state_machine (nullptr), &eng); const state_machine::state test_state_0 ("test state 0", 0); const state_machine::state test_state_1 ("test state 1", 1); diff --git a/gcc/analyzer/program-state.h b/gcc/analyzer/program-state.h index e0f4ee8..4278237 100644 --- a/gcc/analyzer/program-state.h +++ b/gcc/analyzer/program-state.h @@ -22,6 +22,9 @@ along with GCC; see the file COPYING3. If not see #define GCC_ANALYZER_PROGRAM_STATE_H #include "text-art/widget.h" +#include "text-art/tree-widget.h" + +#include "analyzer/store.h" namespace ana { @@ -30,11 +33,23 @@ namespace ana { class extrinsic_state { public: - extrinsic_state (auto_delete_vec <state_machine> &checkers, + extrinsic_state (std::vector<std::unique_ptr<state_machine>> &&checkers, + engine *eng, + logger *logger = nullptr) + : m_checkers (std::move (checkers)), + m_logger (logger), + m_engine (eng) + { + } + + // For use in selftests that use just one state machine + extrinsic_state (std::unique_ptr<state_machine> sm, engine *eng, - logger *logger = NULL) - : m_checkers (checkers), m_logger (logger), m_engine (eng) + logger *logger = nullptr) + : m_logger (logger), + m_engine (eng) { + m_checkers.push_back (std::move (sm)); } const state_machine &get_sm (int idx) const @@ -47,7 +62,7 @@ public: return m_checkers[idx]->get_name (); } - unsigned get_num_checkers () const { return m_checkers.length (); } + unsigned get_num_checkers () const { return m_checkers.size (); } logger *get_logger () const { return m_logger; } @@ -64,7 +79,7 @@ public: private: /* The state machines. */ - auto_delete_vec <state_machine> &m_checkers; + std::vector<std::unique_ptr<state_machine>> m_checkers; logger *m_logger; engine *m_engine; @@ -81,7 +96,7 @@ public: { /* Default ctor needed by hash_map::empty. */ entry_t () - : m_state (0), m_origin (NULL) + : m_state (0), m_origin (nullptr) { } @@ -231,6 +246,15 @@ public: void dump (const extrinsic_state &ext_state, bool simple) const; void dump () const; + std::unique_ptr<diagnostics::digraphs::digraph> + make_diagnostic_state_graph (const extrinsic_state &ext_state) const; + + void + dump_sarif (const extrinsic_state &ext_state) const; + + void + dump_dot (const extrinsic_state &ext_state) const; + std::unique_ptr<json::object> to_json (const extrinsic_state &ext_state) const; @@ -242,12 +266,12 @@ public: void push_call (exploded_graph &eg, exploded_node *enode, - const gcall *call_stmt, + const gcall &call_stmt, uncertainty_t *uncertainty); void returning_call (exploded_graph &eg, exploded_node *enode, - const gcall *call_stmt, + const gcall &call_stmt, uncertainty_t *uncertainty); @@ -298,7 +322,7 @@ public: bool replay_call_summary (call_summary_replay &r, const program_state &summary); - void impl_call_analyzer_dump_state (const gcall *call, + void impl_call_analyzer_dump_state (const gcall &call, const extrinsic_state &ext_state, region_model_context *ctxt); diff --git a/gcc/analyzer/ranges.cc b/gcc/analyzer/ranges.cc index 4c63ecc..1a960fa 100644 --- a/gcc/analyzer/ranges.cc +++ b/gcc/analyzer/ranges.cc @@ -18,34 +18,13 @@ 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/>. */ -#include "config.h" -#define INCLUDE_VECTOR -#include "system.h" -#include "coretypes.h" -#include "tree.h" -#include "diagnostic-core.h" -#include "gimple-pretty-print.h" -#include "function.h" -#include "basic-block.h" -#include "gimple.h" -#include "gimple-iterator.h" -#include "diagnostic-core.h" -#include "graphviz.h" -#include "options.h" -#include "cgraph.h" -#include "tree-dfa.h" -#include "stringpool.h" -#include "convert.h" -#include "target.h" -#include "fold-const.h" -#include "tree-pretty-print.h" -#include "bitmap.h" -#include "analyzer/analyzer.h" -#include "analyzer/analyzer-logging.h" +#include "analyzer/common.h" + +#include "sbitmap.h" #include "ordered-hash-map.h" -#include "options.h" + +#include "analyzer/analyzer-logging.h" #include "analyzer/supergraph.h" -#include "sbitmap.h" #include "analyzer/call-string.h" #include "analyzer/program-point.h" #include "analyzer/store.h" @@ -53,7 +32,6 @@ along with GCC; see the file COPYING3. If not see #include "analyzer/constraint-manager.h" #include "analyzer/analyzer-selftests.h" #include "analyzer/ranges.h" -#include "make-unique.h" #if ENABLE_ANALYZER @@ -158,7 +136,7 @@ symbolic_byte_range::dump (bool simple, region_model_manager &mgr) const std::unique_ptr<json::value> symbolic_byte_range::to_json () const { - auto obj = ::make_unique<json::object> (); + auto obj = std::make_unique<json::object> (); obj->set ("start", m_start.to_json ()); obj->set ("size", m_size.to_json ()); return obj; diff --git a/gcc/analyzer/record-layout.cc b/gcc/analyzer/record-layout.cc index 2eb3444..3446536 100644 --- a/gcc/analyzer/record-layout.cc +++ b/gcc/analyzer/record-layout.cc @@ -18,18 +18,10 @@ 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/>. */ -#include "config.h" -#define INCLUDE_VECTOR -#include "system.h" -#include "coretypes.h" -#include "tree.h" -#include "function.h" -#include "basic-block.h" -#include "gimple.h" -#include "diagnostic-core.h" -#include "diagnostic.h" +#include "analyzer/common.h" + #include "tree-diagnostic.h" -#include "analyzer/analyzer.h" + #include "analyzer/record-layout.h" #if ENABLE_ANALYZER @@ -38,7 +30,7 @@ namespace ana { /* class record_layout. */ -record_layout::record_layout (tree record_type) +record_layout::record_layout (const_tree record_type) { gcc_assert (TREE_CODE (record_type) == RECORD_TYPE); @@ -94,7 +86,7 @@ record_layout::get_item_at (bit_offset_t offset) const FOR_EACH_VEC_ELT (m_items, i, it) if (it->contains_p (offset)) return it; - return NULL; + return nullptr; } /* Subroutine of ctor. Add padding item to NEXT_OFFSET if necessary. */ diff --git a/gcc/analyzer/record-layout.h b/gcc/analyzer/record-layout.h index c1c4189..8649d1d 100644 --- a/gcc/analyzer/record-layout.h +++ b/gcc/analyzer/record-layout.h @@ -36,7 +36,7 @@ public: { public: item (const bit_range &br, - tree field, + const_tree field, bool is_padding) : m_bit_range (br), m_field (field), @@ -69,17 +69,20 @@ public: } bit_range m_bit_range; - tree m_field; + const_tree m_field; bool m_is_padding; }; - record_layout (tree record_type); + record_layout (const_tree record_type); void dump_to_pp (pretty_printer *pp) const; DEBUG_FUNCTION void dump () const; const record_layout::item *get_item_at (bit_offset_t offset) const; + auto begin () const { return m_items.begin (); } + auto end () const { return m_items.end (); } + private: void maybe_pad_to (bit_offset_t next_offset); diff --git a/gcc/analyzer/region-model-asm.cc b/gcc/analyzer/region-model-asm.cc index 0da5cc5..fe70490 100644 --- a/gcc/analyzer/region-model-asm.cc +++ b/gcc/analyzer/region-model-asm.cc @@ -18,26 +18,16 @@ 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/>. */ -#include "config.h" -#define INCLUDE_VECTOR -#include "system.h" -#include "coretypes.h" -#include "tree.h" -#include "function.h" -#include "basic-block.h" -#include "gimple.h" -#include "gimple-iterator.h" -#include "diagnostic-core.h" -#include "pretty-print.h" -#include "analyzer/analyzer.h" +#include "analyzer/common.h" + +#include "stmt.h" + #include "analyzer/analyzer-logging.h" -#include "options.h" #include "analyzer/call-string.h" #include "analyzer/program-point.h" #include "analyzer/store.h" #include "analyzer/region-model.h" #include "analyzer/region-model-reachability.h" -#include "stmt.h" #if ENABLE_ANALYZER @@ -129,7 +119,7 @@ deterministic_p (const gasm *asm_stmt) void region_model::on_asm_stmt (const gasm *stmt, region_model_context *ctxt) { - logger *logger = ctxt ? ctxt->get_logger () : NULL; + logger *logger = ctxt ? ctxt->get_logger () : nullptr; LOG_SCOPE (logger); const unsigned noutputs = gimple_asm_noutputs (stmt); @@ -181,7 +171,8 @@ region_model::on_asm_stmt (const gasm *stmt, region_model_context *ctxt) no point in going further. */ constraint = constraints[i]; if (!parse_output_constraint (&constraint, i, ninputs, noutputs, - &allows_mem, &allows_reg, &is_inout)) + &allows_mem, &allows_reg, &is_inout, + nullptr)) { if (logger) logger->log ("error parsing constraint for output %i: %qs", @@ -214,8 +205,8 @@ region_model::on_asm_stmt (const gasm *stmt, region_model_context *ctxt) const char *constraint = constraints[i + noutputs]; bool allows_reg, allows_mem; if (! parse_input_constraint (&constraint, i, ninputs, noutputs, 0, - constraints.address (), - &allows_mem, &allows_reg)) + constraints.address (), &allows_mem, + &allows_reg, nullptr)) { if (logger) logger->log ("error parsing constraint for input %i: %qs", @@ -226,7 +217,7 @@ region_model::on_asm_stmt (const gasm *stmt, region_model_context *ctxt) tree src_expr = input_tvec[i]; const svalue *src_sval = get_rvalue (src_expr, ctxt); - check_for_poison (src_sval, src_expr, NULL, ctxt); + check_for_poison (src_sval, src_expr, nullptr, ctxt); input_svals.quick_push (src_sval); reachable_regs.handle_sval (src_sval); diff --git a/gcc/analyzer/region-model-manager.cc b/gcc/analyzer/region-model-manager.cc index dfce420..872b1d6 100644 --- a/gcc/analyzer/region-model-manager.cc +++ b/gcc/analyzer/region-model-manager.cc @@ -18,34 +18,16 @@ 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/>. */ -#include "config.h" -#define INCLUDE_VECTOR -#include "system.h" -#include "coretypes.h" -#include "tree.h" -#include "diagnostic-core.h" -#include "gimple-pretty-print.h" -#include "function.h" -#include "basic-block.h" -#include "gimple.h" -#include "gimple-iterator.h" -#include "diagnostic-core.h" -#include "graphviz.h" -#include "options.h" -#include "cgraph.h" -#include "tree-dfa.h" -#include "stringpool.h" -#include "convert.h" -#include "target.h" +#include "analyzer/common.h" + #include "fold-const.h" -#include "tree-pretty-print.h" -#include "bitmap.h" -#include "analyzer/analyzer.h" -#include "analyzer/analyzer-logging.h" #include "ordered-hash-map.h" #include "options.h" #include "analyzer/supergraph.h" #include "sbitmap.h" +#include "target.h" + +#include "analyzer/analyzer-logging.h" #include "analyzer/call-string.h" #include "analyzer/program-point.h" #include "analyzer/store.h" @@ -67,7 +49,7 @@ region_model_manager::region_model_manager (logger *logger) m_root_region (alloc_symbol_id ()), m_stack_region (alloc_symbol_id (), &m_root_region), m_heap_region (alloc_symbol_id (), &m_root_region), - m_unknown_NULL (NULL), + m_unknown_NULL (nullptr), m_checking_feasibility (false), m_max_complexity (0, 0), m_code_region (alloc_symbol_id (), &m_root_region), @@ -277,7 +259,7 @@ region_model_manager::get_or_create_null_ptr (tree pointer_type) return get_or_create_int_cst (pointer_type, 0); } -/* Return the svalue * for a unknown_svalue for TYPE (which can be NULL), +/* Return the svalue * for a unknown_svalue for TYPE (which can be NULL_TREE), creating it if necessary. The unknown_svalue instances are reused, based on pointer equality of the types */ @@ -325,7 +307,7 @@ region_model_manager::get_or_create_initial_value (const region *reg, bool check_poisoned) { if (!reg->can_have_initial_svalue_p () && check_poisoned) - return get_or_create_poisoned_svalue (POISON_KIND_UNINIT, + return get_or_create_poisoned_svalue (poison_kind::uninit, reg->get_type ()); /* The initial value of a cast is a cast of the initial value. */ @@ -425,7 +407,7 @@ region_model_manager::get_ptr_svalue (tree ptr_type, const region *pointee) /* Subroutine of region_model_manager::get_or_create_unaryop. Attempt to fold the inputs and return a simpler svalue *. - Otherwise, return NULL. */ + Otherwise, return nullptr. */ const svalue * region_model_manager::maybe_fold_unaryop (tree type, enum tree_code op, @@ -534,7 +516,7 @@ region_model_manager::maybe_fold_unaryop (tree type, enum tree_code op, } } - return NULL; + return nullptr; } /* Return the svalue * for an unary operation OP on ARG with a result of @@ -612,7 +594,7 @@ region_model_manager::get_or_create_cast (tree type, const svalue *arg) If COMPOUND_SVAL has a value for the appropriate bits, return it, shifted accordingly. - Otherwise return NULL. */ + Otherwise return nullptr. */ const svalue * region_model_manager:: @@ -624,7 +606,7 @@ maybe_undo_optimize_bit_field_compare (tree type, if (!type) return nullptr; if (!INTEGRAL_TYPE_P (type)) - return NULL; + return nullptr; const binding_map &map = compound_sval->get_map (); unsigned HOST_WIDE_INT mask = TREE_INT_CST_LOW (cst); @@ -632,7 +614,7 @@ maybe_undo_optimize_bit_field_compare (tree type, compound_sval has a value for those bits. */ bit_range bits (0, 0); if (!bit_range::from_mask (mask, &bits)) - return NULL; + return nullptr; bit_range bound_bits (bits); if (BYTES_BIG_ENDIAN) @@ -642,7 +624,7 @@ maybe_undo_optimize_bit_field_compare (tree type, = get_store_manager ()->get_concrete_binding (bound_bits); const svalue *sval = map.get (conc); if (!sval) - return NULL; + return nullptr; /* We have a value; shift it by the correct number of bits. */ @@ -659,7 +641,7 @@ maybe_undo_optimize_bit_field_compare (tree type, /* Subroutine of region_model_manager::get_or_create_binop. Attempt to fold the inputs and return a simpler svalue *. - Otherwise, return NULL. */ + Otherwise, return nullptr. */ const svalue * region_model_manager::maybe_fold_binop (tree type, enum tree_code op, @@ -687,7 +669,7 @@ region_model_manager::maybe_fold_binop (tree type, enum tree_code op, if ((type && FLOAT_TYPE_P (type)) || (arg0->get_type () && FLOAT_TYPE_P (arg0->get_type ())) || (arg1->get_type () && FLOAT_TYPE_P (arg1->get_type ()))) - return NULL; + return nullptr; switch (op) { @@ -916,7 +898,7 @@ region_model_manager::maybe_fold_binop (tree type, enum tree_code op, /* etc. */ - return NULL; + return nullptr; } /* Return the svalue * for an binary operation OP on ARG0 and ARG1 @@ -951,7 +933,7 @@ region_model_manager::get_or_create_binop (tree type, enum tree_code op, } /* Subroutine of region_model_manager::get_or_create_sub_svalue. - Return a folded svalue, or NULL. */ + Return a folded svalue, or nullptr. */ const svalue * region_model_manager::maybe_fold_sub_svalue (tree type, @@ -962,6 +944,12 @@ region_model_manager::maybe_fold_sub_svalue (tree type, if (!parent_svalue->can_have_associated_state_p ()) return get_or_create_unknown_svalue (type); + /* If we have a subvalue of a zero constant, it's zero. */ + if (tree cst = parent_svalue->maybe_get_constant ()) + if (TREE_CODE (cst) == INTEGER_CST) + if (zerop (cst)) + return get_or_create_cast (type, parent_svalue); + /* If we have a subregion of a zero-fill, it's zero. */ if (const unaryop_svalue *unary = parent_svalue->dyn_cast_unaryop_svalue ()) @@ -1032,7 +1020,7 @@ region_model_manager::maybe_fold_sub_svalue (tree type, if (type) return get_or_create_cast (type, repeated_sval->get_inner_svalue ()); - return NULL; + return nullptr; } /* Return the svalue * for extracting a subvalue of type TYPE from @@ -1058,7 +1046,7 @@ region_model_manager::get_or_create_sub_svalue (tree type, } /* Subroutine of region_model_manager::get_or_create_repeated_svalue. - Return a folded svalue, or NULL. */ + Return a folded svalue, or nullptr. */ const svalue * region_model_manager::maybe_fold_repeated_svalue (tree type, @@ -1092,7 +1080,7 @@ region_model_manager::maybe_fold_repeated_svalue (tree type, if (zerop (cst) && type) return get_or_create_cast (type, inner_svalue); - return NULL; + return nullptr; } /* Return the svalue * of type TYPE in which INNER_SVALUE is repeated @@ -1286,7 +1274,7 @@ region_model_manager::maybe_fold_bits_within_svalue (tree type, } break; } - return NULL; + return nullptr; } /* Return the svalue * of type TYPE for extracting BITS from INNER_SVALUE, @@ -1413,7 +1401,7 @@ region_model_manager::get_or_create_conjured_svalue (tree type, } /* Subroutine of region_model_manager::get_or_create_asm_output_svalue. - Return a folded svalue, or NULL. */ + Return a folded svalue, or nullptr. */ const svalue * region_model_manager:: @@ -1425,7 +1413,7 @@ maybe_fold_asm_output_svalue (tree type, if (iter->get_kind () == SK_UNKNOWN) return get_or_create_unknown_svalue (type); - return NULL; + return nullptr; } /* Return the svalue * of type TYPE for OUTPUT_IDX of the deterministic @@ -1513,7 +1501,7 @@ get_or_create_const_fn_result_svalue (tree type, /* Given DATA_CST (a STRING_CST or RAW_DATA_CST) and BYTE_OFFSET_CST a constant, attempt to get the character at that offset, returning either - the svalue for the character constant, or NULL if unsuccessful. */ + the svalue for the character constant, or nullptr if unsuccessful. */ const svalue * region_model_manager::maybe_get_char_from_cst (tree data_cst, @@ -1545,7 +1533,7 @@ get_string_cst_size (const_tree string_cst) /* Given STRING_CST, a STRING_CST and BYTE_OFFSET_CST a constant, attempt to get the character at that offset, returning either - the svalue for the character constant, or NULL if unsuccessful. */ + the svalue for the character constant, or nullptr if unsuccessful. */ const svalue * region_model_manager::maybe_get_char_from_string_cst (tree string_cst, @@ -1564,7 +1552,7 @@ region_model_manager::maybe_get_char_from_string_cst (tree string_cst, if (compare_constants (byte_offset_cst, GE_EXPR, get_string_cst_size (string_cst)).is_true ()) - return NULL; + return nullptr; int char_val; if (compare_tree_int (byte_offset_cst, @@ -1579,12 +1567,12 @@ region_model_manager::maybe_get_char_from_string_cst (tree string_cst, = build_int_cst_type (TREE_TYPE (TREE_TYPE (string_cst)), char_val); return get_or_create_constant_svalue (char_cst); } - return NULL; + return nullptr; } /* Given RAW_DATA_CST, a RAW_DATA_CST and BYTE_OFFSET_CST a constant, attempt to get the character at that offset, returning either - the svalue for the character constant, or NULL if unsuccessful. */ + the svalue for the character constant, or nullptr if unsuccessful. */ const svalue * region_model_manager::maybe_get_char_from_raw_data_cst (tree raw_data_cst, @@ -1813,7 +1801,7 @@ region_model_manager::get_cast_region (const region *original_region, } /* Return the frame_region for call to FUN from CALLING_FRAME, creating it - if necessary. CALLING_FRAME may be NULL. */ + if necessary. CALLING_FRAME may be nullptr. */ const frame_region * region_model_manager::get_frame_region (const frame_region *calling_frame, diff --git a/gcc/analyzer/region-model-manager.h b/gcc/analyzer/region-model-manager.h index c3f0a64..865bedf 100644 --- a/gcc/analyzer/region-model-manager.h +++ b/gcc/analyzer/region-model-manager.h @@ -31,7 +31,7 @@ namespace ana { class region_model_manager { public: - region_model_manager (logger *logger = NULL); + region_model_manager (logger *logger = nullptr); ~region_model_manager (); unsigned get_num_symbols () const { return m_next_symbol_id; } diff --git a/gcc/analyzer/region-model-reachability.cc b/gcc/analyzer/region-model-reachability.cc index 4799ba8..0fe324d 100644 --- a/gcc/analyzer/region-model-reachability.cc +++ b/gcc/analyzer/region-model-reachability.cc @@ -18,37 +18,18 @@ 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/>. */ -#include "config.h" -#define INCLUDE_VECTOR -#include "system.h" -#include "coretypes.h" -#include "tree.h" -#include "function.h" -#include "basic-block.h" -#include "gimple.h" -#include "gimple-iterator.h" -#include "diagnostic-core.h" -#include "graphviz.h" -#include "options.h" -#include "cgraph.h" -#include "tree-dfa.h" -#include "stringpool.h" -#include "convert.h" -#include "target.h" -#include "fold-const.h" -#include "tree-pretty-print.h" -#include "bitmap.h" -#include "analyzer/analyzer.h" -#include "analyzer/analyzer-logging.h" +#include "analyzer/common.h" + #include "ordered-hash-map.h" -#include "options.h" +#include "diagnostic.h" +#include "tree-diagnostic.h" + +#include "analyzer/analyzer-logging.h" #include "analyzer/call-string.h" #include "analyzer/program-point.h" #include "analyzer/store.h" #include "analyzer/region-model.h" #include "analyzer/region-model-reachability.h" -#include "diagnostic.h" -#include "tree-diagnostic.h" #if ENABLE_ANALYZER @@ -87,7 +68,7 @@ reachable_regions::init_cluster (const region *base_reg) if (const symbolic_region *sym_reg = base_reg->dyn_cast_symbolic_region ()) { const svalue *ptr = sym_reg->get_pointer (); - if (ptr->implicitly_live_p (NULL, m_model)) + if (ptr->implicitly_live_p (nullptr, m_model)) add (base_reg, true); switch (ptr->get_kind ()) { @@ -103,7 +84,7 @@ reachable_regions::init_cluster (const region *base_reg) const region *other_base_reg = init_sval_reg->get_base_region (); const binding_cluster *other_cluster = m_store->get_cluster (other_base_reg); - if (other_cluster == NULL + if (other_cluster == nullptr || !other_cluster->touched_p ()) add (base_reg, true); } @@ -150,7 +131,7 @@ reachable_regions::add (const region *reg, bool is_mutable) if (binding_cluster *bind_cluster = m_store->get_cluster (base_reg)) bind_cluster->for_each_value (handle_sval_cb, this); else - handle_sval (m_model->get_store_value (reg, NULL)); + handle_sval (m_model->get_store_value (reg, nullptr)); } void diff --git a/gcc/analyzer/region-model.cc b/gcc/analyzer/region-model.cc index 84b81e9..618d96b 100644 --- a/gcc/analyzer/region-model.cc +++ b/gcc/analyzer/region-model.cc @@ -18,70 +18,53 @@ 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/>. */ -#include "config.h" #define INCLUDE_ALGORITHM -#define INCLUDE_VECTOR -#include "system.h" -#include "coretypes.h" -#include "make-unique.h" -#include "tree.h" -#include "function.h" -#include "basic-block.h" -#include "gimple.h" -#include "gimple-iterator.h" -#include "diagnostic-core.h" -#include "graphviz.h" +#include "analyzer/common.h" + +#include "ordered-hash-map.h" #include "options.h" #include "cgraph.h" -#include "tree-dfa.h" +#include "cfg.h" +#include "sbitmap.h" +#include "diagnostics/event-id.h" +#include "stor-layout.h" #include "stringpool.h" -#include "convert.h" +#include "attribs.h" +#include "tree-object-size.h" +#include "gimple-ssa.h" +#include "tree-phinodes.h" +#include "tree-ssa-operands.h" +#include "ssa-iterators.h" #include "target.h" -#include "fold-const.h" +#include "calls.h" +#include "is-a.h" +#include "gcc-rich-location.h" +#include "gcc-urlifier.h" +#include "diagnostics/sarif-sink.h" #include "tree-pretty-print.h" -#include "diagnostic-color.h" -#include "bitmap.h" -#include "selftest.h" +#include "fold-const.h" #include "selftest-tree.h" -#include "analyzer/analyzer.h" + +#include "text-art/tree-widget.h" + #include "analyzer/analyzer-logging.h" -#include "ordered-hash-map.h" -#include "options.h" -#include "cgraph.h" -#include "cfg.h" #include "analyzer/supergraph.h" -#include "sbitmap.h" #include "analyzer/call-string.h" #include "analyzer/program-point.h" #include "analyzer/store.h" #include "analyzer/region-model.h" #include "analyzer/constraint-manager.h" -#include "diagnostic-event-id.h" -#include "analyzer/sm.h" -#include "diagnostic-event-id.h" #include "analyzer/sm.h" #include "analyzer/pending-diagnostic.h" #include "analyzer/region-model-reachability.h" #include "analyzer/analyzer-selftests.h" #include "analyzer/program-state.h" #include "analyzer/call-summary.h" -#include "stor-layout.h" -#include "attribs.h" -#include "tree-object-size.h" -#include "gimple-ssa.h" -#include "tree-phinodes.h" -#include "tree-ssa-operands.h" -#include "ssa-iterators.h" -#include "calls.h" -#include "is-a.h" -#include "gcc-rich-location.h" #include "analyzer/checker-event.h" #include "analyzer/checker-path.h" #include "analyzer/feasible-graph.h" #include "analyzer/record-layout.h" -#include "diagnostic-format-sarif.h" -#include "text-art/tree-widget.h" -#include "gcc-urlifier.h" +#include "analyzer/function-set.h" #if ENABLE_ANALYZER @@ -176,7 +159,7 @@ region_to_value_map::operator== (const region_to_value_map &other) const const region *reg = iter.first; const svalue *sval = iter.second; const svalue * const *other_slot = other.get (reg); - if (other_slot == NULL) + if (other_slot == nullptr) return false; if (sval != *other_slot) return false; @@ -235,7 +218,7 @@ region_to_value_map::dump (bool simple) const std::unique_ptr<json::object> region_to_value_map::to_json () const { - auto map_obj = ::make_unique<json::object> (); + auto map_obj = std::make_unique<json::object> (); auto_vec<const region *> regs; for (iterator iter = begin (); iter != end (); ++iter) @@ -332,12 +315,97 @@ region_to_value_map::purge_state_involving (const svalue *sval) m_hash_map.remove (iter); } +// struct exception_node + +bool +exception_node::operator== (const exception_node &other) const +{ + return (m_exception_sval == other.m_exception_sval + && m_typeinfo_sval == other.m_typeinfo_sval + && m_destructor_sval == other.m_destructor_sval); +} + +void +exception_node::dump_to_pp (pretty_printer *pp, + bool simple) const +{ + pp_printf (pp, "{exception: "); + m_exception_sval->dump_to_pp (pp, simple); + pp_string (pp, ", typeinfo: "); + m_typeinfo_sval->dump_to_pp (pp, simple); + pp_string (pp, ", destructor: "); + m_destructor_sval->dump_to_pp (pp, simple); + pp_string (pp, "}"); +} + +void +exception_node::dump (FILE *fp, bool simple) const +{ + tree_dump_pretty_printer pp (fp); + dump_to_pp (&pp, simple); + pp_newline (&pp); +} + +/* Dump a multiline representation of this model to stderr. */ + +DEBUG_FUNCTION void +exception_node::dump (bool simple) const +{ + dump (stderr, simple); +} + +DEBUG_FUNCTION void +exception_node::dump () const +{ + text_art::dump (*this); +} + +std::unique_ptr<json::object> +exception_node::to_json () const +{ + auto obj = std::make_unique<json::object> (); + obj->set ("exception", m_exception_sval->to_json ()); + obj->set ("typeinfo", m_typeinfo_sval->to_json ()); + obj->set ("destructor", m_destructor_sval->to_json ()); + return obj; +} + +std::unique_ptr<text_art::tree_widget> +exception_node::make_dump_widget (const text_art::dump_widget_info &dwi) const +{ + using text_art::tree_widget; + std::unique_ptr<tree_widget> w + (tree_widget::from_fmt (dwi, nullptr, "Exception Node")); + + w->add_child (m_exception_sval->make_dump_widget (dwi, "exception")); + w->add_child (m_typeinfo_sval->make_dump_widget (dwi, "typeinfo")); + w->add_child (m_destructor_sval->make_dump_widget (dwi, "destructor")); + + return w; +} + +tree +exception_node::maybe_get_type () const +{ + return m_typeinfo_sval->maybe_get_type_from_typeinfo (); +} + +void +exception_node::add_to_reachable_regions (reachable_regions ®s) const +{ + regs.handle_sval (m_exception_sval); + regs.handle_sval (m_typeinfo_sval); + regs.handle_sval (m_destructor_sval); +} + /* class region_model. */ /* Ctor for region_model: construct an "empty" model. */ region_model::region_model (region_model_manager *mgr) -: m_mgr (mgr), m_store (), m_current_frame (NULL), +: m_mgr (mgr), m_store (), m_current_frame (nullptr), + m_thrown_exceptions_stack (), + m_caught_exceptions_stack (), m_dynamic_extents () { m_constraints = new constraint_manager (mgr); @@ -349,6 +417,8 @@ region_model::region_model (const region_model &other) : m_mgr (other.m_mgr), m_store (other.m_store), m_constraints (new constraint_manager (*other.m_constraints)), m_current_frame (other.m_current_frame), + m_thrown_exceptions_stack (other.m_thrown_exceptions_stack), + m_caught_exceptions_stack (other.m_caught_exceptions_stack), m_dynamic_extents (other.m_dynamic_extents) { } @@ -375,6 +445,9 @@ region_model::operator= (const region_model &other) m_current_frame = other.m_current_frame; + m_thrown_exceptions_stack = other.m_thrown_exceptions_stack; + m_caught_exceptions_stack = other.m_caught_exceptions_stack; + m_dynamic_extents = other.m_dynamic_extents; return *this; @@ -401,6 +474,11 @@ region_model::operator== (const region_model &other) const if (m_current_frame != other.m_current_frame) return false; + if (m_thrown_exceptions_stack != other.m_thrown_exceptions_stack) + return false; + if (m_caught_exceptions_stack != other.m_caught_exceptions_stack) + return false; + if (m_dynamic_extents != other.m_dynamic_extents) return false; @@ -427,7 +505,7 @@ void region_model::dump_to_pp (pretty_printer *pp, bool simple, bool multiline) const { - /* Dump stack. */ + /* Dump frame stack. */ pp_printf (pp, "stack depth: %i", get_stack_depth ()); if (multiline) pp_newline (pp); @@ -448,6 +526,50 @@ region_model::dump_to_pp (pretty_printer *pp, bool simple, if (!multiline) pp_string (pp, "}"); + /* Dump exception stacks. */ + if (m_thrown_exceptions_stack.size () > 0) + { + pp_printf (pp, "thrown exceptions: %i", (int)m_thrown_exceptions_stack.size ()); + if (multiline) + pp_newline (pp); + else + pp_string (pp, " {"); + for (size_t idx = 0; idx < m_thrown_exceptions_stack.size (); ++idx) + { + if (multiline) + pp_string (pp, " "); + else if (idx > 0) + pp_string (pp, ", "); + pp_printf (pp, "exception (index %i): ", (int)idx); + m_thrown_exceptions_stack[idx].dump_to_pp (pp, simple); + if (multiline) + pp_newline (pp); + } + if (!multiline) + pp_string (pp, "}"); + } + if (m_caught_exceptions_stack.size () > 0) + { + pp_printf (pp, "caught exceptions: %i", (int)m_caught_exceptions_stack.size ()); + if (multiline) + pp_newline (pp); + else + pp_string (pp, " {"); + for (size_t idx = 0; idx < m_caught_exceptions_stack.size (); ++idx) + { + if (multiline) + pp_string (pp, " "); + else if (idx > 0) + pp_string (pp, ", "); + pp_printf (pp, "exception (index %i): ", (int)idx); + m_caught_exceptions_stack[idx].dump_to_pp (pp, simple); + if (multiline) + pp_newline (pp); + } + if (!multiline) + pp_string (pp, "}"); + } + /* Dump store. */ if (!multiline) pp_string (pp, ", {"); @@ -515,11 +637,22 @@ region_model::debug () const std::unique_ptr<json::object> region_model::to_json () const { - auto model_obj = ::make_unique<json::object> (); + auto model_obj = std::make_unique<json::object> (); model_obj->set ("store", m_store.to_json ()); model_obj->set ("constraints", m_constraints->to_json ()); if (m_current_frame) model_obj->set ("current_frame", m_current_frame->to_json ()); + + auto thrown_exceptions_arr = std::make_unique<json::array> (); + for (auto &node : m_thrown_exceptions_stack) + thrown_exceptions_arr->append (node.to_json ()); + model_obj->set ("thrown_exception_stack", std::move (thrown_exceptions_arr)); + + auto caught_exceptions_arr = std::make_unique<json::array> (); + for (auto &node : m_caught_exceptions_stack) + caught_exceptions_arr->append (node.to_json ()); + model_obj->set ("caught_exception_stack", std::move (caught_exceptions_arr)); + model_obj->set ("dynamic_extents", m_dynamic_extents.to_json ()); return model_obj; } @@ -543,6 +676,26 @@ region_model::make_dump_widget (const text_art::dump_widget_info &dwi) const m_current_frame->dump_to_pp (pp, simple); model_widget->add_child (tree_widget::make (dwi, pp)); } + + if (m_thrown_exceptions_stack.size () > 0) + { + auto thrown_exceptions_widget + = tree_widget::make (dwi, "Thrown Exceptions"); + for (auto &thrown_exception : m_thrown_exceptions_stack) + thrown_exceptions_widget->add_child + (thrown_exception.make_dump_widget (dwi)); + model_widget->add_child (std::move (thrown_exceptions_widget)); + } + if (m_caught_exceptions_stack.size () > 0) + { + auto caught_exceptions_widget + = tree_widget::make (dwi, "Caught Exceptions"); + for (auto &caught_exception : m_caught_exceptions_stack) + caught_exceptions_widget->add_child + (caught_exception.make_dump_widget (dwi)); + model_widget->add_child (std::move (caught_exceptions_widget)); + } + model_widget->add_child (m_store.make_dump_widget (dwi, m_mgr->get_store_manager ())); @@ -606,7 +759,7 @@ public: bool use_of_uninit_p () const final override { - return m_pkind == POISON_KIND_UNINIT; + return m_pkind == poison_kind::uninit; } bool operator== (const poisoned_value_diagnostic &other) const @@ -622,12 +775,12 @@ public: { default: gcc_unreachable (); - case POISON_KIND_UNINIT: + case poison_kind::uninit: return OPT_Wanalyzer_use_of_uninitialized_value; - case POISON_KIND_FREED: - case POISON_KIND_DELETED: + case poison_kind::freed: + case poison_kind::deleted: return OPT_Wanalyzer_use_after_free; - case POISON_KIND_POPPED_STACK: + case poison_kind::popped_stack: return OPT_Wanalyzer_use_of_pointer_in_stale_stack_frame; } } @@ -640,28 +793,28 @@ public: { default: gcc_unreachable (); - case POISON_KIND_UNINIT: + case poison_kind::uninit: { ctxt.add_cwe (457); /* "CWE-457: Use of Uninitialized Variable". */ return ctxt.warn ("use of uninitialized value %qE", m_expr); } break; - case POISON_KIND_FREED: + case poison_kind::freed: { ctxt.add_cwe (416); /* "CWE-416: Use After Free". */ return ctxt.warn ("use after %<free%> of %qE", m_expr); } break; - case POISON_KIND_DELETED: + case poison_kind::deleted: { ctxt.add_cwe (416); /* "CWE-416: Use After Free". */ return ctxt.warn ("use after %<delete%> of %qE", m_expr); } break; - case POISON_KIND_POPPED_STACK: + case poison_kind::popped_stack: { /* TODO: which CWE? */ return ctxt.warn @@ -680,28 +833,28 @@ public: { default: gcc_unreachable (); - case POISON_KIND_UNINIT: + case poison_kind::uninit: { pp_printf (&pp, "use of uninitialized value %qE here", m_expr); return true; } - case POISON_KIND_FREED: + case poison_kind::freed: { pp_printf (&pp, "use after %<free%> of %qE here", m_expr); return true; } - case POISON_KIND_DELETED: + case poison_kind::deleted: { pp_printf (&pp, "use after %<delete%> of %qE here", m_expr); return true; } - case POISON_KIND_POPPED_STACK: + case poison_kind::popped_stack: { pp_printf (&pp, "dereferencing pointer %qE to within stale stack frame", @@ -742,7 +895,7 @@ public: /* Couldn't get state; accept this diagnostic. */ return true; - const svalue *fsval = emission_model.get_rvalue (m_check_expr, NULL); + const svalue *fsval = emission_model.get_rvalue (m_check_expr, nullptr); /* Check to see if the expr is also poisoned in FNODE (and in the same way). */ const poisoned_svalue * fspval = fsval->dyn_cast_poisoned_svalue (); @@ -754,9 +907,10 @@ public: } void - maybe_add_sarif_properties (sarif_object &result_obj) const final override + maybe_add_sarif_properties (diagnostics::sarif_object &result_obj) + const final override { - sarif_property_bag &props = result_obj.get_or_create_properties (); + auto &props = result_obj.get_or_create_properties (); #define PROPERTY_PREFIX "gcc/analyzer/poisoned_value_diagnostic/" props.set (PROPERTY_PREFIX "expr", tree_to_json (m_expr)); props.set_string (PROPERTY_PREFIX "kind", poison_kind_to_str (m_pkind)); @@ -956,10 +1110,10 @@ public: { if (reg == m_base_reg_a) emission_path.add_event - (make_unique<ptrdiff_region_creation_event> (loc_info, true)); + (std::make_unique<ptrdiff_region_creation_event> (loc_info, true)); else if (reg == m_base_reg_b) emission_path.add_event - (make_unique<ptrdiff_region_creation_event> (loc_info, false)); + (std::make_unique<ptrdiff_region_creation_event> (loc_info, false)); } bool @@ -1009,17 +1163,18 @@ check_for_invalid_ptrdiff (const gassign *assign, if (base_reg_b->get_kind () == RK_SYMBOLIC) return; - ctxt.warn (make_unique<undefined_ptrdiff_diagnostic> (assign, - sval_a, - sval_b, - base_reg_a, - base_reg_b)); + ctxt.warn + (std::make_unique<undefined_ptrdiff_diagnostic> (assign, + sval_a, + sval_b, + base_reg_a, + base_reg_b)); } /* If ASSIGN is a stmt that can be modelled via set_value (lhs_reg, SVALUE, CTXT) for some SVALUE, get the SVALUE. - Otherwise return NULL. */ + Otherwise return nullptr. */ const svalue * region_model::get_gassign_result (const gassign *assign, @@ -1042,7 +1197,7 @@ region_model::get_gassign_result (const gassign *assign, switch (op) { default: - return NULL; + return nullptr; case POINTER_PLUS_EXPR: { @@ -1192,13 +1347,13 @@ region_model::get_gassign_result (const gassign *assign, { if (tree_int_cst_sgn (rhs2_cst) < 0) ctxt->warn - (make_unique<shift_count_negative_diagnostic> + (std::make_unique<shift_count_negative_diagnostic> (assign, rhs2_cst)); else if (compare_tree_int (rhs2_cst, TYPE_PRECISION (TREE_TYPE (rhs1))) >= 0) ctxt->warn - (make_unique<shift_count_overflow_diagnostic> + (std::make_unique<shift_count_overflow_diagnostic> (assign, int (TYPE_PRECISION (TREE_TYPE (rhs1))), rhs2_cst)); @@ -1307,8 +1462,8 @@ within_short_circuited_stmt_p (const region_model *model, that implies that the value of the second arg doesn't matter, i.e. 1 for bitwise or, 0 for bitwise and. */ tree other_arg = gimple_assign_rhs1 (use_assign); - /* Use a NULL ctxt here to avoid generating warnings. */ - const svalue *other_arg_sval = model->get_rvalue (other_arg, NULL); + /* Use a nullptr ctxt here to avoid generating warnings. */ + const svalue *other_arg_sval = model->get_rvalue (other_arg, nullptr); tree other_arg_cst = other_arg_sval->maybe_get_constant (); if (!other_arg_cst) return false; @@ -1375,7 +1530,7 @@ due_to_ifn_deferred_init_p (const gassign *assign_stmt) /* Check for SVAL being poisoned, adding a warning to CTXT. Return SVAL, or, if a warning is added, another value, to avoid repeatedly complaining about the same poisoned value in followup code. - SRC_REGION is a hint about where SVAL came from, and can be NULL. */ + SRC_REGION is a hint about where SVAL came from, and can be nullptr. */ const svalue * region_model::check_for_poison (const svalue *sval, @@ -1392,12 +1547,12 @@ region_model::check_for_poison (const svalue *sval, /* Ignore uninitialized uses of empty types; there's nothing to initialize. */ - if (pkind == POISON_KIND_UNINIT + if (pkind == poison_kind::uninit && sval->get_type () && is_empty_type (sval->get_type ())) return sval; - if (pkind == POISON_KIND_UNINIT) + if (pkind == poison_kind::uninit) if (const gimple *curr_stmt = ctxt->get_stmt ()) if (const gassign *assign_stmt = dyn_cast <const gassign *> (curr_stmt)) @@ -1418,7 +1573,7 @@ region_model::check_for_poison (const svalue *sval, the tree other than via the def stmts, using fixup_tree_for_diagnostic. */ tree diag_arg = fixup_tree_for_diagnostic (expr); - if (src_region == NULL && pkind == POISON_KIND_UNINIT) + if (src_region == nullptr && pkind == poison_kind::uninit) src_region = get_region_for_poisoned_expr (expr); /* Can we reliably get the poisoned value from "expr"? @@ -1427,15 +1582,16 @@ region_model::check_for_poison (const svalue *sval, Hence we only query its value now, and only use it if we get the poisoned value back again. */ tree check_expr = expr; - const svalue *foo_sval = get_rvalue (expr, NULL); + const svalue *foo_sval = get_rvalue (expr, nullptr); if (foo_sval == sval) check_expr = expr; else - check_expr = NULL; - if (ctxt->warn (make_unique<poisoned_value_diagnostic> (diag_arg, - pkind, - src_region, - check_expr))) + check_expr = nullptr; + if (ctxt->warn + (std::make_unique<poisoned_value_diagnostic> (diag_arg, + pkind, + src_region, + check_expr))) { /* We only want to report use of a poisoned value at the first place it gets used; return an unknown value to avoid generating @@ -1451,7 +1607,7 @@ region_model::check_for_poison (const svalue *sval, /* Attempt to get a region for describing EXPR, the source of region of a poisoned_svalue for use in a poisoned_value_diagnostic. - Return NULL if there is no good region to use. */ + Return nullptr if there is no good region to use. */ const region * region_model::get_region_for_poisoned_expr (tree expr) const @@ -1462,9 +1618,9 @@ region_model::get_region_for_poisoned_expr (tree expr) const if (decl && DECL_P (decl)) expr = decl; else - return NULL; + return nullptr; } - return get_lvalue (expr, NULL); + return get_lvalue (expr, nullptr); } /* Update this model for the ASSIGN stmt, using CTXT to report any @@ -1493,7 +1649,7 @@ region_model::on_assignment (const gassign *assign, region_model_context *ctxt) if (const svalue *sval = get_gassign_result (assign, ctxt)) { tree expr = get_diagnostic_tree_for_gassign (assign); - check_for_poison (sval, expr, NULL, ctxt); + check_for_poison (sval, expr, nullptr, ctxt); set_value (lhs_reg, sval, ctxt); return; } @@ -1553,7 +1709,7 @@ region_model::on_assignment (const gassign *assign, region_model_context *ctxt) /* e.g. "struct s2 x = {{'A', 'B', 'C', 'D'}};". */ const svalue *rhs_sval = get_rvalue (rhs1, ctxt); m_store.set_value (m_mgr->get_store_manager(), lhs_reg, rhs_sval, - ctxt ? ctxt->get_uncertainty () : NULL); + ctxt ? ctxt->get_uncertainty () : nullptr); } break; } @@ -1570,13 +1726,15 @@ region_model::on_stmt_pre (const gimple *stmt, { switch (gimple_code (stmt)) { - default: - /* No-op for now. */ - break; - - case GIMPLE_DEBUG: - /* We should have stripped these out when building the supergraph. */ - gcc_unreachable (); + case GIMPLE_COND: + case GIMPLE_EH_DISPATCH: + case GIMPLE_GOTO: + case GIMPLE_LABEL: + case GIMPLE_NOP: + case GIMPLE_PREDICT: + case GIMPLE_RESX: + case GIMPLE_SWITCH: + /* No-ops here. */ break; case GIMPLE_ASSIGN: @@ -1601,7 +1759,7 @@ region_model::on_stmt_pre (const gimple *stmt, anything, for which we don't have a function body, or for which we don't know the fndecl. */ const gcall *call = as_a <const gcall *> (stmt); - *out_unknown_side_effects = on_call_pre (call, ctxt); + *out_unknown_side_effects = on_call_pre (*call, ctxt); } break; @@ -1611,6 +1769,13 @@ region_model::on_stmt_pre (const gimple *stmt, on_return (return_, ctxt); } break; + + /* We don't expect to see any other statement kinds in the analyzer. */ + case GIMPLE_DEBUG: // should have stripped these out when building the supergraph + default: + internal_error ("unexpected gimple stmt code: %qs", + gimple_code_name[gimple_code (stmt)]); + break; } } @@ -1689,7 +1854,7 @@ region_model::check_call_format_attr (const call_details &cd, }; call_arg_details arg_details (m_cd, m_fmt_param_idx); - add_note (make_unique<reason_format_attr> (arg_details)); + add_note (std::make_unique<reason_format_attr> (arg_details)); } private: const call_details &m_cd; @@ -1775,7 +1940,7 @@ region_model::update_for_nonzero_return (const call_details &cd) to set an upper bound on the size of a copy_to_user. Attempt to simplify such sizes by trying to get the upper bound as a constant. - Return the simplified svalue if possible, or NULL otherwise. */ + Return the simplified svalue if possible, or nullptr otherwise. */ static const svalue * maybe_simplify_upper_bound (const svalue *num_bytes_sval, @@ -1793,7 +1958,7 @@ maybe_simplify_upper_bound (const svalue *num_bytes_sval, when recording the diagnostic, or note that we're using the upper bound. */ } - return NULL; + return nullptr; } /* Attempt to get an upper bound for the size of a copy when simulating a @@ -1804,7 +1969,7 @@ maybe_simplify_upper_bound (const svalue *num_bytes_sval, that, use the size of SRC_REG if constant. Return a symbolic value for an upper limit on the number of bytes - copied, or NULL if no such value could be determined. */ + copied, or nullptr if no such value could be determined. */ const svalue * region_model::maybe_get_copy_bounds (const region *src_reg, @@ -1830,7 +1995,7 @@ region_model::maybe_get_copy_bounds (const region *src_reg, return num_bytes_sval; /* Non-constant: give up. */ - return NULL; + return nullptr; } /* Get any known_function for FNDECL for call CD. @@ -1838,7 +2003,7 @@ region_model::maybe_get_copy_bounds (const region *src_reg, The call must match all assumptions made by the known_function (such as e.g. "argument 1's type must be a pointer type"). - Return NULL if no known_function is found, or it does not match the + Return nullptr if no known_function is found, or it does not match the assumption(s). */ const known_function * @@ -1848,7 +2013,7 @@ region_model::get_known_function (tree fndecl, const call_details &cd) const return known_fn_mgr->get_match (fndecl, cd); } -/* Get any known_function for IFN, or NULL. */ +/* Get any known_function for IFN, or nullptr. */ const known_function * region_model::get_known_function (enum internal_fn ifn) const @@ -1858,12 +2023,12 @@ region_model::get_known_function (enum internal_fn ifn) const } /* Get any builtin_known_function for CALL and emit any warning to CTXT - if not NULL. + if not nullptr. The call must match all assumptions made by the known_function (such as e.g. "argument 1's type must be a pointer type"). - Return NULL if no builtin_known_function is found, or it does + Return nullptr if no builtin_known_function is found, or it does not match the assumption(s). Internally calls get_known_function to find a known_function and cast it @@ -1898,19 +2063,183 @@ region_model::get_known_function (enum internal_fn ifn) const attributes. */ const builtin_known_function * -region_model::get_builtin_kf (const gcall *call, - region_model_context *ctxt /* = NULL */) const +region_model::get_builtin_kf (const gcall &call, + region_model_context *ctxt /* = nullptr */) const { region_model *mut_this = const_cast <region_model *> (this); tree callee_fndecl = mut_this->get_fndecl_for_call (call, ctxt); if (! callee_fndecl) - return NULL; + return nullptr; call_details cd (call, mut_this, ctxt); if (const known_function *kf = get_known_function (callee_fndecl, cd)) return kf->dyn_cast_builtin_kf (); - return NULL; + return nullptr; +} + +/* Subclass of custom_edge_info for use by exploded_edges that represent + an exception being thrown from a call we don't have the code for. */ + +class exception_thrown_from_unrecognized_call : public custom_edge_info +{ +public: + exception_thrown_from_unrecognized_call (const gcall &call, + tree fndecl) + : m_call (call), + m_fndecl (fndecl) + { + } + + void print (pretty_printer *pp) const final override + { + if (m_fndecl) + pp_printf (pp, "if %qD throws an exception...", m_fndecl); + else + pp_printf (pp, "if the called function throws an exception..."); + }; + + bool + update_model (region_model *model, + const exploded_edge *, + region_model_context *ctxt) const final override + { + /* Allocate an exception and set it as the current exception. */ + const region *exception_reg + = model->get_or_create_region_for_heap_alloc + (nullptr, /* We don't know the size of the region. */ + ctxt); + + region_model_manager *mgr = model->get_manager (); + conjured_purge p (model, ctxt); + + /* The contents of the region are some conjured svalue. */ + const svalue *exception_sval + = mgr->get_or_create_conjured_svalue (NULL_TREE, + &m_call, + exception_reg, p, 0); + model->set_value (exception_reg, exception_sval, ctxt); + const svalue *exception_ptr_sval + = mgr->get_ptr_svalue (ptr_type_node, exception_reg); + const svalue *tinfo_sval + = mgr->get_or_create_conjured_svalue (ptr_type_node, + &m_call, + exception_reg, p, 1); + const svalue *destructor_sval + = mgr->get_or_create_conjured_svalue (ptr_type_node, + &m_call, + exception_reg, p, 2); + + /* Push a new exception_node on the model's thrown exception stack. */ + exception_node eh_node (exception_ptr_sval, tinfo_sval, destructor_sval); + model->push_thrown_exception (eh_node); + + return true; + } + + void + add_events_to_path (checker_path *emission_path, + const exploded_edge &eedge) const final override + { + const exploded_node *dst_node = eedge.m_dest; + const program_point &dst_point = dst_node->get_point (); + const int dst_stack_depth = dst_point.get_stack_depth (); + + emission_path->add_event + (std::make_unique<throw_from_call_to_external_fn_event> + (event_loc_info (m_call.location, + dst_point.get_fndecl (), + dst_stack_depth), + dst_node, + m_call, + m_fndecl)); + } + + exploded_node * + create_enode (exploded_graph &eg, + const program_point &point, + program_state &&state, + exploded_node *enode_for_diag, + region_model_context *ctxt) const final override + { + exploded_node *thrown_enode + = eg.get_or_create_node (point, state, enode_for_diag, + /* Don't add to worklist. */ + false); + if (!thrown_enode) + return nullptr; + + /* Add successor edges for thrown_enode "by hand" for the exception. */ + eg.unwind_from_exception (*thrown_enode, + &m_call, + ctxt); + return thrown_enode; + } + +private: + const gcall &m_call; + tree m_fndecl; // could be null +}; + +/* Get a set of functions that are assumed to not throw exceptions. */ + +static function_set +get_fns_assumed_not_to_throw () +{ + // TODO: populate this list more fully + static const char * const fn_names[] = { + /* This array must be kept sorted. */ + + "fclose" + }; + const size_t count = ARRAY_SIZE (fn_names); + function_set fs (fn_names, count); + return fs; +} + +/* Return true if CALL could throw an exception. + FNDECL could be NULL_TREE. */ + +static bool +can_throw_p (const gcall &call, tree fndecl) +{ + if (!flag_exceptions) + return false; + + if (gimple_call_nothrow_p (&call)) + return false; + + if (fndecl) + { + const function_set fs = get_fns_assumed_not_to_throw (); + if (fs.contains_decl_p (fndecl)) + return false; + } + + return true; +} + +/* Given CALL where we don't know what code is being called + (by not having the body of FNDECL, or having NULL_TREE for FNDECL), + potentially bifurcate control flow to simulate the call throwing + an exception. */ + +void +region_model::check_for_throw_inside_call (const gcall &call, + tree fndecl, + region_model_context *ctxt) +{ + if (!ctxt) + return; + + /* Could this function throw an exception? + If so, add an extra e-edge for that. */ + if (!can_throw_p (call, fndecl)) + return; + + auto throws_exception + = std::make_unique<exception_thrown_from_unrecognized_call> (call, fndecl); + ctxt->bifurcate (std::move (throws_exception)); } /* Update this model for the CALL stmt, using CTXT to report any @@ -1925,7 +2254,7 @@ region_model::get_builtin_kf (const gcall *call, fndecl it is). */ bool -region_model::on_call_pre (const gcall *call, region_model_context *ctxt) +region_model::on_call_pre (const gcall &call, region_model_context *ctxt) { call_details cd (call, this, ctxt); @@ -1935,8 +2264,8 @@ region_model::on_call_pre (const gcall *call, region_model_context *ctxt) Handle IFN_DEFERRED_INIT by treating it as no-op: don't touch the lhs of the call, so that it is still uninitialized from the point of view of the analyzer. */ - if (gimple_call_internal_p (call) - && gimple_call_internal_fn (call) == IFN_DEFERRED_INIT) + if (gimple_call_internal_p (&call) + && gimple_call_internal_fn (&call) == IFN_DEFERRED_INIT) return false; /* No side effects. */ /* Get svalues for all of the arguments at the callsite, to ensure that we @@ -1948,9 +2277,9 @@ region_model::on_call_pre (const gcall *call, region_model_context *ctxt) tree callee_fndecl = get_fndecl_for_call (call, ctxt); - if (gimple_call_internal_p (call)) + if (gimple_call_internal_p (&call)) if (const known_function *kf - = get_known_function (gimple_call_internal_fn (call))) + = get_known_function (gimple_call_internal_fn (&call))) { kf->impl_call_pre (cd); return false; /* No further side effects. */ @@ -1958,6 +2287,7 @@ region_model::on_call_pre (const gcall *call, region_model_context *ctxt) if (!callee_fndecl) { + check_for_throw_inside_call (call, NULL_TREE, ctxt); cd.set_any_lhs_with_defaults (); return true; /* Unknown side effects. */ } @@ -1978,7 +2308,10 @@ region_model::on_call_pre (const gcall *call, region_model_context *ctxt) return true; /* Unknown side effects. */ if (!fndecl_has_gimple_body_p (callee_fndecl)) - return true; /* Unknown side effects. */ + { + check_for_throw_inside_call (call, callee_fndecl, ctxt); + return true; /* Unknown side effects. */ + } return false; /* No side effects. */ } @@ -1994,7 +2327,7 @@ region_model::on_call_pre (const gcall *call, region_model_context *ctxt) to purge state. */ void -region_model::on_call_post (const gcall *call, +region_model::on_call_post (const gcall &call, bool unknown_side_effects, region_model_context *ctxt) { @@ -2102,12 +2435,11 @@ private: attribute. */ void -region_model::check_function_attr_access (const gcall *call, +region_model::check_function_attr_access (const gcall &call, tree callee_fndecl, region_model_context *ctxt, rdwr_map &rdwr_idx) const { - gcc_assert (call); gcc_assert (callee_fndecl); gcc_assert (ctxt); @@ -2145,8 +2477,8 @@ region_model::check_function_attr_access (const gcall *call, } void add_annotations () final override { - add_note (make_unique<reason_attr_access> - (m_callee_fndecl, m_access)); + add_note (std::make_unique<reason_attr_access> + (m_callee_fndecl, m_access)); } private: tree m_callee_fndecl; @@ -2157,7 +2489,7 @@ region_model::check_function_attr_access (const gcall *call, note added to them. */ annotating_ctxt my_ctxt (callee_fndecl, *access, ctxt); - tree ptr_tree = gimple_call_arg (call, access->ptrarg); + tree ptr_tree = gimple_call_arg (&call, access->ptrarg); const svalue *ptr_sval = get_rvalue (ptr_tree, &my_ctxt); const region *reg = deref_rvalue (ptr_sval, ptr_tree, &my_ctxt); check_region_for_write (reg, nullptr, &my_ctxt); @@ -2171,13 +2503,12 @@ region_model::check_function_attr_access (const gcall *call, void region_model:: -check_one_function_attr_null_terminated_string_arg (const gcall *call, +check_one_function_attr_null_terminated_string_arg (const gcall &call, tree callee_fndecl, region_model_context *ctxt, rdwr_map &rdwr_idx, tree attr) { - gcc_assert (call); gcc_assert (callee_fndecl); gcc_assert (ctxt); gcc_assert (attr); @@ -2245,12 +2576,11 @@ check_one_function_attr_null_terminated_string_arg (const gcall *call, void region_model:: -check_function_attr_null_terminated_string_arg (const gcall *call, +check_function_attr_null_terminated_string_arg (const gcall &call, tree callee_fndecl, region_model_context *ctxt, rdwr_map &rdwr_idx) { - gcc_assert (call); gcc_assert (callee_fndecl); gcc_assert (ctxt); @@ -2275,11 +2605,10 @@ check_function_attr_null_terminated_string_arg (const gcall *call, function attributes, complaining to CTXT about any issues. */ void -region_model::check_function_attrs (const gcall *call, +region_model::check_function_attrs (const gcall &call, tree callee_fndecl, region_model_context *ctxt) { - gcc_assert (call); gcc_assert (callee_fndecl); gcc_assert (ctxt); @@ -2310,7 +2639,7 @@ region_model::check_function_attrs (const gcall *call, from their values, and from values that point to them. */ void -region_model::handle_unrecognized_call (const gcall *call, +region_model::handle_unrecognized_call (const gcall &call, region_model_context *ctxt) { tree fndecl = get_fndecl_for_call (call, ctxt); @@ -2331,7 +2660,8 @@ region_model::handle_unrecognized_call (const gcall *call, tree iter_param_types = NULL_TREE; if (fndecl) iter_param_types = TYPE_ARG_TYPES (TREE_TYPE (fndecl)); - for (unsigned arg_idx = 0; arg_idx < gimple_call_num_args (call); arg_idx++) + for (unsigned arg_idx = 0; arg_idx < gimple_call_num_args (&call); + arg_idx++) { /* Track expected param type, where available. */ tree param_type = NULL_TREE; @@ -2342,13 +2672,13 @@ region_model::handle_unrecognized_call (const gcall *call, iter_param_types = TREE_CHAIN (iter_param_types); } - tree parm = gimple_call_arg (call, arg_idx); + tree parm = gimple_call_arg (&call, arg_idx); const svalue *parm_sval = get_rvalue (parm, ctxt); reachable_regs.handle_parm (parm_sval, param_type); } } - uncertainty_t *uncertainty = ctxt ? ctxt->get_uncertainty () : NULL; + uncertainty_t *uncertainty = ctxt ? ctxt->get_uncertainty () : nullptr; /* Purge sm-state for the svalues that were reachable, both in non-mutable and mutable form. */ @@ -2466,11 +2796,11 @@ region_model::on_return (const greturn *return_stmt, region_model_context *ctxt) 0), as opposed to any second return due to longjmp/sigsetjmp. */ void -region_model::on_setjmp (const gcall *call, const exploded_node *enode, +region_model::on_setjmp (const gcall &call, const exploded_node *enode, region_model_context *ctxt) { - const svalue *buf_ptr = get_rvalue (gimple_call_arg (call, 0), ctxt); - const region *buf_reg = deref_rvalue (buf_ptr, gimple_call_arg (call, 0), + const svalue *buf_ptr = get_rvalue (gimple_call_arg (&call, 0), ctxt); + const region *buf_reg = deref_rvalue (buf_ptr, gimple_call_arg (&call, 0), ctxt); /* Create a setjmp_svalue for this call and store it in BUF_REG's @@ -2484,7 +2814,7 @@ region_model::on_setjmp (const gcall *call, const exploded_node *enode, } /* Direct calls to setjmp return 0. */ - if (tree lhs = gimple_call_lhs (call)) + if (tree lhs = gimple_call_lhs (&call)) { const svalue *new_sval = m_mgr->get_or_create_int_cst (TREE_TYPE (lhs), 0); @@ -2499,23 +2829,23 @@ region_model::on_setjmp (const gcall *call, const exploded_node *enode, done, and should be done by the caller. */ void -region_model::on_longjmp (const gcall *longjmp_call, const gcall *setjmp_call, +region_model::on_longjmp (const gcall &longjmp_call, const gcall &setjmp_call, int setjmp_stack_depth, region_model_context *ctxt) { /* Evaluate the val, using the frame of the "longjmp". */ - tree fake_retval = gimple_call_arg (longjmp_call, 1); + tree fake_retval = gimple_call_arg (&longjmp_call, 1); const svalue *fake_retval_sval = get_rvalue (fake_retval, ctxt); /* Pop any frames until we reach the stack depth of the function where setjmp was called. */ gcc_assert (get_stack_depth () >= setjmp_stack_depth); while (get_stack_depth () > setjmp_stack_depth) - pop_frame (NULL, NULL, ctxt, nullptr, false); + pop_frame (nullptr, nullptr, ctxt, nullptr, false); gcc_assert (get_stack_depth () == setjmp_stack_depth); /* Assign to LHS of "setjmp" in new_state. */ - if (tree lhs = gimple_call_lhs (setjmp_call)) + if (tree lhs = gimple_call_lhs (&setjmp_call)) { /* Passing 0 as the val to longjmp leads to setjmp returning 1. */ const svalue *zero_sval @@ -2711,7 +3041,7 @@ const region * region_model::get_lvalue (path_var pv, region_model_context *ctxt) const { if (pv.m_tree == NULL_TREE) - return NULL; + return nullptr; const region *result_reg = get_lvalue_1 (pv, ctxt); assert_compat_types (result_reg->get_type (), TREE_TYPE (pv.m_tree)); @@ -2856,13 +3186,13 @@ const svalue * region_model::get_rvalue (path_var pv, region_model_context *ctxt) const { if (pv.m_tree == NULL_TREE) - return NULL; + return nullptr; const svalue *result_sval = get_rvalue_1 (pv, ctxt); assert_compat_types (result_sval->get_type (), TREE_TYPE (pv.m_tree)); - result_sval = check_for_poison (result_sval, pv.m_tree, NULL, ctxt); + result_sval = check_for_poison (result_sval, pv.m_tree, nullptr, ctxt); return result_sval; } @@ -3092,7 +3422,7 @@ region_model::deref_rvalue (const svalue *ptr_sval, tree ptr_tree, const poisoned_svalue *poisoned_sval = as_a <const poisoned_svalue *> (ptr_sval); enum poison_kind pkind = poisoned_sval->get_poison_kind (); - ctxt->warn (::make_unique<poisoned_value_diagnostic> + ctxt->warn (std::make_unique<poisoned_value_diagnostic> (ptr, pkind, nullptr, nullptr)); } } @@ -3250,7 +3580,7 @@ void region_model::check_for_writable_region (const region* dest_reg, region_model_context *ctxt) const { - /* Fail gracefully if CTXT is NULL. */ + /* Fail gracefully if CTXT is nullptr. */ if (!ctxt) return; @@ -3263,16 +3593,18 @@ region_model::check_for_writable_region (const region* dest_reg, { const function_region *func_reg = as_a <const function_region *> (base_reg); tree fndecl = func_reg->get_fndecl (); - ctxt->warn (make_unique<write_to_const_diagnostic> - (func_reg, fndecl)); + ctxt->warn + (std::make_unique<write_to_const_diagnostic> + (func_reg, fndecl)); } break; case RK_LABEL: { const label_region *label_reg = as_a <const label_region *> (base_reg); tree label = label_reg->get_label (); - ctxt->warn (make_unique<write_to_const_diagnostic> - (label_reg, label)); + ctxt->warn + (std::make_unique<write_to_const_diagnostic> + (label_reg, label)); } break; case RK_DECL: @@ -3285,11 +3617,13 @@ region_model::check_for_writable_region (const region* dest_reg, "this" param is "T* const"). */ if (TREE_READONLY (decl) && is_global_var (decl)) - ctxt->warn (make_unique<write_to_const_diagnostic> (dest_reg, decl)); + ctxt->warn + (std::make_unique<write_to_const_diagnostic> (dest_reg, decl)); } break; case RK_STRING: - ctxt->warn (make_unique<write_to_string_literal_diagnostic> (dest_reg)); + ctxt->warn + (std::make_unique<write_to_string_literal_diagnostic> (dest_reg)); break; } } @@ -3311,13 +3645,13 @@ region_model::get_capacity (const region *reg) const { tree type = TREE_TYPE (decl); tree size = TYPE_SIZE (type); - return get_rvalue (size, NULL); + return get_rvalue (size, nullptr); } else { tree size = decl_init_size (decl, false); if (size) - return get_rvalue (size, NULL); + return get_rvalue (size, nullptr); } } break; @@ -3367,10 +3701,10 @@ region_model::check_region_access (const region *reg, { default: gcc_unreachable (); - case DIR_READ: + case access_direction::read: /* Currently a no-op. */ break; - case DIR_WRITE: + case access_direction::write: check_for_writable_region (reg, ctxt); break; } @@ -3384,7 +3718,7 @@ region_model::check_region_for_write (const region *dest_reg, const svalue *sval_hint, region_model_context *ctxt) const { - check_region_access (dest_reg, DIR_WRITE, sval_hint, ctxt); + check_region_access (dest_reg, access_direction::write, sval_hint, ctxt); } /* If CTXT is non-NULL, use it to warn about any problems reading from REG. @@ -3394,7 +3728,7 @@ bool region_model::check_region_for_read (const region *src_reg, region_model_context *ctxt) const { - return check_region_access (src_reg, DIR_READ, NULL, ctxt); + return check_region_access (src_reg, access_direction::read, nullptr, ctxt); } /* Concrete subclass for casts of pointers that lead to trailing bytes. */ @@ -3491,7 +3825,8 @@ public: checker_path &emission_path) final override { emission_path.add_event - (make_unique<region_creation_event_allocation_size> (capacity, loc_info)); + (std::make_unique<region_creation_event_allocation_size> + (capacity, loc_info)); m_has_allocation_event = true; } @@ -3501,10 +3836,11 @@ public: interest->add_region_creation (m_rhs); } - void maybe_add_sarif_properties (sarif_object &result_obj) + void + maybe_add_sarif_properties (diagnostics::sarif_object &result_obj) const final override { - sarif_property_bag &props = result_obj.get_or_create_properties (); + auto &props = result_obj.get_or_create_properties (); #define PROPERTY_PREFIX "gcc/analyzer/dubious_allocation_size/" props.set (PROPERTY_PREFIX "lhs", m_lhs->to_json ()); props.set (PROPERTY_PREFIX "rhs", m_rhs->to_json ()); @@ -3781,7 +4117,7 @@ void region_model::check_region_size (const region *lhs_reg, const svalue *rhs_sval, region_model_context *ctxt) const { - if (!ctxt || ctxt->get_stmt () == NULL) + if (!ctxt || ctxt->get_stmt () == nullptr) return; /* Only report warnings on assignments that actually change the type. */ if (!is_any_cast_p (ctxt->get_stmt ())) @@ -3827,9 +4163,10 @@ region_model::check_region_size (const region *lhs_reg, const svalue *rhs_sval, if (TREE_CODE (cst_cap) == INTEGER_CST && !capacity_compatible_with_type (cst_cap, pointee_size_tree, is_struct)) - ctxt->warn (make_unique <dubious_allocation_size> (lhs_reg, rhs_reg, - capacity, cst_cap, - ctxt->get_stmt ())); + ctxt->warn + (std::make_unique <dubious_allocation_size> (lhs_reg, rhs_reg, + capacity, cst_cap, + ctxt->get_stmt ())); } break; default: @@ -3841,10 +4178,11 @@ region_model::check_region_size (const region *lhs_reg, const svalue *rhs_sval, m_constraints)) { tree expr = get_representative_tree (capacity); - ctxt->warn (make_unique <dubious_allocation_size> (lhs_reg, - rhs_reg, - capacity, expr, - ctxt->get_stmt ())); + ctxt->warn + (std::make_unique <dubious_allocation_size> (lhs_reg, + rhs_reg, + capacity, expr, + ctxt->get_stmt ())); } } break; @@ -3872,7 +4210,7 @@ region_model::set_value (const region *lhs_reg, const svalue *rhs_sval, check_region_for_write (lhs_reg, rhs_sval, ctxt); m_store.set_value (m_mgr->get_store_manager(), lhs_reg, rhs_sval, - ctxt ? ctxt->get_uncertainty () : NULL); + ctxt ? ctxt->get_uncertainty () : nullptr); } /* Set the value of the region given by LHS to the value given by RHS. */ @@ -4509,7 +4847,7 @@ region_model::scan_for_null_terminator (const region *reg, Simulate scanning through the buffer, reading until we find a 0 byte (equivalent to calling strlen). - Complain and return NULL if: + Complain and return nullptr if: - the buffer pointed to isn't null-terminated - the buffer pointed to has any uninitalized bytes before any 0-terminator - any of the reads aren't within the bounds of the underlying base region @@ -4537,7 +4875,7 @@ region_model::check_for_null_terminated_string_arg (const call_details &cd, Simulate scanning through the buffer, reading until we find a 0 byte (equivalent to calling strlen). - Complain and return NULL if: + Complain and return nullptr if: - the buffer pointed to isn't null-terminated - the buffer pointed to has any uninitalized bytes before any 0-terminator - any of the reads aren't within the bounds of the underlying base region @@ -4546,7 +4884,7 @@ region_model::check_for_null_terminated_string_arg (const call_details &cd, (including the null terminator) if INCLUDE_TERMINATOR is true, or strlen of the buffer (not including the null terminator) if it is false. - Also, when returning an svalue, if OUT_SVAL is non-NULL, write to + Also, when returning an svalue, if OUT_SVAL is non-nullptr, write to *OUT_SVAL with an svalue representing the content of the buffer up to and including the terminator. @@ -4637,9 +4975,11 @@ region_model::check_for_null_terminated_string_arg (const call_details &cd, m_cd.get_model ()->get_current_function ()->decl, m_cd.get_model ()->get_stack_depth ()); - add_event (make_unique<null_terminator_check_event> (loc_info, - arg_details)); - add_note (make_unique <null_terminator_check_decl_note> (arg_details)); + add_event + (std::make_unique<null_terminator_check_event> (loc_info, + arg_details)); + add_note + (std::make_unique <null_terminator_check_decl_note> (arg_details)); } private: const call_details &m_cd; @@ -5385,7 +5725,7 @@ region_model::add_constraint (tree lhs, enum tree_code op, tree rhs, { bool sat = add_constraint (lhs, op, rhs, ctxt); if (!sat && out) - *out = make_unique <rejected_op_constraint> (*this, lhs, op, rhs); + *out = std::make_unique <rejected_op_constraint> (*this, lhs, op, rhs); return sat; } @@ -5507,7 +5847,7 @@ region_model::get_representative_path_var (const svalue *sval, svalue_set *visited, logger *logger) const { - if (sval == NULL) + if (sval == nullptr) return path_var (NULL_TREE, 0); LOG_SCOPE (logger); @@ -5866,7 +6206,7 @@ region_model::maybe_update_for_edge (const superedge &edge, break; } - if (last_stmt == NULL) + if (last_stmt == nullptr) return true; /* Apply any constraints for conditionals/switch/computed-goto statements. */ @@ -5885,17 +6225,22 @@ region_model::maybe_update_for_edge (const superedge &edge, ctxt, out); } + if (const geh_dispatch *eh_dispatch_stmt + = dyn_cast <const geh_dispatch *> (last_stmt)) + { + const eh_dispatch_cfg_superedge *eh_dispatch_cfg_sedge + = as_a <const eh_dispatch_cfg_superedge *> (&edge); + return apply_constraints_for_eh_dispatch (*eh_dispatch_cfg_sedge, + eh_dispatch_stmt, + ctxt, out); + } + if (const ggoto *goto_stmt = dyn_cast <const ggoto *> (last_stmt)) { const cfg_superedge *cfg_sedge = as_a <const cfg_superedge *> (&edge); return apply_constraints_for_ggoto (*cfg_sedge, goto_stmt, ctxt); } - /* Apply any constraints due to an exception being thrown. */ - if (const cfg_superedge *cfg_sedge = dyn_cast <const cfg_superedge *> (&edge)) - if (cfg_sedge->get_flags () & EDGE_EH) - return apply_constraints_for_exception (last_stmt, ctxt, out); - return true; } @@ -5905,29 +6250,29 @@ region_model::maybe_update_for_edge (const superedge &edge, caller's frame. */ void -region_model::update_for_gcall (const gcall *call_stmt, +region_model::update_for_gcall (const gcall &call_stmt, region_model_context *ctxt, function *callee) { /* Build a vec of argument svalues, using the current top frame for resolving tree expressions. */ - auto_vec<const svalue *> arg_svals (gimple_call_num_args (call_stmt)); + auto_vec<const svalue *> arg_svals (gimple_call_num_args (&call_stmt)); - for (unsigned i = 0; i < gimple_call_num_args (call_stmt); i++) + for (unsigned i = 0; i < gimple_call_num_args (&call_stmt); i++) { - tree arg = gimple_call_arg (call_stmt, i); + tree arg = gimple_call_arg (&call_stmt, i); arg_svals.quick_push (get_rvalue (arg, ctxt)); } if(!callee) { /* Get the function * from the gcall. */ - tree fn_decl = get_fndecl_for_call (call_stmt,ctxt); + tree fn_decl = get_fndecl_for_call (call_stmt, ctxt); callee = DECL_STRUCT_FUNCTION (fn_decl); } gcc_assert (callee); - push_frame (*callee, &arg_svals, ctxt); + push_frame (*callee, &call_stmt, &arg_svals, ctxt); } /* Pop the top-most frame_region from the stack, and copy the return @@ -5935,14 +6280,14 @@ region_model::update_for_gcall (const gcall *call_stmt, the call (if any). */ void -region_model::update_for_return_gcall (const gcall *call_stmt, +region_model::update_for_return_gcall (const gcall &call_stmt, region_model_context *ctxt) { /* Get the lvalue for the result of the call, passing it to pop_frame, so that pop_frame can determine the region with respect to the *caller* frame. */ - tree lhs = gimple_call_lhs (call_stmt); - pop_frame (lhs, NULL, ctxt, call_stmt); + tree lhs = gimple_call_lhs (&call_stmt); + pop_frame (lhs, nullptr, ctxt, &call_stmt); } /* Extract calling information from the superedge and update the model for the @@ -5952,7 +6297,7 @@ void region_model::update_for_call_superedge (const call_superedge &call_edge, region_model_context *ctxt) { - const gcall *call_stmt = call_edge.get_call_stmt (); + const gcall &call_stmt = call_edge.get_call_stmt (); update_for_gcall (call_stmt, ctxt, call_edge.get_callee_function ()); } @@ -5963,7 +6308,7 @@ void region_model::update_for_return_superedge (const return_superedge &return_edge, region_model_context *ctxt) { - const gcall *call_stmt = return_edge.get_call_stmt (); + const gcall &call_stmt = return_edge.get_call_stmt (); update_for_return_gcall (call_stmt, ctxt); } @@ -6018,7 +6363,7 @@ apply_constraints_for_gcond (const cfg_superedge &sedge, std::unique_ptr<rejected_constraint> *out) { ::edge cfg_edge = sedge.get_cfg_edge (); - gcc_assert (cfg_edge != NULL); + gcc_assert (cfg_edge != nullptr); gcc_assert (cfg_edge->flags & (EDGE_TRUE_VALUE | EDGE_FALSE_VALUE)); enum tree_code op = gimple_cond_code (cond_stmt); @@ -6036,7 +6381,7 @@ static bool has_nondefault_case_for_value_p (const gswitch *switch_stmt, tree int_cst) { /* We expect the initial label to be the default; skip it. */ - gcc_assert (CASE_LOW (gimple_switch_label (switch_stmt, 0)) == NULL); + gcc_assert (CASE_LOW (gimple_switch_label (switch_stmt, 0)) == NULL_TREE); unsigned min_idx = 1; unsigned max_idx = gimple_switch_num_labels (switch_stmt) - 1; @@ -6156,7 +6501,7 @@ apply_constraints_for_gswitch (const switch_cfg_superedge &edge, && !ctxt->possibly_tainted_p (index_sval)) { if (out) - *out = make_unique <rejected_default_case> (*this); + *out = std::make_unique <rejected_default_case> (*this); return false; } @@ -6165,12 +6510,180 @@ apply_constraints_for_gswitch (const switch_cfg_superedge &edge, = ranges_mgr->get_or_create_ranges_for_switch (&edge, switch_stmt); bool sat = m_constraints->add_bounded_ranges (index_sval, all_cases_ranges); if (!sat && out) - *out = make_unique <rejected_ranges_constraint> (*this, index, all_cases_ranges); + *out = std::make_unique <rejected_ranges_constraint> + (*this, index, all_cases_ranges); if (sat && ctxt && !all_cases_ranges->empty_p ()) ctxt->on_bounded_ranges (*index_sval, *all_cases_ranges); return sat; } +class rejected_eh_dispatch : public rejected_constraint +{ +public: + rejected_eh_dispatch (const region_model &model) + : rejected_constraint (model) + {} + + void dump_to_pp (pretty_printer *pp) const final override + { + pp_printf (pp, "rejected_eh_dispatch"); + } +}; + +static bool +exception_matches_type_p (tree exception_type, + tree catch_type) +{ + if (catch_type == exception_type) + return true; + + /* TODO (PR analyzer/119697): we should also handle subclasses etc; + see the rules in https://en.cppreference.com/w/cpp/language/catch + + It looks like we should be calling (or emulating) + can_convert_eh from the C++ FE, but that's specific to the C++ FE. */ + + return false; +} + +static bool +matches_any_exception_type_p (eh_catch ehc, tree exception_type) +{ + if (ehc->type_list == NULL_TREE) + /* All exceptions are caught here. */ + return true; + + for (tree iter = ehc->type_list; iter; iter = TREE_CHAIN (iter)) + if (exception_matches_type_p (TREE_VALUE (iter), + exception_type)) + return true; + return false; +} + +bool +region_model:: +apply_constraints_for_eh_dispatch (const eh_dispatch_cfg_superedge &edge, + const geh_dispatch *, + region_model_context *ctxt, + std::unique_ptr<rejected_constraint> *out) +{ + const exception_node *current_node = get_current_thrown_exception (); + gcc_assert (current_node); + tree curr_exception_type = current_node->maybe_get_type (); + if (!curr_exception_type) + /* We don't know the specific type. */ + return true; + + return edge.apply_constraints (this, ctxt, curr_exception_type, out); +} + +bool +region_model:: +apply_constraints_for_eh_dispatch_try (const eh_dispatch_try_cfg_superedge &edge, + region_model_context */*ctxt*/, + tree exception_type, + std::unique_ptr<rejected_constraint> *out) +{ + /* TODO: can we rely on this ordering? + or do we need to iterate through prev_catch ? */ + /* The exception must not match any of the previous edges. */ + for (auto sibling_sedge : edge.m_src->m_succs) + { + if (sibling_sedge == &edge) + break; + + const eh_dispatch_try_cfg_superedge *sibling_eh_sedge + = as_a <const eh_dispatch_try_cfg_superedge *> (sibling_sedge); + if (eh_catch ehc = sibling_eh_sedge->get_eh_catch ()) + if (matches_any_exception_type_p (ehc, exception_type)) + { + /* The earlier sibling matches, so the "unhandled" edge is + not taken. */ + if (out) + *out = std::make_unique<rejected_eh_dispatch> (*this); + return false; + } + } + + if (eh_catch ehc = edge.get_eh_catch ()) + { + /* We have an edge that tried to match one or more types. */ + + /* The exception must not match any of the previous edges. */ + + /* It must match this type. */ + if (matches_any_exception_type_p (ehc, exception_type)) + return true; + else + { + /* Exception type doesn't match. */ + if (out) + *out = std::make_unique<rejected_eh_dispatch> (*this); + return false; + } + } + else + { + /* This is the "unhandled exception" edge. + If we get here then no sibling edges matched; + we will follow this edge. */ + return true; + } +} + +bool +region_model:: +apply_constraints_for_eh_dispatch_allowed (const eh_dispatch_allowed_cfg_superedge &edge, + region_model_context */*ctxt*/, + tree exception_type, + std::unique_ptr<rejected_constraint> *out) +{ + auto curr_thrown_exception_node = get_current_thrown_exception (); + gcc_assert (curr_thrown_exception_node); + tree curr_exception_type = curr_thrown_exception_node->maybe_get_type (); + eh_region eh_reg = edge.get_eh_region (); + tree type_list = eh_reg->u.allowed.type_list; + + switch (edge.get_eh_kind ()) + { + default: + gcc_unreachable (); + case eh_dispatch_allowed_cfg_superedge::eh_kind::expected: + if (!curr_exception_type) + { + /* We don't know the specific type; + assume we have one of an expected type. */ + return true; + } + for (tree iter = type_list; iter; iter = TREE_CHAIN (iter)) + if (exception_matches_type_p (TREE_VALUE (iter), + exception_type)) + return true; + if (out) + *out = std::make_unique<rejected_eh_dispatch> (*this); + return false; + + case eh_dispatch_allowed_cfg_superedge::eh_kind::unexpected: + if (!curr_exception_type) + { + /* We don't know the specific type; + assume we don't have one of an expected type. */ + if (out) + *out = std::make_unique<rejected_eh_dispatch> (*this); + return false; + } + for (tree iter = type_list; iter; iter = TREE_CHAIN (iter)) + if (exception_matches_type_p (TREE_VALUE (iter), + exception_type)) + { + if (out) + *out = std::make_unique<rejected_eh_dispatch> (*this); + return false; + } + return true; + } +} + /* Given an edge reached by GOTO_STMT, determine appropriate constraints for the edge to be taken. @@ -6202,38 +6715,6 @@ region_model::apply_constraints_for_ggoto (const cfg_superedge &edge, return true; } -/* Apply any constraints due to an exception being thrown at LAST_STMT. - - If they are feasible, add the constraints and return true. - - Return false if the constraints contradict existing knowledge - (and so the edge should not be taken). - When returning false, if OUT is non-NULL, write a new rejected_constraint - to it. */ - -bool -region_model:: -apply_constraints_for_exception (const gimple *last_stmt, - region_model_context *ctxt, - std::unique_ptr<rejected_constraint> *out) -{ - gcc_assert (last_stmt); - if (const gcall *call = dyn_cast <const gcall *> (last_stmt)) - if (tree callee_fndecl = get_fndecl_for_call (call, ctxt)) - if (is_named_call_p (callee_fndecl, "operator new", call, 1) - || is_named_call_p (callee_fndecl, "operator new []", call, 1)) - { - /* We have an exception thrown from operator new. - Add a constraint that the result was NULL, to avoid a false - leak report due to the result being lost when following - the EH edge. */ - if (tree lhs = gimple_call_lhs (call)) - return add_constraint (lhs, EQ_EXPR, null_pointer_node, ctxt, out); - return true; - } - return true; -} - /* For use with push_frame when handling a top-level call within the analysis. PARAM has a defined but unknown initial value. Anything it points to has escaped, since the calling context "knows" @@ -6265,6 +6746,10 @@ region_model::on_top_level_param (tree param, /* Update this region_model to reflect pushing a frame onto the stack for a call to FUN. + If CALL_STMT is non-NULL, this is for the interprocedural case where + we already have an execution path into the caller. It can be NULL for + top-level entrypoints into the analysis, or in selftests. + If ARG_SVALS is non-NULL, use it to populate the parameters in the new frame. Otherwise, the params have their initial_svalues. @@ -6273,14 +6758,32 @@ region_model::on_top_level_param (tree param, const region * region_model::push_frame (const function &fun, + const gcall *call_stmt, const vec<const svalue *> *arg_svals, region_model_context *ctxt) { - m_current_frame = m_mgr->get_frame_region (m_current_frame, fun); + tree fndecl = fun.decl; if (arg_svals) { + /* If the result of the callee is DECL_BY_REFERENCE, then + we'll need to store a reference to the caller's lhs of + CALL_STMT within callee's result. + If so, determine the region of CALL_STMT's lhs within + the caller's frame before updating m_current_frame. */ + const region *caller_return_by_reference_reg = nullptr; + if (tree result = DECL_RESULT (fndecl)) + if (DECL_BY_REFERENCE (result)) + { + gcc_assert (call_stmt); + tree lhs = gimple_call_lhs (call_stmt); + gcc_assert (lhs); + caller_return_by_reference_reg = get_lvalue (lhs, ctxt); + } + + /* Update m_current_frame. */ + m_current_frame = m_mgr->get_frame_region (m_current_frame, fun); + /* Arguments supplied from a caller frame. */ - tree fndecl = fun.decl; unsigned idx = 0; for (tree iter_parm = DECL_ARGUMENTS (fndecl); iter_parm; iter_parm = DECL_CHAIN (iter_parm), ++idx) @@ -6308,13 +6811,39 @@ region_model::push_frame (const function &fun, va_arg_idx); set_value (var_arg_reg, arg_sval, ctxt); } + + /* If the result of the callee is DECL_BY_REFERENCE, then above + we should have determined the region within the + caller's frame that the callee will be writing back to. + Use this now to initialize the reference in callee's frame. */ + if (tree result = DECL_RESULT (fndecl)) + if (DECL_BY_REFERENCE (result)) + { + /* Get reference to the caller lhs. */ + gcc_assert (caller_return_by_reference_reg); + const svalue *ref_sval + = m_mgr->get_ptr_svalue (TREE_TYPE (result), + caller_return_by_reference_reg); + + /* Get region for default val of DECL_RESULT within the + callee. */ + tree result_default_ssa = get_ssa_default_def (fun, result); + gcc_assert (result_default_ssa); + const region *callee_result_reg + = get_lvalue (result_default_ssa, ctxt); + + /* Set the callee's reference to refer to the caller's lhs. */ + set_value (callee_result_reg, ref_sval, ctxt); + } } else { /* Otherwise we have a top-level call within the analysis. The params have defined but unknown initial values. Anything they point to has escaped. */ - tree fndecl = fun.decl; + + /* Update m_current_frame. */ + m_current_frame = m_mgr->get_frame_region (m_current_frame, fun); /* Handle "__attribute__((nonnull))". */ tree fntype = TREE_TYPE (fndecl); @@ -6382,7 +6911,7 @@ public: {} std::unique_ptr<stmt_finder> clone () const override { - return ::make_unique<my_finder> (m_call_stmt, m_caller_frame); + return std::make_unique<my_finder> (m_call_stmt, m_caller_frame); } const gimple *find_stmt (const exploded_path &) override { @@ -6433,7 +6962,7 @@ private: Purge the frame region and all its descendent regions. Convert any pointers that point into such regions into - POISON_KIND_POPPED_STACK svalues. */ + poison_kind::popped_stack svalues. */ void region_model::pop_frame (tree result_lvalue, @@ -6454,7 +6983,7 @@ region_model::pop_frame (tree result_lvalue, /* Evaluate the result, within the callee frame. */ tree fndecl = m_current_frame->get_function ().decl; tree result = DECL_RESULT (fndecl); - const svalue *retval = NULL; + const svalue *retval = nullptr; if (result && TREE_TYPE (result) != void_type_node && eval_return_svalue) @@ -6467,7 +6996,11 @@ region_model::pop_frame (tree result_lvalue, /* Pop the frame. */ m_current_frame = m_current_frame->get_calling_frame (); - if (result_lvalue && retval) + if (result_lvalue + && retval + /* Don't write back for DECL_BY_REFERENCE; the writes + should have happened within the callee already. */ + && !DECL_BY_REFERENCE (result)) { gcc_assert (eval_return_svalue); @@ -6483,7 +7016,7 @@ region_model::pop_frame (tree result_lvalue, set_value (result_dst_reg, retval, call_stmt ? &caller_ctxt : ctxt); } - unbind_region_and_descendents (frame_reg,POISON_KIND_POPPED_STACK); + unbind_region_and_descendents (frame_reg,poison_kind::popped_stack); notify_on_pop_frame (this, &pre_popped_model, retval, ctxt); } @@ -6640,6 +7173,14 @@ region_model::can_merge_with_p (const region_model &other_model, for (auto iter : m.m_svals_changing_meaning) out_model->m_constraints->purge_state_involving (iter); + if (m_thrown_exceptions_stack != other_model.m_thrown_exceptions_stack) + return false; + out_model->m_thrown_exceptions_stack = m_thrown_exceptions_stack; + + if (m_caught_exceptions_stack != other_model.m_caught_exceptions_stack) + return false; + out_model->m_caught_exceptions_stack = m_caught_exceptions_stack; + return true; } @@ -6647,10 +7188,10 @@ region_model::can_merge_with_p (const region_model &other_model, otherwise. */ tree -region_model::get_fndecl_for_call (const gcall *call, +region_model::get_fndecl_for_call (const gcall &call, region_model_context *ctxt) { - tree fn_ptr = gimple_call_fn (call); + tree fn_ptr = gimple_call_fn (&call); if (fn_ptr == NULL_TREE) return NULL_TREE; const svalue *fn_ptr_sval = get_rvalue (fn_ptr, ctxt); @@ -6773,7 +7314,7 @@ private: class contains_floating_point_visitor : public visitor { public: - contains_floating_point_visitor (const svalue *root_sval) : m_result (NULL) + contains_floating_point_visitor (const svalue *root_sval) : m_result (nullptr) { root_sval->accept (this); } @@ -6825,7 +7366,7 @@ region_model::check_dynamic_size_for_floats (const svalue *size_in_bytes, if (const svalue *float_sval = v.get_svalue_to_report ()) { tree diag_arg = get_representative_tree (float_sval); - ctxt->warn (make_unique<float_as_size_arg> (diag_arg)); + ctxt->warn (std::make_unique<float_as_size_arg> (diag_arg)); } } @@ -6894,6 +7435,12 @@ region_model::get_referenced_base_regions (auto_bitmap &out_ids) const reachable_regs.add (base_reg, false); } + for (auto &eh_node : m_thrown_exceptions_stack) + eh_node.add_to_reachable_regions (reachable_regs); + for (auto &eh_node : m_caught_exceptions_stack) + eh_node.add_to_reachable_regions (reachable_regs); + + bitmap_clear (out_ids); for (auto iter_reg : reachable_regs) bitmap_set_bit (out_ids, iter_reg->get_id ()); @@ -6931,7 +7478,7 @@ region_model::set_dynamic_extents (const region *reg, m_dynamic_extents.put (reg, size_in_bytes); } -/* Get the recording of REG in bytes, or NULL if no dynamic size was +/* Get the recording of REG in bytes, or nullptr if no dynamic size was recorded. */ const svalue * @@ -6939,7 +7486,7 @@ region_model::get_dynamic_extents (const region *reg) const { if (const svalue * const *slot = m_dynamic_extents.get (reg)) return *slot; - return NULL; + return nullptr; } /* Unset any recorded dynamic size of REG. */ @@ -7050,9 +7597,10 @@ public: } void - maybe_add_sarif_properties (sarif_object &result_obj) const final override + maybe_add_sarif_properties (diagnostics::sarif_object &result_obj) + const final override { - sarif_property_bag &props = result_obj.get_or_create_properties (); + auto &props = result_obj.get_or_create_properties (); #define PROPERTY_PREFIX "gcc/-Wanalyzer-exposure-through-uninit-copy/" props.set (PROPERTY_PREFIX "src_region", m_src_region->to_json ()); props.set (PROPERTY_PREFIX "dest_region", m_dest_region->to_json ()); @@ -7077,7 +7625,7 @@ private: { const poisoned_svalue *poisoned_sval = as_a <const poisoned_svalue *> (m_copied_sval); - gcc_assert (poisoned_sval->get_poison_kind () == POISON_KIND_UNINIT); + gcc_assert (poisoned_sval->get_poison_kind () == poison_kind::uninit); /* Give up if don't have type information. */ if (m_copied_sval->get_type () == NULL_TREE) @@ -7102,7 +7650,7 @@ private: const svalue *sval = iter.second; if (const poisoned_svalue *psval = sval->dyn_cast_poisoned_svalue ()) - if (psval->get_poison_kind () == POISON_KIND_UNINIT) + if (psval->get_poison_kind () == poison_kind::uninit) { const binding_key *key = iter.first; const concrete_binding *ckey @@ -7154,7 +7702,7 @@ private: const svalue *sval = iter.second; if (const poisoned_svalue *psval = sval->dyn_cast_poisoned_svalue ()) - if (psval->get_poison_kind () == POISON_KIND_UNINIT) + if (psval->get_poison_kind () == poison_kind::uninit) { const binding_key *key = iter.first; const concrete_binding *ckey @@ -7171,8 +7719,7 @@ private: tree type = m_copied_sval->get_type (); if (type && TREE_CODE (type) == RECORD_TYPE) { - // (std::make_unique is C++14) - layout = std::unique_ptr<record_layout> (new record_layout (type)); + layout = std::make_unique<record_layout> (type); if (0) layout->dump (); @@ -7254,7 +7801,7 @@ private: static void complain_about_fully_uninit_item (const record_layout::item &item) { - tree field = item.m_field; + const_tree field = item.m_field; bit_size_t num_bits = item.m_bit_range.m_size_in_bits; if (item.m_is_padding) { @@ -7315,7 +7862,7 @@ private: static void complain_about_partially_uninit_item (const record_layout::item &item) { - tree field = item.m_field; + const_tree field = item.m_field; if (item.m_is_padding) inform (DECL_SOURCE_LOCATION (field), "padding after field %qD is partially uninitialized", @@ -7358,7 +7905,7 @@ contains_uninit_p (const svalue *sval) { const poisoned_svalue *psval = as_a <const poisoned_svalue *> (sval); - return psval->get_poison_kind () == POISON_KIND_UNINIT; + return psval->get_poison_kind () == poison_kind::uninit; } case SK_COMPOUND: { @@ -7370,7 +7917,7 @@ contains_uninit_p (const svalue *sval) const svalue *sval = iter.second; if (const poisoned_svalue *psval = sval->dyn_cast_poisoned_svalue ()) - if (psval->get_poison_kind () == POISON_KIND_UNINIT) + if (psval->get_poison_kind () == poison_kind::uninit) return true; } @@ -7386,7 +7933,7 @@ contains_uninit_p (const svalue *sval) Check that COPIED_SVAL is fully initialized. If not, complain about an infoleak to CTXT. - SRC_REG can be NULL; if non-NULL it is used as a hint in the diagnostic + SRC_REG can be nullptr; if non-NULL it is used as a hint in the diagnostic as to where COPIED_SVAL came from. */ void @@ -7397,9 +7944,10 @@ region_model::maybe_complain_about_infoleak (const region *dst_reg, { /* Check for exposure. */ if (contains_uninit_p (copied_sval)) - ctxt->warn (make_unique<exposure_through_uninit_copy> (src_reg, - dst_reg, - copied_sval)); + ctxt->warn + (std::make_unique<exposure_through_uninit_copy> (src_reg, + dst_reg, + copied_sval)); } /* Set errno to a positive symbolic int, as if some error has occurred. */ @@ -7411,7 +7959,7 @@ region_model::set_errno (const call_details &cd) conjured_purge p (this, cd.get_ctxt ()); const svalue *new_errno_sval = m_mgr->get_or_create_conjured_svalue (integer_type_node, - cd.get_call_stmt (), + &cd.get_call_stmt (), errno_reg, p); const svalue *zero = m_mgr->get_or_create_int_cst (integer_type_node, 0); @@ -7538,8 +8086,8 @@ void rejected_op_constraint::dump_to_pp (pretty_printer *pp) const { region_model m (m_model); - const svalue *lhs_sval = m.get_rvalue (m_lhs, NULL); - const svalue *rhs_sval = m.get_rvalue (m_rhs, NULL); + const svalue *lhs_sval = m.get_rvalue (m_lhs, nullptr); + const svalue *rhs_sval = m.get_rvalue (m_rhs, nullptr); lhs_sval->dump_to_pp (pp, true); pp_printf (pp, " %s ", op_symbol_code (m_op)); rhs_sval->dump_to_pp (pp, true); @@ -7559,7 +8107,7 @@ void rejected_ranges_constraint::dump_to_pp (pretty_printer *pp) const { region_model m (m_model); - const svalue *sval = m.get_rvalue (m_expr, NULL); + const svalue *sval = m.get_rvalue (m_expr, nullptr); sval->dump_to_pp (pp, true); pp_string (pp, " in "); m_ranges->dump_to_pp (pp, true); @@ -7660,7 +8208,7 @@ assert_condition (const location &loc, tree lhs, tree_code op, tree rhs, tristate expected) { - tristate actual = model.eval_condition (lhs, op, rhs, NULL); + tristate actual = model.eval_condition (lhs, op, rhs, nullptr); ASSERT_EQ_AT (loc, actual, expected); } @@ -7749,7 +8297,7 @@ make_test_compound_type (const char *name, bool is_struct, TYPE_NAME (t) = get_identifier (name); TYPE_SIZE (t) = 0; - tree fieldlist = NULL; + tree fieldlist = NULL_TREE; int i; tree field; FOR_EACH_VEC_ELT (*fields, i, field) @@ -7804,22 +8352,22 @@ test_struct () region_model_manager mgr; region_model model (&mgr); - model.set_value (c_x, int_17, NULL); - model.set_value (c_y, int_m3, NULL); + model.set_value (c_x, int_17, nullptr); + model.set_value (c_y, int_m3, nullptr); /* Verify get_offset for "c.x". */ { - const region *c_x_reg = model.get_lvalue (c_x, NULL); + const region *c_x_reg = model.get_lvalue (c_x, nullptr); region_offset offset = c_x_reg->get_offset (&mgr); - ASSERT_EQ (offset.get_base_region (), model.get_lvalue (c, NULL)); + ASSERT_EQ (offset.get_base_region (), model.get_lvalue (c, nullptr)); ASSERT_EQ (offset.get_bit_offset (), 0); } /* Verify get_offset for "c.y". */ { - const region *c_y_reg = model.get_lvalue (c_y, NULL); + const region *c_y_reg = model.get_lvalue (c_y, nullptr); region_offset offset = c_y_reg->get_offset (&mgr); - ASSERT_EQ (offset.get_base_region (), model.get_lvalue (c, NULL)); + ASSERT_EQ (offset.get_base_region (), model.get_lvalue (c, nullptr)); ASSERT_EQ (offset.get_bit_offset (), INT_TYPE_SIZE); } } @@ -7840,7 +8388,7 @@ test_array_1 () tree a_0 = build4 (ARRAY_REF, char_type_node, a, int_0, NULL_TREE, NULL_TREE); tree char_A = build_int_cst (char_type_node, 'A'); - model.set_value (a_0, char_A, NULL); + model.set_value (a_0, char_A, nullptr); } /* Verify that region_model::get_representative_tree works as expected. */ @@ -7854,7 +8402,7 @@ test_get_representative_tree () { tree string_cst = build_string (4, "foo"); region_model m (&mgr); - const svalue *str_sval = m.get_rvalue (string_cst, NULL); + const svalue *str_sval = m.get_rvalue (string_cst, nullptr); tree rep = m.get_representative_tree (str_sval); ASSERT_EQ (rep, string_cst); } @@ -7863,7 +8411,7 @@ test_get_representative_tree () { tree string_cst_ptr = build_string_literal (4, "foo"); region_model m (&mgr); - const svalue *str_sval = m.get_rvalue (string_cst_ptr, NULL); + const svalue *str_sval = m.get_rvalue (string_cst_ptr, nullptr); tree rep = m.get_representative_tree (str_sval); ASSERT_DUMP_TREE_EQ (rep, "&\"foo\"[0]"); } @@ -7988,12 +8536,12 @@ test_unique_unknowns () /* Different types (or the NULL type) should have different unknown_svalues. */ - const svalue *unknown_NULL_type = mgr.get_or_create_unknown_svalue (NULL); + const svalue *unknown_NULL_type = mgr.get_or_create_unknown_svalue (nullptr); ASSERT_NE (unknown_NULL_type, unknown_int); /* Repeated calls with NULL for the type should get the same "unknown" svalue. */ - const svalue *unknown_NULL_type_2 = mgr.get_or_create_unknown_svalue (NULL); + const svalue *unknown_NULL_type_2 = mgr.get_or_create_unknown_svalue (nullptr); ASSERT_EQ (unknown_NULL_type, unknown_NULL_type_2); } @@ -8337,9 +8885,9 @@ test_assignment () region_model model (&mgr); ADD_SAT_CONSTRAINT (model, x, EQ_EXPR, int_0); ASSERT_CONDITION_UNKNOWN (model, y, EQ_EXPR, int_0); - model.set_value (model.get_lvalue (y, NULL), - model.get_rvalue (int_0, NULL), - NULL); + model.set_value (model.get_lvalue (y, nullptr), + model.get_rvalue (int_0, nullptr), + nullptr); ASSERT_CONDITION_TRUE (model, y, EQ_EXPR, int_0); ASSERT_CONDITION_TRUE (model, y, EQ_EXPR, x); } @@ -8367,16 +8915,16 @@ test_compound_assignment () region_model_manager mgr; region_model model (&mgr); - model.set_value (c_x, int_17, NULL); - model.set_value (c_y, int_m3, NULL); + model.set_value (c_x, int_17, nullptr); + model.set_value (c_y, int_m3, nullptr); /* Copy c to d. */ - const svalue *sval = model.get_rvalue (c, NULL); - model.set_value (model.get_lvalue (d, NULL), sval, NULL); + const svalue *sval = model.get_rvalue (c, nullptr); + model.set_value (model.get_lvalue (d, nullptr), sval, nullptr); /* Check that the fields have the same svalues. */ - ASSERT_EQ (model.get_rvalue (c_x, NULL), model.get_rvalue (d_x, NULL)); - ASSERT_EQ (model.get_rvalue (c_y, NULL), model.get_rvalue (d_y, NULL)); + ASSERT_EQ (model.get_rvalue (c_x, nullptr), model.get_rvalue (d_x, nullptr)); + ASSERT_EQ (model.get_rvalue (c_y, nullptr), model.get_rvalue (d_y, nullptr)); } /* Verify the details of pushing and popping stack frames. */ @@ -8432,7 +8980,7 @@ test_stack_frames () /* Push stack frame for "parent_fn". */ const region *parent_frame_reg = model.push_frame (*DECL_STRUCT_FUNCTION (parent_fndecl), - NULL, &ctxt); + nullptr, nullptr, &ctxt); ASSERT_EQ (model.get_current_frame (), parent_frame_reg); ASSERT_TRUE (model.region_exists_p (parent_frame_reg)); const region *a_in_parent_reg = model.get_lvalue (a, &ctxt); @@ -8447,7 +8995,8 @@ test_stack_frames () /* Push stack frame for "child_fn". */ const region *child_frame_reg - = model.push_frame (*DECL_STRUCT_FUNCTION (child_fndecl), NULL, &ctxt); + = model.push_frame (*DECL_STRUCT_FUNCTION (child_fndecl), + nullptr, nullptr, &ctxt); ASSERT_EQ (model.get_current_frame (), child_frame_reg); ASSERT_TRUE (model.region_exists_p (child_frame_reg)); const region *x_in_child_reg = model.get_lvalue (x, &ctxt); @@ -8465,7 +9014,7 @@ test_stack_frames () model.set_value (p_in_globals_reg, mgr.get_ptr_svalue (ptr_type_node, x_in_child_reg), &ctxt); - ASSERT_EQ (p_in_globals_reg->maybe_get_frame_region (), NULL); + ASSERT_EQ (p_in_globals_reg->maybe_get_frame_region (), nullptr); /* Point another global pointer at p: q = &p. */ const region *q_in_globals_reg = model.get_lvalue (q, &ctxt); @@ -8479,16 +9028,16 @@ test_stack_frames () ASSERT_FALSE (a_in_parent_reg->descendent_of_p (child_frame_reg)); /* Pop the "child_fn" frame from the stack. */ - model.pop_frame (NULL, NULL, &ctxt, nullptr); + model.pop_frame (nullptr, nullptr, &ctxt, nullptr); ASSERT_FALSE (model.region_exists_p (child_frame_reg)); ASSERT_TRUE (model.region_exists_p (parent_frame_reg)); /* Verify that p (which was pointing at the local "x" in the popped frame) has been poisoned. */ - const svalue *new_p_sval = model.get_rvalue (p, NULL); + const svalue *new_p_sval = model.get_rvalue (p, nullptr); ASSERT_EQ (new_p_sval->get_kind (), SK_POISONED); ASSERT_EQ (new_p_sval->dyn_cast_poisoned_svalue ()->get_poison_kind (), - POISON_KIND_POPPED_STACK); + poison_kind::popped_stack); /* Verify that q still points to p, in spite of the region renumbering. */ @@ -8540,7 +9089,8 @@ test_get_representative_path_var () for (int depth = 0; depth < 5; depth++) { const region *frame_n_reg - = model.push_frame (*DECL_STRUCT_FUNCTION (fndecl), NULL, &ctxt); + = model.push_frame (*DECL_STRUCT_FUNCTION (fndecl), + nullptr, nullptr, &ctxt); const region *parm_n_reg = model.get_lvalue (path_var (n, depth), &ctxt); parm_regs.safe_push (parm_n_reg); @@ -8562,7 +9112,7 @@ test_get_representative_path_var () } /* ...and that we can lookup lvalues for locals for all frames, not just the top. */ - ASSERT_EQ (model.get_lvalue (path_var (n, depth), NULL), + ASSERT_EQ (model.get_lvalue (path_var (n, depth), nullptr), parm_regs[depth]); /* ...and that we can locate the svalues. */ { @@ -8591,22 +9141,22 @@ test_equality_1 () /* Verify that setting state in model1 makes the models non-equal. */ tree x = build_global_decl ("x", integer_type_node); - model0.set_value (x, int_42, NULL); - ASSERT_EQ (model0.get_rvalue (x, NULL)->maybe_get_constant (), int_42); + model0.set_value (x, int_42, nullptr); + ASSERT_EQ (model0.get_rvalue (x, nullptr)->maybe_get_constant (), int_42); ASSERT_NE (model0, model1); /* Verify the copy-ctor. */ region_model model2 (model0); ASSERT_EQ (model0, model2); - ASSERT_EQ (model2.get_rvalue (x, NULL)->maybe_get_constant (), int_42); + ASSERT_EQ (model2.get_rvalue (x, nullptr)->maybe_get_constant (), int_42); ASSERT_NE (model1, model2); /* Verify that models obtained from copy-ctor are independently editable w/o affecting the original model. */ - model2.set_value (x, int_17, NULL); + model2.set_value (x, int_17, nullptr); ASSERT_NE (model0, model2); - ASSERT_EQ (model2.get_rvalue (x, NULL)->maybe_get_constant (), int_17); - ASSERT_EQ (model0.get_rvalue (x, NULL)->maybe_get_constant (), int_42); + ASSERT_EQ (model2.get_rvalue (x, nullptr)->maybe_get_constant (), int_17); + ASSERT_EQ (model0.get_rvalue (x, nullptr)->maybe_get_constant (), int_42); } /* Verify that region models for @@ -8625,20 +9175,20 @@ test_canonicalization_2 () region_model_manager mgr; region_model model0 (&mgr); - model0.set_value (model0.get_lvalue (x, NULL), - model0.get_rvalue (int_42, NULL), - NULL); - model0.set_value (model0.get_lvalue (y, NULL), - model0.get_rvalue (int_113, NULL), - NULL); + model0.set_value (model0.get_lvalue (x, nullptr), + model0.get_rvalue (int_42, nullptr), + nullptr); + model0.set_value (model0.get_lvalue (y, nullptr), + model0.get_rvalue (int_113, nullptr), + nullptr); region_model model1 (&mgr); - model1.set_value (model1.get_lvalue (y, NULL), - model1.get_rvalue (int_113, NULL), - NULL); - model1.set_value (model1.get_lvalue (x, NULL), - model1.get_rvalue (int_42, NULL), - NULL); + model1.set_value (model1.get_lvalue (y, nullptr), + model1.get_rvalue (int_113, nullptr), + nullptr); + model1.set_value (model1.get_lvalue (x, nullptr), + model1.get_rvalue (int_42, nullptr), + nullptr); ASSERT_EQ (model0, model1); } @@ -8659,12 +9209,12 @@ test_canonicalization_3 () region_model_manager mgr; region_model model0 (&mgr); - model0.add_constraint (x, GT_EXPR, int_3, NULL); - model0.add_constraint (y, GT_EXPR, int_42, NULL); + model0.add_constraint (x, GT_EXPR, int_3, nullptr); + model0.add_constraint (y, GT_EXPR, int_42, nullptr); region_model model1 (&mgr); - model1.add_constraint (y, GT_EXPR, int_42, NULL); - model1.add_constraint (x, GT_EXPR, int_3, NULL); + model1.add_constraint (y, GT_EXPR, int_42, nullptr); + model1.add_constraint (x, GT_EXPR, int_3, nullptr); model0.canonicalize (); model1.canonicalize (); @@ -8684,7 +9234,7 @@ test_canonicalization_4 () region_model model (&mgr); for (tree cst : csts) - model.get_rvalue (cst, NULL); + model.get_rvalue (cst, nullptr); model.canonicalize (); } @@ -8693,7 +9243,7 @@ test_canonicalization_4 () with values VAL_A and VAL_B for EXPR that they are mergable. Write the merged model to *OUT_MERGED_MODEL, and the merged svalue ptr to *OUT_MERGED_SVALUE. - If VAL_A or VAL_B are NULL_TREE, don't populate EXPR + If VAL_A or VAL_B are nullptr_TREE, don't populate EXPR for that region_model. */ static void @@ -8786,9 +9336,11 @@ test_state_merging () region_model model0 (&mgr); region_model model1 (&mgr); ASSERT_EQ (model0.get_stack_depth (), 0); - model0.push_frame (*DECL_STRUCT_FUNCTION (test_fndecl), NULL, &ctxt); + model0.push_frame (*DECL_STRUCT_FUNCTION (test_fndecl), + nullptr, nullptr, &ctxt); ASSERT_EQ (model0.get_stack_depth (), 1); - model1.push_frame (*DECL_STRUCT_FUNCTION (test_fndecl), NULL, &ctxt); + model1.push_frame (*DECL_STRUCT_FUNCTION (test_fndecl), + nullptr, nullptr, &ctxt); placeholder_svalue test_sval (mgr.alloc_symbol_id (), integer_type_node, "test sval"); @@ -8880,9 +9432,10 @@ test_state_merging () /* Pointers: non-NULL and non-NULL: ptr to a local. */ { region_model model0 (&mgr); - model0.push_frame (*DECL_STRUCT_FUNCTION (test_fndecl), NULL, NULL); - model0.set_value (model0.get_lvalue (p, NULL), - model0.get_rvalue (addr_of_a, NULL), NULL); + model0.push_frame (*DECL_STRUCT_FUNCTION (test_fndecl), + nullptr, nullptr, nullptr); + model0.set_value (model0.get_lvalue (p, nullptr), + model0.get_rvalue (addr_of_a, nullptr), nullptr); region_model model1 (model0); ASSERT_EQ (model0, model1); @@ -8906,7 +9459,7 @@ test_state_merging () const region_svalue *merged_p_ptr = merged_p_sval->dyn_cast_region_svalue (); const region *merged_p_star_reg = merged_p_ptr->get_pointee (); - ASSERT_EQ (merged_p_star_reg, merged.get_lvalue (y, NULL)); + ASSERT_EQ (merged_p_star_reg, merged.get_lvalue (y, nullptr)); } /* Pointers: non-NULL ptrs to different globals: should be unknown. */ @@ -9019,17 +9572,19 @@ test_state_merging () frame points to a local in a more recent stack frame. */ { region_model model0 (&mgr); - model0.push_frame (*DECL_STRUCT_FUNCTION (test_fndecl), NULL, NULL); - const region *q_in_first_frame = model0.get_lvalue (q, NULL); + model0.push_frame (*DECL_STRUCT_FUNCTION (test_fndecl), + nullptr, nullptr, nullptr); + const region *q_in_first_frame = model0.get_lvalue (q, nullptr); /* Push a second frame. */ const region *reg_2nd_frame - = model0.push_frame (*DECL_STRUCT_FUNCTION (test_fndecl), NULL, NULL); + = model0.push_frame (*DECL_STRUCT_FUNCTION (test_fndecl), + nullptr, nullptr, nullptr); /* Have a pointer in the older frame point to a local in the more recent frame. */ - const svalue *sval_ptr = model0.get_rvalue (addr_of_a, NULL); - model0.set_value (q_in_first_frame, sval_ptr, NULL); + const svalue *sval_ptr = model0.get_rvalue (addr_of_a, nullptr); + model0.set_value (q_in_first_frame, sval_ptr, nullptr); /* Verify that it's pointing at the newer frame. */ const region *reg_pointee = sval_ptr->maybe_get_region (); @@ -9051,9 +9606,10 @@ test_state_merging () /* Verify that we can merge a model in which a local points to a global. */ { region_model model0 (&mgr); - model0.push_frame (*DECL_STRUCT_FUNCTION (test_fndecl), NULL, NULL); - model0.set_value (model0.get_lvalue (q, NULL), - model0.get_rvalue (addr_of_y, NULL), NULL); + model0.push_frame (*DECL_STRUCT_FUNCTION (test_fndecl), + nullptr, nullptr, nullptr); + model0.set_value (model0.get_lvalue (q, nullptr), + model0.get_rvalue (addr_of_y, nullptr), nullptr); region_model model1 (model0); ASSERT_EQ (model0, model1); @@ -9085,14 +9641,14 @@ test_constraint_merging () /* model0: 0 <= (x == y) < n. */ region_model model0 (&mgr); model0.add_constraint (x, EQ_EXPR, y, &ctxt); - model0.add_constraint (x, GE_EXPR, int_0, NULL); - model0.add_constraint (x, LT_EXPR, n, NULL); + model0.add_constraint (x, GE_EXPR, int_0, nullptr); + model0.add_constraint (x, LT_EXPR, n, nullptr); /* model1: z != 5 && (0 <= x < n). */ region_model model1 (&mgr); - model1.add_constraint (z, NE_EXPR, int_5, NULL); - model1.add_constraint (x, GE_EXPR, int_0, NULL); - model1.add_constraint (x, LT_EXPR, n, NULL); + model1.add_constraint (z, NE_EXPR, int_5, nullptr); + model1.add_constraint (x, GE_EXPR, int_0, nullptr); + model1.add_constraint (x, LT_EXPR, n, nullptr); /* They should be mergeable; the merged constraints should be: (0 <= x < n). */ @@ -9317,17 +9873,17 @@ test_malloc_constraints () const svalue *size_in_bytes = mgr.get_or_create_unknown_svalue (size_type_node); const region *reg - = model.get_or_create_region_for_heap_alloc (size_in_bytes, NULL); + = model.get_or_create_region_for_heap_alloc (size_in_bytes, nullptr); const svalue *sval = mgr.get_ptr_svalue (ptr_type_node, reg); - model.set_value (model.get_lvalue (p, NULL), sval, NULL); - model.set_value (q, p, NULL); + model.set_value (model.get_lvalue (p, nullptr), sval, nullptr); + model.set_value (q, p, nullptr); ASSERT_CONDITION_UNKNOWN (model, p, NE_EXPR, null_ptr); ASSERT_CONDITION_UNKNOWN (model, p, EQ_EXPR, null_ptr); ASSERT_CONDITION_UNKNOWN (model, q, NE_EXPR, null_ptr); ASSERT_CONDITION_UNKNOWN (model, q, EQ_EXPR, null_ptr); - model.add_constraint (p, NE_EXPR, null_ptr, NULL); + model.add_constraint (p, NE_EXPR, null_ptr, nullptr); ASSERT_CONDITION_TRUE (model, p, NE_EXPR, null_ptr); ASSERT_CONDITION_FALSE (model, p, EQ_EXPR, null_ptr); @@ -9349,25 +9905,25 @@ test_var () region_model_manager mgr; region_model model (&mgr); - const region *i_reg = model.get_lvalue (i, NULL); + const region *i_reg = model.get_lvalue (i, nullptr); ASSERT_EQ (i_reg->get_kind (), RK_DECL); /* Reading "i" should give a symbolic "initial value". */ - const svalue *sval_init = model.get_rvalue (i, NULL); + const svalue *sval_init = model.get_rvalue (i, nullptr); ASSERT_EQ (sval_init->get_kind (), SK_INITIAL); ASSERT_EQ (sval_init->dyn_cast_initial_svalue ()->get_region (), i_reg); /* ..and doing it again should give the same "initial value". */ - ASSERT_EQ (model.get_rvalue (i, NULL), sval_init); + ASSERT_EQ (model.get_rvalue (i, nullptr), sval_init); /* "i = 17;". */ - model.set_value (i, int_17, NULL); - ASSERT_EQ (model.get_rvalue (i, NULL), - model.get_rvalue (int_17, NULL)); + model.set_value (i, int_17, nullptr); + ASSERT_EQ (model.get_rvalue (i, nullptr), + model.get_rvalue (int_17, nullptr)); /* "i = -3;". */ - model.set_value (i, int_m3, NULL); - ASSERT_EQ (model.get_rvalue (i, NULL), - model.get_rvalue (int_m3, NULL)); + model.set_value (i, int_m3, nullptr); + ASSERT_EQ (model.get_rvalue (i, nullptr), + model.get_rvalue (int_m3, nullptr)); /* Verify get_offset for "i". */ { @@ -9406,38 +9962,41 @@ test_array_2 () region_model_manager mgr; region_model model (&mgr); /* "arr[0] = 17;". */ - model.set_value (arr_0, int_17, NULL); + model.set_value (arr_0, int_17, nullptr); /* "arr[1] = -3;". */ - model.set_value (arr_1, int_m3, NULL); + model.set_value (arr_1, int_m3, nullptr); - ASSERT_EQ (model.get_rvalue (arr_0, NULL), model.get_rvalue (int_17, NULL)); - ASSERT_EQ (model.get_rvalue (arr_1, NULL), model.get_rvalue (int_m3, NULL)); + ASSERT_EQ (model.get_rvalue (arr_0, nullptr), + model.get_rvalue (int_17, nullptr)); + ASSERT_EQ (model.get_rvalue (arr_1, nullptr), + model.get_rvalue (int_m3, nullptr)); /* Overwrite a pre-existing binding: "arr[1] = 42;". */ - model.set_value (arr_1, int_42, NULL); - ASSERT_EQ (model.get_rvalue (arr_1, NULL), model.get_rvalue (int_42, NULL)); + model.set_value (arr_1, int_42, nullptr); + ASSERT_EQ (model.get_rvalue (arr_1, nullptr), + model.get_rvalue (int_42, nullptr)); /* Verify get_offset for "arr[0]". */ { - const region *arr_0_reg = model.get_lvalue (arr_0, NULL); + const region *arr_0_reg = model.get_lvalue (arr_0, nullptr); region_offset offset = arr_0_reg->get_offset (&mgr); - ASSERT_EQ (offset.get_base_region (), model.get_lvalue (arr, NULL)); + ASSERT_EQ (offset.get_base_region (), model.get_lvalue (arr, nullptr)); ASSERT_EQ (offset.get_bit_offset (), 0); } /* Verify get_offset for "arr[1]". */ { - const region *arr_1_reg = model.get_lvalue (arr_1, NULL); + const region *arr_1_reg = model.get_lvalue (arr_1, nullptr); region_offset offset = arr_1_reg->get_offset (&mgr); - ASSERT_EQ (offset.get_base_region (), model.get_lvalue (arr, NULL)); + ASSERT_EQ (offset.get_base_region (), model.get_lvalue (arr, nullptr)); ASSERT_EQ (offset.get_bit_offset (), INT_TYPE_SIZE); } /* Verify get_offset for "arr[i]". */ { - const region *arr_i_reg = model.get_lvalue (arr_i, NULL); + const region *arr_i_reg = model.get_lvalue (arr_i, nullptr); region_offset offset = arr_i_reg->get_offset (&mgr); - ASSERT_EQ (offset.get_base_region (), model.get_lvalue (arr, NULL)); + ASSERT_EQ (offset.get_base_region (), model.get_lvalue (arr, nullptr)); const svalue *offset_sval = offset.get_symbolic_byte_offset (); if (const svalue *cast = offset_sval->maybe_undo_cast ()) offset_sval = cast; @@ -9445,14 +10004,15 @@ test_array_2 () } /* "arr[i] = i;" - this should remove the earlier bindings. */ - model.set_value (arr_i, i, NULL); - ASSERT_EQ (model.get_rvalue (arr_i, NULL), model.get_rvalue (i, NULL)); - ASSERT_EQ (model.get_rvalue (arr_0, NULL)->get_kind (), SK_UNKNOWN); + model.set_value (arr_i, i, nullptr); + ASSERT_EQ (model.get_rvalue (arr_i, nullptr), model.get_rvalue (i, nullptr)); + ASSERT_EQ (model.get_rvalue (arr_0, nullptr)->get_kind (), SK_UNKNOWN); /* "arr[0] = 17;" - this should remove the arr[i] binding. */ - model.set_value (arr_0, int_17, NULL); - ASSERT_EQ (model.get_rvalue (arr_0, NULL), model.get_rvalue (int_17, NULL)); - ASSERT_EQ (model.get_rvalue (arr_i, NULL)->get_kind (), SK_UNKNOWN); + model.set_value (arr_0, int_17, nullptr); + ASSERT_EQ (model.get_rvalue (arr_0, nullptr), + model.get_rvalue (int_17, nullptr)); + ASSERT_EQ (model.get_rvalue (arr_i, nullptr)->get_kind (), SK_UNKNOWN); } /* Smoketest of dereferencing a pointer via MEM_REF. */ @@ -9479,12 +10039,12 @@ test_mem_ref () region_model model (&mgr); /* "x = 17;". */ - model.set_value (x, int_17, NULL); + model.set_value (x, int_17, nullptr); /* "p = &x;". */ - model.set_value (p, addr_of_x, NULL); + model.set_value (p, addr_of_x, nullptr); - const svalue *sval = model.get_rvalue (star_p, NULL); + const svalue *sval = model.get_rvalue (star_p, nullptr); ASSERT_EQ (sval->maybe_get_constant (), int_17); } @@ -9530,8 +10090,8 @@ test_POINTER_PLUS_EXPR_then_MEM_REF () region_model m (&mgr); tree int_42 = build_int_cst (integer_type_node, 42); - m.set_value (mem_ref, int_42, NULL); - ASSERT_EQ (m.get_rvalue (mem_ref, NULL)->maybe_get_constant (), int_42); + m.set_value (mem_ref, int_42, nullptr); + ASSERT_EQ (m.get_rvalue (mem_ref, nullptr)->maybe_get_constant (), int_42); } /* Verify that malloc works. */ @@ -9583,7 +10143,7 @@ test_alloca () /* Push stack frame. */ const region *frame_reg = model.push_frame (*DECL_STRUCT_FUNCTION (fndecl), - NULL, &ctxt); + nullptr, nullptr, &ctxt); /* "p = alloca (n * 4);". */ const svalue *size_sval = model.get_rvalue (n_times_4, &ctxt); const region *reg = model.create_region_for_alloca (size_sval, &ctxt); @@ -9594,8 +10154,8 @@ test_alloca () /* Verify that the pointers to the alloca region are replaced by poisoned values when the frame is popped. */ - model.pop_frame (NULL, NULL, &ctxt, nullptr); - ASSERT_EQ (model.get_rvalue (p, NULL)->get_kind (), SK_POISONED); + model.pop_frame (nullptr, nullptr, &ctxt, nullptr); + ASSERT_EQ (model.get_rvalue (p, nullptr)->get_kind (), SK_POISONED); } /* Verify that svalue::involves_p works. */ diff --git a/gcc/analyzer/region-model.h b/gcc/analyzer/region-model.h index d8e508d..6271ea2 100644 --- a/gcc/analyzer/region-model.h +++ b/gcc/analyzer/region-model.h @@ -250,6 +250,41 @@ typedef void (*pop_frame_callback) (const region_model *model, const svalue *retval, region_model_context *ctxt); +/* Roughly equivalent to a struct __cxa_exception, except we store a std::vector + rather than a linked list. */ + +struct exception_node +{ + exception_node (const svalue *exception_sval, + const svalue *typeinfo_sval, + const svalue *destructor_sval) + : m_exception_sval (exception_sval), + m_typeinfo_sval (typeinfo_sval), + m_destructor_sval (destructor_sval) + { + } + + bool operator== (const exception_node &other) const; + + void dump_to_pp (pretty_printer *pp, bool simple) const; + void dump (FILE *fp, bool simple) const; + void dump (bool simple) const; + void dump () const; + + std::unique_ptr<json::object> to_json () const; + + std::unique_ptr<text_art::tree_widget> + make_dump_widget (const text_art::dump_widget_info &dwi) const; + + tree maybe_get_type () const; + + void add_to_reachable_regions (reachable_regions &) const; + + const svalue *m_exception_sval; + const svalue *m_typeinfo_sval; + const svalue *m_destructor_sval; +}; + /* 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 @@ -305,8 +340,8 @@ 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); - void on_call_post (const gcall *stmt, + 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); @@ -323,16 +358,16 @@ class region_model bool unmergeable); void update_for_nonzero_return (const call_details &cd); - void handle_unrecognized_call (const gcall *call, + 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, + 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, + 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, @@ -349,14 +384,16 @@ class region_model region_model_context *ctxt, std::unique_ptr<rejected_constraint> *out); - void update_for_gcall (const gcall *call_stmt, - region_model_context *ctxt, - function *callee = NULL); + void update_for_gcall (const gcall &call_stmt, + region_model_context *ctxt, + function *callee = nullptr); - void update_for_return_gcall (const gcall *call_stmt, - region_model_context *ctxt); + void update_for_return_gcall (const gcall &call_stmt, + region_model_context *ctxt); - const region *push_frame (const function &fun, const vec<const svalue *> *arg_sids, + const region *push_frame (const function &fun, + const gcall *call_stmt, + const vec<const svalue *> *arg_sids, region_model_context *ctxt); const frame_region *get_current_frame () const { return m_current_frame; } const function *get_current_function () const; @@ -480,11 +517,11 @@ class region_model 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; + const extrinsic_state *ext_state = nullptr, + const program_state *state_a = nullptr, + const program_state *state_b = nullptr) const; - tree get_fndecl_for_call (const gcall *call, + 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; @@ -562,8 +599,8 @@ class region_model const svalue **out_sval) const; const builtin_known_function * - get_builtin_kf (const gcall *call, - region_model_context *ctxt = NULL) const; + get_builtin_kf (const gcall &call, + region_model_context *ctxt = nullptr) const; static void register_pop_frame_callback (const pop_frame_callback &callback) @@ -583,6 +620,56 @@ class region_model bool called_from_main_p () const; + void push_thrown_exception (const exception_node &node) + { + m_thrown_exceptions_stack.push_back (node); + } + const exception_node *get_current_thrown_exception () const + { + if (m_thrown_exceptions_stack.empty ()) + return nullptr; + return &m_thrown_exceptions_stack.back (); + } + exception_node pop_thrown_exception () + { + gcc_assert (!m_thrown_exceptions_stack.empty ()); + const exception_node retval = m_thrown_exceptions_stack.back (); + m_thrown_exceptions_stack.pop_back (); + return retval; + } + + void push_caught_exception (const exception_node &node) + { + m_caught_exceptions_stack.push_back (node); + } + const exception_node *get_current_caught_exception () const + { + if (m_caught_exceptions_stack.empty ()) + return nullptr; + return &m_caught_exceptions_stack.back (); + } + exception_node pop_caught_exception () + { + gcc_assert (!m_caught_exceptions_stack.empty ()); + const exception_node retval = m_caught_exceptions_stack.back (); + m_caught_exceptions_stack.pop_back (); + return retval; + } + + bool + apply_constraints_for_eh_dispatch_try + (const eh_dispatch_try_cfg_superedge &edge, + region_model_context *ctxt, + tree exception_type, + std::unique_ptr<rejected_constraint> *out); + + bool + apply_constraints_for_eh_dispatch_allowed + (const eh_dispatch_allowed_cfg_superedge &edge, + region_model_context *ctxt, + tree exception_type, + std::unique_ptr<rejected_constraint> *out); + 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; @@ -621,9 +708,12 @@ private: 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, - std::unique_ptr<rejected_constraint> *out); + + bool + apply_constraints_for_eh_dispatch (const eh_dispatch_cfg_superedge &edge, + const geh_dispatch *eh_dispatch_stmt, + region_model_context *ctxt, + std::unique_ptr<rejected_constraint> *out); int poison_any_pointers_to_descendents (const region *reg, enum poison_kind pkind); @@ -672,23 +762,27 @@ private: 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, + 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, + 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, + 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, + void check_function_attrs (const gcall &call, tree callee_fndecl, region_model_context *ctxt); + void check_for_throw_inside_call (const gcall &call, + tree 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; @@ -699,6 +793,9 @@ private: const frame_region *m_current_frame; + std::vector<exception_node> m_thrown_exceptions_stack; + std::vector<exception_node> m_caught_exceptions_stack; + /* 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 @@ -721,7 +818,7 @@ class region_model_context 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; + const stmt_finder *custom_finder = nullptr) = 0; /* Hook for clients to add a note to the last previously stored pending diagnostic. */ @@ -815,13 +912,15 @@ class region_model_context const state_machine **out_sm, unsigned *out_sm_idx) { - return get_state_map_by_name ("malloc", out_smap, out_sm, out_sm_idx, NULL); + return get_state_map_by_name ("malloc", out_smap, out_sm, out_sm_idx, + nullptr); } 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); + return get_state_map_by_name ("taint", out_smap, out_sm, out_sm_idx, + nullptr); } bool possibly_tainted_p (const svalue *sval); @@ -831,6 +930,8 @@ class region_model_context virtual const exploded_graph *get_eg () const = 0; + virtual const program_state *get_state () const = 0; + /* Hooks for detecting infinite loops. */ virtual void maybe_did_work () = 0; virtual bool checking_for_infinite_loop_p () const = 0; @@ -849,7 +950,7 @@ public: void on_svalue_leak (const svalue *) override {} void on_liveness_change (const svalue_set &, const region_model *) override {} - logger *get_logger () override { return NULL; } + logger *get_logger () override { return nullptr; } void on_condition (const svalue *lhs ATTRIBUTE_UNUSED, enum tree_code op ATTRIBUTE_UNUSED, const svalue *rhs ATTRIBUTE_UNUSED) override @@ -872,14 +973,14 @@ public: void on_escaped_function (tree) override {} - uncertainty_t *get_uncertainty () override { return NULL; } + uncertainty_t *get_uncertainty () override { return nullptr; } void purge_state_involving (const svalue *sval ATTRIBUTE_UNUSED) override {} void bifurcate (std::unique_ptr<custom_edge_info> info) override; void terminate_path () override; - const extrinsic_state *get_ext_state () const override { return NULL; } + const extrinsic_state *get_ext_state () const override { return nullptr; } bool get_state_map_by_name (const char *, sm_state_map **, @@ -890,8 +991,10 @@ public: return false; } - const gimple *get_stmt () const override { return NULL; } - const exploded_graph *get_eg () const override { return NULL; } + const gimple *get_stmt () const override { return nullptr; } + const exploded_graph *get_eg () const override { return nullptr; } + const program_state *get_state () const override { return nullptr; } + void maybe_did_work () override {} bool checking_for_infinite_loop_p () const override { return false; } void on_unusable_in_infinite_loop () override {} @@ -1070,6 +1173,14 @@ class region_model_context_decorator : public region_model_context return nullptr; } + const program_state *get_state () const override + { + if (m_inner) + return m_inner->get_state (); + else + return nullptr; + } + void maybe_did_work () override { if (m_inner) @@ -1240,7 +1351,7 @@ private: class engine { public: - engine (const supergraph *sg = NULL, logger *logger = NULL); + engine (const supergraph *sg = nullptr, logger *logger = nullptr); const supergraph *get_supergraph () { return m_sg; } region_model_manager *get_model_manager () { return &m_mgr; } known_function_manager *get_known_function_manager () @@ -1299,7 +1410,7 @@ private: #define ADD_SAT_CONSTRAINT(MODEL, LHS, OP, RHS) \ SELFTEST_BEGIN_STMT \ - bool sat = (MODEL).add_constraint (LHS, OP, RHS, NULL); \ + bool sat = (MODEL).add_constraint (LHS, OP, RHS, nullptr); \ ASSERT_TRUE (sat); \ SELFTEST_END_STMT @@ -1308,7 +1419,7 @@ private: #define ADD_UNSAT_CONSTRAINT(MODEL, LHS, OP, RHS) \ SELFTEST_BEGIN_STMT \ - bool sat = (MODEL).add_constraint (LHS, OP, RHS, NULL); \ + bool sat = (MODEL).add_constraint (LHS, OP, RHS, nullptr); \ ASSERT_FALSE (sat); \ SELFTEST_END_STMT diff --git a/gcc/analyzer/region.cc b/gcc/analyzer/region.cc index d464153..e27fc6e 100644 --- a/gcc/analyzer/region.cc +++ b/gcc/analyzer/region.cc @@ -18,38 +18,19 @@ 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/>. */ -#include "config.h" -#define INCLUDE_VECTOR -#include "system.h" -#include "coretypes.h" -#include "tree.h" -#include "diagnostic-core.h" -#include "gimple-pretty-print.h" -#include "function.h" -#include "basic-block.h" -#include "gimple.h" -#include "gimple-iterator.h" -#include "diagnostic-core.h" -#include "graphviz.h" -#include "options.h" -#include "cgraph.h" -#include "tree-dfa.h" -#include "stringpool.h" -#include "convert.h" -#include "target.h" -#include "fold-const.h" -#include "tree-pretty-print.h" -#include "diagnostic-color.h" -#include "bitmap.h" -#include "analyzer/analyzer.h" -#include "analyzer/analyzer-logging.h" +#include "analyzer/common.h" + #include "ordered-hash-map.h" #include "options.h" #include "cgraph.h" #include "cfg.h" #include "digraph.h" -#include "analyzer/supergraph.h" #include "sbitmap.h" +#include "fold-const.h" +#include "tree-ssa.h" + +#include "analyzer/analyzer-logging.h" +#include "analyzer/supergraph.h" #include "analyzer/call-string.h" #include "analyzer/program-point.h" #include "analyzer/store.h" @@ -58,7 +39,6 @@ along with GCC; see the file COPYING3. If not see #include "analyzer/sm.h" #include "analyzer/program-state.h" #include "text-art/dump.h" -#include "make-unique.h" #if ENABLE_ANALYZER @@ -144,7 +124,7 @@ region_offset::dump (bool simple) const } /* An svalue that matches the pattern (BASE * FACTOR) + OFFSET - where FACTOR or OFFSET could be the identity (represented as NULL). */ + where FACTOR or OFFSET could be the identity (represented as nullptr). */ struct linear_op { @@ -251,7 +231,7 @@ struct linear_op { *out = linear_op (binop_sval.get_arg0 (), binop_sval.get_arg1 (), - NULL); + nullptr); return true; } else if (binop_sval.get_op () == PLUS_EXPR) @@ -270,7 +250,7 @@ struct linear_op } *out = linear_op (binop_sval.get_arg0 (), - NULL, + nullptr, binop_sval.get_arg1 ()); return true; } @@ -296,8 +276,8 @@ operator< (const region_offset &a, const region_offset &b) const svalue &a_sval = *a.get_symbolic_byte_offset (); const svalue &b_sval = *b.get_symbolic_byte_offset (); - linear_op op_a (NULL, NULL, NULL); - linear_op op_b (NULL, NULL, NULL); + linear_op op_a (nullptr, nullptr, nullptr); + linear_op op_b (nullptr, nullptr, nullptr); if (linear_op::from_svalue (a_sval, &op_a) && linear_op::from_svalue (b_sval, &op_b)) { @@ -338,8 +318,8 @@ operator<= (const region_offset &a, const region_offset &b) const svalue &a_sval = *a.get_symbolic_byte_offset (); const svalue &b_sval = *b.get_symbolic_byte_offset (); - linear_op op_a (NULL, NULL, NULL); - linear_op op_b (NULL, NULL, NULL); + linear_op op_a (nullptr, nullptr, nullptr); + linear_op op_b (nullptr, nullptr, nullptr); if (linear_op::from_svalue (a_sval, &op_a) && linear_op::from_svalue (b_sval, &op_b)) { @@ -468,7 +448,7 @@ region::descendent_of_p (const region *elder) const } /* If this region is a frame_region, or a descendent of one, return it. - Otherwise return NULL. */ + Otherwise return nullptr. */ const frame_region * region::maybe_get_frame_region () const @@ -480,7 +460,7 @@ region::maybe_get_frame_region () const return frame_reg; iter = iter->get_parent_region (); } - return NULL; + return nullptr; } /* Get the memory space of this region. */ @@ -567,15 +547,12 @@ region::can_have_initial_svalue_p () const case SSA_NAME: { + /* Some SSA names have an implicit default defined value. */ tree ssa_name = decl; - /* SSA names that are the default defn of a PARM_DECL - have initial_svalues; other SSA names don't. */ - if (SSA_NAME_IS_DEFAULT_DEF (ssa_name) - && SSA_NAME_VAR (ssa_name) - && TREE_CODE (SSA_NAME_VAR (ssa_name)) == PARM_DECL) - return true; - else - return false; + if (SSA_NAME_IS_DEFAULT_DEF (ssa_name)) + return ssa_defined_default_def_p (ssa_name); + /* Others don't. */ + return false; } } } @@ -632,7 +609,7 @@ region::calc_initial_value_at_main (region_model_manager *mgr) const } /* If this region is a decl_region, return the decl. - Otherwise return NULL. */ + Otherwise return NULL_TREE. */ tree region::maybe_get_decl () const @@ -792,7 +769,7 @@ get_field_at_bit_offset (tree record_type, bit_offset_t bit_offset) { gcc_assert (TREE_CODE (record_type) == RECORD_TYPE); if (bit_offset < 0) - return NULL; + return nullptr; /* Find the first field that has an offset > BIT_OFFSET, then return the one preceding it. @@ -902,7 +879,7 @@ region::calc_offset (region_model_manager *mgr) const { const region *iter_region = this; bit_offset_t accum_bit_offset = 0; - const svalue *accum_byte_sval = NULL; + const svalue *accum_byte_sval = nullptr; while (iter_region) { @@ -1039,7 +1016,7 @@ std::unique_ptr<json::value> region::to_json () const { label_text desc = get_desc (true); - auto reg_js = ::make_unique<json::string> (desc.get ()); + auto reg_js = std::make_unique<json::string> (desc.get ()); return reg_js; } @@ -1178,7 +1155,7 @@ region::is_named_decl_p (const char *decl_name) const region::region (complexity c, symbol::id_t id, const region *parent, tree type) : symbol (c, id), m_parent (parent), m_type (type), - m_cached_offset (NULL), m_cached_init_sval_at_main (NULL) + m_cached_offset (nullptr), m_cached_init_sval_at_main (nullptr) { gcc_assert (type == NULL_TREE || TYPE_P (type)); } @@ -1571,7 +1548,7 @@ heap_region::print_dump_widget_label (pretty_printer *pp) const /* root_region's ctor. */ root_region::root_region (symbol::id_t id) -: region (complexity (1, 1), id, NULL, NULL_TREE) +: region (complexity (1, 1), id, nullptr, NULL_TREE) { } @@ -1703,7 +1680,7 @@ decl_region::print_dump_widget_label (pretty_printer *pp) const int decl_region::get_stack_depth () const { - if (get_parent_region () == NULL) + if (get_parent_region () == nullptr) return 0; if (const frame_region *frame_reg = get_parent_region ()->dyn_cast_frame_region ()) @@ -1713,7 +1690,7 @@ decl_region::get_stack_depth () const /* If the underlying decl is in the global constant pool, return an svalue representing the constant value. - Otherwise return NULL. */ + Otherwise return nullptr. */ const svalue * decl_region::maybe_get_constant_value (region_model_manager *mgr) const @@ -1723,7 +1700,7 @@ decl_region::maybe_get_constant_value (region_model_manager *mgr) const && DECL_INITIAL (m_decl) && TREE_CODE (DECL_INITIAL (m_decl)) == CONSTRUCTOR) return get_svalue_for_constructor (DECL_INITIAL (m_decl), mgr); - return NULL; + return nullptr; } /* Implementation of decl_region::get_svalue_for_constructor @@ -1765,7 +1742,7 @@ decl_region::get_svalue_for_constructor (tree ctor, "main" (either based on DECL_INITIAL, or implicit initialization to zero. - Return NULL if there is a problem. */ + Return nullptr if there is a problem. */ const svalue * decl_region::get_svalue_for_initializer (region_model_manager *mgr) const @@ -1776,10 +1753,10 @@ decl_region::get_svalue_for_initializer (region_model_manager *mgr) const /* If we have an "extern" decl then there may be an initializer in another TU. */ if (DECL_EXTERNAL (m_decl)) - return NULL; + return nullptr; if (empty_p ()) - return NULL; + return nullptr; /* Implicit initialization to zero; use a compound_svalue for it. Doing so requires that we have a concrete binding for this region, @@ -1788,12 +1765,12 @@ decl_region::get_svalue_for_initializer (region_model_manager *mgr) const const binding_key *binding = binding_key::make (mgr->get_store_manager (), this); if (binding->symbolic_p ()) - return NULL; + return nullptr; /* If we don't care about tracking the content of this region, then it's unused, and the value doesn't matter. */ if (!tracked_p ()) - return NULL; + return nullptr; binding_cluster c (this); c.zero_fill_region (mgr->get_store_manager (), this); @@ -1804,14 +1781,14 @@ decl_region::get_svalue_for_initializer (region_model_manager *mgr) const /* LTO can write out error_mark_node as the DECL_INITIAL for simple scalar values (to avoid writing out an extra section). */ if (init == error_mark_node) - return NULL; + return nullptr; if (TREE_CODE (init) == CONSTRUCTOR) return get_svalue_for_constructor (init, mgr); /* Reuse the get_rvalue logic from region_model. */ region_model m (mgr); - return m.get_rvalue (path_var (init, 0), NULL); + return m.get_rvalue (path_var (init, 0), nullptr); } /* Subroutine of symnode_requires_tracking_p; return true if REF @@ -1825,7 +1802,7 @@ ipa_ref_requires_tracking (ipa_ref *ref) if (ref->use != IPA_REF_ADDR) return true; - if (ref->stmt == NULL) + if (ref->stmt == nullptr) return true; switch (ref->stmt->code) @@ -1835,12 +1812,12 @@ ipa_ref_requires_tracking (ipa_ref *ref) case GIMPLE_CALL: { cgraph_node *caller_cnode = dyn_cast <cgraph_node *> (ref->referring); - if (caller_cnode == NULL) + if (caller_cnode == nullptr) return true; cgraph_edge *edge = caller_cnode->get_edge (ref->stmt); if (!edge) return true; - if (edge->callee == NULL) + if (edge->callee == nullptr) return true; /* e.g. call through function ptr. */ if (edge->callee->definition) return true; @@ -1875,7 +1852,7 @@ symnode_requires_tracking_p (symtab_node *symnode) if (symnode->externally_visible) return true; tree context_fndecl = DECL_CONTEXT (symnode->decl); - if (context_fndecl == NULL) + if (context_fndecl == nullptr) return true; if (TREE_CODE (context_fndecl) != FUNCTION_DECL) return true; diff --git a/gcc/analyzer/region.h b/gcc/analyzer/region.h index 801a8d3..e01a2e2 100644 --- a/gcc/analyzer/region.h +++ b/gcc/analyzer/region.h @@ -129,29 +129,29 @@ public: virtual enum region_kind get_kind () const = 0; virtual const frame_region * - dyn_cast_frame_region () const { return NULL; } + dyn_cast_frame_region () const { return nullptr; } virtual const function_region * - dyn_cast_function_region () const { return NULL; } + dyn_cast_function_region () const { return nullptr; } virtual const symbolic_region * - dyn_cast_symbolic_region () const { return NULL; } + dyn_cast_symbolic_region () const { return nullptr; } virtual const decl_region * - dyn_cast_decl_region () const { return NULL; } + dyn_cast_decl_region () const { return nullptr; } virtual const field_region * - dyn_cast_field_region () const { return NULL; } + dyn_cast_field_region () const { return nullptr; } virtual const element_region * - dyn_cast_element_region () const { return NULL; } + dyn_cast_element_region () const { return nullptr; } virtual const offset_region * - dyn_cast_offset_region () const { return NULL; } + dyn_cast_offset_region () const { return nullptr; } virtual const sized_region * - dyn_cast_sized_region () const { return NULL; } + dyn_cast_sized_region () const { return nullptr; } virtual const cast_region * - dyn_cast_cast_region () const { return NULL; } + dyn_cast_cast_region () const { return nullptr; } virtual const string_region * - dyn_cast_string_region () const { return NULL; } + dyn_cast_string_region () const { return nullptr; } virtual const bit_range_region * - dyn_cast_bit_range_region () const { return NULL; } + dyn_cast_bit_range_region () const { return nullptr; } virtual const var_arg_region * - dyn_cast_var_arg_region () const { return NULL; } + dyn_cast_var_arg_region () const { return nullptr; } virtual void accept (visitor *v) const; @@ -324,7 +324,7 @@ public: key_t (const frame_region *calling_frame, const function &fun) : m_calling_frame (calling_frame), m_fun (&fun) { - /* calling_frame can be NULL. */ + /* calling_frame can be nullptr. */ } hashval_t hash () const @@ -342,12 +342,12 @@ public: } void mark_deleted () { m_fun = reinterpret_cast<function *> (1); } - void mark_empty () { m_fun = NULL; } + void mark_empty () { m_fun = nullptr; } bool is_deleted () const { return m_fun == reinterpret_cast<function *> (1); } - bool is_empty () const { return m_fun == NULL; } + bool is_empty () const { return m_fun == nullptr; } const frame_region *m_calling_frame; const function *m_fun; @@ -685,12 +685,12 @@ public: } void mark_deleted () { m_sval_ptr = reinterpret_cast<const svalue *> (1); } - void mark_empty () { m_sval_ptr = NULL; } + void mark_empty () { m_sval_ptr = nullptr; } bool is_deleted () const { return m_sval_ptr == reinterpret_cast<const svalue *> (1); } - bool is_empty () const { return m_sval_ptr == NULL; } + bool is_empty () const { return m_sval_ptr == nullptr; } const region *m_parent; const svalue *m_sval_ptr; @@ -744,7 +744,7 @@ public: decl_region (symbol::id_t id, const region *parent, tree decl) : region (complexity (parent), id, parent, TREE_TYPE (decl)), m_decl (decl), m_tracked (calc_tracked_p (decl)), - m_ctor_svalue (NULL) + m_ctor_svalue (nullptr) {} enum region_kind get_kind () const final override { return RK_DECL; } @@ -904,12 +904,12 @@ public: } void mark_deleted () { m_index = reinterpret_cast<const svalue *> (1); } - void mark_empty () { m_index = NULL; } + void mark_empty () { m_index = nullptr; } bool is_deleted () const { return m_index == reinterpret_cast<const svalue *> (1); } - bool is_empty () const { return m_index == NULL; } + bool is_empty () const { return m_index == nullptr; } const region *m_parent; tree m_element_type; @@ -998,12 +998,12 @@ public: } void mark_deleted () { m_byte_offset = reinterpret_cast<const svalue *> (1); } - void mark_empty () { m_byte_offset = NULL; } + void mark_empty () { m_byte_offset = nullptr; } bool is_deleted () const { return m_byte_offset == reinterpret_cast<const svalue *> (1); } - bool is_empty () const { return m_byte_offset == NULL; } + bool is_empty () const { return m_byte_offset == nullptr; } const region *m_parent; tree m_element_type; @@ -1094,12 +1094,12 @@ public: } void mark_deleted () { m_byte_size_sval = reinterpret_cast<const svalue *> (1); } - void mark_empty () { m_byte_size_sval = NULL; } + void mark_empty () { m_byte_size_sval = nullptr; } bool is_deleted () const { return m_byte_size_sval == reinterpret_cast<const svalue *> (1); } - bool is_empty () const { return m_byte_size_sval == NULL; } + bool is_empty () const { return m_byte_size_sval == nullptr; } const region *m_parent; tree m_element_type; @@ -1342,12 +1342,12 @@ public: } void mark_deleted () { m_parent = reinterpret_cast<const region *> (1); } - void mark_empty () { m_parent = NULL; } + void mark_empty () { m_parent = nullptr; } bool is_deleted () const { return m_parent == reinterpret_cast<const region *> (1); } - bool is_empty () const { return m_parent == NULL; } + bool is_empty () const { return m_parent == nullptr; } const region *m_parent; tree m_type; @@ -1432,12 +1432,12 @@ public: { m_parent = reinterpret_cast<const frame_region *> (1); } - void mark_empty () { m_parent = NULL; } + void mark_empty () { m_parent = nullptr; } bool is_deleted () const { return m_parent == reinterpret_cast<const frame_region *> (1); } - bool is_empty () const { return m_parent == NULL; } + bool is_empty () const { return m_parent == nullptr; } const frame_region *m_parent; unsigned m_idx; diff --git a/gcc/analyzer/sm-fd.cc b/gcc/analyzer/sm-fd.cc index e9016f1..b8ae0a1 100644 --- a/gcc/analyzer/sm-fd.cc +++ b/gcc/analyzer/sm-fd.cc @@ -18,32 +18,21 @@ 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/>. */ -#include "config.h" -#define INCLUDE_VECTOR -#include "system.h" -#include "coretypes.h" -#include "make-unique.h" -#include "tree.h" -#include "function.h" -#include "basic-block.h" -#include "gimple.h" -#include "options.h" -#include "diagnostic-core.h" -#include "diagnostic-path.h" -#include "analyzer/analyzer.h" -#include "diagnostic-event-id.h" +#include "analyzer/common.h" + +#include "diagnostics/event-id.h" +#include "stringpool.h" +#include "attribs.h" + #include "analyzer/analyzer-logging.h" #include "analyzer/sm.h" #include "analyzer/pending-diagnostic.h" #include "analyzer/function-set.h" #include "analyzer/analyzer-selftests.h" -#include "stringpool.h" -#include "attribs.h" #include "analyzer/call-string.h" #include "analyzer/program-point.h" #include "analyzer/store.h" #include "analyzer/region-model.h" -#include "bitmap.h" #include "analyzer/program-state.h" #include "analyzer/supergraph.h" #include "analyzer/analyzer-language.h" @@ -127,7 +116,11 @@ public: const svalue *rhs) const final override; bool can_purge_p (state_t s) const final override; - std::unique_ptr<pending_diagnostic> on_leak (tree var) const final override; + + std::unique_ptr<pending_diagnostic> + on_leak (tree var, + const program_state *old_state, + const program_state *new_state) const final override; bool is_unchecked_fd_p (state_t s) const; bool is_valid_fd_p (state_t s) const; @@ -221,7 +214,7 @@ public: /* State for a file descriptor that we do not want to track anymore . */ state_t m_stop; - /* Stashed constant values from the frontend. These could be NULL. */ + /* Stashed constant values from the frontend. These could be NULL_TREE. */ tree m_O_ACCMODE; tree m_O_RDONLY; tree m_O_WRONLY; @@ -230,17 +223,17 @@ public: private: void on_open (sm_context &sm_ctxt, const supernode *node, const gimple *stmt, - const gcall *call) const; + const gcall &call) const; void on_creat (sm_context &sm_ctxt, const supernode *node, const gimple *stmt, - const gcall *call) const; + const gcall &call) const; void on_close (sm_context &sm_ctxt, const supernode *node, const gimple *stmt, - const gcall *call) const; + const gcall &call) const; void on_read (sm_context &sm_ctxt, const supernode *node, const gimple *stmt, - const gcall *call, const tree callee_fndecl) const; + const gcall &call, const tree callee_fndecl) const; void on_write (sm_context &sm_ctxt, const supernode *node, const gimple *stmt, - const gcall *call, const tree callee_fndecl) const; + const gcall &call, const tree callee_fndecl) const; void check_for_open_fd (sm_context &sm_ctxt, const supernode *node, - const gimple *stmt, const gcall *call, + const gimple *stmt, const gcall &call, const tree callee_fndecl, enum access_directions access_fn) const; @@ -253,11 +246,11 @@ private: const gimple *stmt, const svalue *lhs) const; void check_for_fd_attrs (sm_context &sm_ctxt, const supernode *node, - const gimple *stmt, const gcall *call, + const gimple *stmt, const gcall &call, const tree callee_fndecl, const char *attr_name, access_directions fd_attr_access_dir) const; void check_for_dup (sm_context &sm_ctxt, const supernode *node, - const gimple *stmt, const gcall *call, const tree callee_fndecl, + const gimple *stmt, const gcall &call, const tree callee_fndecl, enum dup kind) const; state_t get_state_for_socket_type (const svalue *socket_type_sval) const; @@ -268,7 +261,7 @@ private: const svalue *fd_sval, const supernode *node, state_t old_state, - bool *complained = NULL) const; + bool *complained = nullptr) const; bool check_for_new_socket_fd (const call_details &cd, bool successful, sm_context &sm_ctxt, @@ -399,21 +392,22 @@ public: return false; } - diagnostic_event::meaning + diagnostics::paths::event::meaning get_meaning_for_state_change ( const evdesc::state_change &change) const final override { + using event = diagnostics::paths::event; if (change.m_old_state == m_sm.get_start_state () && (m_sm.is_unchecked_fd_p (change.m_new_state) || change.m_new_state == m_sm.m_new_datagram_socket || change.m_new_state == m_sm.m_new_stream_socket || change.m_new_state == m_sm.m_new_unknown_socket)) - return diagnostic_event::meaning (diagnostic_event::VERB_acquire, - diagnostic_event::NOUN_resource); + return event::meaning (event::verb::acquire, + event::noun::resource); if (change.m_new_state == m_sm.m_closed) - return diagnostic_event::meaning (diagnostic_event::VERB_release, - diagnostic_event::NOUN_resource); - return diagnostic_event::meaning (); + return event::meaning (event::verb::release, + event::noun::resource); + return event::meaning (); } protected: @@ -433,7 +427,7 @@ public: fd_param_diagnostic (const fd_state_machine &sm, tree arg, tree callee_fndecl) : fd_diagnostic (sm, arg), m_callee_fndecl (callee_fndecl), - m_attr_name (NULL), m_arg_idx (-1) + m_attr_name (nullptr), m_arg_idx (-1) { } @@ -488,7 +482,14 @@ protected: class fd_leak : public fd_diagnostic { public: - fd_leak (const fd_state_machine &sm, tree arg) : fd_diagnostic (sm, arg) {} + fd_leak (const fd_state_machine &sm, tree arg, + const program_state *final_state) + : fd_diagnostic (sm, arg), + m_final_state () + { + if (final_state) + m_final_state = std::make_unique<program_state> (*final_state); + } const char * get_kind () const final override @@ -554,8 +555,15 @@ public: return true; } + const program_state * + get_final_state () const final override + { + return m_final_state.get (); + } + private: - diagnostic_event_id_t m_open_event; + diagnostics::paths::event_id_t m_open_event; + std::unique_ptr<program_state> m_final_state; }; class fd_access_mode_mismatch : public fd_param_diagnostic @@ -698,7 +706,7 @@ public: } private: - diagnostic_event_id_t m_first_close_event; + diagnostics::paths::event_id_t m_first_close_event; }; class fd_use_after_close : public fd_param_diagnostic @@ -777,7 +785,7 @@ public: } private: - diagnostic_event_id_t m_first_close_event; + diagnostics::paths::event_id_t m_first_close_event; }; class fd_use_without_check : public fd_param_diagnostic @@ -847,7 +855,7 @@ public: } private: - diagnostic_event_id_t m_first_open_event; + diagnostics::paths::event_id_t m_first_open_event; }; /* Concrete pending_diagnostic subclass for -Wanalyzer-fd-phase-mismatch. */ @@ -1307,7 +1315,7 @@ fd_state_machine::valid_to_unchecked_state (state_t state) const return m_unchecked_read_only; else gcc_unreachable (); - return NULL; + return nullptr; } void @@ -1316,7 +1324,7 @@ fd_state_machine::mark_as_valid_fd (region_model *model, const svalue *fd_sval, const extrinsic_state &ext_state) const { - smap->set_state (model, fd_sval, m_valid_read_write, NULL, ext_state); + smap->set_state (model, fd_sval, m_valid_read_write, nullptr, ext_state); } bool @@ -1324,70 +1332,70 @@ fd_state_machine::on_stmt (sm_context &sm_ctxt, const supernode *node, const gimple *stmt) const { if (const gcall *call = dyn_cast<const gcall *> (stmt)) - if (tree callee_fndecl = sm_ctxt.get_fndecl_for_call (call)) + if (tree callee_fndecl = sm_ctxt.get_fndecl_for_call (*call)) { - if (is_named_call_p (callee_fndecl, "open", call, 2)) + if (is_named_call_p (callee_fndecl, "open", *call, 2)) { - on_open (sm_ctxt, node, stmt, call); + on_open (sm_ctxt, node, stmt, *call); return true; } // "open" - if (is_named_call_p (callee_fndecl, "creat", call, 2)) + if (is_named_call_p (callee_fndecl, "creat", *call, 2)) { - on_creat (sm_ctxt, node, stmt, call); + on_creat (sm_ctxt, node, stmt, *call); return true; } // "creat" - if (is_named_call_p (callee_fndecl, "close", call, 1)) + if (is_named_call_p (callee_fndecl, "close", *call, 1)) { - on_close (sm_ctxt, node, stmt, call); + on_close (sm_ctxt, node, stmt, *call); return true; } // "close" - if (is_named_call_p (callee_fndecl, "write", call, 3)) + if (is_named_call_p (callee_fndecl, "write", *call, 3)) { - on_write (sm_ctxt, node, stmt, call, callee_fndecl); + on_write (sm_ctxt, node, stmt, *call, callee_fndecl); return true; } // "write" - if (is_named_call_p (callee_fndecl, "read", call, 3)) + if (is_named_call_p (callee_fndecl, "read", *call, 3)) { - on_read (sm_ctxt, node, stmt, call, callee_fndecl); + on_read (sm_ctxt, node, stmt, *call, callee_fndecl); return true; } // "read" - if (is_named_call_p (callee_fndecl, "dup", call, 1)) + if (is_named_call_p (callee_fndecl, "dup", *call, 1)) { - check_for_dup (sm_ctxt, node, stmt, call, callee_fndecl, DUP_1); + check_for_dup (sm_ctxt, node, stmt, *call, callee_fndecl, DUP_1); return true; } - if (is_named_call_p (callee_fndecl, "dup2", call, 2)) + if (is_named_call_p (callee_fndecl, "dup2", *call, 2)) { - check_for_dup (sm_ctxt, node, stmt, call, callee_fndecl, DUP_2); + check_for_dup (sm_ctxt, node, stmt, *call, callee_fndecl, DUP_2); return true; } - if (is_named_call_p (callee_fndecl, "dup3", call, 3)) + if (is_named_call_p (callee_fndecl, "dup3", *call, 3)) { - check_for_dup (sm_ctxt, node, stmt, call, callee_fndecl, DUP_3); + check_for_dup (sm_ctxt, node, stmt, *call, callee_fndecl, DUP_3); return true; } { // Handle __attribute__((fd_arg)) - check_for_fd_attrs (sm_ctxt, node, stmt, call, callee_fndecl, + check_for_fd_attrs (sm_ctxt, node, stmt, *call, callee_fndecl, "fd_arg", DIRS_READ_WRITE); // Handle __attribute__((fd_arg_read)) - check_for_fd_attrs (sm_ctxt, node, stmt, call, callee_fndecl, + check_for_fd_attrs (sm_ctxt, node, stmt, *call, callee_fndecl, "fd_arg_read", DIRS_READ); // Handle __attribute__((fd_arg_write)) - check_for_fd_attrs (sm_ctxt, node, stmt, call, callee_fndecl, + check_for_fd_attrs (sm_ctxt, node, stmt, *call, callee_fndecl, "fd_arg_write", DIRS_WRITE); } } @@ -1398,7 +1406,7 @@ fd_state_machine::on_stmt (sm_context &sm_ctxt, const supernode *node, void fd_state_machine::check_for_fd_attrs ( sm_context &sm_ctxt, const supernode *node, const gimple *stmt, - const gcall *call, const tree callee_fndecl, const char *attr_name, + const gcall &call, const tree callee_fndecl, const char *attr_name, access_directions fd_attr_access_dir) const { /* Handle interesting fd attributes of the callee_fndecl, @@ -1431,9 +1439,9 @@ fd_state_machine::check_for_fd_attrs ( if (bitmap_empty_p (argmap)) return; - for (unsigned arg_idx = 0; arg_idx < gimple_call_num_args (call); arg_idx++) + for (unsigned arg_idx = 0; arg_idx < gimple_call_num_args (&call); arg_idx++) { - tree arg = gimple_call_arg (call, arg_idx); + tree arg = gimple_call_arg (&call, arg_idx); tree diag_arg = sm_ctxt.get_diagnostic_tree (arg); state_t state = sm_ctxt.get_state (stmt, arg); bool bit_set = bitmap_bit_p (argmap, arg_idx); @@ -1449,7 +1457,7 @@ fd_state_machine::check_for_fd_attrs ( { sm_ctxt.warn (node, stmt, arg, - make_unique<fd_use_after_close> + std::make_unique<fd_use_after_close> (*this, diag_arg, fndecl, attr_name, arg_idx)); @@ -1461,7 +1469,7 @@ fd_state_machine::check_for_fd_attrs ( if (!is_constant_fd_p (state)) { sm_ctxt.warn (node, stmt, arg, - make_unique<fd_use_without_check> + std::make_unique<fd_use_without_check> (*this, diag_arg, fndecl, attr_name, arg_idx)); @@ -1477,13 +1485,13 @@ fd_state_machine::check_for_fd_attrs ( if (is_writeonly_fd_p (state)) { - sm_ctxt.warn ( - node, stmt, arg, - make_unique<fd_access_mode_mismatch> (*this, diag_arg, - DIRS_WRITE, - fndecl, - attr_name, - arg_idx)); + sm_ctxt.warn + (node, stmt, arg, + std::make_unique<fd_access_mode_mismatch> (*this, diag_arg, + DIRS_WRITE, + fndecl, + attr_name, + arg_idx)); } break; @@ -1491,13 +1499,13 @@ fd_state_machine::check_for_fd_attrs ( if (is_readonly_fd_p (state)) { - sm_ctxt.warn ( - node, stmt, arg, - make_unique<fd_access_mode_mismatch> (*this, diag_arg, - DIRS_READ, - fndecl, - attr_name, - arg_idx)); + sm_ctxt.warn + (node, stmt, arg, + std::make_unique<fd_access_mode_mismatch> (*this, diag_arg, + DIRS_READ, + fndecl, + attr_name, + arg_idx)); } break; @@ -1509,12 +1517,12 @@ fd_state_machine::check_for_fd_attrs ( void fd_state_machine::on_open (sm_context &sm_ctxt, const supernode *node, - const gimple *stmt, const gcall *call) const + const gimple *stmt, const gcall &call) const { - tree lhs = gimple_call_lhs (call); + tree lhs = gimple_call_lhs (&call); if (lhs) { - tree arg = gimple_call_arg (call, 1); + tree arg = gimple_call_arg (&call, 1); enum access_mode mode = READ_WRITE; if (TREE_CODE (arg) == INTEGER_CST) { @@ -1539,29 +1547,29 @@ fd_state_machine::on_open (sm_context &sm_ctxt, const supernode *node, else { sm_ctxt.warn (node, stmt, NULL_TREE, - make_unique<fd_leak> (*this, NULL_TREE)); + std::make_unique<fd_leak> (*this, NULL_TREE, nullptr)); } } void fd_state_machine::on_creat (sm_context &sm_ctxt, const supernode *node, - const gimple *stmt, const gcall *call) const + const gimple *stmt, const gcall &call) const { - tree lhs = gimple_call_lhs (call); + tree lhs = gimple_call_lhs (&call); if (lhs) sm_ctxt.on_transition (node, stmt, lhs, m_start, m_unchecked_write_only); else sm_ctxt.warn (node, stmt, NULL_TREE, - make_unique<fd_leak> (*this, NULL_TREE)); + std::make_unique<fd_leak> (*this, NULL_TREE, nullptr)); } void fd_state_machine::check_for_dup (sm_context &sm_ctxt, const supernode *node, - const gimple *stmt, const gcall *call, + const gimple *stmt, const gcall &call, const tree callee_fndecl, enum dup kind) const { - tree lhs = gimple_call_lhs (call); - tree arg_1 = gimple_call_arg (call, 0); + tree lhs = gimple_call_lhs (&call); + tree arg_1 = gimple_call_arg (&call, 0); state_t state_arg_1 = sm_ctxt.get_state (stmt, arg_1); if (state_arg_1 == m_stop) return; @@ -1587,7 +1595,7 @@ fd_state_machine::check_for_dup (sm_context &sm_ctxt, const supernode *node, case DUP_2: case DUP_3: - tree arg_2 = gimple_call_arg (call, 1); + tree arg_2 = gimple_call_arg (&call, 1); state_t state_arg_2 = sm_ctxt.get_state (stmt, arg_2); tree diag_arg_2 = sm_ctxt.get_diagnostic_tree (arg_2); if (state_arg_2 == m_stop) @@ -1598,8 +1606,8 @@ fd_state_machine::check_for_dup (sm_context &sm_ctxt, const supernode *node, { sm_ctxt.warn ( node, stmt, arg_2, - make_unique<fd_use_without_check> (*this, diag_arg_2, - callee_fndecl)); + std::make_unique<fd_use_without_check> (*this, diag_arg_2, + callee_fndecl)); return; } /* dup2 returns value of its second argument on success.But, the @@ -1620,9 +1628,9 @@ fd_state_machine::check_for_dup (sm_context &sm_ctxt, const supernode *node, void fd_state_machine::on_close (sm_context &sm_ctxt, const supernode *node, - const gimple *stmt, const gcall *call) const + const gimple *stmt, const gcall &call) const { - tree arg = gimple_call_arg (call, 0); + tree arg = gimple_call_arg (&call, 0); state_t state = sm_ctxt.get_state (stmt, arg); tree diag_arg = sm_ctxt.get_diagnostic_tree (arg); @@ -1646,20 +1654,20 @@ fd_state_machine::on_close (sm_context &sm_ctxt, const supernode *node, if (is_closed_fd_p (state)) { sm_ctxt.warn (node, stmt, arg, - make_unique<fd_double_close> (*this, diag_arg)); + std::make_unique<fd_double_close> (*this, diag_arg)); sm_ctxt.set_next_state (stmt, arg, m_stop); } } void fd_state_machine::on_read (sm_context &sm_ctxt, const supernode *node, - const gimple *stmt, const gcall *call, + const gimple *stmt, const gcall &call, const tree callee_fndecl) const { check_for_open_fd (sm_ctxt, node, stmt, call, callee_fndecl, DIRS_READ); } void fd_state_machine::on_write (sm_context &sm_ctxt, const supernode *node, - const gimple *stmt, const gcall *call, + const gimple *stmt, const gcall &call, const tree callee_fndecl) const { check_for_open_fd (sm_ctxt, node, stmt, call, callee_fndecl, DIRS_WRITE); @@ -1668,18 +1676,18 @@ fd_state_machine::on_write (sm_context &sm_ctxt, const supernode *node, void fd_state_machine::check_for_open_fd ( sm_context &sm_ctxt, const supernode *node, const gimple *stmt, - const gcall *call, const tree callee_fndecl, + const gcall &call, const tree callee_fndecl, enum access_directions callee_fndecl_dir) const { - tree arg = gimple_call_arg (call, 0); + tree arg = gimple_call_arg (&call, 0); tree diag_arg = sm_ctxt.get_diagnostic_tree (arg); state_t state = sm_ctxt.get_state (stmt, arg); if (is_closed_fd_p (state)) { sm_ctxt.warn (node, stmt, arg, - make_unique<fd_use_after_close> (*this, diag_arg, - callee_fndecl)); + std::make_unique<fd_use_after_close> (*this, diag_arg, + callee_fndecl)); } else @@ -1690,10 +1698,10 @@ fd_state_machine::check_for_open_fd ( /* Complain about fncall on socket in wrong phase. */ sm_ctxt.warn (node, stmt, arg, - make_unique<fd_phase_mismatch> (*this, diag_arg, - callee_fndecl, - state, - EXPECTED_PHASE_CAN_TRANSFER)); + std::make_unique<fd_phase_mismatch> (*this, diag_arg, + callee_fndecl, + state, + EXPECTED_PHASE_CAN_TRANSFER)); else if (!(is_valid_fd_p (state) || state == m_new_datagram_socket || state == m_bound_unknown_socket @@ -1704,8 +1712,8 @@ fd_state_machine::check_for_open_fd ( if (!is_constant_fd_p (state)) sm_ctxt.warn ( node, stmt, arg, - make_unique<fd_use_without_check> (*this, diag_arg, - callee_fndecl)); + std::make_unique<fd_use_without_check> (*this, diag_arg, + callee_fndecl)); } switch (callee_fndecl_dir) { @@ -1716,8 +1724,8 @@ fd_state_machine::check_for_open_fd ( { tree diag_arg = sm_ctxt.get_diagnostic_tree (arg); sm_ctxt.warn (node, stmt, arg, - make_unique<fd_access_mode_mismatch> ( - *this, diag_arg, DIRS_WRITE, callee_fndecl)); + std::make_unique<fd_access_mode_mismatch> + (*this, diag_arg, DIRS_WRITE, callee_fndecl)); } break; @@ -1727,8 +1735,8 @@ fd_state_machine::check_for_open_fd ( { tree diag_arg = sm_ctxt.get_diagnostic_tree (arg); sm_ctxt.warn (node, stmt, arg, - make_unique<fd_access_mode_mismatch> ( - *this, diag_arg, DIRS_READ, callee_fndecl)); + std::make_unique<fd_access_mode_mismatch> + (*this, diag_arg, DIRS_READ, callee_fndecl)); } break; } @@ -1775,21 +1783,21 @@ fd_state_machine::on_socket (const call_details &cd, sm_context &sm_ctxt, const extrinsic_state &ext_state) const { - const gcall *stmt = cd.get_call_stmt (); + const gcall &call = cd.get_call_stmt (); engine *eng = ext_state.get_engine (); const supergraph *sg = eng->get_supergraph (); - const supernode *node = sg->get_supernode_for_stmt (stmt); + const supernode *node = sg->get_supernode_for_stmt (&call); region_model *model = cd.get_model (); if (successful) { - if (gimple_call_lhs (stmt)) + if (gimple_call_lhs (&call)) { conjured_purge p (model, cd.get_ctxt ()); region_model_manager *mgr = model->get_manager (); const svalue *new_fd = mgr->get_or_create_conjured_svalue (integer_type_node, - stmt, + &call, cd.get_lhs_region (), p); if (!add_constraint_ge_zero (model, new_fd, cd.get_ctxt ())) @@ -1798,12 +1806,12 @@ fd_state_machine::on_socket (const call_details &cd, const svalue *socket_type_sval = cd.get_arg_svalue (1); state_machine::state_t new_state = get_state_for_socket_type (socket_type_sval); - sm_ctxt.on_transition (node, stmt, new_fd, m_start, new_state); + sm_ctxt.on_transition (node, &call, new_fd, m_start, new_state); model->set_value (cd.get_lhs_region (), new_fd, cd.get_ctxt ()); } else - sm_ctxt.warn (node, stmt, NULL_TREE, - make_unique<fd_leak> (*this, NULL_TREE)); + sm_ctxt.warn (node, &call, NULL_TREE, + std::make_unique<fd_leak> (*this, NULL_TREE, nullptr)); } else { @@ -1833,15 +1841,15 @@ fd_state_machine::check_for_socket_fd (const call_details &cd, state_t old_state, bool *complained) const { - const gcall *stmt = cd.get_call_stmt (); + const gcall &call = cd.get_call_stmt (); if (is_closed_fd_p (old_state)) { tree diag_arg = sm_ctxt.get_diagnostic_tree (fd_sval); sm_ctxt.warn - (node, stmt, fd_sval, - make_unique<fd_use_after_close> (*this, diag_arg, - cd.get_fndecl_for_call ())); + (node, &call, fd_sval, + std::make_unique<fd_use_after_close> (*this, diag_arg, + cd.get_fndecl_for_call ())); if (complained) *complained = true; if (successful) @@ -1852,11 +1860,11 @@ fd_state_machine::check_for_socket_fd (const call_details &cd, /* Complain about non-socket. */ tree diag_arg = sm_ctxt.get_diagnostic_tree (fd_sval); sm_ctxt.warn - (node, stmt, fd_sval, - make_unique<fd_type_mismatch> (*this, diag_arg, - cd.get_fndecl_for_call (), - old_state, - EXPECTED_TYPE_SOCKET)); + (node, &call, fd_sval, + std::make_unique<fd_type_mismatch> (*this, diag_arg, + cd.get_fndecl_for_call (), + old_state, + EXPECTED_TYPE_SOCKET)); if (complained) *complained = true; if (successful) @@ -1866,9 +1874,9 @@ fd_state_machine::check_for_socket_fd (const call_details &cd, { tree diag_arg = sm_ctxt.get_diagnostic_tree (fd_sval); sm_ctxt.warn - (node, stmt, fd_sval, - make_unique<fd_use_without_check> (*this, diag_arg, - cd.get_fndecl_for_call ())); + (node, &call, fd_sval, + std::make_unique<fd_use_without_check> (*this, diag_arg, + cd.get_fndecl_for_call ())); if (complained) *complained = true; if (successful) @@ -1929,11 +1937,11 @@ fd_state_machine::check_for_new_socket_fd (const call_details &cd, /* Complain about "bind" or "connect" in wrong phase. */ tree diag_arg = sm_ctxt.get_diagnostic_tree (fd_sval); sm_ctxt.warn - (node, cd.get_call_stmt (), fd_sval, - make_unique<fd_phase_mismatch> (*this, diag_arg, - cd.get_fndecl_for_call (), - old_state, - expected_phase)); + (node, &cd.get_call_stmt (), fd_sval, + std::make_unique<fd_phase_mismatch> (*this, diag_arg, + cd.get_fndecl_for_call (), + old_state, + expected_phase)); if (successful) return false; } @@ -1941,7 +1949,7 @@ fd_state_machine::check_for_new_socket_fd (const call_details &cd, { /* If we were in the start state, assume we had a new socket. */ if (old_state == m_start) - sm_ctxt.set_next_state (cd.get_call_stmt (), fd_sval, + sm_ctxt.set_next_state (&cd.get_call_stmt (), fd_sval, m_new_unknown_socket); } @@ -1963,13 +1971,13 @@ fd_state_machine::on_bind (const call_details &cd, sm_context &sm_ctxt, const extrinsic_state &ext_state) const { - const gcall *stmt = cd.get_call_stmt (); + const gcall &call = cd.get_call_stmt (); engine *eng = ext_state.get_engine (); const supergraph *sg = eng->get_supergraph (); - const supernode *node = sg->get_supernode_for_stmt (stmt); + const supernode *node = sg->get_supernode_for_stmt (&call); const svalue *fd_sval = cd.get_arg_svalue (0); region_model *model = cd.get_model (); - state_t old_state = sm_ctxt.get_state (stmt, fd_sval); + state_t old_state = sm_ctxt.get_state (&call, fd_sval); if (!check_for_new_socket_fd (cd, successful, sm_ctxt, fd_sval, node, old_state, @@ -1978,7 +1986,7 @@ fd_state_machine::on_bind (const call_details &cd, if (successful) { - state_t next_state = NULL; + state_t next_state = nullptr; if (old_state == m_new_stream_socket) next_state = m_bound_stream_socket; else if (old_state == m_new_datagram_socket) @@ -1992,7 +2000,7 @@ fd_state_machine::on_bind (const call_details &cd, next_state = m_stop; else gcc_unreachable (); - sm_ctxt.set_next_state (cd.get_call_stmt (), fd_sval, next_state); + sm_ctxt.set_next_state (&cd.get_call_stmt (), fd_sval, next_state); model->update_for_zero_return (cd, true); } else @@ -2015,13 +2023,13 @@ fd_state_machine::on_listen (const call_details &cd, sm_context &sm_ctxt, const extrinsic_state &ext_state) const { - const gcall *stmt = cd.get_call_stmt (); + const gcall &call = cd.get_call_stmt (); engine *eng = ext_state.get_engine (); const supergraph *sg = eng->get_supergraph (); - const supernode *node = sg->get_supernode_for_stmt (cd.get_call_stmt ()); + const supernode *node = sg->get_supernode_for_stmt (&cd.get_call_stmt ()); const svalue *fd_sval = cd.get_arg_svalue (0); region_model *model = cd.get_model (); - state_t old_state = sm_ctxt.get_state (stmt, fd_sval); + state_t old_state = sm_ctxt.get_state (&call, fd_sval); /* We expect a stream socket that's had "bind" called on it. */ if (!check_for_socket_fd (cd, successful, sm_ctxt, fd_sval, node, old_state)) @@ -2039,18 +2047,18 @@ fd_state_machine::on_listen (const call_details &cd, tree diag_arg = sm_ctxt.get_diagnostic_tree (fd_sval); if (is_stream_socket_fd_p (old_state)) sm_ctxt.warn - (node, stmt, fd_sval, - make_unique<fd_phase_mismatch> (*this, diag_arg, - cd.get_fndecl_for_call (), - old_state, - EXPECTED_PHASE_CAN_LISTEN)); + (node, &call, fd_sval, + std::make_unique<fd_phase_mismatch> (*this, diag_arg, + cd.get_fndecl_for_call (), + old_state, + EXPECTED_PHASE_CAN_LISTEN)); else sm_ctxt.warn - (node, stmt, fd_sval, - make_unique<fd_type_mismatch> (*this, diag_arg, - cd.get_fndecl_for_call (), - old_state, - EXPECTED_TYPE_STREAM_SOCKET)); + (node, &call, fd_sval, + std::make_unique<fd_type_mismatch> (*this, diag_arg, + cd.get_fndecl_for_call (), + old_state, + EXPECTED_TYPE_STREAM_SOCKET)); if (successful) return false; } @@ -2058,7 +2066,7 @@ fd_state_machine::on_listen (const call_details &cd, if (successful) { model->update_for_zero_return (cd, true); - sm_ctxt.set_next_state (cd.get_call_stmt (), fd_sval, + sm_ctxt.set_next_state (&cd.get_call_stmt (), fd_sval, m_listening_stream_socket); } else @@ -2067,7 +2075,7 @@ fd_state_machine::on_listen (const call_details &cd, model->update_for_int_cst_return (cd, -1, true); model->set_errno (cd); if (old_state == m_start) - sm_ctxt.set_next_state (cd.get_call_stmt (), fd_sval, + sm_ctxt.set_next_state (&cd.get_call_stmt (), fd_sval, m_bound_stream_socket); } @@ -2084,15 +2092,15 @@ fd_state_machine::on_accept (const call_details &cd, sm_context &sm_ctxt, const extrinsic_state &ext_state) const { - const gcall *stmt = cd.get_call_stmt (); + const gcall &call = cd.get_call_stmt (); engine *eng = ext_state.get_engine (); const supergraph *sg = eng->get_supergraph (); - const supernode *node = sg->get_supernode_for_stmt (stmt); + const supernode *node = sg->get_supernode_for_stmt (&call); const svalue *fd_sval = cd.get_arg_svalue (0); const svalue *address_sval = cd.get_arg_svalue (1); const svalue *len_ptr_sval = cd.get_arg_svalue (2); region_model *model = cd.get_model (); - state_t old_state = sm_ctxt.get_state (stmt, fd_sval); + state_t old_state = sm_ctxt.get_state (&call, fd_sval); if (!address_sval->all_zeroes_p ()) { @@ -2127,14 +2135,14 @@ fd_state_machine::on_accept (const call_details &cd, old_len_sval); const svalue *new_addr_sval = mgr->get_or_create_conjured_svalue (NULL_TREE, - stmt, + &call, old_sized_address_reg, p); model->set_value (old_sized_address_reg, new_addr_sval, cd.get_ctxt ()); const svalue *new_addr_len = mgr->get_or_create_conjured_svalue (NULL_TREE, - stmt, + &call, len_reg, p); model->set_value (len_reg, new_addr_len, cd.get_ctxt ()); @@ -2148,7 +2156,7 @@ fd_state_machine::on_accept (const call_details &cd, if (old_state == m_start || old_state == m_constant_fd) /* If we were in the start state (or a constant), assume we had the expected state. */ - sm_ctxt.set_next_state (cd.get_call_stmt (), fd_sval, + sm_ctxt.set_next_state (&cd.get_call_stmt (), fd_sval, m_listening_stream_socket); else if (old_state == m_stop) { @@ -2160,18 +2168,18 @@ fd_state_machine::on_accept (const call_details &cd, tree diag_arg = sm_ctxt.get_diagnostic_tree (fd_sval); if (is_stream_socket_fd_p (old_state)) sm_ctxt.warn - (node, stmt, fd_sval, - make_unique<fd_phase_mismatch> (*this, diag_arg, - cd.get_fndecl_for_call (), - old_state, - EXPECTED_PHASE_CAN_ACCEPT)); + (node, &call, fd_sval, + std::make_unique<fd_phase_mismatch> (*this, diag_arg, + cd.get_fndecl_for_call (), + old_state, + EXPECTED_PHASE_CAN_ACCEPT)); else sm_ctxt.warn - (node, stmt, fd_sval, - make_unique<fd_type_mismatch> (*this, diag_arg, - cd.get_fndecl_for_call (), - old_state, - EXPECTED_TYPE_STREAM_SOCKET)); + (node, &call, fd_sval, + std::make_unique<fd_type_mismatch> (*this, diag_arg, + cd.get_fndecl_for_call (), + old_state, + EXPECTED_TYPE_STREAM_SOCKET)); if (successful) return false; } @@ -2179,24 +2187,24 @@ fd_state_machine::on_accept (const call_details &cd, if (successful) { /* Return new conjured FD in "connected" state. */ - if (gimple_call_lhs (stmt)) + if (gimple_call_lhs (&call)) { conjured_purge p (model, cd.get_ctxt ()); region_model_manager *mgr = model->get_manager (); const svalue *new_fd = mgr->get_or_create_conjured_svalue (integer_type_node, - stmt, + &call, cd.get_lhs_region (), p); if (!add_constraint_ge_zero (model, new_fd, cd.get_ctxt ())) return false; - sm_ctxt.on_transition (node, stmt, new_fd, + sm_ctxt.on_transition (node, &call, new_fd, m_start, m_connected_stream_socket); model->set_value (cd.get_lhs_region (), new_fd, cd.get_ctxt ()); } else - sm_ctxt.warn (node, stmt, NULL_TREE, - make_unique<fd_leak> (*this, NULL_TREE)); + sm_ctxt.warn (node, &call, NULL_TREE, + std::make_unique<fd_leak> (*this, NULL_TREE, nullptr)); } else { @@ -2218,13 +2226,13 @@ fd_state_machine::on_connect (const call_details &cd, sm_context &sm_ctxt, const extrinsic_state &ext_state) const { - const gcall *stmt = cd.get_call_stmt (); + const gcall &call = cd.get_call_stmt (); engine *eng = ext_state.get_engine (); const supergraph *sg = eng->get_supergraph (); - const supernode *node = sg->get_supernode_for_stmt (stmt); + const supernode *node = sg->get_supernode_for_stmt (&call); const svalue *fd_sval = cd.get_arg_svalue (0); region_model *model = cd.get_model (); - state_t old_state = sm_ctxt.get_state (stmt, fd_sval); + state_t old_state = sm_ctxt.get_state (&call, fd_sval); if (!check_for_new_socket_fd (cd, successful, sm_ctxt, fd_sval, node, old_state, @@ -2234,7 +2242,7 @@ fd_state_machine::on_connect (const call_details &cd, if (successful) { model->update_for_zero_return (cd, true); - state_t next_state = NULL; + state_t next_state = nullptr; if (old_state == m_new_stream_socket) next_state = m_connected_stream_socket; else if (old_state == m_new_datagram_socket) @@ -2250,7 +2258,7 @@ fd_state_machine::on_connect (const call_details &cd, next_state = m_stop; else gcc_unreachable (); - sm_ctxt.set_next_state (cd.get_call_stmt (), fd_sval, next_state); + sm_ctxt.set_next_state (&cd.get_call_stmt (), fd_sval, next_state); } else { @@ -2332,16 +2340,18 @@ fd_state_machine::can_purge_p (state_t s) const } std::unique_ptr<pending_diagnostic> -fd_state_machine::on_leak (tree var) const +fd_state_machine::on_leak (tree var, + const program_state *, + const program_state *new_state) const { - return make_unique<fd_leak> (*this, var); + return std::make_unique<fd_leak> (*this, var, new_state); } } // namespace -state_machine * +std::unique_ptr<state_machine> make_fd_state_machine (logger *logger) { - return new fd_state_machine (logger); + return std::make_unique<fd_state_machine> (logger); } static bool @@ -2372,7 +2382,7 @@ region_model::mark_as_valid_fd (const svalue *sval, region_model_context *ctxt) { sm_state_map *smap; const fd_state_machine *fd_sm; - if (!get_fd_state (ctxt, &smap, &fd_sm, NULL, NULL)) + if (!get_fd_state (ctxt, &smap, &fd_sm, nullptr, nullptr)) return; const extrinsic_state *ext_state = ctxt->get_ext_state (); if (!ext_state) @@ -2401,7 +2411,7 @@ public: sm_state_map *smap; const fd_state_machine *fd_sm; std::unique_ptr<sm_context> sm_ctxt; - if (!get_fd_state (ctxt, &smap, &fd_sm, NULL, &sm_ctxt)) + if (!get_fd_state (ctxt, &smap, &fd_sm, nullptr, &sm_ctxt)) { cd.set_any_lhs_with_defaults (); return true; @@ -2426,8 +2436,10 @@ public: { if (cd.get_ctxt ()) { - cd.get_ctxt ()->bifurcate (make_unique<outcome_of_socket> (cd, false)); - cd.get_ctxt ()->bifurcate (make_unique<outcome_of_socket> (cd, true)); + cd.get_ctxt ()->bifurcate + (std::make_unique<outcome_of_socket> (cd, false)); + cd.get_ctxt ()->bifurcate + (std::make_unique<outcome_of_socket> (cd, true)); cd.get_ctxt ()->terminate_path (); } } @@ -2454,7 +2466,7 @@ public: sm_state_map *smap; const fd_state_machine *fd_sm; std::unique_ptr<sm_context> sm_ctxt; - if (!get_fd_state (ctxt, &smap, &fd_sm, NULL, &sm_ctxt)) + if (!get_fd_state (ctxt, &smap, &fd_sm, nullptr, &sm_ctxt)) { cd.set_any_lhs_with_defaults (); return true; @@ -2478,8 +2490,10 @@ public: { if (cd.get_ctxt ()) { - cd.get_ctxt ()->bifurcate (make_unique<outcome_of_bind> (cd, false)); - cd.get_ctxt ()->bifurcate (make_unique<outcome_of_bind> (cd, true)); + cd.get_ctxt ()->bifurcate + (std::make_unique<outcome_of_bind> (cd, false)); + cd.get_ctxt ()->bifurcate + (std::make_unique<outcome_of_bind> (cd, true)); cd.get_ctxt ()->terminate_path (); } } @@ -2505,7 +2519,7 @@ class kf_listen : public known_function sm_state_map *smap; const fd_state_machine *fd_sm; std::unique_ptr<sm_context> sm_ctxt; - if (!get_fd_state (ctxt, &smap, &fd_sm, NULL, &sm_ctxt)) + if (!get_fd_state (ctxt, &smap, &fd_sm, nullptr, &sm_ctxt)) { cd.set_any_lhs_with_defaults (); return true; @@ -2530,8 +2544,10 @@ class kf_listen : public known_function { if (cd.get_ctxt ()) { - cd.get_ctxt ()->bifurcate (make_unique<outcome_of_listen> (cd, false)); - cd.get_ctxt ()->bifurcate (make_unique<outcome_of_listen> (cd, true)); + cd.get_ctxt ()->bifurcate + (std::make_unique<outcome_of_listen> (cd, false)); + cd.get_ctxt ()->bifurcate + (std::make_unique<outcome_of_listen> (cd, true)); cd.get_ctxt ()->terminate_path (); } } @@ -2557,7 +2573,7 @@ class kf_accept : public known_function sm_state_map *smap; const fd_state_machine *fd_sm; std::unique_ptr<sm_context> sm_ctxt; - if (!get_fd_state (ctxt, &smap, &fd_sm, NULL, &sm_ctxt)) + if (!get_fd_state (ctxt, &smap, &fd_sm, nullptr, &sm_ctxt)) { cd.set_any_lhs_with_defaults (); return true; @@ -2584,8 +2600,10 @@ class kf_accept : public known_function { if (cd.get_ctxt ()) { - cd.get_ctxt ()->bifurcate (make_unique<outcome_of_accept> (cd, false)); - cd.get_ctxt ()->bifurcate (make_unique<outcome_of_accept> (cd, true)); + cd.get_ctxt ()->bifurcate + (std::make_unique<outcome_of_accept> (cd, false)); + cd.get_ctxt ()->bifurcate + (std::make_unique<outcome_of_accept> (cd, true)); cd.get_ctxt ()->terminate_path (); } } @@ -2612,7 +2630,7 @@ public: sm_state_map *smap; const fd_state_machine *fd_sm; std::unique_ptr<sm_context> sm_ctxt; - if (!get_fd_state (ctxt, &smap, &fd_sm, NULL, &sm_ctxt)) + if (!get_fd_state (ctxt, &smap, &fd_sm, nullptr, &sm_ctxt)) { cd.set_any_lhs_with_defaults (); return true; @@ -2638,8 +2656,10 @@ public: { if (cd.get_ctxt ()) { - cd.get_ctxt ()->bifurcate (make_unique<outcome_of_connect> (cd, false)); - cd.get_ctxt ()->bifurcate (make_unique<outcome_of_connect> (cd, true)); + cd.get_ctxt ()->bifurcate + (std::make_unique<outcome_of_connect> (cd, false)); + cd.get_ctxt ()->bifurcate + (std::make_unique<outcome_of_connect> (cd, true)); cd.get_ctxt ()->terminate_path (); } } @@ -2688,7 +2708,7 @@ class kf_isatty : public known_function sm_state_map *smap; const fd_state_machine *fd_sm; std::unique_ptr<sm_context> sm_ctxt; - if (!get_fd_state (ctxt, &smap, &fd_sm, NULL, &sm_ctxt)) + if (!get_fd_state (ctxt, &smap, &fd_sm, nullptr, &sm_ctxt)) return true; const extrinsic_state *ext_state = ctxt->get_ext_state (); if (!ext_state) @@ -2696,7 +2716,7 @@ class kf_isatty : public known_function const svalue *fd_sval = cd.get_arg_svalue (0); state_machine::state_t old_state - = sm_ctxt->get_state (cd.get_call_stmt (), fd_sval); + = sm_ctxt->get_state (&cd.get_call_stmt (), fd_sval); if (fd_sm->is_closed_fd_p (old_state) || old_state == fd_sm->m_invalid) @@ -2716,8 +2736,10 @@ public: { if (cd.get_ctxt ()) { - cd.get_ctxt ()->bifurcate (make_unique<outcome_of_isatty> (cd, false)); - cd.get_ctxt ()->bifurcate (make_unique<outcome_of_isatty> (cd, true)); + cd.get_ctxt ()->bifurcate + (std::make_unique<outcome_of_isatty> (cd, false)); + cd.get_ctxt ()->bifurcate + (std::make_unique<outcome_of_isatty> (cd, true)); cd.get_ctxt ()->terminate_path (); } } @@ -2773,7 +2795,7 @@ class kf_pipe : public known_function conjured_purge p (model, cd.get_ctxt ()); const svalue *fd_sval = mgr->get_or_create_conjured_svalue (integer_type_node, - cd.get_call_stmt (), + &cd.get_call_stmt (), element_reg, p); model->set_value (element_reg, fd_sval, cd.get_ctxt ()); @@ -2799,8 +2821,10 @@ public: { if (cd.get_ctxt ()) { - cd.get_ctxt ()->bifurcate (make_unique<failure> (cd)); - cd.get_ctxt ()->bifurcate (make_unique<success> (cd)); + cd.get_ctxt ()->bifurcate + (std::make_unique<failure> (cd)); + cd.get_ctxt ()->bifurcate + (std::make_unique<success> (cd)); cd.get_ctxt ()->terminate_path (); } } @@ -2848,15 +2872,15 @@ public: void register_known_fd_functions (known_function_manager &kfm) { - kfm.add ("accept", make_unique<kf_accept> ()); - kfm.add ("bind", make_unique<kf_bind> ()); - kfm.add ("connect", make_unique<kf_connect> ()); - kfm.add ("isatty", make_unique<kf_isatty> ()); - kfm.add ("listen", make_unique<kf_listen> ()); - kfm.add ("pipe", make_unique<kf_pipe> (1)); - kfm.add ("pipe2", make_unique<kf_pipe> (2)); - kfm.add ("read", make_unique<kf_read> ()); - kfm.add ("socket", make_unique<kf_socket> ()); + kfm.add ("accept", std::make_unique<kf_accept> ()); + kfm.add ("bind", std::make_unique<kf_bind> ()); + kfm.add ("connect", std::make_unique<kf_connect> ()); + kfm.add ("isatty", std::make_unique<kf_isatty> ()); + kfm.add ("listen", std::make_unique<kf_listen> ()); + kfm.add ("pipe", std::make_unique<kf_pipe> (1)); + kfm.add ("pipe2", std::make_unique<kf_pipe> (2)); + kfm.add ("read", std::make_unique<kf_read> ()); + kfm.add ("socket", std::make_unique<kf_socket> ()); } } // namespace ana diff --git a/gcc/analyzer/sm-file.cc b/gcc/analyzer/sm-file.cc index 1e41dc9..c852c02 100644 --- a/gcc/analyzer/sm-file.cc +++ b/gcc/analyzer/sm-file.cc @@ -18,28 +18,19 @@ 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/>. */ -#include "config.h" -#define INCLUDE_VECTOR -#include "system.h" -#include "coretypes.h" -#include "make-unique.h" -#include "tree.h" -#include "function.h" -#include "basic-block.h" -#include "gimple.h" -#include "options.h" -#include "diagnostic-core.h" -#include "diagnostic-path.h" -#include "analyzer/analyzer.h" -#include "diagnostic-event-id.h" +#include "analyzer/common.h" + +#include "diagnostics/event-id.h" +#include "selftest.h" + #include "analyzer/analyzer-logging.h" #include "analyzer/sm.h" #include "analyzer/pending-diagnostic.h" #include "analyzer/function-set.h" #include "analyzer/analyzer-selftests.h" -#include "selftest.h" #include "analyzer/call-string.h" #include "analyzer/program-point.h" +#include "analyzer/program-state.h" #include "analyzer/store.h" #include "analyzer/region-model.h" #include "analyzer/call-details.h" @@ -82,7 +73,11 @@ public: const svalue *rhs) const final override; bool can_purge_p (state_t s) const final override; - std::unique_ptr<pending_diagnostic> on_leak (tree var) const final override; + + std::unique_ptr<pending_diagnostic> + on_leak (tree var, + const program_state *old_state, + const program_state *new_state) const final override; /* State for a FILE * returned from fopen that hasn't been checked for NULL. @@ -160,18 +155,20 @@ public: return false; } - diagnostic_event::meaning + diagnostics::paths::event::meaning get_meaning_for_state_change (const evdesc::state_change &change) const final override { + using event = diagnostics::paths::event; + if (change.m_old_state == m_sm.get_start_state () && change.m_new_state == m_sm.m_unchecked) - return diagnostic_event::meaning (diagnostic_event::VERB_acquire, - diagnostic_event::NOUN_resource); + return event::meaning (event::verb::acquire, + event::noun::resource); if (change.m_new_state == m_sm.m_closed) - return diagnostic_event::meaning (diagnostic_event::VERB_release, - diagnostic_event::NOUN_resource); - return diagnostic_event::meaning (); + return event::meaning (event::verb::release, + event::noun::resource); + return event::meaning (); } protected: @@ -230,15 +227,20 @@ public: } private: - diagnostic_event_id_t m_first_fclose_event; + diagnostics::paths::event_id_t m_first_fclose_event; }; class file_leak : public file_diagnostic { public: - file_leak (const fileptr_state_machine &sm, tree arg) - : file_diagnostic (sm, arg) - {} + file_leak (const fileptr_state_machine &sm, tree arg, + const program_state *final_state) + : file_diagnostic (sm, arg), + m_final_state () + { + if (final_state) + m_final_state = std::make_unique<program_state> (*final_state); + } const char *get_kind () const final override { return "file_leak"; } @@ -296,8 +298,15 @@ public: return true; } + const program_state * + get_final_state () const final override + { + return m_final_state.get (); + } + private: - diagnostic_event_id_t m_fopen_event; + diagnostics::paths::event_id_t m_fopen_event; + std::unique_ptr<program_state> m_final_state; }; /* fileptr_state_machine's ctor. */ @@ -403,9 +412,9 @@ fileptr_state_machine::on_stmt (sm_context &sm_ctxt, const gimple *stmt) const { if (const gcall *call = dyn_cast <const gcall *> (stmt)) - if (tree callee_fndecl = sm_ctxt.get_fndecl_for_call (call)) + if (tree callee_fndecl = sm_ctxt.get_fndecl_for_call (*call)) { - if (is_named_call_p (callee_fndecl, "fopen", call, 2)) + if (is_named_call_p (callee_fndecl, "fopen", *call, 2)) { tree lhs = gimple_call_lhs (call); if (lhs) @@ -417,7 +426,7 @@ fileptr_state_machine::on_stmt (sm_context &sm_ctxt, return true; } - if (is_named_call_p (callee_fndecl, "fclose", call, 1)) + if (is_named_call_p (callee_fndecl, "fclose", *call, 1)) { tree arg = gimple_call_arg (call, 0); @@ -433,7 +442,8 @@ fileptr_state_machine::on_stmt (sm_context &sm_ctxt, { tree diag_arg = sm_ctxt.get_diagnostic_tree (arg); sm_ctxt.warn (node, stmt, arg, - make_unique<double_fclose> (*this, diag_arg)); + std::make_unique<double_fclose> (*this, + diag_arg)); sm_ctxt.set_next_state (stmt, arg, m_stop); } return true; @@ -501,19 +511,21 @@ fileptr_state_machine::can_purge_p (state_t s) const state 'unchecked' and 'nonnull'. */ std::unique_ptr<pending_diagnostic> -fileptr_state_machine::on_leak (tree var) const +fileptr_state_machine::on_leak (tree var, + const program_state *, + const program_state *new_state) const { - return make_unique<file_leak> (*this, var); + return std::make_unique<file_leak> (*this, var, new_state); } } // anonymous namespace /* Internal interface to this file. */ -state_machine * +std::unique_ptr<state_machine> make_fileptr_state_machine (logger *logger) { - return new fileptr_state_machine (logger); + return std::make_unique<fileptr_state_machine> (logger); } /* Handler for various stdio-related builtins that merely have external @@ -655,40 +667,40 @@ public: void register_known_file_functions (known_function_manager &kfm) { - kfm.add (BUILT_IN_FPRINTF, make_unique<kf_stdio_output_fn> ()); - kfm.add (BUILT_IN_FPRINTF_UNLOCKED, make_unique<kf_stdio_output_fn> ()); - kfm.add (BUILT_IN_FPUTC, make_unique<kf_stdio_output_fn> ()); - kfm.add (BUILT_IN_FPUTC_UNLOCKED, make_unique<kf_stdio_output_fn> ()); - kfm.add (BUILT_IN_FPUTS, make_unique<kf_stdio_output_fn> ()); - kfm.add (BUILT_IN_FPUTS_UNLOCKED, make_unique<kf_stdio_output_fn> ()); - kfm.add (BUILT_IN_FWRITE, make_unique<kf_stdio_output_fn> ()); - kfm.add (BUILT_IN_FWRITE_UNLOCKED, make_unique<kf_stdio_output_fn> ()); - kfm.add (BUILT_IN_PRINTF, make_unique<kf_stdio_output_fn> ()); - kfm.add (BUILT_IN_PRINTF_UNLOCKED, make_unique<kf_stdio_output_fn> ()); - kfm.add (BUILT_IN_PUTC, make_unique<kf_stdio_output_fn> ()); - kfm.add (BUILT_IN_PUTCHAR, make_unique<kf_stdio_output_fn> ()); - kfm.add (BUILT_IN_PUTCHAR_UNLOCKED, make_unique<kf_stdio_output_fn> ()); - kfm.add (BUILT_IN_PUTC_UNLOCKED, make_unique<kf_stdio_output_fn> ()); - kfm.add (BUILT_IN_PUTS, make_unique<kf_stdio_output_fn> ()); - kfm.add (BUILT_IN_PUTS_UNLOCKED, make_unique<kf_stdio_output_fn> ()); - kfm.add (BUILT_IN_VFPRINTF, make_unique<kf_stdio_output_fn> ()); - kfm.add (BUILT_IN_VPRINTF, make_unique<kf_stdio_output_fn> ()); - - kfm.add ("ferror", make_unique<kf_ferror> ()); - kfm.add ("fgets", make_unique<kf_fgets> ()); - kfm.add ("fgets_unlocked", make_unique<kf_fgets> ()); // non-standard - kfm.add ("fileno", make_unique<kf_fileno> ()); - kfm.add ("fread", make_unique<kf_fread> ()); - kfm.add ("getc", make_unique<kf_getc> ()); - kfm.add ("getchar", make_unique<kf_getchar> ()); + kfm.add (BUILT_IN_FPRINTF, std::make_unique<kf_stdio_output_fn> ()); + kfm.add (BUILT_IN_FPRINTF_UNLOCKED, std::make_unique<kf_stdio_output_fn> ()); + kfm.add (BUILT_IN_FPUTC, std::make_unique<kf_stdio_output_fn> ()); + kfm.add (BUILT_IN_FPUTC_UNLOCKED, std::make_unique<kf_stdio_output_fn> ()); + kfm.add (BUILT_IN_FPUTS, std::make_unique<kf_stdio_output_fn> ()); + kfm.add (BUILT_IN_FPUTS_UNLOCKED, std::make_unique<kf_stdio_output_fn> ()); + kfm.add (BUILT_IN_FWRITE, std::make_unique<kf_stdio_output_fn> ()); + kfm.add (BUILT_IN_FWRITE_UNLOCKED, std::make_unique<kf_stdio_output_fn> ()); + kfm.add (BUILT_IN_PRINTF, std::make_unique<kf_stdio_output_fn> ()); + kfm.add (BUILT_IN_PRINTF_UNLOCKED, std::make_unique<kf_stdio_output_fn> ()); + kfm.add (BUILT_IN_PUTC, std::make_unique<kf_stdio_output_fn> ()); + kfm.add (BUILT_IN_PUTCHAR, std::make_unique<kf_stdio_output_fn> ()); + kfm.add (BUILT_IN_PUTCHAR_UNLOCKED, std::make_unique<kf_stdio_output_fn> ()); + kfm.add (BUILT_IN_PUTC_UNLOCKED, std::make_unique<kf_stdio_output_fn> ()); + kfm.add (BUILT_IN_PUTS, std::make_unique<kf_stdio_output_fn> ()); + kfm.add (BUILT_IN_PUTS_UNLOCKED, std::make_unique<kf_stdio_output_fn> ()); + kfm.add (BUILT_IN_VFPRINTF, std::make_unique<kf_stdio_output_fn> ()); + kfm.add (BUILT_IN_VPRINTF, std::make_unique<kf_stdio_output_fn> ()); + + kfm.add ("ferror", std::make_unique<kf_ferror> ()); + kfm.add ("fgets", std::make_unique<kf_fgets> ()); + kfm.add ("fgets_unlocked", std::make_unique<kf_fgets> ()); // non-standard + kfm.add ("fileno", std::make_unique<kf_fileno> ()); + kfm.add ("fread", std::make_unique<kf_fread> ()); + kfm.add ("getc", std::make_unique<kf_getc> ()); + kfm.add ("getchar", std::make_unique<kf_getchar> ()); /* Some C++ implementations use the std:: copies of these functions from <cstdio> for <stdio.h>, so we must match against these too. */ - kfm.add_std_ns ("ferror", make_unique<kf_ferror> ()); - kfm.add_std_ns ("fgets", make_unique<kf_fgets> ()); - kfm.add_std_ns ("fread", make_unique<kf_fread> ()); - kfm.add_std_ns ("getc", make_unique<kf_getc> ()); - kfm.add_std_ns ("getchar", make_unique<kf_getchar> ()); + kfm.add_std_ns ("ferror", std::make_unique<kf_ferror> ()); + kfm.add_std_ns ("fgets", std::make_unique<kf_fgets> ()); + kfm.add_std_ns ("fread", std::make_unique<kf_fread> ()); + kfm.add_std_ns ("getc", std::make_unique<kf_getc> ()); + kfm.add_std_ns ("getchar", std::make_unique<kf_getchar> ()); } #if CHECKING_P diff --git a/gcc/analyzer/sm-malloc.cc b/gcc/analyzer/sm-malloc.cc index 6972a55..a6b1421 100644 --- a/gcc/analyzer/sm-malloc.cc +++ b/gcc/analyzer/sm-malloc.cc @@ -18,21 +18,13 @@ 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/>. */ -#include "config.h" -#define INCLUDE_VECTOR -#include "system.h" -#include "coretypes.h" -#include "make-unique.h" -#include "tree.h" -#include "function.h" -#include "basic-block.h" -#include "gimple.h" -#include "options.h" -#include "bitmap.h" -#include "diagnostic-core.h" -#include "diagnostic-path.h" -#include "analyzer/analyzer.h" -#include "diagnostic-event-id.h" +#include "analyzer/common.h" + +#include "diagnostics/event-id.h" +#include "stringpool.h" +#include "attribs.h" +#include "xml-printer.h" + #include "analyzer/analyzer-logging.h" #include "analyzer/sm.h" #include "analyzer/pending-diagnostic.h" @@ -41,13 +33,12 @@ along with GCC; see the file COPYING3. If not see #include "analyzer/store.h" #include "analyzer/region-model.h" #include "analyzer/call-details.h" -#include "stringpool.h" -#include "attribs.h" #include "analyzer/function-set.h" #include "analyzer/program-state.h" #include "analyzer/checker-event.h" #include "analyzer/exploded-graph.h" #include "analyzer/inlining-iterator.h" +#include "analyzer/ana-state-to-diagnostic-state.h" #if ENABLE_ANALYZER @@ -150,7 +141,7 @@ struct assumed_non_null_state : public allocation_state assumed_non_null_state (const char *name, unsigned id, const frame_region *frame) : allocation_state (name, id, RS_ASSUMED_NON_NULL, - NULL, NULL), + nullptr, nullptr), m_frame (frame) { gcc_assert (m_frame); @@ -301,7 +292,7 @@ struct deallocator_set_map_traits static inline hashval_t hash (const key_type &k) { - gcc_assert (k != NULL); + gcc_assert (k != nullptr); gcc_assert (k != reinterpret_cast<key_type> (1)); hashval_t result = 0; @@ -335,7 +326,7 @@ struct deallocator_set_map_traits template <typename T> static inline void mark_empty (T &entry) { - entry.m_key = NULL; + entry.m_key = nullptr; } template <typename T> static inline bool is_deleted (const T &entry) @@ -345,7 +336,7 @@ struct deallocator_set_map_traits template <typename T> static inline bool is_empty (const T &entry) { - return entry.m_key == NULL; + return entry.m_key == nullptr; } static const bool empty_zero_p = false; }; @@ -414,7 +405,11 @@ public: const frame_region *) const final override; bool can_purge_p (state_t s) const final override; - std::unique_ptr<pending_diagnostic> on_leak (tree var) const final override; + + std::unique_ptr<pending_diagnostic> + on_leak (tree var, + const program_state *old_state, + const program_state *new_state) const final override; bool reset_when_passed_to_unknown_fn_p (state_t s, bool is_mutable) const final override; @@ -440,6 +435,11 @@ public: const svalue *new_ptr_sval, const extrinsic_state &ext_state) const; + void + add_state_to_state_graph (analyzer_state_graph &out_state_graph, + const svalue &sval, + state_machine::state_t state) const final override; + standard_deallocator_set m_free; standard_deallocator_set m_scalar_delete; standard_deallocator_set m_vector_delete; @@ -482,22 +482,22 @@ private: tree ptr) const; void on_allocator_call (sm_context &sm_ctxt, - const gcall *call, + const gcall &call, const deallocator_set *deallocators, bool returns_nonnull = false) const; void handle_free_of_non_heap (sm_context &sm_ctxt, const supernode *node, - const gcall *call, + const gcall &call, tree arg, const deallocator *d) const; void on_deallocator_call (sm_context &sm_ctxt, const supernode *node, - const gcall *call, + const gcall &call, const deallocator *d, unsigned argno) const; void on_realloc_call (sm_context &sm_ctxt, const supernode *node, - const gcall *call) const; + const gcall &call) const; void on_zero_assignment (sm_context &sm_ctxt, const gimple *stmt, tree lhs) const; @@ -535,7 +535,7 @@ deallocator::deallocator (malloc_state_machine *sm, enum wording wording) : m_name (name), m_wording (wording), - m_freed (sm->add_state ("freed", RS_FREED, NULL, this)) + m_freed (sm->add_state ("freed", RS_FREED, nullptr, this)) { } @@ -579,8 +579,8 @@ standard_deallocator::standard_deallocator (malloc_state_machine *sm, deallocator_set::deallocator_set (malloc_state_machine *sm, enum wording wording) : m_wording (wording), - m_unchecked (sm->add_state ("unchecked", RS_UNCHECKED, this, NULL)), - m_nonnull (sm->add_state ("nonnull", RS_NONNULL, this, NULL)) + m_unchecked (sm->add_state ("unchecked", RS_UNCHECKED, this, nullptr)), + m_nonnull (sm->add_state ("nonnull", RS_NONNULL, this, nullptr)) { } @@ -625,7 +625,7 @@ custom_deallocator_set::maybe_get_single () const { if (m_deallocator_vec.length () == 1) return m_deallocator_vec[0]; - return NULL; + return nullptr; } void @@ -673,14 +673,15 @@ standard_deallocator_set::dump_to_pp (pretty_printer *pp) const pp_character (pp, '}'); } -/* Return STATE cast to the custom state subclass, or NULL for the start state. +/* Return STATE cast to the custom state subclass, or nullptr for the + start state. Everything should be an allocation_state apart from the start state. */ static const allocation_state * dyn_cast_allocation_state (state_machine::state_t state) { if (state->get_id () == 0) - return NULL; + return nullptr; return static_cast <const allocation_state *> (state); } @@ -796,8 +797,13 @@ public: else { if (change.m_expr) - pp_printf (&pp, "%qE is NULL", - change.m_expr); + { + if (zerop (change.m_expr)) + pp_printf (&pp, "using NULL here"); + else + pp_printf (&pp, "%qE is NULL", + change.m_expr); + } else pp_printf (&pp, "%qs is NULL", "<unknown>"); @@ -808,18 +814,18 @@ public: return false; } - diagnostic_event::meaning + diagnostics::paths::event::meaning get_meaning_for_state_change (const evdesc::state_change &change) const final override { if (change.m_old_state == m_sm.get_start_state () && unchecked_p (change.m_new_state)) - return diagnostic_event::meaning (diagnostic_event::VERB_acquire, - diagnostic_event::NOUN_memory); + return diagnostics::paths::event::meaning (diagnostics::paths::event::verb::acquire, + diagnostics::paths::event::noun::memory); if (freed_p (change.m_new_state)) - return diagnostic_event::meaning (diagnostic_event::VERB_release, - diagnostic_event::NOUN_memory); - return diagnostic_event::meaning (); + return diagnostics::paths::event::meaning (diagnostics::paths::event::verb::release, + diagnostics::paths::event::noun::memory); + return diagnostics::paths::event::meaning (); } protected: @@ -912,7 +918,7 @@ public: } private: - diagnostic_event_id_t m_alloc_event; + diagnostics::paths::event_id_t m_alloc_event; const deallocator_set *m_expected_deallocators; const deallocator *m_actual_dealloc; }; @@ -982,7 +988,7 @@ public: } private: - diagnostic_event_id_t m_first_free_event; + diagnostics::paths::event_id_t m_first_free_event; const char *m_funcname; }; @@ -1025,7 +1031,7 @@ public: } protected: - diagnostic_event_id_t m_origin_of_unchecked_event; + diagnostics::paths::event_id_t m_origin_of_unchecked_event; }; /* Concrete subclass for describing dereference of a possible NULL @@ -1413,15 +1419,21 @@ public: } private: - diagnostic_event_id_t m_free_event; + diagnostics::paths::event_id_t m_free_event; const deallocator *m_deallocator; }; class malloc_leak : public malloc_diagnostic { public: - malloc_leak (const malloc_state_machine &sm, tree arg) - : malloc_diagnostic (sm, arg) {} + malloc_leak (const malloc_state_machine &sm, tree arg, + const program_state *final_state) + : malloc_diagnostic (sm, arg), + m_final_state () + { + if (final_state) + m_final_state = std::make_unique<program_state> (*final_state); + } const char *get_kind () const final override { return "malloc_leak"; } @@ -1481,8 +1493,15 @@ public: return true; } + const program_state * + get_final_state () const final override + { + return m_final_state.get (); + } + private: - diagnostic_event_id_t m_alloc_event; + diagnostics::paths::event_id_t m_alloc_event; + std::unique_ptr<program_state> m_final_state; }; class free_of_non_heap : public malloc_diagnostic @@ -1577,9 +1596,9 @@ class deref_before_check : public malloc_diagnostic public: deref_before_check (const malloc_state_machine &sm, tree arg) : malloc_diagnostic (sm, arg), - m_deref_enode (NULL), - m_deref_expr (NULL), - m_check_enode (NULL) + m_deref_enode (nullptr), + m_deref_expr (nullptr), + m_check_enode (nullptr) { gcc_assert (arg); } @@ -1749,7 +1768,7 @@ private: return result; } - diagnostic_event_id_t m_first_deref_event; + diagnostics::paths::event_id_t m_first_deref_event; const exploded_node *m_deref_enode; tree m_deref_expr; const exploded_node *m_check_enode; @@ -1804,9 +1823,9 @@ malloc_state_machine::malloc_state_machine (logger *logger) m_realloc (this, "realloc", WORDING_REALLOCATED) { gcc_assert (m_start->get_id () == 0); - m_null = add_state ("null", RS_FREED, NULL, NULL); - m_non_heap = add_state ("non-heap", RS_NON_HEAP, NULL, NULL); - m_stop = add_state ("stop", RS_STOP, NULL, NULL); + m_null = add_state ("null", RS_FREED, nullptr, nullptr); + m_non_heap = add_state ("non-heap", RS_NON_HEAP, nullptr, nullptr); + m_stop = add_state ("stop", RS_STOP, nullptr, nullptr); } malloc_state_machine::~malloc_state_machine () @@ -1834,7 +1853,7 @@ malloc_state_machine::add_state (const char *name, enum resource_state rs, return a custom_deallocator_set for them, consolidating them to ensure uniqueness of the sets. - Return NULL if it has no such attributes. */ + Return nullptr if it has no such attributes. */ const custom_deallocator_set * malloc_state_machine:: @@ -1843,7 +1862,7 @@ get_or_create_custom_deallocator_set (tree allocator_fndecl) /* Early rejection of decls without attributes. */ tree attrs = DECL_ATTRIBUTES (allocator_fndecl); if (!attrs) - return NULL; + return nullptr; /* Otherwise, call maybe_create_custom_deallocator_set, memoizing the result. */ @@ -1861,7 +1880,7 @@ get_or_create_custom_deallocator_set (tree allocator_fndecl) custom_deallocator_set for them, consolidating them to ensure uniqueness of the sets. - Return NULL if it has no such attributes. + Return nullptr if it has no such attributes. Subroutine of get_or_create_custom_deallocator_set which memoizes the result. */ @@ -1892,7 +1911,7 @@ maybe_create_custom_deallocator_set (tree allocator_fndecl) /* If there weren't any deallocators, bail. */ if (deallocator_vec.length () == 0) - return NULL; + return nullptr; /* Consolidate, so that we reuse existing deallocator_set instances. */ @@ -1955,7 +1974,7 @@ get_or_create_assumed_non_null_state_for_frame (const frame_region *frame) builtin. */ static bool -known_allocator_p (const_tree fndecl, const gcall *call) +known_allocator_p (const_tree fndecl, const gcall &call) { /* Either it is a function we know by name and number of arguments... */ if (is_named_call_p (fndecl, "malloc", call, 1) @@ -1997,7 +2016,7 @@ malloc_state_machine::maybe_assume_non_null (sm_context &sm_ctxt, tree null_ptr_cst = build_int_cst (TREE_TYPE (ptr), 0); tristate known_non_null - = old_model->eval_condition (ptr, NE_EXPR, null_ptr_cst, NULL); + = old_model->eval_condition (ptr, NE_EXPR, null_ptr_cst, nullptr); if (known_non_null.is_unknown ()) { /* Cast away const-ness for cache-like operations. */ @@ -2029,9 +2048,10 @@ malloc_state_machine::handle_nonnull (sm_context &sm_ctxt, if (unchecked_p (state)) { tree diag_arg = sm_ctxt.get_diagnostic_tree (arg); - sm_ctxt.warn (node, stmt, arg, - make_unique<possible_null_arg> (*this, diag_arg, fndecl, - i)); + sm_ctxt.warn + (node, stmt, arg, + std::make_unique<possible_null_arg> (*this, diag_arg, fndecl, + i)); const allocation_state *astate = as_a_allocation_state (state); sm_ctxt.set_next_state (stmt, arg, astate->get_nonnull ()); @@ -2040,7 +2060,7 @@ malloc_state_machine::handle_nonnull (sm_context &sm_ctxt, { tree diag_arg = sm_ctxt.get_diagnostic_tree (arg); sm_ctxt.warn (node, stmt, arg, - make_unique<null_arg> (*this, diag_arg, fndecl, i)); + std::make_unique<null_arg> (*this, diag_arg, fndecl, i)); sm_ctxt.set_next_state (stmt, arg, m_stop); } else if (state == m_start) @@ -2054,9 +2074,11 @@ malloc_state_machine::on_stmt (sm_context &sm_ctxt, const supernode *node, const gimple *stmt) const { - if (const gcall *call = dyn_cast <const gcall *> (stmt)) - if (tree callee_fndecl = sm_ctxt.get_fndecl_for_call (call)) + if (const gcall *call_stmt = dyn_cast <const gcall *> (stmt)) + if (tree callee_fndecl = sm_ctxt.get_fndecl_for_call (*call_stmt)) { + const gcall &call = *call_stmt; + if (known_allocator_p (callee_fndecl, call)) { on_allocator_call (sm_ctxt, call, &m_free); @@ -2092,7 +2114,7 @@ malloc_state_machine::on_stmt (sm_context &sm_ctxt, if (is_named_call_p (callee_fndecl, "alloca", call, 1) || is_named_call_p (callee_fndecl, "__builtin_alloca", call, 1)) { - tree lhs = gimple_call_lhs (call); + tree lhs = gimple_call_lhs (&call); if (lhs) sm_ctxt.on_transition (node, stmt, lhs, m_start, m_non_heap); return true; @@ -2175,19 +2197,27 @@ malloc_state_machine::on_stmt (sm_context &sm_ctxt, unsigned int idx = TREE_INT_CST_LOW (TREE_VALUE (args)) - 1; unsigned int idx2 = TREE_INT_CST_LOW (TREE_VALUE (TREE_CHAIN (args))) - 1; + unsigned int idx3 = idx2; + if (tree chain2 = TREE_CHAIN (TREE_CHAIN (args))) + idx3 = TREE_INT_CST_LOW (TREE_VALUE (chain2)) - 1; if (idx < gimple_call_num_args (stmt) - && idx2 < gimple_call_num_args (stmt)) + && idx2 < gimple_call_num_args (stmt) + && idx3 < gimple_call_num_args (stmt)) { tree arg = gimple_call_arg (stmt, idx); tree arg2 = gimple_call_arg (stmt, idx2); + tree arg3 = gimple_call_arg (stmt, idx3); if (TREE_CODE (TREE_TYPE (arg)) != POINTER_TYPE || !INTEGRAL_TYPE_P (TREE_TYPE (arg2)) - || integer_zerop (arg2)) + || !INTEGRAL_TYPE_P (TREE_TYPE (arg3)) + || integer_zerop (arg2) + || integer_zerop (arg3)) continue; - if (integer_nonzerop (arg2)) + if (integer_nonzerop (arg2) && integer_nonzerop (arg3)) ; else - /* FIXME: Use ranger here to query arg2 range? */ + /* FIXME: Use ranger here to query arg2 and arg3 + ranges? */ continue; handle_nonnull (sm_ctxt, node, stmt, fndecl, arg, idx); } @@ -2260,8 +2290,8 @@ malloc_state_machine::on_stmt (sm_context &sm_ctxt, { tree diag_arg = sm_ctxt.get_diagnostic_tree (arg); sm_ctxt.warn (node, stmt, arg, - make_unique<possible_null_deref> (*this, - diag_arg)); + std::make_unique<possible_null_deref> (*this, + diag_arg)); const allocation_state *astate = as_a_allocation_state (state); sm_ctxt.set_next_state (stmt, arg, astate->get_nonnull ()); } @@ -2269,7 +2299,7 @@ malloc_state_machine::on_stmt (sm_context &sm_ctxt, { tree diag_arg = sm_ctxt.get_diagnostic_tree (arg); sm_ctxt.warn (node, stmt, arg, - make_unique<null_deref> (*this, diag_arg)); + std::make_unique<null_deref> (*this, diag_arg)); sm_ctxt.set_next_state (stmt, arg, m_stop); } else if (freed_p (state)) @@ -2277,7 +2307,7 @@ malloc_state_machine::on_stmt (sm_context &sm_ctxt, tree diag_arg = sm_ctxt.get_diagnostic_tree (arg); const allocation_state *astate = as_a_allocation_state (state); sm_ctxt.warn (node, stmt, arg, - make_unique<use_after_free> + std::make_unique<use_after_free> (*this, diag_arg, astate->m_deallocator)); sm_ctxt.set_next_state (stmt, arg, m_stop); } @@ -2339,7 +2369,7 @@ maybe_complain_about_deref_before_check (sm_context &sm_ctxt, if (diag_ptr) sm_ctxt.warn (node, stmt, ptr, - make_unique<deref_before_check> (*this, diag_ptr)); + std::make_unique<deref_before_check> (*this, diag_ptr)); sm_ctxt.set_next_state (stmt, ptr, m_stop); } @@ -2349,15 +2379,15 @@ maybe_complain_about_deref_before_check (sm_context &sm_ctxt, void malloc_state_machine::on_allocator_call (sm_context &sm_ctxt, - const gcall *call, + const gcall &call, const deallocator_set *deallocators, bool returns_nonnull) const { - tree lhs = gimple_call_lhs (call); + tree lhs = gimple_call_lhs (&call); if (lhs) { - if (sm_ctxt.get_state (call, lhs) == m_start) - sm_ctxt.set_next_state (call, lhs, + if (sm_ctxt.get_state (&call, lhs) == m_start) + sm_ctxt.set_next_state (&call, lhs, (returns_nonnull ? deallocators->m_nonnull : deallocators->m_unchecked)); @@ -2374,40 +2404,40 @@ malloc_state_machine::on_allocator_call (sm_context &sm_ctxt, void malloc_state_machine::handle_free_of_non_heap (sm_context &sm_ctxt, const supernode *node, - const gcall *call, + const gcall &call, tree arg, const deallocator *d) const { tree diag_arg = sm_ctxt.get_diagnostic_tree (arg); - const region *freed_reg = NULL; + const region *freed_reg = nullptr; if (const program_state *old_state = sm_ctxt.get_old_program_state ()) { const region_model *old_model = old_state->m_region_model; - const svalue *ptr_sval = old_model->get_rvalue (arg, NULL); - freed_reg = old_model->deref_rvalue (ptr_sval, arg, NULL); + const svalue *ptr_sval = old_model->get_rvalue (arg, nullptr); + freed_reg = old_model->deref_rvalue (ptr_sval, arg, nullptr); } - sm_ctxt.warn (node, call, arg, - make_unique<free_of_non_heap> + sm_ctxt.warn (node, &call, arg, + std::make_unique<free_of_non_heap> (*this, diag_arg, freed_reg, d->m_name)); - sm_ctxt.set_next_state (call, arg, m_stop); + sm_ctxt.set_next_state (&call, arg, m_stop); } void malloc_state_machine::on_deallocator_call (sm_context &sm_ctxt, const supernode *node, - const gcall *call, + const gcall &call, const deallocator *d, unsigned argno) const { - if (argno >= gimple_call_num_args (call)) + if (argno >= gimple_call_num_args (&call)) return; - tree arg = gimple_call_arg (call, argno); + tree arg = gimple_call_arg (&call, argno); - state_t state = sm_ctxt.get_state (call, arg); + state_t state = sm_ctxt.get_state (&call, arg); /* start/assumed_non_null/unchecked/nonnull -> freed. */ if (state == m_start || assumed_non_null_p (state)) - sm_ctxt.set_next_state (call, arg, d->m_freed); + sm_ctxt.set_next_state (&call, arg, d->m_freed); else if (unchecked_p (state) || nonnull_p (state)) { const allocation_state *astate = as_a_allocation_state (state); @@ -2416,13 +2446,13 @@ malloc_state_machine::on_deallocator_call (sm_context &sm_ctxt, { /* Wrong allocator. */ tree diag_arg = sm_ctxt.get_diagnostic_tree (arg); - sm_ctxt.warn (node, call, arg, - make_unique<mismatching_deallocation> + sm_ctxt.warn (node, &call, arg, + std::make_unique<mismatching_deallocation> (*this, diag_arg, astate->m_deallocators, d)); } - sm_ctxt.set_next_state (call, arg, d->m_freed); + sm_ctxt.set_next_state (&call, arg, d->m_freed); } /* Keep state "null" as-is, rather than transitioning to "freed"; @@ -2431,9 +2461,9 @@ malloc_state_machine::on_deallocator_call (sm_context &sm_ctxt, { /* freed -> stop, with warning. */ tree diag_arg = sm_ctxt.get_diagnostic_tree (arg); - sm_ctxt.warn (node, call, arg, - make_unique<double_free> (*this, diag_arg, d->m_name)); - sm_ctxt.set_next_state (call, arg, m_stop); + sm_ctxt.warn (node, &call, arg, + std::make_unique<double_free> (*this, diag_arg, d->m_name)); + sm_ctxt.set_next_state (&call, arg, m_stop); } else if (state == m_non_heap) { @@ -2453,14 +2483,14 @@ malloc_state_machine::on_deallocator_call (sm_context &sm_ctxt, void malloc_state_machine::on_realloc_call (sm_context &sm_ctxt, const supernode *node, - const gcall *call) const + const gcall &call) const { const unsigned argno = 0; const deallocator *d = &m_realloc; - tree arg = gimple_call_arg (call, argno); + tree arg = gimple_call_arg (&call, argno); - state_t state = sm_ctxt.get_state (call, arg); + state_t state = sm_ctxt.get_state (&call, arg); if (unchecked_p (state) || nonnull_p (state)) { @@ -2470,11 +2500,11 @@ malloc_state_machine::on_realloc_call (sm_context &sm_ctxt, { /* Wrong allocator. */ tree diag_arg = sm_ctxt.get_diagnostic_tree (arg); - sm_ctxt.warn (node, call, arg, - make_unique<mismatching_deallocation> + sm_ctxt.warn (node, &call, arg, + std::make_unique<mismatching_deallocation> (*this, diag_arg, astate->m_deallocators, d)); - sm_ctxt.set_next_state (call, arg, m_stop); + sm_ctxt.set_next_state (&call, arg, m_stop); if (path_context *path_ctxt = sm_ctxt.get_path_context ()) path_ctxt->terminate_path (); } @@ -2483,9 +2513,9 @@ malloc_state_machine::on_realloc_call (sm_context &sm_ctxt, { /* freed -> stop, with warning. */ tree diag_arg = sm_ctxt.get_diagnostic_tree (arg); - sm_ctxt.warn (node, call, arg, - make_unique<double_free> (*this, diag_arg, "free")); - sm_ctxt.set_next_state (call, arg, m_stop); + sm_ctxt.warn (node, &call, arg, + std::make_unique<double_free> (*this, diag_arg, "free")); + sm_ctxt.set_next_state (&call, arg, m_stop); if (path_context *path_ctxt = sm_ctxt.get_path_context ()) path_ctxt->terminate_path (); } @@ -2592,9 +2622,11 @@ malloc_state_machine::can_purge_p (state_t s) const 'nonnull'). */ std::unique_ptr<pending_diagnostic> -malloc_state_machine::on_leak (tree var) const +malloc_state_machine::on_leak (tree var, + const program_state *, + const program_state *new_state) const { - return make_unique<malloc_leak> (*this, var); + return std::make_unique<malloc_leak> (*this, var, new_state); } /* Implementation of state_machine::reset_when_passed_to_unknown_fn_p vfunc @@ -2627,7 +2659,7 @@ malloc_state_machine::maybe_get_merged_states_nonequal (state_t state_a, return m_start; if (state_a == m_start && assumed_non_null_p (state_b)) return m_start; - return NULL; + return nullptr; } /* Return true if calls to FNDECL are known to not affect this sm-state. */ @@ -2685,11 +2717,11 @@ on_realloc_with_move (region_model *model, { smap->set_state (model, old_ptr_sval, m_free.m_deallocator.m_freed, - NULL, ext_state); + nullptr, ext_state); smap->set_state (model, new_ptr_sval, m_free.m_nonnull, - NULL, ext_state); + nullptr, ext_state); } /* Hook for get_or_create_region_for_heap_alloc for the case when we want @@ -2700,17 +2732,71 @@ malloc_state_machine::transition_ptr_sval_non_null (region_model *model, const svalue *new_ptr_sval, const extrinsic_state &ext_state) const { - smap->set_state (model, new_ptr_sval, m_free.m_nonnull, NULL, ext_state); + smap->set_state (model, new_ptr_sval, m_free.m_nonnull, nullptr, ext_state); +} + +static enum diagnostics::state_graphs::node_dynalloc_state +get_dynalloc_state_for_state (enum resource_state rs) +{ + switch (rs) + { + default: + gcc_unreachable (); + case RS_START: + case RS_NULL: + case RS_NON_HEAP: + case RS_STOP: + return diagnostics::state_graphs::node_dynalloc_state::unknown; + + case RS_ASSUMED_NON_NULL: + return diagnostics::state_graphs::node_dynalloc_state::nonnull; + + case RS_UNCHECKED: + return diagnostics::state_graphs::node_dynalloc_state::unchecked; + case RS_NONNULL: + return diagnostics::state_graphs::node_dynalloc_state::nonnull; + case RS_FREED: + return diagnostics::state_graphs::node_dynalloc_state::freed; + } +} + +void +malloc_state_machine:: +add_state_to_state_graph (analyzer_state_graph &out_state_graph, + const svalue &sval, + state_machine::state_t state) const +{ + if (const region *reg = sval.maybe_get_region ()) + { + auto reg_node = out_state_graph.get_or_create_state_node (*reg); + auto alloc_state = as_a_allocation_state (state); + gcc_assert (alloc_state); + + reg_node.set_dynalloc_state + (get_dynalloc_state_for_state (alloc_state->m_rs)); + if (alloc_state->m_deallocators) + { + pretty_printer pp; + alloc_state->m_deallocators->dump_to_pp (&pp); + reg_node.m_node.set_attr (STATE_NODE_PREFIX, + "expected-deallocators", + pp_formatted_text (&pp)); + } + if (alloc_state->m_deallocator) + reg_node.m_node.set_attr (STATE_NODE_PREFIX, + "deallocator", + alloc_state->m_deallocator->m_name); + } } } // anonymous namespace /* Internal interface to this file. */ -state_machine * +std::unique_ptr<state_machine> make_malloc_state_machine (logger *logger) { - return new malloc_state_machine (logger); + return std::make_unique<malloc_state_machine> (logger); } /* Specialcase hook for handling realloc, for use by diff --git a/gcc/analyzer/sm-pattern-test.cc b/gcc/analyzer/sm-pattern-test.cc index 5b98067..12449a1 100644 --- a/gcc/analyzer/sm-pattern-test.cc +++ b/gcc/analyzer/sm-pattern-test.cc @@ -20,19 +20,11 @@ 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/>. */ -#include "config.h" -#define INCLUDE_VECTOR -#include "system.h" -#include "coretypes.h" -#include "make-unique.h" -#include "tree.h" -#include "function.h" -#include "basic-block.h" -#include "gimple.h" +#include "analyzer/common.h" + #include "tree-pretty-print.h" -#include "diagnostic-path.h" -#include "analyzer/analyzer.h" -#include "diagnostic-event-id.h" +#include "diagnostics/event-id.h" + #include "analyzer/analyzer-logging.h" #include "analyzer/sm.h" #include "analyzer/pending-diagnostic.h" @@ -130,7 +122,7 @@ pattern_test_state_machine::on_condition (sm_context &sm_ctxt, enum tree_code op, const svalue *rhs) const { - if (stmt == NULL) + if (stmt == nullptr) return; tree rhs_cst = rhs->maybe_get_constant (); @@ -140,7 +132,7 @@ pattern_test_state_machine::on_condition (sm_context &sm_ctxt, if (tree lhs_expr = sm_ctxt.get_diagnostic_tree (lhs)) { sm_ctxt.warn (node, stmt, lhs_expr, - make_unique<pattern_match> (lhs_expr, op, rhs_cst)); + std::make_unique<pattern_match> (lhs_expr, op, rhs_cst)); } } @@ -154,10 +146,10 @@ pattern_test_state_machine::can_purge_p (state_t s ATTRIBUTE_UNUSED) const /* Internal interface to this file. */ -state_machine * +std::unique_ptr<state_machine> make_pattern_test_state_machine (logger *logger) { - return new pattern_test_state_machine (logger); + return std::make_unique<pattern_test_state_machine> (logger); } } // namespace ana diff --git a/gcc/analyzer/sm-sensitive.cc b/gcc/analyzer/sm-sensitive.cc index 6e185cb..4611b10e 100644 --- a/gcc/analyzer/sm-sensitive.cc +++ b/gcc/analyzer/sm-sensitive.cc @@ -19,19 +19,10 @@ 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/>. */ -#include "config.h" -#include "system.h" -#include "coretypes.h" -#include "make-unique.h" -#include "tree.h" -#include "function.h" -#include "basic-block.h" -#include "gimple.h" -#include "options.h" -#include "diagnostic-core.h" -#include "diagnostic-path.h" -#include "analyzer/analyzer.h" -#include "diagnostic-event-id.h" +#include "analyzer/common.h" + +#include "diagnostics/event-id.h" + #include "analyzer/analyzer-logging.h" #include "analyzer/sm.h" #include "analyzer/pending-diagnostic.h" @@ -115,14 +106,15 @@ public: return false; } - diagnostic_event::meaning + diagnostics::paths::event::meaning get_meaning_for_state_change (const evdesc::state_change &change) const final override { + using event = diagnostics::paths::event; + if (change.m_new_state == m_sm.m_sensitive) - return diagnostic_event::meaning (diagnostic_event::VERB_acquire, - diagnostic_event::NOUN_sensitive); - return diagnostic_event::meaning (); + return event::meaning (event::verb::acquire, event::noun::sensitive); + return event::meaning (); } bool describe_call_with_state (pretty_printer &pp, @@ -171,7 +163,7 @@ public: private: const sensitive_state_machine &m_sm; tree m_arg; - diagnostic_event_id_t m_first_sensitive_event; + diagnostics::paths::event_id_t m_first_sensitive_event; }; /* sensitive_state_machine's ctor. */ @@ -196,8 +188,8 @@ sensitive_state_machine::warn_for_any_exposure (sm_context &sm_ctxt, { tree diag_arg = sm_ctxt.get_diagnostic_tree (arg); sm_ctxt.warn (node, stmt, arg, - make_unique<exposure_through_output_file> (*this, - diag_arg)); + std::make_unique<exposure_through_output_file> (*this, + diag_arg)); } } @@ -210,9 +202,9 @@ sensitive_state_machine::on_stmt (sm_context &sm_ctxt, const gimple *stmt) const { if (const gcall *call = dyn_cast <const gcall *> (stmt)) - if (tree callee_fndecl = sm_ctxt.get_fndecl_for_call (call)) + if (tree callee_fndecl = sm_ctxt.get_fndecl_for_call (*call)) { - if (is_named_call_p (callee_fndecl, "getpass", call, 1)) + if (is_named_call_p (callee_fndecl, "getpass", *call, 1)) { tree lhs = gimple_call_lhs (call); if (lhs) @@ -230,7 +222,7 @@ sensitive_state_machine::on_stmt (sm_context &sm_ctxt, } return true; } - else if (is_named_call_p (callee_fndecl, "fwrite", call, 4)) + else if (is_named_call_p (callee_fndecl, "fwrite", *call, 4)) { tree arg = gimple_call_arg (call, 0); warn_for_any_exposure (sm_ctxt, node, stmt, arg); @@ -251,10 +243,10 @@ sensitive_state_machine::can_purge_p (state_t s ATTRIBUTE_UNUSED) const /* Internal interface to this file. */ -state_machine * +std::unique_ptr<state_machine> make_sensitive_state_machine (logger *logger) { - return new sensitive_state_machine (logger); + return std::make_unique<sensitive_state_machine> (logger); } } // namespace ana diff --git a/gcc/analyzer/sm-signal.cc b/gcc/analyzer/sm-signal.cc index f8b378f..35ecde1 100644 --- a/gcc/analyzer/sm-signal.cc +++ b/gcc/analyzer/sm-signal.cc @@ -20,39 +20,28 @@ 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/>. */ -#include "config.h" -#define INCLUDE_VECTOR -#include "system.h" -#include "coretypes.h" -#include "make-unique.h" -#include "tree.h" -#include "function.h" -#include "basic-block.h" -#include "gimple.h" -#include "options.h" -#include "bitmap.h" -#include "diagnostic-core.h" -#include "diagnostic-path.h" -#include "analyzer/analyzer.h" -#include "diagnostic-event-id.h" -#include "analyzer/analyzer-logging.h" -#include "analyzer/sm.h" -#include "analyzer/pending-diagnostic.h" +#include "analyzer/common.h" + +#include "diagnostics/event-id.h" #include "sbitmap.h" #include "ordered-hash-map.h" #include "selftest.h" +#include "cfg.h" +#include "gimple-iterator.h" +#include "cgraph.h" +#include "shortest-paths.h" + +#include "analyzer/analyzer-logging.h" +#include "analyzer/sm.h" +#include "analyzer/pending-diagnostic.h" #include "analyzer/call-string.h" #include "analyzer/program-point.h" #include "analyzer/store.h" #include "analyzer/region-model.h" #include "analyzer/program-state.h" #include "analyzer/checker-path.h" -#include "cfg.h" -#include "gimple-iterator.h" -#include "cgraph.h" #include "analyzer/supergraph.h" #include "analyzer/diagnostic-manager.h" -#include "shortest-paths.h" #include "analyzer/exploded-graph.h" #include "analyzer/function-set.h" #include "analyzer/analyzer-selftests.h" @@ -95,7 +84,7 @@ class signal_unsafe_call : public pending_diagnostic_subclass<signal_unsafe_call> { public: - signal_unsafe_call (const signal_state_machine &sm, const gcall *unsafe_call, + signal_unsafe_call (const signal_state_machine &sm, const gcall &unsafe_call, tree unsafe_fndecl) : m_sm (sm), m_unsafe_call (unsafe_call), m_unsafe_fndecl (unsafe_fndecl) { @@ -106,7 +95,7 @@ public: bool operator== (const signal_unsafe_call &other) const { - return m_unsafe_call == other.m_unsafe_call; + return &m_unsafe_call == &other.m_unsafe_call; } int get_controlling_option () const final override @@ -126,7 +115,7 @@ public: suggesting the replacement. */ if (const char *replacement = get_replacement_fn ()) { - location_t note_loc = gimple_location (m_unsafe_call); + location_t note_loc = gimple_location (&m_unsafe_call); /* It would be nice to add a fixit, but the gimple call location covers the whole call expression. It isn't currently possible to cut this down to just the call @@ -170,7 +159,7 @@ public: private: const signal_state_machine &m_sm; - const gcall *m_unsafe_call; + const gcall &m_unsafe_call; tree m_unsafe_fndecl; /* Returns a replacement function as text if it exists. Currently @@ -184,7 +173,7 @@ private: if (id_equal ("exit", DECL_NAME (m_unsafe_fndecl))) return "_exit"; - return NULL; + return nullptr; } }; @@ -207,7 +196,7 @@ update_model_for_signal_handler (region_model *model, gcc_assert (model); /* Purge all state within MODEL. */ *model = region_model (model->get_manager ()); - model->push_frame (handler_fun, NULL, NULL); + model->push_frame (handler_fun, nullptr, nullptr, nullptr); } /* Custom exploded_edge info: entry into a signal-handler. */ @@ -236,10 +225,10 @@ public: const final override { emission_path->add_event - (make_unique<precanned_custom_event> - (event_loc_info (UNKNOWN_LOCATION, NULL_TREE, 0), - "later on," - " when the signal is delivered to the process")); + (std::make_unique<precanned_custom_event> + (event_loc_info (UNKNOWN_LOCATION, NULL_TREE, 0), + "later on," + " when the signal is delivered to the process")); } }; @@ -280,9 +269,9 @@ public: state_entering_handler, src_enode); if (dst_enode) - eg->add_edge (src_enode, dst_enode, NULL, /*state_change (),*/ + eg->add_edge (src_enode, dst_enode, nullptr, /*state_change (),*/ true, /* assume does work */ - make_unique<signal_delivery_edge_info_t> ()); + std::make_unique<signal_delivery_edge_info_t> ()); } const signal_state_machine &m_sm; @@ -342,9 +331,9 @@ signal_state_machine::on_stmt (sm_context &sm_ctxt, if (global_state == m_start) { if (const gcall *call = dyn_cast <const gcall *> (stmt)) - if (tree callee_fndecl = sm_ctxt.get_fndecl_for_call (call)) - if (is_named_call_p (callee_fndecl, "signal", call, 2) - || is_std_named_call_p (callee_fndecl, "signal", call, 2)) + if (tree callee_fndecl = sm_ctxt.get_fndecl_for_call (*call)) + if (is_named_call_p (callee_fndecl, "signal", *call, 2) + || is_std_named_call_p (callee_fndecl, "signal", *call, 2)) { tree handler = gimple_call_arg (call, 1); if (TREE_CODE (handler) == ADDR_EXPR @@ -359,12 +348,12 @@ signal_state_machine::on_stmt (sm_context &sm_ctxt, else if (global_state == m_in_signal_handler) { if (const gcall *call = dyn_cast <const gcall *> (stmt)) - if (tree callee_fndecl = sm_ctxt.get_fndecl_for_call (call)) + if (tree callee_fndecl = sm_ctxt.get_fndecl_for_call (*call)) if (signal_unsafe_p (callee_fndecl)) if (sm_ctxt.get_global_state () == m_in_signal_handler) sm_ctxt.warn (node, stmt, NULL_TREE, - make_unique<signal_unsafe_call> - (*this, call, callee_fndecl)); + std::make_unique<signal_unsafe_call> + (*this, *call, callee_fndecl)); } return false; @@ -380,10 +369,10 @@ signal_state_machine::can_purge_p (state_t s ATTRIBUTE_UNUSED) const /* Internal interface to this file. */ -state_machine * +std::unique_ptr<state_machine> make_signal_state_machine (logger *logger) { - return new signal_state_machine (logger); + return std::make_unique<signal_state_machine> (logger); } #if CHECKING_P diff --git a/gcc/analyzer/sm-taint.cc b/gcc/analyzer/sm-taint.cc index 5d0aec3..f2a94e8 100644 --- a/gcc/analyzer/sm-taint.cc +++ b/gcc/analyzer/sm-taint.cc @@ -20,20 +20,8 @@ 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/>. */ -#include "config.h" -#define INCLUDE_VECTOR -#include "system.h" -#include "coretypes.h" -#include "make-unique.h" -#include "tree.h" -#include "function.h" -#include "basic-block.h" -#include "gimple.h" -#include "options.h" -#include "diagnostic-core.h" -#include "diagnostic-path.h" -#include "analyzer/analyzer.h" -#include "analyzer/analyzer-logging.h" +#include "analyzer/common.h" + #include "gimple-iterator.h" #include "ordered-hash-map.h" #include "cgraph.h" @@ -42,6 +30,10 @@ along with GCC; see the file COPYING3. If not see #include "stringpool.h" #include "attribs.h" #include "fold-const.h" +#include "diagnostics/sarif-sink.h" +#include "gcc-urlifier.h" + +#include "analyzer/analyzer-logging.h" #include "analyzer/supergraph.h" #include "analyzer/call-string.h" #include "analyzer/program-point.h" @@ -51,8 +43,6 @@ along with GCC; see the file COPYING3. If not see #include "analyzer/program-state.h" #include "analyzer/pending-diagnostic.h" #include "analyzer/constraint-manager.h" -#include "diagnostic-format-sarif.h" -#include "gcc-urlifier.h" #if ENABLE_ANALYZER @@ -140,7 +130,7 @@ private: void check_for_tainted_size_arg (sm_context &sm_ctxt, const supernode *node, - const gcall *call, + const gcall &call, tree callee_fndecl) const; void check_for_tainted_divisor (sm_context &sm_ctxt, const supernode *node, @@ -219,20 +209,22 @@ public: return false; } - diagnostic_event::meaning + diagnostics::paths::event::meaning get_meaning_for_state_change (const evdesc::state_change &change) const final override { + using event = diagnostics::paths::event; if (change.m_new_state == m_sm.m_tainted) - return diagnostic_event::meaning (diagnostic_event::VERB_acquire, - diagnostic_event::NOUN_taint); - return diagnostic_event::meaning (); + return event::meaning (event::verb::acquire, + event::noun::taint); + return event::meaning (); } - void maybe_add_sarif_properties (sarif_object &result_obj) + void + maybe_add_sarif_properties (diagnostics::sarif_object &result_obj) const override { - sarif_property_bag &props = result_obj.get_or_create_properties (); + auto &props = result_obj.get_or_create_properties (); #define PROPERTY_PREFIX "gcc/analyzer/taint_diagnostic/" props.set (PROPERTY_PREFIX "arg", tree_to_json (m_arg)); props.set_string (PROPERTY_PREFIX "has_bounds", @@ -505,11 +497,12 @@ public: } } - void maybe_add_sarif_properties (sarif_object &result_obj) + void + maybe_add_sarif_properties (diagnostics::sarif_object &result_obj) const final override { taint_diagnostic::maybe_add_sarif_properties (result_obj); - sarif_property_bag &props = result_obj.get_or_create_properties (); + auto &props = result_obj.get_or_create_properties (); #define PROPERTY_PREFIX "gcc/analyzer/tainted_offset/" props.set (PROPERTY_PREFIX "offset", m_offset->to_json ()); #undef PROPERTY_PREFIX @@ -874,11 +867,12 @@ public: } } - void maybe_add_sarif_properties (sarif_object &result_obj) + void + maybe_add_sarif_properties (diagnostics::sarif_object &result_obj) const final override { taint_diagnostic::maybe_add_sarif_properties (result_obj); - sarif_property_bag &props = result_obj.get_or_create_properties (); + auto &props = result_obj.get_or_create_properties (); #define PROPERTY_PREFIX "gcc/analyzer/tainted_allocation_size/" props.set (PROPERTY_PREFIX "size_in_bytes", m_size_in_bytes->to_json ()); #undef PROPERTY_PREFIX @@ -944,7 +938,7 @@ public: macro when we're describing them. */ return linemap_resolve_location (line_table, loc, LRK_SPELLING_LOCATION, - NULL); + nullptr); else return pending_diagnostic::fixup_location (loc, primary); } @@ -1070,12 +1064,12 @@ taint_state_machine::alt_get_inherited_state (const sm_state_map &map, case BIT_AND_EXPR: case RSHIFT_EXPR: - return NULL; + return nullptr; } } break; } - return NULL; + return nullptr; } /* Return true iff FNDECL should be considered to be an assertion failure @@ -1099,9 +1093,9 @@ taint_state_machine::on_stmt (sm_context &sm_ctxt, const gimple *stmt) const { if (const gcall *call = dyn_cast <const gcall *> (stmt)) - if (tree callee_fndecl = sm_ctxt.get_fndecl_for_call (call)) + if (tree callee_fndecl = sm_ctxt.get_fndecl_for_call (*call)) { - if (is_named_call_p (callee_fndecl, "fread", call, 4)) + if (is_named_call_p (callee_fndecl, "fread", *call, 4)) { tree arg = gimple_call_arg (call, 0); @@ -1117,14 +1111,14 @@ taint_state_machine::on_stmt (sm_context &sm_ctxt, /* External function with "access" attribute. */ if (sm_ctxt.unknown_side_effects_p ()) - check_for_tainted_size_arg (sm_ctxt, node, call, callee_fndecl); + check_for_tainted_size_arg (sm_ctxt, node, *call, callee_fndecl); if (is_assertion_failure_handler_p (callee_fndecl) && sm_ctxt.get_global_state () == m_tainted_control_flow) { sm_ctxt.warn (node, call, NULL_TREE, - make_unique<tainted_assertion> (*this, NULL_TREE, - callee_fndecl)); + std::make_unique<tainted_assertion> (*this, NULL_TREE, + callee_fndecl)); } } // TODO: ...etc; many other sources of untrusted data @@ -1185,7 +1179,7 @@ taint_state_machine::check_control_flow_arg_for_taint (sm_context &sm_ctxt, tree expr) const { const region_model *old_model = sm_ctxt.get_old_region_model (); - const svalue *sval = old_model->get_rvalue (expr, NULL); + const svalue *sval = old_model->get_rvalue (expr, nullptr); state_t state = sm_ctxt.get_state (stmt, sval); enum bounds b; if (get_taint (state, TREE_TYPE (expr), &b)) @@ -1204,7 +1198,7 @@ taint_state_machine::on_condition (sm_context &sm_ctxt, enum tree_code op, const svalue *rhs) const { - if (stmt == NULL) + if (stmt == nullptr) return; if (lhs->get_kind () == SK_UNKNOWN @@ -1433,7 +1427,7 @@ taint_state_machine::combine_states (state_t s0, state_t s1) const void taint_state_machine::check_for_tainted_size_arg (sm_context &sm_ctxt, const supernode *node, - const gcall *call, + const gcall &call, tree callee_fndecl) const { tree fntype = TREE_TYPE (callee_fndecl); @@ -1464,17 +1458,17 @@ taint_state_machine::check_for_tainted_size_arg (sm_context &sm_ctxt, if (access->sizarg == UINT_MAX) continue; - tree size_arg = gimple_call_arg (call, access->sizarg); + tree size_arg = gimple_call_arg (&call, access->sizarg); - state_t state = sm_ctxt.get_state (call, size_arg); + state_t state = sm_ctxt.get_state (&call, size_arg); enum bounds b; if (get_taint (state, TREE_TYPE (size_arg), &b)) { const char* const access_str = TREE_STRING_POINTER (access->to_external_string ()); tree diag_size = sm_ctxt.get_diagnostic_tree (size_arg); - sm_ctxt.warn (node, call, size_arg, - make_unique<tainted_access_attrib_size> + sm_ctxt.warn (node, &call, size_arg, + std::make_unique<tainted_access_attrib_size> (*this, diag_size, b, callee_fndecl, access->sizarg, @@ -1502,7 +1496,7 @@ taint_state_machine::check_for_tainted_divisor (sm_context &sm_ctxt, if (!INTEGRAL_TYPE_P (TREE_TYPE (divisor_expr))) return; - const svalue *divisor_sval = old_model->get_rvalue (divisor_expr, NULL); + const svalue *divisor_sval = old_model->get_rvalue (divisor_expr, nullptr); state_t state = sm_ctxt.get_state (assign, divisor_sval); enum bounds b; @@ -1518,8 +1512,9 @@ taint_state_machine::check_for_tainted_divisor (sm_context &sm_ctxt, return; tree diag_divisor = sm_ctxt.get_diagnostic_tree (divisor_expr); - sm_ctxt.warn (node, assign, divisor_expr, - make_unique <tainted_divisor> (*this, diag_divisor, b)); + sm_ctxt.warn + (node, assign, divisor_expr, + std::make_unique <tainted_divisor> (*this, diag_divisor, b)); sm_ctxt.set_next_state (assign, divisor_sval, m_stop); } } @@ -1528,10 +1523,10 @@ taint_state_machine::check_for_tainted_divisor (sm_context &sm_ctxt, /* Internal interface to this file. */ -state_machine * +std::unique_ptr<state_machine> make_taint_state_machine (logger *logger) { - return new taint_state_machine (logger); + return std::make_unique<taint_state_machine> (logger); } /* A closed concrete range. */ @@ -1682,8 +1677,8 @@ region_model::check_region_for_taint (const region *reg, if (index_can_be_out_of_bounds_p (element_reg)) { tree arg = get_representative_tree (index); - ctxt->warn (make_unique<tainted_array_index> (taint_sm, - arg, b)); + ctxt->warn (std::make_unique<tainted_array_index> (taint_sm, + arg, b)); } else if (ctxt->get_logger ()) ctxt->get_logger ()->log ("rejecting tainted_array_index as" @@ -1709,8 +1704,8 @@ region_model::check_region_for_taint (const region *reg, if (taint_sm.get_taint (state, effective_type, &b)) { tree arg = get_representative_tree (offset); - ctxt->warn (make_unique<tainted_offset> (taint_sm, arg, b, - offset)); + ctxt->warn (std::make_unique<tainted_offset> (taint_sm, arg, b, + offset)); } } break; @@ -1727,7 +1722,7 @@ region_model::check_region_for_taint (const region *reg, if (taint_sm.get_taint (state, size_sval->get_type (), &b)) { tree arg = get_representative_tree (size_sval); - ctxt->warn (make_unique<tainted_size> (taint_sm, arg, b)); + ctxt->warn (std::make_unique<tainted_size> (taint_sm, arg, b)); } } break; @@ -1773,7 +1768,7 @@ region_model::check_dynamic_size_for_taint (enum memory_space mem_space, if (taint_sm.get_taint (state, size_in_bytes->get_type (), &b)) { tree arg = get_representative_tree (size_in_bytes); - ctxt->warn (make_unique<tainted_allocation_size> + ctxt->warn (std::make_unique<tainted_allocation_size> (taint_sm, arg, size_in_bytes, b, mem_space)); } } @@ -1802,7 +1797,7 @@ region_model::mark_as_tainted (const svalue *sval, if (!ext_state) return; - smap->set_state (this, sval, taint_sm.m_tainted, NULL, *ext_state); + smap->set_state (this, sval, taint_sm.m_tainted, nullptr, *ext_state); } /* Return true if SVAL could possibly be attacker-controlled. */ diff --git a/gcc/analyzer/sm.cc b/gcc/analyzer/sm.cc index 3e7fa66..c93e9c2 100644 --- a/gcc/analyzer/sm.cc +++ b/gcc/analyzer/sm.cc @@ -18,21 +18,11 @@ 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/>. */ -#include "config.h" -#define INCLUDE_VECTOR -#include "system.h" -#include "coretypes.h" -#include "tree.h" -#include "function.h" -#include "basic-block.h" -#include "gimple.h" -#include "options.h" -#include "function.h" -#include "diagnostic-core.h" -#include "pretty-print.h" -#include "diagnostic.h" +#define INCLUDE_LIST +#include "analyzer/common.h" + #include "tree-diagnostic.h" -#include "analyzer/analyzer.h" + #include "analyzer/analyzer-logging.h" #include "analyzer/sm.h" #include "analyzer/call-string.h" @@ -41,7 +31,6 @@ along with GCC; see the file COPYING3. If not see #include "analyzer/svalue.h" #include "analyzer/program-state.h" #include "analyzer/pending-diagnostic.h" -#include "make-unique.h" #if ENABLE_ANALYZER @@ -83,7 +72,7 @@ state_machine::state::to_json () const pretty_printer pp; pp_format_decoder (&pp) = default_tree_printer; dump_to_pp (&pp); - return ::make_unique<json::string> (pp_formatted_text (&pp)); + return std::make_unique<json::string> (pp_formatted_text (&pp)); } /* class state_machine. */ @@ -127,9 +116,11 @@ state_machine::get_state_by_name (const char *name) const /* Base implementation of state_machine::on_leak. */ std::unique_ptr<pending_diagnostic> -state_machine::on_leak (tree var ATTRIBUTE_UNUSED) const +state_machine::on_leak (tree var ATTRIBUTE_UNUSED, + const program_state *old_state ATTRIBUTE_UNUSED, + const program_state *new_state ATTRIBUTE_UNUSED) const { - return NULL; + return nullptr; } /* Dump a multiline representation of this state machine to PP. */ @@ -154,11 +145,11 @@ state_machine::dump_to_pp (pretty_printer *pp) const std::unique_ptr<json::object> state_machine::to_json () const { - auto sm_obj = ::make_unique<json::object> (); + auto sm_obj = std::make_unique<json::object> (); sm_obj->set_string ("name", m_name); { - auto states_arr = ::make_unique<json::array> (); + auto states_arr = std::make_unique<json::array> (); unsigned i; state *s; FOR_EACH_VEC_ELT (m_states, i, s) @@ -169,6 +160,21 @@ state_machine::to_json () const return sm_obj; } +void +state_machine::add_state_to_state_graph (analyzer_state_graph &out_state_graph, + const svalue &sval, + state_machine::state_t state) const +{ + // no-op +} + +void +state_machine::add_global_state_to_state_graph (analyzer_state_graph &out_state_graph, + state_machine::state_t state) const +{ + // no-op +} + /* class sm_context. */ const region_model * @@ -177,39 +183,44 @@ sm_context::get_old_region_model () const if (const program_state *old_state = get_old_program_state ()) return old_state->m_region_model; else - return NULL; + return nullptr; } /* Create instances of the various state machines, each using LOGGER, - and populate OUT with them. */ + returning a vector of them. */ -void -make_checkers (auto_delete_vec <state_machine> &out, logger *logger) +std::vector<std::unique_ptr<state_machine>> +make_checkers (logger *logger) { - out.safe_push (make_malloc_state_machine (logger)); - out.safe_push (make_fileptr_state_machine (logger)); - out.safe_push (make_fd_state_machine (logger)); - out.safe_push (make_taint_state_machine (logger)); - out.safe_push (make_sensitive_state_machine (logger)); - out.safe_push (make_signal_state_machine (logger)); - out.safe_push (make_va_list_state_machine (logger)); + /* Start with a list so that we can filter it. */ + std::list<std::unique_ptr<state_machine>> out; + out.push_back (make_malloc_state_machine (logger)); + out.push_back (make_fileptr_state_machine (logger)); + out.push_back (make_fd_state_machine (logger)); + out.push_back (make_taint_state_machine (logger)); + out.push_back (make_sensitive_state_machine (logger)); + out.push_back (make_signal_state_machine (logger)); + out.push_back (make_va_list_state_machine (logger)); /* We only attempt to run the pattern tests if it might have been manually enabled (for DejaGnu purposes). */ if (flag_analyzer_checker) - out.safe_push (make_pattern_test_state_machine (logger)); + out.push_back (make_pattern_test_state_machine (logger)); if (flag_analyzer_checker) { - unsigned read_index, write_index; - state_machine **sm; - - /* TODO: this leaks the machines - Would be nice to log the things that were removed. */ - VEC_ORDERED_REMOVE_IF (out, read_index, write_index, sm, - 0 != strcmp (flag_analyzer_checker, - (*sm)->get_name ())); + out.remove_if ([] (auto &sm) + { + return 0 != strcmp (flag_analyzer_checker, + sm->get_name ()); + }); } + + std::vector<std::unique_ptr<state_machine>> out_vec; + for (auto &iter: out) + out_vec.push_back (std::move (iter)); + + return out_vec; } } // namespace ana diff --git a/gcc/analyzer/sm.h b/gcc/analyzer/sm.h index a1f96e2..4633fac 100644 --- a/gcc/analyzer/sm.h +++ b/gcc/analyzer/sm.h @@ -28,6 +28,7 @@ namespace ana { class state_machine; class sm_context; class pending_diagnostic; +class analyzer_state_graph; extern bool any_pointer_p (tree expr); extern bool any_pointer_p (const svalue *sval); @@ -75,7 +76,7 @@ public: const svalue *, const extrinsic_state &) const { - return NULL; + return nullptr; } virtual bool @@ -136,7 +137,9 @@ public: /* Called when VAR leaks (and !can_purge_p). */ virtual std::unique_ptr<pending_diagnostic> - on_leak (tree var ATTRIBUTE_UNUSED) const; + on_leak (tree var ATTRIBUTE_UNUSED, + const program_state *old_state, + const program_state *new_state) const; /* Return true if S should be reset to "start" for values passed (or reachable from) calls to unknown functions. IS_MUTABLE is true for pointers as @@ -152,7 +155,7 @@ public: } /* Attempt to get a state for the merger of STATE_A and STATE_B, - or return NULL if merging shouldn't occur, so that differences + or return nullptr if merging shouldn't occur, so that differences between sm-state will lead to separate exploded nodes. Most state machines will only merge equal states, but can @@ -173,7 +176,7 @@ public: state_t state_b ATTRIBUTE_UNUSED) const { /* By default, non-equal sm states should inhibit merger of enodes. */ - return NULL; + return nullptr; } void validate (state_t s) const; @@ -184,6 +187,15 @@ public: state_t get_start_state () const { return m_start; } + virtual void + add_state_to_state_graph (analyzer_state_graph &out_state_graph, + const svalue &sval, + state_machine::state_t state) const; + + virtual void + add_global_state_to_state_graph (analyzer_state_graph &out_state_graph, + state_machine::state_t state) const; + protected: state_t add_state (const char *name); state_t add_custom_state (state *s) @@ -235,7 +247,7 @@ public: Use in preference to gimple_call_fndecl (and gimple_call_addr_fndecl), since it can look through function pointer assignments and other callback handling. */ - virtual tree get_fndecl_for_call (const gcall *call) = 0; + virtual tree get_fndecl_for_call (const gcall &call) = 0; /* Get the old state of VAR at STMT. */ virtual state_machine::state_t get_state (const gimple *stmt, @@ -318,7 +330,7 @@ public: virtual path_context *get_path_context () const { - return NULL; + return nullptr; } /* Are we handling an external function with unknown side effects? */ @@ -341,17 +353,17 @@ protected: /* The various state_machine subclasses are hidden in their respective implementation files. */ -extern void make_checkers (auto_delete_vec <state_machine> &out, - logger *logger); - -extern state_machine *make_malloc_state_machine (logger *logger); -extern state_machine *make_fileptr_state_machine (logger *logger); -extern state_machine *make_taint_state_machine (logger *logger); -extern state_machine *make_sensitive_state_machine (logger *logger); -extern state_machine *make_signal_state_machine (logger *logger); -extern state_machine *make_pattern_test_state_machine (logger *logger); -extern state_machine *make_va_list_state_machine (logger *logger); -extern state_machine *make_fd_state_machine (logger *logger); +extern std::vector<std::unique_ptr<state_machine>> +make_checkers (logger *logger); + +extern std::unique_ptr<state_machine> make_malloc_state_machine (logger *); +extern std::unique_ptr<state_machine> make_fileptr_state_machine (logger *); +extern std::unique_ptr<state_machine> make_taint_state_machine (logger *); +extern std::unique_ptr<state_machine> make_sensitive_state_machine (logger *); +extern std::unique_ptr<state_machine> make_signal_state_machine (logger *); +extern std::unique_ptr<state_machine> make_pattern_test_state_machine (logger *); +extern std::unique_ptr<state_machine> make_va_list_state_machine (logger *); +extern std::unique_ptr<state_machine> make_fd_state_machine (logger *); } // namespace ana diff --git a/gcc/analyzer/state-purge.cc b/gcc/analyzer/state-purge.cc index b7a5260..1371db9 100644 --- a/gcc/analyzer/state-purge.cc +++ b/gcc/analyzer/state-purge.cc @@ -18,26 +18,21 @@ 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/>. */ -#include "config.h" -#define INCLUDE_VECTOR -#include "system.h" -#include "coretypes.h" -#include "tree.h" +#include "analyzer/common.h" + #include "timevar.h" -#include "tree-ssa-alias.h" -#include "function.h" -#include "basic-block.h" -#include "gimple.h" -#include "stringpool.h" +#include "gimple-pretty-print.h" #include "tree-vrp.h" #include "gimple-ssa.h" +#include "stringpool.h" #include "tree-ssanames.h" #include "tree-phinodes.h" #include "options.h" #include "ssa-iterators.h" -#include "diagnostic-core.h" -#include "gimple-pretty-print.h" -#include "analyzer/analyzer.h" +#include "gimple-iterator.h" +#include "gimple-walk.h" +#include "cgraph.h" + #include "analyzer/call-string.h" #include "analyzer/supergraph.h" #include "analyzer/program-point.h" @@ -45,8 +40,6 @@ along with GCC; see the file COPYING3. If not see #include "analyzer/state-purge.h" #include "analyzer/store.h" #include "analyzer/region-model.h" -#include "gimple-walk.h" -#include "cgraph.h" #if ENABLE_ANALYZER @@ -666,7 +659,7 @@ state_purge_per_ssa_name::process_point (const function_point &point, if (snode->entry_p ()) { add_to_worklist - (function_point::before_supernode (snode, NULL), + (function_point::before_supernode (snode, nullptr), worklist, logger); } } @@ -737,7 +730,7 @@ state_purge_per_decl::process_worklists (const state_purge_map &map, worklist.safe_push (iter); region_model model (mgr); - model.push_frame (get_function (), NULL, NULL); + model.push_frame (get_function (), nullptr, nullptr, nullptr); /* Process worklist by walking backwards until we reach a stmt that fully overwrites the decl. */ @@ -848,8 +841,8 @@ fully_overwrites_p (const gimple *stmt, tree decl, We can't just check for equality; consider the case of "s.field = EXPR;" where the stmt writes to the only field of "s", and there's no padding. */ - const region *lhs_reg = model.get_lvalue (lhs, NULL); - const region *decl_reg = model.get_lvalue (decl, NULL); + const region *lhs_reg = model.get_lvalue (lhs, nullptr); + const region *decl_reg = model.get_lvalue (decl, nullptr); if (same_binding_p (lhs_reg, decl_reg, model.get_manager ()->get_store_manager ())) return true; @@ -1081,7 +1074,7 @@ state_purge_annotator::add_node_annotations (graphviz_out *gv, const supernode &n, bool within_table) const { - if (m_map == NULL) + if (m_map == nullptr) return false; if (within_table) @@ -1098,7 +1091,7 @@ state_purge_annotator::add_node_annotations (graphviz_out *gv, Determine which points to dump. */ auto_vec<function_point> points; if (n.entry_p () || n.m_returning_call) - points.safe_push (function_point::before_supernode (&n, NULL)); + points.safe_push (function_point::before_supernode (&n, nullptr)); else for (auto inedge : n.m_preds) points.safe_push (function_point::before_supernode (&n, inedge)); @@ -1160,7 +1153,7 @@ state_purge_annotator::add_stmt_annotations (graphviz_out *gv, if (within_row) return; - if (m_map == NULL) + if (m_map == nullptr) return; if (stmt->code == GIMPLE_PHI) diff --git a/gcc/analyzer/state-purge.h b/gcc/analyzer/state-purge.h index bbe78a5..2bac564 100644 --- a/gcc/analyzer/state-purge.h +++ b/gcc/analyzer/state-purge.h @@ -109,7 +109,7 @@ public: = const_cast <decl_map_t&> (m_decl_map).get (decl)) return *slot; else - return NULL; + return nullptr; } state_purge_per_decl & diff --git a/gcc/analyzer/store.cc b/gcc/analyzer/store.cc index ab469dd..942c945 100644 --- a/gcc/analyzer/store.cc +++ b/gcc/analyzer/store.cc @@ -18,44 +18,23 @@ 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/>. */ -#include "config.h" -#define INCLUDE_VECTOR -#include "system.h" -#include "coretypes.h" -#include "tree.h" -#include "function.h" -#include "basic-block.h" -#include "gimple.h" -#include "gimple-iterator.h" -#include "diagnostic-core.h" -#include "graphviz.h" -#include "options.h" -#include "cgraph.h" -#include "tree-dfa.h" -#include "stringpool.h" -#include "convert.h" -#include "target.h" -#include "fold-const.h" -#include "tree-pretty-print.h" -#include "diagnostic-color.h" -#include "bitmap.h" -#include "selftest.h" -#include "analyzer/analyzer.h" -#include "analyzer/analyzer-logging.h" +#include "analyzer/common.h" + #include "ordered-hash-map.h" -#include "options.h" #include "cfg.h" -#include "analyzer/supergraph.h" #include "sbitmap.h" +#include "stor-layout.h" + +#include "text-art/tree-widget.h" + +#include "analyzer/analyzer-logging.h" +#include "analyzer/supergraph.h" #include "analyzer/call-string.h" #include "analyzer/program-point.h" #include "analyzer/store.h" #include "analyzer/region-model.h" #include "analyzer/call-summary.h" #include "analyzer/analyzer-selftests.h" -#include "stor-layout.h" -#include "text-art/tree-widget.h" -#include "make-unique.h" #if ENABLE_ANALYZER @@ -234,7 +213,7 @@ bit_range::dump () const std::unique_ptr<json::object> bit_range::to_json () const { - auto obj = ::make_unique<json::object> (); + auto obj = std::make_unique<json::object> (); obj->set ("start_bit_offset", bit_offset_to_json (m_start_bit_offset)); obj->set ("size_in_bits", @@ -508,7 +487,7 @@ byte_range::dump () const std::unique_ptr<json::object> byte_range::to_json () const { - auto obj = ::make_unique<json::object> (); + auto obj = std::make_unique<json::object> (); obj->set ("start_byte_offset", byte_offset_to_json (m_start_byte_offset)); obj->set ("size_in_bytes", @@ -679,7 +658,7 @@ binding_map::operator== (const binding_map &other) const const svalue *sval = (*iter).second; const svalue **other_slot = const_cast <map_t &> (other.m_map).get (key); - if (other_slot == NULL) + if (other_slot == nullptr) return false; if (sval != *other_slot) return false; @@ -773,7 +752,7 @@ binding_map::dump (bool simple) const std::unique_ptr<json::object> binding_map::to_json () const { - auto map_obj = ::make_unique<json::object> (); + auto map_obj = std::make_unique<json::object> (); auto_vec <const binding_key *> binding_keys; for (map_t::iterator iter = m_map.begin (); @@ -943,7 +922,7 @@ get_svalue_for_ctor_val (tree val, region_model_manager *mgr) { /* Reuse the get_rvalue logic from region_model. */ region_model m (mgr); - return m.get_rvalue (path_var (val, 0), NULL); + return m.get_rvalue (path_var (val, 0), nullptr); } /* Bind values from CONSTRUCTOR to this map, relative to @@ -1455,7 +1434,7 @@ binding_cluster::validate () const std::unique_ptr<json::object> binding_cluster::to_json () const { - auto cluster_obj = ::make_unique<json::object> (); + auto cluster_obj = std::make_unique<json::object> (); cluster_obj->set_bool ("escaped", m_escaped); cluster_obj->set_bool ("touched", m_touched); @@ -1585,7 +1564,7 @@ binding_cluster::bind_compound_sval (store_manager *mgr, void binding_cluster::clobber_region (store_manager *mgr, const region *reg) { - remove_overlapping_bindings (mgr, reg, NULL, NULL); + remove_overlapping_bindings (mgr, reg, nullptr, nullptr); } /* Remove any bindings for REG within this cluster. */ @@ -1705,7 +1684,7 @@ binding_cluster::get_binding (store_manager *mgr, const region *reg) const { if (reg->empty_p ()) - return NULL; + return nullptr; const binding_key *reg_binding = binding_key::make (mgr, reg); const svalue *sval = m_map.get (reg_binding); if (sval) @@ -1773,7 +1752,7 @@ binding_cluster::get_binding_recursive (store_manager *mgr, return rmm_mgr->get_or_create_sub_svalue (reg->get_type (), parent_sval, reg); } - return NULL; + return nullptr; } /* Get any value bound for REG within this cluster. */ @@ -1821,7 +1800,7 @@ binding_cluster::get_any_binding (store_manager *mgr, return compound_sval; /* Otherwise, the initial value, or uninitialized. */ - return NULL; + return nullptr; } /* Attempt to get a compound_svalue for the bindings within the cluster @@ -1833,7 +1812,7 @@ binding_cluster::get_any_binding (store_manager *mgr, For example, REG could be one element within an array of structs. - Return the resulting compound_svalue, or NULL if there's a problem. */ + Return the resulting compound_svalue, or nullptr if there's a problem. */ const svalue * binding_cluster::maybe_get_compound_binding (store_manager *mgr, @@ -1842,13 +1821,13 @@ binding_cluster::maybe_get_compound_binding (store_manager *mgr, region_offset cluster_offset = m_base_region->get_offset (mgr->get_svalue_manager ()); if (cluster_offset.symbolic_p ()) - return NULL; + return nullptr; region_offset reg_offset = reg->get_offset (mgr->get_svalue_manager ()); if (reg_offset.symbolic_p ()) - return NULL; + return nullptr; if (reg->empty_p ()) - return NULL; + return nullptr; region_model_manager *sval_mgr = mgr->get_svalue_manager (); @@ -1901,7 +1880,7 @@ binding_cluster::maybe_get_compound_binding (store_manager *mgr, bit_size_t reg_bit_size; if (!reg->get_bit_size (®_bit_size)) - return NULL; + return nullptr; bit_range reg_range (reg_offset.get_bit_offset (), reg_bit_size); @@ -1930,7 +1909,7 @@ binding_cluster::maybe_get_compound_binding (store_manager *mgr, it overlaps with offset_concrete_key. */ default_map.remove_overlapping_bindings (mgr, offset_concrete_key, - NULL, NULL, false); + nullptr, nullptr, false); } else if (bound_range.contains_p (reg_range, &subrange)) { @@ -1964,16 +1943,16 @@ binding_cluster::maybe_get_compound_binding (store_manager *mgr, it overlaps with overlap_concrete_key. */ default_map.remove_overlapping_bindings (mgr, overlap_concrete_key, - NULL, NULL, false); + nullptr, nullptr, false); } } else /* Can't handle symbolic bindings. */ - return NULL; + return nullptr; } if (result_map.elements () == 0) - return NULL; + return nullptr; /* Merge any bindings from default_map into result_map. */ for (auto iter : default_map) @@ -2051,23 +2030,23 @@ binding_cluster::can_merge_p (const binding_cluster *cluster_a, /* At least one of CLUSTER_A and CLUSTER_B are non-NULL, but either could be NULL. Handle these cases. */ - if (cluster_a == NULL) + if (cluster_a == nullptr) { - gcc_assert (cluster_b != NULL); + gcc_assert (cluster_b != nullptr); gcc_assert (cluster_b->m_base_region == out_cluster->m_base_region); out_cluster->make_unknown_relative_to (cluster_b, out_store, mgr); return true; } - if (cluster_b == NULL) + if (cluster_b == nullptr) { - gcc_assert (cluster_a != NULL); + gcc_assert (cluster_a != nullptr); gcc_assert (cluster_a->m_base_region == out_cluster->m_base_region); out_cluster->make_unknown_relative_to (cluster_a, out_store, mgr); return true; } /* The "both inputs are non-NULL" case. */ - gcc_assert (cluster_a != NULL && cluster_b != NULL); + gcc_assert (cluster_a != nullptr && cluster_b != nullptr); gcc_assert (cluster_a->m_base_region == out_cluster->m_base_region); gcc_assert (cluster_b->m_base_region == out_cluster->m_base_region); @@ -2208,7 +2187,7 @@ binding_cluster::mark_as_escaped () Use P to purge state involving conjured_svalues. */ void -binding_cluster::on_unknown_fncall (const gcall *call, +binding_cluster::on_unknown_fncall (const gcall &call, store_manager *mgr, const conjured_purge &p) { @@ -2221,7 +2200,7 @@ binding_cluster::on_unknown_fncall (const gcall *call, /* Bind it to a new "conjured" value using CALL. */ const svalue *sval = mgr->get_svalue_manager ()->get_or_create_conjured_svalue - (m_base_region->get_type (), call, m_base_region, p); + (m_base_region->get_type (), &call, m_base_region, p); bind (mgr, m_base_region, sval); } @@ -2340,7 +2319,7 @@ binding_cluster::get_representative_path_vars (const region_model *model, } } -/* Get any svalue bound to KEY, or NULL. */ +/* Get any svalue bound to KEY, or nullptr. */ const svalue * binding_cluster::get_any_value (const binding_key *key) const @@ -2355,16 +2334,16 @@ binding_cluster::get_any_value (const binding_key *key) const const svalue * binding_cluster::maybe_get_simple_value (store_manager *mgr) const { - /* Fail gracefully if MGR is NULL to make it easier to dump store + /* Fail gracefully if MGR is nullptr to make it easier to dump store instances in the debugger. */ - if (mgr == NULL) - return NULL; + if (mgr == nullptr) + return nullptr; if (m_map.elements () != 1) - return NULL; + return nullptr; if (m_base_region->empty_p ()) - return NULL; + return nullptr; const binding_key *key = binding_key::make (mgr, m_base_region); return get_any_value (key); @@ -2488,7 +2467,7 @@ store::operator== (const store &other) const binding_cluster *c = (*iter).second; binding_cluster **other_slot = const_cast <cluster_map_t &> (other.m_cluster_map).get (reg); - if (other_slot == NULL) + if (other_slot == nullptr) return false; if (*c != **other_slot) return false; @@ -2541,7 +2520,7 @@ get_sorted_parent_regions (auto_vec<const region *> *out, /* Dump a representation of this store to PP, using SIMPLE to control how svalues and regions are printed. - MGR is used for simplifying dumps if non-NULL, but can also be NULL + MGR is used for simplifying dumps if non-NULL, but can also be nullptr (to make it easier to use from the debugger). */ void @@ -2650,7 +2629,7 @@ DEBUG_FUNCTION void store::dump (bool simple) const { tree_dump_pretty_printer pp (stderr); - dump_to_pp (&pp, simple, true, NULL); + dump_to_pp (&pp, simple, true, nullptr); pp_newline (&pp); } @@ -2672,7 +2651,7 @@ store::validate () const std::unique_ptr<json::object> store::to_json () const { - auto store_obj = ::make_unique<json::object> (); + auto store_obj = std::make_unique<json::object> (); /* Sort into some deterministic order. */ auto_vec<const region *> base_regions; @@ -2695,7 +2674,7 @@ store::to_json () const { gcc_assert (parent_reg); - auto clusters_in_parent_reg_obj = ::make_unique<json::object> (); + auto clusters_in_parent_reg_obj = std::make_unique<json::object> (); const region *base_reg; unsigned j; @@ -2782,7 +2761,7 @@ store::make_dump_widget (const text_art::dump_widget_info &dwi, return store_widget; } -/* Get any svalue bound to REG, or NULL. */ +/* Get any svalue bound to REG, or nullptr. */ const svalue * store::get_any_binding (store_manager *mgr, const region *reg) const @@ -2791,7 +2770,7 @@ store::get_any_binding (store_manager *mgr, const region *reg) const binding_cluster **cluster_slot = const_cast <cluster_map_t &> (m_cluster_map).get (base_reg); if (!cluster_slot) - return NULL; + return nullptr; return (*cluster_slot)->get_any_binding (mgr, reg); } @@ -2817,7 +2796,7 @@ store::set_value (store_manager *mgr, const region *lhs_reg, { /* Reject attempting to bind values into a symbolic region for an unknown ptr; merely invalidate values below. */ - lhs_cluster = NULL; + lhs_cluster = nullptr; /* The LHS of the write is *UNKNOWN. If the RHS is a pointer, then treat the region being pointed to as having escaped. */ @@ -2839,7 +2818,7 @@ store::set_value (store_manager *mgr, const region *lhs_reg, { /* Reject attempting to bind values into an untracked region; merely invalidate values below. */ - lhs_cluster = NULL; + lhs_cluster = nullptr; } /* Bindings to a cluster can affect other clusters if a symbolic @@ -2859,7 +2838,7 @@ store::set_value (store_manager *mgr, const region *lhs_reg, const region *iter_base_reg = (*iter).first; binding_cluster *iter_cluster = (*iter).second; if (iter_base_reg != lhs_base_reg - && (lhs_cluster == NULL + && (lhs_cluster == nullptr || lhs_cluster->symbolic_p () || iter_cluster->symbolic_p ())) { @@ -3118,7 +3097,7 @@ store::purge_state_involving (const svalue *sval, purge_cluster (iter); } -/* Get the cluster for BASE_REG, or NULL (const version). */ +/* Get the cluster for BASE_REG, or nullptr (const version). */ const binding_cluster * store::get_cluster (const region *base_reg) const @@ -3129,10 +3108,10 @@ store::get_cluster (const region *base_reg) const = const_cast <cluster_map_t &> (m_cluster_map).get (base_reg)) return *slot; else - return NULL; + return nullptr; } -/* Get the cluster for BASE_REG, or NULL (non-const version). */ +/* Get the cluster for BASE_REG, or nullptr (non-const version). */ binding_cluster * store::get_cluster (const region *base_reg) @@ -3142,7 +3121,7 @@ store::get_cluster (const region *base_reg) if (binding_cluster **slot = m_cluster_map.get (base_reg)) return *slot; else - return NULL; + return nullptr; } /* Get the cluster for BASE_REG, creating it if doesn't already exist. */ @@ -3259,7 +3238,7 @@ store::mark_as_escaped (const region *base_reg) (either in this fncall, or in a prior one). */ void -store::on_unknown_fncall (const gcall *call, store_manager *mgr, +store::on_unknown_fncall (const gcall &call, store_manager *mgr, const conjured_purge &p) { m_called_unknown_fn = true; @@ -3342,9 +3321,9 @@ store::remove_overlapping_bindings (store_manager *mgr, const region *reg, delete cluster; return; } - /* Pass NULL for the maybe_live_values here, as we don't want to + /* Pass nullptr for the maybe_live_values here, as we don't want to record the old svalues as being maybe-bound. */ - cluster->remove_overlapping_bindings (mgr, reg, uncertainty, NULL); + cluster->remove_overlapping_bindings (mgr, reg, uncertainty, nullptr); } } @@ -3587,7 +3566,7 @@ store::replay_call_summary_cluster (call_summary_replay &r, caller_sval = reg_mgr->get_or_create_unknown_svalue (summary_sval->get_type ()); set_value (mgr, caller_dest_reg, - caller_sval, NULL /* uncertainty_t * */); + caller_sval, nullptr /* uncertainty_t * */); } break; @@ -3612,7 +3591,7 @@ store::replay_call_summary_cluster (call_summary_replay &r, caller_sval = reg_mgr->get_or_create_unknown_svalue (summary_sval->get_type ()); set_value (mgr, caller_dest_reg, - caller_sval, NULL /* uncertainty_t * */); + caller_sval, nullptr /* uncertainty_t * */); } break; @@ -3807,7 +3786,7 @@ assert_disjoint (const location &loc, static void test_binding_key_overlap () { - store_manager mgr (NULL); + store_manager mgr (nullptr); /* Various 8-bit bindings. */ const concrete_binding *cb_0_7 = mgr.get_concrete_binding (0, 8); diff --git a/gcc/analyzer/store.h b/gcc/analyzer/store.h index 6c84812..95d38e3 100644 --- a/gcc/analyzer/store.h +++ b/gcc/analyzer/store.h @@ -222,9 +222,9 @@ public: static int cmp (const binding_key *, const binding_key *); virtual const concrete_binding *dyn_cast_concrete_binding () const - { return NULL; } + { return nullptr; } virtual const symbolic_binding *dyn_cast_symbolic_binding () const - { return NULL; } + { return nullptr; } }; /* A concrete range of bits. */ @@ -299,6 +299,16 @@ struct bit_range bool as_byte_range (byte_range *out) const; + bool + operator< (const bit_range &other) const + { + if (m_start_bit_offset < other.m_start_bit_offset) + return true; + if (m_start_bit_offset > other.m_start_bit_offset) + return false; + return (m_size_in_bits < other.m_size_in_bits); + } + bit_offset_t m_start_bit_offset; bit_size_t m_size_in_bits; }; @@ -482,10 +492,10 @@ public: static int cmp_ptr_ptr (const void *, const void *); void mark_deleted () { m_region = reinterpret_cast<const region *> (1); } - void mark_empty () { m_region = NULL; } + void mark_empty () { m_region = nullptr; } bool is_deleted () const { return m_region == reinterpret_cast<const region *> (1); } - bool is_empty () const { return m_region == NULL; } + bool is_empty () const { return m_region == nullptr; } private: const region *m_region; @@ -528,7 +538,7 @@ public: if (slot) return *slot; else - return NULL; + return nullptr; } bool put (const binding_key *k, const svalue *v) { @@ -666,7 +676,7 @@ public: store_manager *mgr); void mark_as_escaped (); - void on_unknown_fncall (const gcall *call, store_manager *mgr, + void on_unknown_fncall (const gcall &call, store_manager *mgr, const conjured_purge &p); void on_asm (const gasm *stmt, store_manager *mgr, const conjured_purge &p); @@ -800,7 +810,7 @@ public: model_merger *merger); void mark_as_escaped (const region *base_reg); - void on_unknown_fncall (const gcall *call, store_manager *mgr, + void on_unknown_fncall (const gcall &call, store_manager *mgr, const conjured_purge &p); bool escaped_p (const region *reg) const; diff --git a/gcc/analyzer/supergraph.cc b/gcc/analyzer/supergraph.cc index f35adf0..8592db7 100644 --- a/gcc/analyzer/supergraph.cc +++ b/gcc/analyzer/supergraph.cc @@ -18,42 +18,23 @@ 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/>. */ -#include "config.h" -#include "system.h" -#include "coretypes.h" -#include "tree.h" -#include "tm.h" -#include "toplev.h" -#include "hash-table.h" -#include "vec.h" -#include "ggc.h" -#include "basic-block.h" -#include "function.h" -#include "gimple.h" -#include "gimple-iterator.h" -#include "gimple-fold.h" -#include "tree-eh.h" -#include "gimple-expr.h" -#include "is-a.h" +#include "analyzer/common.h" + #include "timevar.h" #include "gimple-pretty-print.h" -#include "tree-pretty-print.h" -#include "graphviz.h" -#include "cgraph.h" -#include "tree-dfa.h" -#include "bitmap.h" -#include "cfganal.h" -#include "function.h" -#include "analyzer/analyzer.h" #include "ordered-hash-map.h" #include "options.h" #include "cgraph.h" #include "cfg.h" #include "digraph.h" #include "tree-cfg.h" +#include "tree-dfa.h" +#include "cfganal.h" +#include "except.h" + #include "analyzer/supergraph.h" #include "analyzer/analyzer-logging.h" -#include "make-unique.h" +#include "analyzer/region-model.h" #if ENABLE_ANALYZER @@ -67,7 +48,7 @@ get_ultimate_function_for_cgraph_edge (cgraph_edge *edge) { cgraph_node *ultimate_node = edge->callee->ultimate_alias_target (); if (!ultimate_node) - return NULL; + return nullptr; return ultimate_node->get_fun (); } @@ -78,15 +59,15 @@ supergraph_call_edge (function *fun, const gimple *stmt) { const gcall *call = dyn_cast<const gcall *> (stmt); if (!call) - return NULL; + return nullptr; cgraph_edge *edge = cgraph_node::get (fun->decl)->get_edge (const_cast <gimple *> (stmt)); if (!edge) - return NULL; + return nullptr; if (!edge->callee) - return NULL; /* e.g. for a function pointer. */ + return nullptr; /* e.g. for a function pointer. */ if (!get_ultimate_function_for_cgraph_edge (edge)) - return NULL; + return nullptr; return edge; } @@ -165,7 +146,8 @@ supergraph::supergraph (logger *logger) FOR_ALL_BB_FN (bb, fun) { /* The initial supernode for the BB gets the phi nodes (if any). */ - supernode *node_for_stmts = add_node (fun, bb, NULL, phi_nodes (bb)); + supernode *node_for_stmts + = add_node (fun, bb, nullptr, phi_nodes (bb)); m_bb_to_initial_node.put (bb, node_for_stmts); for (gphi_iterator gpi = gsi_start_phis (bb); !gsi_end_p (gpi); gsi_next (&gpi)) @@ -190,12 +172,12 @@ supergraph::supergraph (logger *logger) m_stmt_to_node_t.put (stmt, node_for_stmts); m_stmt_uids.make_uid_unique (stmt); if (cgraph_edge *edge = supergraph_call_edge (fun, stmt)) - { - m_cgraph_edge_to_caller_prev_node.put(edge, node_for_stmts); - node_for_stmts = add_node (fun, bb, as_a <gcall *> (stmt), - NULL); - m_cgraph_edge_to_caller_next_node.put (edge, node_for_stmts); - } + { + m_cgraph_edge_to_caller_prev_node.put(edge, node_for_stmts); + node_for_stmts = add_node (fun, bb, as_a <gcall *> (stmt), + nullptr); + m_cgraph_edge_to_caller_next_node.put (edge, node_for_stmts); + } else { // maybe call is via a function pointer @@ -206,13 +188,13 @@ supergraph::supergraph (logger *logger) if (!edge || !edge->callee) { supernode *old_node_for_stmts = node_for_stmts; - node_for_stmts = add_node (fun, bb, call, NULL); + node_for_stmts = add_node (fun, bb, call, nullptr); superedge *sedge = new callgraph_superedge (old_node_for_stmts, node_for_stmts, SUPEREDGE_INTRAPROCEDURAL_CALL, - NULL); + nullptr); add_edge (sedge); } } @@ -465,11 +447,11 @@ supergraph::dump_dot (const char *path, const dump_args_t &dump_args) const std::unique_ptr<json::object> supergraph::to_json () const { - auto sgraph_obj = ::make_unique<json::object> (); + auto sgraph_obj = std::make_unique<json::object> (); /* Nodes. */ { - auto nodes_arr = ::make_unique<json::array> (); + auto nodes_arr = std::make_unique<json::array> (); unsigned i; supernode *n; FOR_EACH_VEC_ELT (m_nodes, i, n) @@ -479,7 +461,7 @@ supergraph::to_json () const /* Edges. */ { - auto edges_arr = ::make_unique<json::array> (); + auto edges_arr = std::make_unique<json::array> (); unsigned i; superedge *n; FOR_EACH_VEC_ELT (m_edges, i, n) @@ -511,21 +493,25 @@ supergraph::add_node (function *fun, basic_block bb, gcall *returning_call, /* Create a new cfg_superedge from SRC to DEST for the underlying CFG edge E, adding it to this supergraph. - If the edge is for a switch statement, create a switch_cfg_superedge - subclass. */ + If the edge is for a switch or eh_dispatch statement, create a + switch_cfg_superedge or eh_dispatch_cfg_superedge subclass, + respectively */ cfg_superedge * supergraph::add_cfg_edge (supernode *src, supernode *dest, ::edge e) { - /* Special-case switch edges. */ + /* Special-case switch and eh_dispatch edges. */ gimple *stmt = src->get_last_stmt (); - cfg_superedge *new_edge; + std::unique_ptr<cfg_superedge> new_edge; if (stmt && stmt->code == GIMPLE_SWITCH) - new_edge = new switch_cfg_superedge (src, dest, e); + new_edge = std::make_unique<switch_cfg_superedge> (src, dest, e); + else if (stmt && stmt->code == GIMPLE_EH_DISPATCH) + new_edge = eh_dispatch_cfg_superedge::make (src, dest, e, + as_a <geh_dispatch *> (stmt)); else - new_edge = new cfg_superedge (src, dest, e); - add_edge (new_edge); - return new_edge; + new_edge = std::make_unique<cfg_superedge> (src, dest, e); + add_edge (new_edge.get ()); + return new_edge.release (); } /* Create and add a call_superedge representing an interprocedural call @@ -720,7 +706,7 @@ supernode::dump_dot_id (pretty_printer *pp) const std::unique_ptr<json::object> supernode::to_json () const { - auto snode_obj = ::make_unique<json::object> (); + auto snode_obj = std::make_unique<json::object> (); snode_obj->set_integer ("idx", m_index); snode_obj->set_integer ("bb_idx", m_bb->index); @@ -737,7 +723,7 @@ supernode::to_json () const /* Phi nodes. */ { - auto phi_arr = ::make_unique<json::array> (); + auto phi_arr = std::make_unique<json::array> (); for (gphi_iterator gpi = const_cast<supernode *> (this)->start_phis (); !gsi_end_p (gpi); gsi_next (&gpi)) { @@ -752,7 +738,7 @@ supernode::to_json () const /* Statements. */ { - auto stmt_arr = ::make_unique<json::array> (); + auto stmt_arr = std::make_unique<json::array> (); int i; gimple *stmt; FOR_EACH_VEC_ELT (m_stmts, i, stmt) @@ -983,7 +969,7 @@ superedge::dump_dot (graphviz_out *gv, const dump_args_t &) const std::unique_ptr<json::object> superedge::to_json () const { - auto sedge_obj = ::make_unique<json::object> (); + auto sedge_obj = std::make_unique<json::object> (); sedge_obj->set_string ("kind", edge_kind_to_string (m_kind)); sedge_obj->set_integer ("src_idx", m_src->m_index); sedge_obj->set_integer ("dst_idx", m_dest->m_index); @@ -999,25 +985,25 @@ superedge::to_json () const } /* If this is an intraprocedural superedge, return the associated - CFG edge. Otherwise, return NULL. */ + CFG edge. Otherwise, return nullptr. */ ::edge superedge::get_any_cfg_edge () const { if (const cfg_superedge *sub = dyn_cast_cfg_superedge ()) return sub->get_cfg_edge (); - return NULL; + return nullptr; } /* If this is an interprocedural superedge, return the associated - cgraph_edge *. Otherwise, return NULL. */ + cgraph_edge *. Otherwise, return nullptr. */ cgraph_edge * superedge::get_any_callgraph_edge () const { if (const callgraph_superedge *sub = dyn_cast_callgraph_superedge ()) return sub->m_cedge; - return NULL; + return nullptr; } /* Build a description of this superedge (e.g. "true" for the true @@ -1030,6 +1016,7 @@ label_text superedge::get_description (bool user_facing) const { pretty_printer pp; + pp_format_decoder (&pp) = default_tree_printer; dump_label_to_pp (&pp, user_facing); return label_text::take (xstrdup (pp_formatted_text (&pp))); } @@ -1099,6 +1086,8 @@ cfg_superedge::get_phi_arg (const gphi *phi) const return gimple_phi_arg_def (phi, index); } +/* class switch_cfg_superedge : public cfg_superedge. */ + switch_cfg_superedge::switch_cfg_superedge (supernode *src, supernode *dst, ::edge e) @@ -1206,6 +1195,203 @@ switch_cfg_superedge::implicitly_created_default_p () const return EXPR_LOCATION (case_label) == UNKNOWN_LOCATION; } +/* class eh_dispatch_cfg_superedge : public cfg_superedge. */ + +/* Given an ERT_TRY region, get the eh_catch corresponding to + the label of DST_SNODE, if any. */ + +static eh_catch +get_catch (eh_region eh_reg, supernode *dst_snode) +{ + gcc_assert (eh_reg->type == ERT_TRY); + + tree dst_snode_label = dst_snode->get_label (); + if (!dst_snode_label) + return nullptr; + + for (eh_catch iter = eh_reg->u.eh_try.first_catch; + iter; + iter = iter->next_catch) + if (iter->label == dst_snode_label) + return iter; + + return nullptr; +} + +std::unique_ptr<eh_dispatch_cfg_superedge> +eh_dispatch_cfg_superedge::make (supernode *src_snode, + supernode *dst_snode, + ::edge e, + const geh_dispatch *eh_dispatch_stmt) +{ + const eh_status *eh = src_snode->get_function ()->eh; + gcc_assert (eh); + int region_idx = gimple_eh_dispatch_region (eh_dispatch_stmt); + gcc_assert (region_idx > 0); + gcc_assert ((*eh->region_array)[region_idx]); + eh_region eh_reg = (*eh->region_array)[region_idx]; + gcc_assert (eh_reg); + switch (eh_reg->type) + { + default: + gcc_unreachable (); + case ERT_CLEANUP: + // TODO + gcc_unreachable (); + break; + case ERT_TRY: + { + eh_catch ehc = get_catch (eh_reg, dst_snode); + return std::make_unique<eh_dispatch_try_cfg_superedge> + (src_snode, dst_snode, + e, eh_dispatch_stmt, + eh_reg, ehc); + } + break; + case ERT_ALLOWED_EXCEPTIONS: + return std::make_unique<eh_dispatch_allowed_cfg_superedge> + (src_snode, dst_snode, + e, eh_dispatch_stmt, + eh_reg); + break; + case ERT_MUST_NOT_THROW: + // TODO + gcc_unreachable (); + break; + } +} + +eh_dispatch_cfg_superedge:: +eh_dispatch_cfg_superedge (supernode *src, + supernode *dst, + ::edge e, + const geh_dispatch *eh_dispatch_stmt, + eh_region eh_reg) +: cfg_superedge (src, dst, e), + m_eh_dispatch_stmt (eh_dispatch_stmt), + m_eh_region (eh_reg) +{ + gcc_assert (m_eh_region); +} + +const eh_status & +eh_dispatch_cfg_superedge::get_eh_status () const +{ + const eh_status *eh = m_src->get_function ()->eh; + gcc_assert (eh); + return *eh; +} + +// class eh_dispatch_try_cfg_superedge : public eh_dispatch_cfg_superedge + +/* Implementation of superedge::dump_label_to_pp for CFG superedges for + "eh_dispatch" statements for ERT_TRY regions. */ + +void +eh_dispatch_try_cfg_superedge::dump_label_to_pp (pretty_printer *pp, + bool user_facing) const +{ + if (!user_facing) + pp_string (pp, "ERT_TRY: "); + if (m_eh_catch) + { + bool first = true; + for (tree iter = m_eh_catch->type_list; iter; iter = TREE_CHAIN (iter)) + { + if (!first) + pp_string (pp, ", "); + pp_printf (pp, "on catch %qT", TREE_VALUE (iter)); + first = false; + } + } + else + pp_string (pp, "on uncaught exception"); +} + +bool +eh_dispatch_try_cfg_superedge:: +apply_constraints (region_model *model, + region_model_context *ctxt, + tree exception_type, + std::unique_ptr<rejected_constraint> *out) const +{ + return model->apply_constraints_for_eh_dispatch_try + (*this, ctxt, exception_type, out); +} + +// class eh_dispatch_allowed_cfg_superedge : public eh_dispatch_cfg_superedge + +eh_dispatch_allowed_cfg_superedge:: +eh_dispatch_allowed_cfg_superedge (supernode *src, supernode *dst, ::edge e, + const geh_dispatch *eh_dispatch_stmt, + eh_region eh_reg) +: eh_dispatch_cfg_superedge (src, dst, e, eh_dispatch_stmt, eh_reg) +{ + gcc_assert (eh_reg->type == ERT_ALLOWED_EXCEPTIONS); + + /* We expect two sibling out-edges at an eh_dispatch from such a region: + + - one to a bb without a gimple label, with a resx, + for exceptions of expected types + + - one to a bb with a gimple label, with a call to __cxa_unexpected, + for exceptions of unexpected types. + + Set m_kind for this edge accordingly. */ + gcc_assert (e->src->succs->length () == 2); + tree label_for_unexpected_exceptions = eh_reg->u.allowed.label; + tree label_for_dest_enode = dst->get_label (); + if (label_for_dest_enode == label_for_unexpected_exceptions) + m_kind = eh_kind::unexpected; + else + { + gcc_assert (label_for_dest_enode == nullptr); + m_kind = eh_kind::expected; + } +} + +void +eh_dispatch_allowed_cfg_superedge::dump_label_to_pp (pretty_printer *pp, + bool user_facing) const +{ + if (!user_facing) + { + switch (m_kind) + { + default: + gcc_unreachable (); + case eh_dispatch_allowed_cfg_superedge::eh_kind::expected: + pp_string (pp, "expected: "); + break; + case eh_dispatch_allowed_cfg_superedge::eh_kind::unexpected: + pp_string (pp, "unexpected: "); + break; + } + pp_string (pp, "ERT_ALLOWED_EXCEPTIONS: "); + eh_region eh_reg = get_eh_region (); + bool first = true; + for (tree iter = eh_reg->u.allowed.type_list; iter; + iter = TREE_CHAIN (iter)) + { + if (!first) + pp_string (pp, ", "); + pp_printf (pp, "%qT", TREE_VALUE (iter)); + first = false; + } + } +} + +bool +eh_dispatch_allowed_cfg_superedge:: +apply_constraints (region_model *model, + region_model_context *ctxt, + tree exception_type, + std::unique_ptr<rejected_constraint> *out) const +{ + return model->apply_constraints_for_eh_dispatch_allowed + (*this, ctxt, exception_type, out); +} + /* Implementation of superedge::dump_label_to_pp for interprocedural superedges. */ @@ -1261,13 +1447,13 @@ callgraph_superedge::get_callee_decl () const /* Get the gcall * of this interprocedural call/return edge. */ -gcall * +const gcall & callgraph_superedge::get_call_stmt () const { if (m_cedge) - return m_cedge->call_stmt; + return *m_cedge->call_stmt; - return m_src->get_final_call (); + return *m_src->get_final_call (); } /* Get the calling fndecl at this interprocedural call/return edge. */ @@ -1289,19 +1475,19 @@ callgraph_superedge::get_arg_for_parm (tree parm_to_find, gcc_assert (TREE_CODE (parm_to_find) == PARM_DECL); tree callee = get_callee_decl (); - const gcall *call_stmt = get_call_stmt (); + const gcall &call_stmt = get_call_stmt (); unsigned i = 0; for (tree iter_parm = DECL_ARGUMENTS (callee); iter_parm; iter_parm = DECL_CHAIN (iter_parm), ++i) { - if (i >= gimple_call_num_args (call_stmt)) + if (i >= gimple_call_num_args (&call_stmt)) return NULL_TREE; if (iter_parm == parm_to_find) { if (out) *out = callsite_expr::from_zero_based_param (i); - return gimple_call_arg (call_stmt, i); + return gimple_call_arg (&call_stmt, i); } } @@ -1319,15 +1505,15 @@ callgraph_superedge::get_parm_for_arg (tree arg_to_find, callsite_expr *out) const { tree callee = get_callee_decl (); - const gcall *call_stmt = get_call_stmt (); + const gcall &call_stmt = get_call_stmt (); unsigned i = 0; for (tree iter_parm = DECL_ARGUMENTS (callee); iter_parm; iter_parm = DECL_CHAIN (iter_parm), ++i) { - if (i >= gimple_call_num_args (call_stmt)) + if (i >= gimple_call_num_args (&call_stmt)) return NULL_TREE; - tree param = gimple_call_arg (call_stmt, i); + tree param = gimple_call_arg (&call_stmt, i); if (arg_to_find == param) { if (out) @@ -1353,7 +1539,7 @@ callgraph_superedge::map_expr_from_caller_to_callee (tree caller_expr, if (parm) return parm; /* Otherwise try return value. */ - if (caller_expr == gimple_call_lhs (get_call_stmt ())) + if (caller_expr == gimple_call_lhs (&get_call_stmt ())) { if (out) *out = callsite_expr::from_return_value (); @@ -1388,7 +1574,7 @@ callgraph_superedge::map_expr_from_callee_to_caller (tree callee_expr, { if (out) *out = callsite_expr::from_return_value (); - return gimple_call_lhs (get_call_stmt ()); + return gimple_call_lhs (&get_call_stmt ()); } return NULL_TREE; diff --git a/gcc/analyzer/supergraph.h b/gcc/analyzer/supergraph.h index 6f94f99..f64a2f4 100644 --- a/gcc/analyzer/supergraph.h +++ b/gcc/analyzer/supergraph.h @@ -27,6 +27,7 @@ along with GCC; see the file COPYING3. If not see #include "gimple.h" #include "gimple-iterator.h" #include "digraph.h" +#include "except.h" using namespace ana; @@ -42,6 +43,9 @@ class superedge; class return_superedge; class cfg_superedge; class switch_cfg_superedge; + class eh_dispatch_cfg_superedge; + class eh_dispatch_try_cfg_superedge; + class eh_dispatch_allowed_cfg_superedge; class supercluster; class dot_annotator; @@ -126,7 +130,7 @@ public: return *const_cast <bb_to_node_t &> (m_bb_to_initial_node).get (bb); } - /* Get the supernode containing the second half of the gcall * + /* Get the supernode containing the second half of the gcall & at an interprocedural call, within the caller. */ supernode *get_caller_next_node (cgraph_edge *edge) const { @@ -270,7 +274,7 @@ class supernode : public dnode<supergraph_traits> i.ptr = gimple_seq_first (*pseq); i.seq = pseq; - i.bb = i.ptr ? gimple_bb (i.ptr) : NULL; + i.bb = i.ptr ? gimple_bb (i.ptr) : nullptr; return i; } @@ -283,15 +287,15 @@ class supernode : public dnode<supergraph_traits> gimple *get_last_stmt () const { if (m_stmts.length () == 0) - return NULL; + return nullptr; return m_stmts[m_stmts.length () - 1]; } gcall *get_final_call () const { gimple *stmt = get_last_stmt (); - if (stmt == NULL) - return NULL; + if (stmt == nullptr) + return nullptr; return dyn_cast<gcall *> (stmt); } @@ -327,15 +331,18 @@ class superedge : public dedge<supergraph_traits> enum edge_kind get_kind () const { return m_kind; } - virtual cfg_superedge *dyn_cast_cfg_superedge () { return NULL; } - virtual const cfg_superedge *dyn_cast_cfg_superedge () const { return NULL; } - virtual const switch_cfg_superedge *dyn_cast_switch_cfg_superedge () const { return NULL; } - virtual callgraph_superedge *dyn_cast_callgraph_superedge () { return NULL; } - virtual const callgraph_superedge *dyn_cast_callgraph_superedge () const { return NULL; } - virtual call_superedge *dyn_cast_call_superedge () { return NULL; } - virtual const call_superedge *dyn_cast_call_superedge () const { return NULL; } - virtual return_superedge *dyn_cast_return_superedge () { return NULL; } - virtual const return_superedge *dyn_cast_return_superedge () const { return NULL; } + virtual cfg_superedge *dyn_cast_cfg_superedge () { return nullptr; } + virtual const cfg_superedge *dyn_cast_cfg_superedge () const { return nullptr; } + virtual const switch_cfg_superedge *dyn_cast_switch_cfg_superedge () const { return nullptr; } + virtual const eh_dispatch_cfg_superedge *dyn_cast_eh_dispatch_cfg_superedge () const { return nullptr; } + virtual const eh_dispatch_try_cfg_superedge *dyn_cast_eh_dispatch_try_cfg_superedge () const { return nullptr; } + virtual const eh_dispatch_allowed_cfg_superedge *dyn_cast_eh_dispatch_allowed_cfg_superedge () const { return nullptr; } + virtual callgraph_superedge *dyn_cast_callgraph_superedge () { return nullptr; } + virtual const callgraph_superedge *dyn_cast_callgraph_superedge () const { return nullptr; } + virtual call_superedge *dyn_cast_call_superedge () { return nullptr; } + virtual const call_superedge *dyn_cast_call_superedge () const { return nullptr; } + virtual return_superedge *dyn_cast_return_superedge () { return nullptr; } + virtual const return_superedge *dyn_cast_return_superedge () const { return nullptr; } ::edge get_any_cfg_edge () const; cgraph_edge *get_any_callgraph_edge () const; @@ -415,7 +422,7 @@ class callgraph_superedge : public superedge function *get_caller_function () const; tree get_callee_decl () const; tree get_caller_decl () const; - gcall *get_call_stmt () const; + const gcall &get_call_stmt () const; tree get_arg_for_parm (tree parm, callsite_expr *out) const; tree get_parm_for_arg (tree arg, callsite_expr *out) const; tree map_expr_from_caller_to_callee (tree caller_expr, @@ -587,11 +594,169 @@ template <> inline bool is_a_helper <const switch_cfg_superedge *>::test (const superedge *sedge) { - return sedge->dyn_cast_switch_cfg_superedge () != NULL; + return sedge->dyn_cast_switch_cfg_superedge () != nullptr; } namespace ana { +/* A subclass for edges from eh_dispatch statements, retaining enough + information to identify the various types being caught, vs the + "unhandled type" case, and for adding labels when rendering + via graphviz. + This is abstract; there are concrete subclasses based on the type + of the eh_region. */ + +class eh_dispatch_cfg_superedge : public cfg_superedge +{ + public: + static std::unique_ptr<eh_dispatch_cfg_superedge> + make (supernode *src, + supernode *dest, + ::edge e, + const geh_dispatch *eh_dispatch_stmt); + + const eh_dispatch_cfg_superedge *dyn_cast_eh_dispatch_cfg_superedge () const + final override + { + return this; + } + + const geh_dispatch * + get_eh_dispatch_stmt () const + { + return m_eh_dispatch_stmt; + } + + const eh_status &get_eh_status () const; + eh_region get_eh_region () const { return m_eh_region; } + + virtual bool + apply_constraints (region_model *model, + region_model_context *ctxt, + tree exception_type, + std::unique_ptr<rejected_constraint> *out) const = 0; + +protected: + eh_dispatch_cfg_superedge (supernode *src, supernode *dst, ::edge e, + const geh_dispatch *eh_dispatch_stmt, + eh_region eh_reg); + +private: + const geh_dispatch *m_eh_dispatch_stmt; + eh_region m_eh_region; +}; + +} // namespace ana + +template <> +template <> +inline bool +is_a_helper <const eh_dispatch_cfg_superedge *>::test (const superedge *sedge) +{ + return sedge->dyn_cast_eh_dispatch_cfg_superedge () != nullptr; +} + +namespace ana { + +/* A concrete subclass for edges from an eh_dispatch statements + for ERT_TRY regions. */ + +class eh_dispatch_try_cfg_superedge : public eh_dispatch_cfg_superedge +{ + public: + eh_dispatch_try_cfg_superedge (supernode *src, supernode *dst, ::edge e, + const geh_dispatch *eh_dispatch_stmt, + eh_region eh_reg, + eh_catch ehc) + : eh_dispatch_cfg_superedge (src, dst, e, eh_dispatch_stmt, eh_reg), + m_eh_catch (ehc) + { + gcc_assert (eh_reg->type == ERT_TRY); + } + + const eh_dispatch_try_cfg_superedge * + dyn_cast_eh_dispatch_try_cfg_superedge () const final override + { + return this; + } + + void dump_label_to_pp (pretty_printer *pp, + bool user_facing) const final override; + + eh_catch get_eh_catch () const { return m_eh_catch; } + + bool + apply_constraints (region_model *model, + region_model_context *ctxt, + tree exception_type, + std::unique_ptr<rejected_constraint> *out) + const final override; + +private: + eh_catch m_eh_catch; +}; + +} // namespace ana + +template <> +template <> +inline bool +is_a_helper <const eh_dispatch_try_cfg_superedge *>::test (const superedge *sedge) +{ + return sedge->dyn_cast_eh_dispatch_try_cfg_superedge () != nullptr; +} + +namespace ana { + +/* A concrete subclass for edges from an eh_dispatch statements + for ERT_ALLOWED_EXCEPTIONS regions. */ + +class eh_dispatch_allowed_cfg_superedge : public eh_dispatch_cfg_superedge +{ + public: + enum eh_kind + { + expected, + unexpected + }; + + eh_dispatch_allowed_cfg_superedge (supernode *src, supernode *dst, ::edge e, + const geh_dispatch *eh_dispatch_stmt, + eh_region eh_reg); + + const eh_dispatch_allowed_cfg_superedge * + dyn_cast_eh_dispatch_allowed_cfg_superedge () const final override + { + return this; + } + + void dump_label_to_pp (pretty_printer *pp, + bool user_facing) const final override; + + bool + apply_constraints (region_model *model, + region_model_context *ctxt, + tree exception_type, + std::unique_ptr<rejected_constraint> *out) + const final override; + + enum eh_kind get_eh_kind () const { return m_kind; } + +private: + enum eh_kind m_kind; +}; + +} // namespace ana + +template <> +template <> +inline bool +is_a_helper <const eh_dispatch_allowed_cfg_superedge *>::test (const superedge *sedge) +{ + return sedge->dyn_cast_eh_dispatch_allowed_cfg_superedge () != nullptr; +} + +namespace ana { /* Base class for adding additional content to the .dot output for a supergraph. */ diff --git a/gcc/analyzer/svalue.cc b/gcc/analyzer/svalue.cc index 2e3f051..fbbd1d2 100644 --- a/gcc/analyzer/svalue.cc +++ b/gcc/analyzer/svalue.cc @@ -18,39 +18,22 @@ 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/>. */ -#include "config.h" -#define INCLUDE_VECTOR -#include "system.h" -#include "coretypes.h" -#include "tree.h" -#include "diagnostic-core.h" +#include "analyzer/common.h" + +#include "tree-pretty-print.h" #include "gimple-pretty-print.h" -#include "function.h" -#include "basic-block.h" -#include "gimple.h" -#include "gimple-iterator.h" -#include "diagnostic-core.h" -#include "graphviz.h" -#include "options.h" -#include "cgraph.h" -#include "tree-dfa.h" -#include "stringpool.h" -#include "convert.h" -#include "target.h" #include "fold-const.h" -#include "tree-pretty-print.h" -#include "bitmap.h" -#include "analyzer/analyzer.h" +#include "diagnostic.h" +#include "tree-diagnostic.h" + +#include "text-art/dump.h" + #include "analyzer/analyzer-logging.h" #include "analyzer/call-string.h" #include "analyzer/program-point.h" #include "analyzer/store.h" #include "analyzer/svalue.h" #include "analyzer/region-model.h" -#include "diagnostic.h" -#include "tree-diagnostic.h" -#include "make-unique.h" -#include "text-art/dump.h" #if ENABLE_ANALYZER @@ -107,7 +90,7 @@ std::unique_ptr<json::value> svalue::to_json () const { label_text desc = get_desc (true); - auto sval_js = ::make_unique<json::string> (desc.get ()); + auto sval_js = std::make_unique<json::string> (desc.get ()); return sval_js; } @@ -264,7 +247,7 @@ svalue::maybe_get_constant () const } /* If this svalue is a region_svalue, return the region it points to. - Otherwise return NULL. */ + Otherwise return nullptr. */ const region * svalue::maybe_get_region () const @@ -272,12 +255,12 @@ svalue::maybe_get_region () const if (const region_svalue *region_sval = dyn_cast_region_svalue ()) return region_sval->get_pointee (); else - return NULL; + return nullptr; } /* If this svalue is a cast (i.e a unaryop NOP_EXPR or VIEW_CONVERT_EXPR), return the underlying svalue. - Otherwise return NULL. */ + Otherwise return nullptr. */ const svalue * svalue::maybe_undo_cast () const @@ -288,7 +271,7 @@ svalue::maybe_undo_cast () const if (op == NOP_EXPR || op == VIEW_CONVERT_EXPR) return unaryop_sval->get_arg (); } - return NULL; + return nullptr; } /* If this svalue is an unmergeable decorator around another svalue, return @@ -304,7 +287,7 @@ svalue::unwrap_any_unmergeable () const } /* Attempt to merge THIS with OTHER, returning the merged svalue. - Return NULL if not mergeable. */ + Return nullptr if not mergeable. */ const svalue * svalue::can_merge_p (const svalue *other, @@ -312,22 +295,22 @@ svalue::can_merge_p (const svalue *other, model_merger *merger) const { if (!(get_type () && other->get_type ())) - return NULL; + return nullptr; if (!types_compatible_p (get_type (), other->get_type ())) - return NULL; + return nullptr; /* Reject attempts to merge unmergeable svalues. */ if ((get_kind () == SK_UNMERGEABLE) || (other->get_kind () == SK_UNMERGEABLE)) - return NULL; + return nullptr; /* Reject attempts to merge poisoned svalues with other svalues (either non-poisoned, or other kinds of poison), so that e.g. we identify paths in which a variable is conditionally uninitialized. */ if (get_kind () == SK_POISONED || other->get_kind () == SK_POISONED) - return NULL; + return nullptr; /* Reject attempts to merge NULL pointers with not-NULL-pointers. */ if (POINTER_TYPE_P (get_type ())) @@ -341,16 +324,16 @@ svalue::can_merge_p (const svalue *other, if (zerop (cst1)) null1 = true; if (null0 != null1) - return NULL; + return nullptr; } /* Reject merging svalues that have non-purgable sm-state, to avoid falsely reporting memory leaks by merging them with something else. */ if (!merger->mergeable_svalue_p (this)) - return NULL; + return nullptr; if (!merger->mergeable_svalue_p (other)) - return NULL; + return nullptr; /* Widening. */ /* Merge: (new_cst, existing_cst) -> widen (existing, new). */ @@ -427,7 +410,7 @@ svalue::can_merge_p (const svalue *other, /* Determine if this svalue is either within LIVE_SVALUES, or is implicitly live with respect to LIVE_SVALUES and MODEL. - LIVE_SVALUES can be NULL, in which case determine if this svalue is + LIVE_SVALUES can be nullptr, in which case determine if this svalue is intrinsically live. */ bool @@ -572,8 +555,8 @@ svalue::cmp_ptr (const svalue *sval1, const svalue *sval2) { const poisoned_svalue *poisoned_sval1 = (const poisoned_svalue *)sval1; const poisoned_svalue *poisoned_sval2 = (const poisoned_svalue *)sval2; - return (poisoned_sval1->get_poison_kind () - - poisoned_sval2->get_poison_kind ()); + return (static_cast<int> (poisoned_sval1->get_poison_kind ()) + - static_cast<int> (poisoned_sval2->get_poison_kind ())); } break; case SK_SETJMP: @@ -823,7 +806,7 @@ svalue::maybe_fold_bits_within (tree, region_model_manager *) const { /* By default, don't fold. */ - return NULL; + return nullptr; } /* Base implementation of svalue::all_zeroes_p. @@ -836,7 +819,7 @@ svalue::all_zeroes_p () const } /* If this svalue is a pointer, attempt to determine the base region it points - to. Return NULL on any problems. */ + to. Return nullptr on any problems. */ const region * svalue::maybe_get_deref_base_region () const @@ -847,7 +830,7 @@ svalue::maybe_get_deref_base_region () const switch (iter->get_kind ()) { default: - return NULL; + return nullptr; case SK_REGION: { @@ -869,14 +852,27 @@ svalue::maybe_get_deref_base_region () const continue; default: - return NULL; + return nullptr; } - return NULL; + return nullptr; } } } } +/* If this svalue is a pointer to the typeinfo instance for a particular + type, return that type. Otherwise return NULL_TREE. */ + +tree +svalue::maybe_get_type_from_typeinfo () const +{ + if (const region *reg = maybe_get_region ()) + if (const decl_region *decl_reg = reg->dyn_cast_decl_region ()) + return TREE_TYPE (DECL_NAME (decl_reg->get_decl ())); + + return NULL_TREE; +} + /* class region_svalue : public svalue. */ /* Implementation of svalue::dump_to_pp vfunc for region_svalue. */ @@ -1151,7 +1147,7 @@ constant_svalue::maybe_fold_bits_within (tree type, } /* Otherwise, don't fold. */ - return NULL; + return nullptr; } /* Implementation of svalue::all_zeroes_p for constant_svalue. */ @@ -1234,13 +1230,13 @@ poison_kind_to_str (enum poison_kind kind) { default: gcc_unreachable (); - case POISON_KIND_UNINIT: + case poison_kind::uninit: return "uninit"; - case POISON_KIND_FREED: + case poison_kind::freed: return "freed"; - case POISON_KIND_DELETED: + case poison_kind::deleted: return "deleted"; - case POISON_KIND_POPPED_STACK: + case poison_kind::popped_stack: return "popped stack"; } } @@ -1378,7 +1374,7 @@ initial_svalue::implicitly_live_p (const svalue_set *, a popped stack frame. */ if (model->region_exists_p (m_reg)) { - const svalue *reg_sval = model->get_store_value (m_reg, NULL); + const svalue *reg_sval = model->get_store_value (m_reg, nullptr); if (reg_sval == this) return true; } @@ -1388,7 +1384,7 @@ initial_svalue::implicitly_live_p (const svalue_set *, live in the external caller. */ if (initial_value_of_param_p ()) if (const frame_region *frame_reg = m_reg->maybe_get_frame_region ()) - if (frame_reg->get_calling_frame () == NULL) + if (frame_reg->get_calling_frame () == nullptr) return true; return false; @@ -1512,7 +1508,7 @@ unaryop_svalue::maybe_fold_bits_within (tree type, break; } /* Otherwise, don't fold. */ - return NULL; + return nullptr; } /* class binop_svalue : public svalue. */ @@ -1841,7 +1837,7 @@ repeated_svalue::maybe_fold_bits_within (tree type, } } - return NULL; + return nullptr; } /* class bits_within_svalue : public svalue. */ @@ -2397,7 +2393,7 @@ compound_svalue::maybe_fold_bits_within (tree type, } else /* If we have any symbolic keys we can't get it as bits. */ - return NULL; + return nullptr; } return mgr->get_or_create_compound_svalue (type, result_map); } diff --git a/gcc/analyzer/svalue.h b/gcc/analyzer/svalue.h index bf9e12b..0ccb5ce 100644 --- a/gcc/analyzer/svalue.h +++ b/gcc/analyzer/svalue.h @@ -112,37 +112,37 @@ public: const char *prefix = nullptr) const; virtual const region_svalue * - dyn_cast_region_svalue () const { return NULL; } + dyn_cast_region_svalue () const { return nullptr; } virtual const constant_svalue * - dyn_cast_constant_svalue () const { return NULL; } + dyn_cast_constant_svalue () const { return nullptr; } virtual const poisoned_svalue * - dyn_cast_poisoned_svalue () const { return NULL; } + dyn_cast_poisoned_svalue () const { return nullptr; } virtual const setjmp_svalue * - dyn_cast_setjmp_svalue () const { return NULL; } + dyn_cast_setjmp_svalue () const { return nullptr; } virtual const initial_svalue * - dyn_cast_initial_svalue () const { return NULL; } + dyn_cast_initial_svalue () const { return nullptr; } virtual const unaryop_svalue * - dyn_cast_unaryop_svalue () const { return NULL; } + dyn_cast_unaryop_svalue () const { return nullptr; } virtual const binop_svalue * - dyn_cast_binop_svalue () const { return NULL; } + dyn_cast_binop_svalue () const { return nullptr; } virtual const sub_svalue * - dyn_cast_sub_svalue () const { return NULL; } + dyn_cast_sub_svalue () const { return nullptr; } virtual const repeated_svalue * - dyn_cast_repeated_svalue () const { return NULL; } + dyn_cast_repeated_svalue () const { return nullptr; } virtual const bits_within_svalue * - dyn_cast_bits_within_svalue () const { return NULL; } + dyn_cast_bits_within_svalue () const { return nullptr; } virtual const unmergeable_svalue * - dyn_cast_unmergeable_svalue () const { return NULL; } + dyn_cast_unmergeable_svalue () const { return nullptr; } virtual const widening_svalue * - dyn_cast_widening_svalue () const { return NULL; } + dyn_cast_widening_svalue () const { return nullptr; } virtual const compound_svalue * - dyn_cast_compound_svalue () const { return NULL; } + dyn_cast_compound_svalue () const { return nullptr; } virtual const conjured_svalue * - dyn_cast_conjured_svalue () const { return NULL; } + dyn_cast_conjured_svalue () const { return nullptr; } virtual const asm_output_svalue * - dyn_cast_asm_output_svalue () const { return NULL; } + dyn_cast_asm_output_svalue () const { return nullptr; } virtual const const_fn_result_svalue * - dyn_cast_const_fn_result_svalue () const { return NULL; } + dyn_cast_const_fn_result_svalue () const { return nullptr; } tree maybe_get_constant () const; const region *maybe_get_region () const; @@ -188,6 +188,8 @@ public: const region_model &model, const svalue *outer_sval = nullptr) const; + tree maybe_get_type_from_typeinfo () const; + protected: svalue (complexity c, symbol::id_t id, tree type) : symbol (c, id), m_type (type) @@ -244,7 +246,7 @@ public: : svalue (complexity (reg), id, type), m_reg (reg) { - gcc_assert (m_reg != NULL); + gcc_assert (m_reg != nullptr); } enum svalue_kind get_kind () const final override { return SK_REGION; } @@ -421,19 +423,19 @@ public: /* An enum describing a particular kind of "poisoned" value. */ -enum poison_kind +enum class poison_kind { /* For use to describe uninitialized memory. */ - POISON_KIND_UNINIT, + uninit, /* For use to describe freed memory. */ - POISON_KIND_FREED, + freed, /* For use to describe deleted memory. */ - POISON_KIND_DELETED, + deleted, /* For use on pointers to regions within popped stack frames. */ - POISON_KIND_POPPED_STACK + popped_stack }; extern const char *poison_kind_to_str (enum poison_kind); @@ -454,7 +456,7 @@ public: hashval_t hash () const { inchash::hash hstate; - hstate.add_int (m_kind); + hstate.add_int (static_cast<int> (m_kind)); hstate.add_ptr (m_type); return hstate.end (); } @@ -528,8 +530,8 @@ namespace ana { struct setjmp_record { setjmp_record (const exploded_node *enode, - const gcall *setjmp_call) - : m_enode (enode), m_setjmp_call (setjmp_call) + const gcall &setjmp_call) + : m_enode (enode), m_setjmp_call (&setjmp_call) { } @@ -549,6 +551,7 @@ struct setjmp_record const exploded_node *m_enode; const gcall *m_setjmp_call; + // non-null, but we can't use a reference since we're putting these in a hash_map }; /* Concrete subclass of svalue representing buffers for setjmp/sigsetjmp, @@ -647,7 +650,7 @@ public: initial_svalue (symbol::id_t id, tree type, const region *reg) : svalue (complexity (reg), id, type), m_reg (reg) { - gcc_assert (m_reg != NULL); + gcc_assert (m_reg != nullptr); } enum svalue_kind get_kind () const final override { return SK_INITIAL; } @@ -1544,15 +1547,15 @@ public: && m_idx == other.m_idx); } - /* Use m_stmt to mark empty/deleted, as m_type can be NULL for + /* Use m_stmt to mark empty/deleted, as m_type can be NULL_TREE for legitimate instances. */ void mark_deleted () { m_stmt = reinterpret_cast<const gimple *> (1); } - void mark_empty () { m_stmt = NULL; } + void mark_empty () { m_stmt = nullptr; } bool is_deleted () const { return m_stmt == reinterpret_cast<const gimple *> (1); } - bool is_empty () const { return m_stmt == NULL; } + bool is_empty () const { return m_stmt == nullptr; } tree m_type; const gimple *m_stmt; @@ -1565,7 +1568,7 @@ public: : svalue (complexity (id_reg), id, type), m_stmt (stmt), m_id_reg (id_reg), m_idx (idx) { - gcc_assert (m_stmt != NULL); + gcc_assert (m_stmt != nullptr); } enum svalue_kind get_kind () const final override { return SK_CONJURED; } @@ -1665,15 +1668,15 @@ public: return true; } - /* Use m_asm_string to mark empty/deleted, as m_type can be NULL for + /* Use m_asm_string to mark empty/deleted, as m_type can be NULL_TREE for legitimate instances. */ void mark_deleted () { m_asm_string = reinterpret_cast<const char *> (1); } - void mark_empty () { m_asm_string = NULL; } + void mark_empty () { m_asm_string = nullptr; } bool is_deleted () const { return m_asm_string == reinterpret_cast<const char *> (1); } - bool is_empty () const { return m_asm_string == NULL; } + bool is_empty () const { return m_asm_string == nullptr; } tree m_type; const char *m_asm_string; @@ -1810,12 +1813,12 @@ public: /* Use m_fndecl to mark empty/deleted. */ void mark_deleted () { m_fndecl = reinterpret_cast<tree> (1); } - void mark_empty () { m_fndecl = NULL; } + void mark_empty () { m_fndecl = NULL_TREE; } bool is_deleted () const { return m_fndecl == reinterpret_cast<tree> (1); } - bool is_empty () const { return m_fndecl == NULL; } + bool is_empty () const { return m_fndecl == NULL_TREE; } tree m_type; tree m_fndecl; diff --git a/gcc/analyzer/symbol.cc b/gcc/analyzer/symbol.cc index 068801a..8643284 100644 --- a/gcc/analyzer/symbol.cc +++ b/gcc/analyzer/symbol.cc @@ -18,11 +18,8 @@ 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/>. */ -#include "config.h" -#include "system.h" -#include "coretypes.h" -#include "tree.h" -#include "analyzer/analyzer.h" +#include "analyzer/common.h" + #include "analyzer/symbol.h" #if ENABLE_ANALYZER diff --git a/gcc/analyzer/trimmed-graph.cc b/gcc/analyzer/trimmed-graph.cc index 993084c..bdf378a 100644 --- a/gcc/analyzer/trimmed-graph.cc +++ b/gcc/analyzer/trimmed-graph.cc @@ -18,21 +18,8 @@ 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/>. */ -#include "config.h" -#define INCLUDE_VECTOR -#include "system.h" -#include "coretypes.h" -#include "tree.h" -#include "pretty-print.h" -#include "gcc-rich-location.h" -#include "gimple-pretty-print.h" -#include "function.h" -#include "diagnostic-core.h" -#include "diagnostic-event-id.h" -#include "diagnostic-path.h" -#include "bitmap.h" -#include "ordered-hash-map.h" -#include "analyzer/analyzer.h" +#include "analyzer/common.h" + #include "analyzer/analyzer-logging.h" #include "analyzer/sm.h" #include "analyzer/pending-diagnostic.h" diff --git a/gcc/analyzer/varargs.cc b/gcc/analyzer/varargs.cc index 0cacc9b..1fdfd34 100644 --- a/gcc/analyzer/varargs.cc +++ b/gcc/analyzer/varargs.cc @@ -18,18 +18,8 @@ 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/>. */ -#include "config.h" -#define INCLUDE_VECTOR -#include "system.h" -#include "coretypes.h" -#include "make-unique.h" -#include "tree.h" -#include "function.h" -#include "basic-block.h" -#include "gimple.h" -#include "diagnostic-core.h" -#include "diagnostic-path.h" -#include "analyzer/analyzer.h" +#include "analyzer/common.h" + #include "analyzer/analyzer-logging.h" #include "analyzer/sm.h" #include "analyzer/pending-diagnostic.h" @@ -167,10 +157,10 @@ get_va_list_diag_arg (tree va_list_tree) static const svalue * get_va_copy_arg (const region_model *model, region_model_context *ctxt, - const gcall *call, + const gcall &call, unsigned arg_idx) { - tree arg = gimple_call_arg (call, arg_idx); + tree arg = gimple_call_arg (&call, arg_idx); const svalue *arg_sval = model->get_rvalue (arg, ctxt); if (const svalue *cast = arg_sval->maybe_undo_cast ()) arg_sval = cast; @@ -215,7 +205,11 @@ public: { return s != m_started; } - std::unique_ptr<pending_diagnostic> on_leak (tree var) const final override; + + std::unique_ptr<pending_diagnostic> + on_leak (tree var, + const program_state *old_state, + const program_state *new_state) const final override; /* State for a va_list that is the result of a va_start or va_copy. */ state_t m_started; @@ -225,16 +219,16 @@ public: private: void on_va_start (sm_context &sm_ctxt, const supernode *node, - const gcall *call) const; + const gcall &call) const; void on_va_copy (sm_context &sm_ctxt, const supernode *node, - const gcall *call) const; + const gcall &call) const; void on_va_arg (sm_context &sm_ctxt, const supernode *node, - const gcall *call) const; + const gcall &call) const; void on_va_end (sm_context &sm_ctxt, const supernode *node, - const gcall *call) const; + const gcall &call) const; void check_for_ended_va_list (sm_context &sm_ctxt, const supernode *node, - const gcall *call, + const gcall &call, const svalue *arg, const char *usage_fnname) const; }; @@ -256,10 +250,12 @@ va_list_state_machine::on_stmt (sm_context &sm_ctxt, const supernode *node, const gimple *stmt) const { - if (const gcall *call = dyn_cast <const gcall *> (stmt)) + if (const gcall *call_stmt = dyn_cast <const gcall *> (stmt)) { - if (gimple_call_internal_p (call) - && gimple_call_internal_fn (call) == IFN_VA_ARG) + const gcall &call = *call_stmt; + + if (gimple_call_internal_p (call_stmt) + && gimple_call_internal_fn (call_stmt) == IFN_VA_ARG) { on_va_arg (sm_ctxt, node, call); return false; @@ -267,7 +263,7 @@ va_list_state_machine::on_stmt (sm_context &sm_ctxt, if (tree callee_fndecl = sm_ctxt.get_fndecl_for_call (call)) if (fndecl_built_in_p (callee_fndecl, BUILT_IN_NORMAL) - && gimple_builtin_call_types_compatible_p (call, callee_fndecl)) + && gimple_builtin_call_types_compatible_p (&call, callee_fndecl)) switch (DECL_UNCHECKED_FUNCTION_CODE (callee_fndecl)) { default: @@ -293,24 +289,24 @@ va_list_state_machine::on_stmt (sm_context &sm_ctxt, IDX to CALL. */ static const svalue * -get_stateful_arg (sm_context &sm_ctxt, const gcall *call, unsigned arg_idx) +get_stateful_arg (sm_context &sm_ctxt, const gcall &call, unsigned arg_idx) { - tree ap = gimple_call_arg (call, arg_idx); + tree ap = gimple_call_arg (&call, arg_idx); if (ap && POINTER_TYPE_P (TREE_TYPE (ap))) { if (const program_state *new_state = sm_ctxt.get_new_program_state ()) { const region_model *new_model = new_state->m_region_model; - const svalue *ptr_sval = new_model->get_rvalue (ap, NULL); - const region *reg = new_model->deref_rvalue (ptr_sval, ap, NULL); - const svalue *impl_sval = new_model->get_store_value (reg, NULL); + const svalue *ptr_sval = new_model->get_rvalue (ap, nullptr); + const region *reg = new_model->deref_rvalue (ptr_sval, ap, nullptr); + const svalue *impl_sval = new_model->get_store_value (reg, nullptr); if (const svalue *cast = impl_sval->maybe_undo_cast ()) impl_sval = cast; return impl_sval; } } - return NULL; + return nullptr; } /* Abstract class for diagnostics relating to va_list_state_machine. */ @@ -338,17 +334,17 @@ public: return false; } - diagnostic_event::meaning + diagnostics::paths::event::meaning get_meaning_for_state_change (const evdesc::state_change &change) const final override { if (change.m_new_state == m_sm.m_started) - return diagnostic_event::meaning (diagnostic_event::VERB_acquire, - diagnostic_event::NOUN_resource); + return diagnostics::paths::event::meaning (diagnostics::paths::event::verb::acquire, + diagnostics::paths::event::noun::resource); if (change.m_new_state == m_sm.m_ended) - return diagnostic_event::meaning (diagnostic_event::VERB_release, - diagnostic_event::NOUN_resource); - return diagnostic_event::meaning (); + return diagnostics::paths::event::meaning (diagnostics::paths::event::verb::release, + diagnostics::paths::event::noun::resource); + return diagnostics::paths::event::meaning (); } protected: @@ -374,7 +370,7 @@ protected: return "va_end"; } } - return NULL; + return nullptr; } const va_list_state_machine &m_sm; @@ -456,7 +452,7 @@ public: } private: - diagnostic_event_id_t m_va_end_event; + diagnostics::paths::event_id_t m_va_end_event; const char *m_usage_fnname; }; @@ -468,10 +464,14 @@ class va_list_leak : public va_list_sm_diagnostic { public: va_list_leak (const va_list_state_machine &sm, - const svalue *ap_sval, tree ap_tree) + const svalue *ap_sval, tree ap_tree, + const program_state *final_state) : va_list_sm_diagnostic (sm, ap_sval, ap_tree), - m_start_event_fnname (NULL) + m_start_event_fnname (nullptr), + m_final_state () { + if (final_state) + m_final_state = std::make_unique<program_state> (*final_state); } int get_controlling_option () const final override @@ -532,9 +532,16 @@ public: return true; } + const program_state * + get_final_state () const final override + { + return m_final_state.get (); + } + private: - diagnostic_event_id_t m_start_event; + diagnostics::paths::event_id_t m_start_event; const char *m_start_event_fnname; + std::unique_ptr<program_state> m_final_state; }; /* Update state machine for a "va_start" call. */ @@ -542,14 +549,14 @@ private: void va_list_state_machine::on_va_start (sm_context &sm_ctxt, const supernode *, - const gcall *call) const + const gcall &call) const { const svalue *arg = get_stateful_arg (sm_ctxt, call, 0); if (arg) { /* Transition from start state to "started". */ - if (sm_ctxt.get_state (call, arg) == m_start) - sm_ctxt.set_next_state (call, arg, m_started); + if (sm_ctxt.get_state (&call, arg) == m_start) + sm_ctxt.set_next_state (&call, arg, m_started); } } @@ -558,32 +565,32 @@ va_list_state_machine::on_va_start (sm_context &sm_ctxt, void va_list_state_machine::check_for_ended_va_list (sm_context &sm_ctxt, const supernode *node, - const gcall *call, + const gcall &call, const svalue *arg, const char *usage_fnname) const { - if (sm_ctxt.get_state (call, arg) == m_ended) - sm_ctxt.warn (node, call, arg, - make_unique<va_list_use_after_va_end> + if (sm_ctxt.get_state (&call, arg) == m_ended) + sm_ctxt.warn (node, &call, arg, + std::make_unique<va_list_use_after_va_end> (*this, arg, NULL_TREE, usage_fnname)); } /* Get the svalue with associated va_list_state_machine state for ARG_IDX of CALL to va_copy, if SM_CTXT supports this, - or NULL otherwise. */ + or nullptr otherwise. */ static const svalue * get_stateful_va_copy_arg (sm_context &sm_ctxt, - const gcall *call, + const gcall &call, unsigned arg_idx) { if (const program_state *new_state = sm_ctxt.get_new_program_state ()) { const region_model *new_model = new_state->m_region_model; - const svalue *arg = get_va_copy_arg (new_model, NULL, call, arg_idx); + const svalue *arg = get_va_copy_arg (new_model, nullptr, call, arg_idx); return arg; } - return NULL; + return nullptr; } /* Update state machine for a "va_copy" call. */ @@ -591,7 +598,7 @@ get_stateful_va_copy_arg (sm_context &sm_ctxt, void va_list_state_machine::on_va_copy (sm_context &sm_ctxt, const supernode *node, - const gcall *call) const + const gcall &call) const { const svalue *src_arg = get_stateful_va_copy_arg (sm_ctxt, call, 1); if (src_arg) @@ -601,8 +608,8 @@ va_list_state_machine::on_va_copy (sm_context &sm_ctxt, if (dst_arg) { /* Transition from start state to "started". */ - if (sm_ctxt.get_state (call, dst_arg) == m_start) - sm_ctxt.set_next_state (call, dst_arg, m_started); + if (sm_ctxt.get_state (&call, dst_arg) == m_start) + sm_ctxt.set_next_state (&call, dst_arg, m_started); } } @@ -611,7 +618,7 @@ va_list_state_machine::on_va_copy (sm_context &sm_ctxt, void va_list_state_machine::on_va_arg (sm_context &sm_ctxt, const supernode *node, - const gcall *call) const + const gcall &call) const { const svalue *arg = get_stateful_arg (sm_ctxt, call, 0); if (arg) @@ -623,15 +630,15 @@ va_list_state_machine::on_va_arg (sm_context &sm_ctxt, void va_list_state_machine::on_va_end (sm_context &sm_ctxt, const supernode *node, - const gcall *call) const + const gcall &call) const { const svalue *arg = get_stateful_arg (sm_ctxt, call, 0); if (arg) { - state_t s = sm_ctxt.get_state (call, arg); + state_t s = sm_ctxt.get_state (&call, arg); /* Transition from "started" to "ended". */ if (s == m_started) - sm_ctxt.set_next_state (call, arg, m_ended); + sm_ctxt.set_next_state (&call, arg, m_ended); else if (s == m_ended) check_for_ended_va_list (sm_ctxt, node, call, arg, "va_end"); } @@ -641,19 +648,21 @@ va_list_state_machine::on_va_end (sm_context &sm_ctxt, (for complaining about leaks of values in state 'started'). */ std::unique_ptr<pending_diagnostic> -va_list_state_machine::on_leak (tree var) const +va_list_state_machine::on_leak (tree var, + const program_state *, + const program_state *new_state) const { - return make_unique<va_list_leak> (*this, nullptr, var); + return std::make_unique<va_list_leak> (*this, nullptr, var, new_state); } } // anonymous namespace /* Internal interface to this file. */ -state_machine * +std::unique_ptr<state_machine> make_va_list_state_machine (logger *logger) { - return new va_list_state_machine (logger); + return std::make_unique<va_list_state_machine> (logger); } /* Handler for "__builtin_va_start". */ @@ -731,7 +740,7 @@ kf_va_copy::impl_call_pre (const call_details &cd) const in_va_list = model->check_for_poison (in_va_list, get_va_list_diag_arg (cd.get_arg_tree (1)), - NULL, + nullptr, cd.get_ctxt ()); const region *out_dst_reg @@ -757,13 +766,13 @@ kf_va_copy::impl_call_pre (const call_details &cd) const static int get_num_variadic_arguments (tree callee_fndecl, - const gcall *call_stmt) + const gcall &call_stmt) { int num_positional = 0; for (tree iter_parm = DECL_ARGUMENTS (callee_fndecl); iter_parm; iter_parm = DECL_CHAIN (iter_parm)) num_positional++; - return gimple_call_num_args (call_stmt) - num_positional; + return gimple_call_num_args (&call_stmt) - num_positional; } /* An abstract subclass of pending_diagnostic for diagnostics relating @@ -817,12 +826,12 @@ public: const program_point &src_point = src_node->get_point (); const int src_stack_depth = src_point.get_stack_depth (); const gimple *last_stmt = src_point.get_supernode ()->get_last_stmt (); - const gcall *call_stmt = as_a <const gcall *> (last_stmt); + const gcall &call_stmt = *as_a <const gcall *> (last_stmt); int num_variadic_arguments = get_num_variadic_arguments (dst_node->get_function ()->decl, call_stmt); emission_path->add_event - (make_unique<va_arg_call_event> + (std::make_unique<va_arg_call_event> (eedge, event_loc_info (last_stmt ? last_stmt->location : UNKNOWN_LOCATION, src_point.get_fndecl (), @@ -1011,14 +1020,14 @@ va_arg_compatible_types_p (tree lhs_type, tree arg_type, const svalue &arg_sval) } /* If AP_SVAL is a pointer to a var_arg_region, return that var_arg_region. - Otherwise return NULL. */ + Otherwise return nullptr. */ static const var_arg_region * maybe_get_var_arg_region (const svalue *ap_sval) { if (const region *reg = ap_sval->maybe_get_region ()) return reg->dyn_cast_var_arg_region (); - return NULL; + return nullptr; } /* Handler for "__builtin_va_arg". */ @@ -1078,7 +1087,7 @@ kf_va_arg::impl_call_pre (const call_details &cd) const else { if (ctxt) - ctxt->warn (make_unique <va_arg_type_mismatch> + ctxt->warn (std::make_unique <va_arg_type_mismatch> (va_list_tree, arg_reg, lhs_type, @@ -1089,8 +1098,9 @@ kf_va_arg::impl_call_pre (const call_details &cd) const else { if (ctxt) - ctxt->warn (make_unique <va_list_exhausted> (va_list_tree, - arg_reg)); + ctxt->warn + (std::make_unique <va_list_exhausted> (va_list_tree, + arg_reg)); saw_problem = true; } } @@ -1139,10 +1149,10 @@ public: void register_varargs_builtins (known_function_manager &kfm) { - kfm.add (BUILT_IN_VA_START, make_unique<kf_va_start> ()); - kfm.add (BUILT_IN_VA_COPY, make_unique<kf_va_copy> ()); - kfm.add (IFN_VA_ARG, make_unique<kf_va_arg> ()); - kfm.add (BUILT_IN_VA_END, make_unique<kf_va_end> ()); + kfm.add (BUILT_IN_VA_START, std::make_unique<kf_va_start> ()); + kfm.add (BUILT_IN_VA_COPY, std::make_unique<kf_va_copy> ()); + kfm.add (IFN_VA_ARG, std::make_unique<kf_va_arg> ()); + kfm.add (BUILT_IN_VA_END, std::make_unique<kf_va_end> ()); } } // namespace ana |