aboutsummaryrefslogtreecommitdiff
path: root/gcc/testsuite
diff options
context:
space:
mode:
authorDavid Malcolm <dmalcolm@redhat.com>2020-05-06 15:16:35 -0400
committerGiuliano Belinassi <giuliano.belinassi@usp.br>2020-08-17 15:08:11 -0300
commit594ee5c4fd3f671b3c9e4e4a8d70c19fa9cddaaf (patch)
tree6af0685fdc04e5ff8c696457c75b004928ec28b8 /gcc/testsuite
parente85ed167cdec4b14b11b107ea35628031a70cf51 (diff)
analyzer: rewrite of region and value-handling
This large patch reimplements how the analyzer tracks regions and values. Elimination of region_id and svalue_id ************************************** The patch eliminates region_id and svalue_id in favor of simply using pointers. I'd hoped that the ID classes would make it easier to compare states, avoiding having to compare long hexadecimal addresses in favor of small integers. Unfortunately it added lots of complexity, with the need to remap IDs when comparing or purging states, and the need to "canonicalize" when comparing states. Various "state explosion" bugs in the old implementation were due to failures in canonicalization, where two states that ought to be equal were non-equal due to differences in ID ordering. I spent a lot of time trying to fix canonicalization bugs, and there always seemed to be one more bug. By eliminating IDs in this new implementation, lots of tricky canonicalization goes away and no ID remapping should be needed; almost all of the old validation code becomes redundant. There's still some canonicalization in the new implementation, mostly in constraint_manager, but much less than before. Ownership of regions and svalues ******************************** In the old implementation, each region_model had its own copies of regions and svalues, so there was heap bloat and churn as lots of little objects were cloned when copying program_state instances. In the new implementation the regions and svalues are immutable and are shared thoughout the analysis, rather than being per region_model. They are owned by a manager class, and are effectively singletons. Region and svalue instances can now be compared by pointer rather than by comparing their fields (the manager class takes care of uniqueness). This is a huge simplification, and (I hope) will avoid lots of heap churn as states are copied; all mutable state from regions and svalues is now stored in a "store" class in the region_model. Changes to the meaning of a "region" ************************************ Region subclasses no longer represent internal structure, but instead represent how the regions are reached. So e.g. a global "struct coord c;" is now a decl_region, rather than a struct_region. In the old implementation, the values for each region were stored in the region instances, but in the new implementation the regions are immutable. Memory is now modeled in a new "store" class: a mapping from keys to svalues, where the keys are both concrete bit-offsets from the start of a "base region", and "symbolic" keys (thus hopefully making unions, casts, aliasing etc easier to deal with). So e.g. for assignments to the fields of a struct, it records the mapping from bit-offsets of e.g. field to the values; if that memory is cast to another type and written to, the appropriate clobbering of the bound values can happen. The concept of "what the current stack is" moves from the regions to being a field within the region_model ("m_current_frame"). Bugs fixed by this patch ************************ PR analyzer/93032 (missing leak diagnostic for zlib/contrib/minizip/mztools.c) PR analyzer/93938 (ICE in analyzer) PR analyzer/94011 (ICE in analyzer) PR analyzer/94099 (ICE in analyzer) PR analyzer/94399 (leak false positive with __attribute__((cleanup()))) PR analyzer/94458 (leak false positive) PR analyzer/94503 (ICE on C++ return-value-optimization) PR analyzer/94640 (leak false positive) PR analyzer/94688 (ICE in analyzer) PR analyzer/94689 ("arrays of functions are not meaningful" error) PR analyzer/94839 (leak false positive) PR analyzer/95026 (leak false positive) PR analyzer/95042 (ICE merging const and non-const C++ object instances) PR analyzer/95240 (leak false positive) gcc/ChangeLog: * Makefile.in (ANALYZER_OBJS): Add analyzer/region.o, analyzer/region-model-impl-calls.o, analyzer/region-model-manager.o, analyzer/region-model-reachability.o, analyzer/store.o, and analyzer/svalue.o. * doc/analyzer.texi: Update for changes to analyzer implementation. * tristate.h (tristate::get_value): New accessor. gcc/analyzer/ChangeLog: * analyzer-logging.cc: Ignore "-Wformat-diag". (logger::enter_scope): Use inc_indent in both overloads. (logger::exit_scope): Use dec_indent. * analyzer-logging.h (logger::inc_indent): New. (logger::dec_indent): New. * analyzer-selftests.cc (run_analyzer_selftests): Call analyzer_store_cc_tests. * analyzer-selftests.h (analyzer_store_cc_tests): New decl. * analyzer.cc (get_stmt_location): New function. * analyzer.h (class initial_svalue): New forward decl. (class unaryop_svalue): New forward decl. (class binop_svalue): New forward decl. (class sub_svalue): New forward decl. (class unmergeable_svalue): New forward decl. (class placeholder_svalue): New forward decl. (class widening_svalue): New forward decl. (class compound_svalue): New forward decl. (class conjured_svalue): New forward decl. (svalue_set): New typedef. (class map_region): Delete. (class array_region): Delete. (class frame_region): New forward decl. (class function_region): New forward decl. (class label_region): New forward decl. (class decl_region): New forward decl. (class element_region): New forward decl. (class offset_region): New forward decl. (class cast_region): New forward decl. (class field_region): New forward decl. (class string_region): New forward decl. (class region_model_manager): New forward decl. (class store_manager): New forward decl. (class store): New forward decl. (class call_details): New forward decl. (struct svalue_id_merger_mapping): Delete. (struct canonicalization): Delete. (class function_point): New forward decl. (class engine): New forward decl. (dump_tree): New function decl. (print_quoted_type): New function decl. (readability_comparator): New function decl. (tree_cmp): New function decl. (class path_var): Move here from region-model.h (bit_offset_t, bit_size_t, byte_size_t): New typedefs. (class region_offset): New class. (get_stmt_location): New decl. (struct member_function_hash_traits): New struct. (class consolidation_map): New class. Ignore "-Wformat-diag". * analyzer.opt (-param=analyzer-max-svalue-depth=): New param. (-param=analyzer-max-enodes-for-full-dump=): New param. * call-string.cc: Ignore -Wformat-diag. * checker-path.cc: Move includes of "analyzer/call-string.h" and "analyzer/program-point.h" to before "analyzer/region-model.h", and also include "analyzer/store.h" before it. (state_change_event::state_change_event): Replace "tree var" param with "const svalue *sval". Convert "origin" param from tree to "const svalue *". (state_change_event::get_desc): Call get_representative_tree to convert the var and origin from const svalue * to tree. Use svalue::get_desc rather than %qE when describing state changes. (checker_path::add_final_event): Use get_stmt_location. * checker-path.h (state_change_event::state_change_event): Port from tree to const svalue *. (state_change_event::get_lvalue): Delete. (state_change_event::get_dest_function): New. (state_change_event::m_var): Replace with... (state_change_event::m_sval): ...this. (state_change_event::m_origin): Convert from tree to const svalue *. * constraint-manager.cc: Include "analyzer/call-string.h", "analyzer/program-point.h", and "analyzer/store.h" before "analyzer/region-model.h". (struct bound, struct range): Move to constraint-manager.h. (compare_constants): New function. (range::dump): Rename to... (range::dump_to_pp): ...this. Support NULL constants. (range::dump): Reintroduce for dumping to stderr. (range::constrained_to_single_element): Return result, rather than writing to *OUT. (range::eval_condition): New. (range::below_lower_bound): New. (range::above_upper_bound): New. (equiv_class::equiv_class): Port from svalue_id to const svalue *. (equiv_class::print): Likewise. (equiv_class::hash): Likewise. (equiv_class::operator==): Port from svalue_id to const svalue *. (equiv_class::add): Port from svalue_id to const svalue *. Drop "cm" param. (equiv_class::del): Port from svalue_id to const svalue *. (equiv_class::get_representative): Likewise. (equiv_class::remap_svalue_ids): Delete. (svalue_id_cmp_by_id): Rename to... (svalue_cmp_by_ptr): ...this, porting from svalue_id to const svalue *. (equiv_class::canonicalize): Update qsort comparator. (constraint::implied_by): New. (constraint_manager::constraint_manager): Copy m_mgr in copy ctor. (constraint_manager::dump_to_pp): Add "multiline" param (constraint_manager::dump): Pass "true" for "multiline". (constraint_manager::add_constraint): Port from svalue_id to const svalue *. Split out second part into... (constraint_manager::add_unknown_constraint): ...this new function. Remove self-constraints when merging equivalence classes. (constraint_manager::add_constraint_internal): Remove constraints that would be implied by the new constraint. Port from svalue_id to const svalue *. (constraint_manager::get_equiv_class_by_sid): Rename to... (constraint_manager::get_equiv_class_by_svalue): ...this, porting from svalue_id to const svalue *. (constraint_manager::get_or_add_equiv_class): Port from svalue_id to const svalue *. (constraint_manager::eval_condition): Make const. Call compare_constants and return early if it provides a known result. (constraint_manager::get_ec_bounds): New. (constraint_manager::eval_condition): New overloads. Make existing one const, and use compare_constants. (constraint_manager::purge): Convert "p" param to a template rather that an abstract base class. Port from svalue_id to const svalue *. (class dead_svalue_purger): New class. (constraint_manager::remap_svalue_ids): Delete. (constraint_manager::on_liveness_change): New. (equiv_class_cmp): Port from svalue_id to const svalue *. (constraint_manager::canonicalize): Likewise. Combine with purging of redundant equivalence classes and constraints. (class cleaned_constraint_manager): Delete. (class merger_fact_visitor): Make "m_cm_b" const. Add "m_merger" field. (merger_fact_visitor::fact): Port from svalue_id to const svalue *. Add special case for widening. (constraint_manager::merge): Port from svalue_id to const svalue *. (constraint_manager::clean_merger_input): Delete. (constraint_manager::for_each_fact): Port from svalue_id to const svalue *. (constraint_manager::validate): Likewise. (selftest::test_constraint_conditions): Provide a region_model_manager when creating region_model instances. Add test for self-equality not creating equivalence classes. (selftest::test_transitivity): Provide a region_model_manager when creating region_model instances. Verify that EC-merging happens when constraints are implied. (selftest::test_constant_comparisons): Provide a region_model_manager when creating region_model instances. (selftest::test_constraint_impl): Likewise. Remove over-specified assertions. (selftest::test_equality): Provide a region_model_manager when creating region_model instances. (selftest::test_many_constants): Likewise. Provide a program_point when testing merging. (selftest::run_constraint_manager_tests): Move call to test_constant_comparisons to outside the transitivity guard. * constraint-manager.h (struct bound): Move here from constraint-manager.cc. (struct range): Likewise. (struct::eval_condition): New decl. (struct::below_lower_bound): New decl. (struct::above_upper_bound): New decl. (equiv_class::add): Port from svalue_id to const svalue *. (equiv_class::del): Likewise. (equiv_class::get_representative): Likewise. (equiv_class::remap_svalue_ids): Drop. (equiv_class::m_cst_sid): Convert to.. (equiv_class::m_cst_sval): ...this. (equiv_class::m_vars): Port from svalue_id to const svalue *. (constraint::bool implied_by): New decl. (fact_visitor::on_fact): Port from svalue_id to const svalue *. (constraint_manager::constraint_manager): Add mgr param. (constraint_manager::clone): Delete. (constraint_manager::maybe_get_constant): Delete. (constraint_manager::get_sid_for_constant): Delete. (constraint_manager::get_num_svalues): Delete. (constraint_manager::dump_to_pp): Add "multiline" param. (constraint_manager::get_equiv_class): Port from svalue_id to const svalue *. (constraint_manager::add_constraint): Likewise. (constraint_manager::get_equiv_class_by_sid): Rename to... (constraint_manager::get_equiv_class_by_svalue): ...this, porting from svalue_id to const svalue *. (constraint_manager::add_unknown_constraint): New decl. (constraint_manager::get_or_add_equiv_class): Port from svalue_id to const svalue *. (constraint_manager::eval_condition): Likewise. Add overloads. (constraint_manager::get_ec_bounds): New decl. (constraint_manager::purge): Convert to template. (constraint_manager::remap_svalue_ids): Delete. (constraint_manager::on_liveness_change): New decl. (constraint_manager::canonicalize): Drop param. (constraint_manager::clean_merger_input): Delete. (constraint_manager::m_mgr): New field. * diagnostic-manager.cc: Move includes of "analyzer/call-string.h" and "analyzer/program-point.h" to before "analyzer/region-model.h", and also include "analyzer/store.h" before it. (saved_diagnostic::saved_diagnostic): Add "sval" param. (diagnostic_manager::diagnostic_manager): Add engine param. (diagnostic_manager::add_diagnostic): Add "sval" param, passing it to saved_diagnostic ctor. Update overload to pass NULL for it. (dedupe_winners::dedupe_winners): Add engine param. (dedupe_winners::add): Add "eg" param. Pass m_engine to feasible_p. (dedupe_winner::m_engine): New field. (diagnostic_manager::emit_saved_diagnostics): Pass engine to dedupe_winners. Pass &eg when adding candidates. Pass svalue rather than tree to prune_path. Use get_stmt_location to get primary location of diagnostic. (diagnostic_manager::emit_saved_diagnostic): Likewise. (get_any_origin): Drop. (state_change_event_creator::on_global_state_change): Pass NULL const svalue * rather than NULL_TREE trees to state_change_event ctor. (state_change_event_creator::on_state_change): Port from tree and svalue_id to const svalue *. (for_each_state_change): Port from svalue_id to const svalue *. (struct null_assignment_sm_context): New. (diagnostic_manager::add_events_for_eedge): Add state change events for assignment to NULL. (diagnostic_manager::prune_path): Update param from tree to const svalue *. (diagnostic_manager::prune_for_sm_diagnostic): Port from tracking by tree to by const svalue *. * diagnostic-manager.h (saved_diagnostic::saved_diagnostic): Add sval param. (saved_diagnostic::m_sval): New field. (diagnostic_manager::diagnostic_manager): Add engine param. (diagnostic_manager::get_engine): New. (diagnostic_manager::add_diagnostic): Add "sval" param. (diagnostic_manager::prune_path): Likewise. (diagnostic_manager::prune_for_sm_diagnostic): New overload. (diagnostic_manager::m_eng): New field. * engine.cc: Move includes of "analyzer/call-string.h" and "analyzer/program-point.h" to before "analyzer/region-model.h", and also include "analyzer/store.h" before it. (impl_region_model_context::impl_region_model_context): Update for removal of m_change field. (impl_region_model_context::remap_svalue_ids): Delete. (impl_region_model_context::on_svalue_leak): New. (impl_region_model_context::on_svalue_purge): Delete. (impl_region_model_context::on_liveness_change): New. (impl_region_model_context::on_unknown_change): Update param from svalue_id to const svalue *. Add is_mutable param. (setjmp_svalue::compare_fields): Delete. (setjmp_svalue::accept): New. (setjmp_svalue::add_to_hash): Delete. (setjmp_svalue::dump_to_pp): New. (setjmp_svalue::print_details): Delete. (impl_sm_context::impl_sm_context): Drop "change" param. (impl_sm_context::get_fndecl_for_call): Drop "m_change". (impl_sm_context::on_transition): Drop ATTRIBUTE_UNUSED from "stmt" param. Drop m_change. Port from svalue_id to const svalue *. (impl_sm_context::warn_for_state): Drop m_change. Port from svalue_id to const svalue *. (impl_sm_context::get_readable_tree): Rename to... (impl_sm_context::get_diagnostic_tree): ...this. Port from svalue_id to const svalue *. (impl_sm_context::is_zero_assignment): New. (impl_sm_context::m_change): Delete field. (leak_stmt_finder::find_stmt): Handle m_var being NULL. (readability): Increase penalty for MEM_REF. For SSA_NAMEs, slightly favor the underlying var over the SSA name. Heavily penalize temporaries. Handle RESULT_DECL. (readability_comparator): Make non-static. Consider stack depths. (impl_region_model_context::on_state_leak): Convert from svalue_id to const svalue *, updating for region_model changes. Use id_equal. (impl_region_model_context::on_inherited_svalue): Delete. (impl_region_model_context::on_cast): Delete. (impl_region_model_context::on_condition): Drop m_change. (impl_region_model_context::on_phi): Likewise. (impl_region_model_context::on_unexpected_tree_code): Handle t being NULL. (point_and_state::validate): Update stack checking for region_model changes. (eg_traits::dump_args_t::show_enode_details_p): New. (exploded_node::exploded_node): Initialize m_num_processed_stmts. (exploded_node::get_processed_stmt): New function. (exploded_node::get_dot_fillcolor): Add more colors. (exploded_node::dump_dot): Guard the printing of the point and state with show_enode_details_p. Print the processed stmts for this enode after the initial state. (exploded_node::dump_to_pp): Pass true for new multiline param of program_state::dump_to_pp. (exploded_node::on_stmt): Drop "change" param. Log the stmt. Set input_location. Implement __analyzer_describe. Update implementation of __analyzer_dump and __analyzer_eval. Remove purging of sm-state for unknown fncalls from here. (exploded_node::on_edge): Drop "change" param. (exploded_node::on_longjmp): Port from region_id/svalue_id to const region */const svalue *. Call program_state::detect_leaks. Drop state_change. (exploded_node::detect_leaks): Update for changes to region_model. Call program_state::detect_leaks. (exploded_edge::exploded_edge): Drop ext_state and change params. (exploded_edge::dump_dot): "args" is no longer used. Drop dumping of m_change. (exploded_graph::exploded_graph): Pass engine to m_diagnostic_manager ctor. Use program_point::origin. (exploded_graph::add_function_entry): Drop ctxt. Use program_state::push_frame. Drop state_change. (exploded_graph::get_or_create_node): Drop "change" param. Add "enode_for_diag" param. Update dumping calls for API changes. Pass point to can_merge_with_p. Show enode indices within -Wanalyzer-too-complex diagnostic for hitting the per-point limit. (exploded_graph::add_edge): Drop "change" param. Log which nodes are being connected. Update for changes to exploded_edge ctor. (exploded_graph::get_per_program_point_data): New. (exploded_graph::process_worklist): Pass point to can_merge_with_p. Drop state_change. Update dumping call for API change. (exploded_graph::process_node): Drop state_change. Split the node in-place if an sm-state-change occurs. Update m_num_processed_stmts. Update dumping calls for API change. (exploded_graph::log_stats): Call engine::log_stats. (exploded_graph::dump_states_for_supernode): Update dumping call. (exploded_path::feasible_p): Add "eng" and "eg" params. Rename "i" to "end_idx". Pass the manager to the region_model ctor. Update for every processed stmt in the enode, not just the first. Keep track of which snodes have been visited, and call loop_replay_fixup when revisiting one. (enode_label::get_text): Update dump call for new param. (exploded_graph::dump_exploded_nodes): Likewise. (exploded_graph::get_node_by_index): New. (impl_run_checkers): Create engine instance and pass its address to extrinsic_state ctor. * exploded-graph.h (impl_region_model_context::impl_region_model_context): Drop "change" params. (impl_region_model_context::void remap_svalue_ids): Delete. (impl_region_model_context::on_svalue_purge): Delete. (impl_region_model_context::on_svalue_leak): New. (impl_region_model_context::on_liveness_change): New. (impl_region_model_context::on_state_leak): Update signature. (impl_region_model_context::on_inherited_svalue): Delete. (impl_region_model_context::on_cast): Delete. (impl_region_model_context::on_unknown_change): Update signature. (impl_region_model_context::m_change): Delete. (eg_traits::dump_args_t::show_enode_details_p): New. (exploded_node::on_stmt): Drop "change" param. (exploded_node::on_edge): Likewise. (exploded_node::get_processed_stmt): New decl. (exploded_node::m_num_processed_stmts): New field. (exploded_edge::exploded_edge): Drop ext_state and change params. (exploded_edge::m_change): Delete. (exploded_graph::get_engine): New accessor. (exploded_graph::get_or_create_node): Drop "change" param. Add "enode_for_diag" param. (exploded_graph::add_edge): Drop "change" param. (exploded_graph::get_per_program_point_data): New decl. (exploded_graph::get_node_by_index): New decl. (exploded_path::feasible_p): Add "eng" and "eg" params. * program-point.cc: Include "analyzer/store.h" before including "analyzer/region-model.h". (function_point::function_point): Move here from program-point.h. (function_point::get_function): Likewise. (function_point::from_function_entry): Likewise. (function_point::before_supernode): Likewise. (function_point::next_stmt): New function. * program-point.h (function_point::function_point): Move implementation from here to program-point.cc. (function_point::get_function): Likewise. (function_point::from_function_entry): Likewise. (function_point::before_supernode): Likewise. (function_point::next_stmt): New decl. (program_point::operator!=): New. (program_point::origin): New. (program_point::next_stmt): New. (program_point::m_function_point): Make non-const. * program-state.cc: Move includes of "analyzer/call-string.h" and "analyzer/program-point.h" to before "analyzer/region-model.h", and also include "analyzer/store.h" before it. (extrinsic_state::get_model_manager): New. (sm_state_map::sm_state_map): Pass in sm and sm_idx to ctor, rather than pass the around. (sm_state_map::clone_with_remapping): Delete. (sm_state_map::print): Remove "sm" param in favor of "m_sm". Add "simple" and "multiline" params and support multiline vs single line dumping. (sm_state_map::dump): Remove "sm" param in favor of "m_sm". Add "simple" param. (sm_state_map::hash): Port from svalue_id to const svalue *. (sm_state_map::operator==): Likewise. (sm_state_map::get_state): Likewise. Call canonicalize_svalue on input. Handle inheritance of sm-state. Call get_default_state. (sm_state_map::get_origin): Port from svalue_id to const svalue *. (sm_state_map::set_state): Likewise. Pass in ext_state. Reject attempts to set state on UNKNOWN. (sm_state_map::impl_set_state): Port from svalue_id to const svalue *. Pass in ext_state. Call canonicalize_svalue on input. (sm_state_map::purge_for_unknown_fncall): Delete. (sm_state_map::on_svalue_leak): New. (sm_state_map::remap_svalue_ids): Delete. (sm_state_map::on_liveness_change): New. (sm_state_map::on_unknown_change): Reimplement. (sm_state_map::on_svalue_purge): Delete. (sm_state_map::on_inherited_svalue): Delete. (sm_state_map::on_cast): Delete. (sm_state_map::validate): Delete. (sm_state_map::canonicalize_svalue): New. (program_state::program_state): Update to pass manager to region_model's ctor. Constify num_states and pass state machine and index to sm_state_map ctor. (program_state::print): Update for changes to dump API. (program_state::dump_to_pp): Ignore the summarize param. Add "multiline" param. (program_state::dump_to_file): Add "multiline" param. (program_state::dump): Pass "true" for new "multiline" param. (program_state::push_frame): New. (program_state::on_edge): Drop "change" param. Call program_state::detect_leaks. (program_state::prune_for_point): Add enode_for_diag param. Reimplement based on store class. Call detect_leaks (program_state::remap_svalue_ids): Delete. (program_state::get_representative_tree): Port from svalue_id to const svalue *. (program_state::can_merge_with_p): Add "point" param. Add early reject for sm-differences. Drop id remapping. (program_state::validate): Drop region model and sm_state_map validation. (state_change::sm_change::dump): Delete. (state_change::sm_change::remap_svalue_ids): Delete. (state_change::sm_change::on_svalue_purge): Delete. (log_set_of_svalues): New. (state_change::sm_change::validate): Delete. (state_change::state_change): Delete. (state_change::add_sm_change): Delete. (state_change::affects_p): Delete. (state_change::dump): Delete. (state_change::remap_svalue_ids): Delete. (state_change::on_svalue_purge): Delete. (state_change::validate): Delete. (selftest::assert_dump_eq): Delete. (ASSERT_DUMP_EQ): Delete. (selftest::test_sm_state_map): Update for changes to region_model and sm_state_map, porting from svalue_id to const svalue *. (selftest::test_program_state_dumping): Likewise. Drop test of dumping, renaming to... (selftest::test_program_state_1): ...this. (selftest::test_program_state_dumping_2): Likewise, renaming to... (selftest::test_program_state_2): ...this. (selftest::test_program_state_merging): Update for changes to region_model. (selftest::test_program_state_merging_2): Likewise. (selftest::analyzer_program_state_cc_tests): Update for renamed tests. * program-state.h (extrinsic_state::extrinsic_state): Add logger and engine params. (extrinsic_state::get_logger): New accessor. (extrinsic_state::get_engine): New accessor. (extrinsic_state::get_model_manager): New accessor. (extrinsic_state::m_logger): New field. (extrinsic_state::m_engine): New field. (struct default_hash_traits<svalue_id>): Delete. (pod_hash_traits<svalue_id>::hash): Delete. (pod_hash_traits<svalue_id>::equal): Delete. (pod_hash_traits<svalue_id>::mark_deleted): Delete. (pod_hash_traits<svalue_id>::mark_empty): Delete. (pod_hash_traits<svalue_id>::is_deleted): Delete. (pod_hash_traits<svalue_id>::is_empty): Delete. (sm_state_map::entry_t::entry_t): Port from svalue_id to const svalue *. (sm_state_map::entry_t::m_origin): Likewise. (sm_state_map::map_t): Likewise. (sm_state_map::sm_state_map): Add state_machine and index params. (sm_state_map::clone_with_remapping): Delete. (sm_state_map::print): Drop sm param; add simple and multiline params. (sm_state_map::dump): Drop sm param; add simple param. (sm_state_map::get_state): Port from svalue_id to const svalue *. Add ext_state param. (sm_state_map::get_origin): Likewise. (sm_state_map::set_state): Likewise. (sm_state_map::impl_set_state): Likewise. (sm_state_map::purge_for_unknown_fncall): Delete. (sm_state_map::remap_svalue_ids): Delete. (sm_state_map::on_svalue_purge): Delete. (sm_state_map::on_svalue_leak): New. (sm_state_map::on_liveness_change): New. (sm_state_map::on_inherited_svalue): Delete. (sm_state_map::on_cast): Delete. (sm_state_map::validate): Delete. (sm_state_map::on_unknown_change): Port from svalue_id to const svalue *. Add is_mutable and ext_state params. (sm_state_map::canonicalize_svalue): New. (sm_state_map::m_sm): New field. (sm_state_map::m_sm_idx): New field. (program_state::operator=): Delete. (program_state::dump_to_pp): Drop "summarize" param, adding "simple" and "multiline". (program_state::dump_to_file): Likewise. (program_state::dump): Rename "summarize" to "simple". (program_state::push_frame): New. (program_state::get_current_function): New. (program_state::on_edge): Drop "change" param. (program_state::prune_for_point): Likewise. Add enode_for_diag param. (program_state::remap_svalue_ids): Delete. (program_state::get_representative_tree): Port from svalue_id to const svalue *. (program_state::can_purge_p): Likewise. Pass ext_state to get_state. (program_state::can_merge_with_p): Add point param. (program_state::detect_leaks): New. (state_change_visitor::on_state_change): Port from tree and svalue_id to a pair of const svalue *. (class state_change): Delete. * region.cc: New file. * region-model-impl-calls.cc: New file. * region-model-manager.cc: New file. * region-model-reachability.cc: New file. * region-model-reachability.h: New file. * region-model.cc: Include "analyzer/call-string.h", "analyzer/program-point.h", and "analyzer/store.h" before "analyzer/region-model.h". Include "analyzer/region-model-reachability.h". (dump_tree): Make non-static. (dump_quoted_tree): Make non-static. (print_quoted_type): Make non-static. (path_var::dump): Delete. (dump_separator): Delete. (class impl_constraint_manager): Delete. (svalue_id::print): Delete. (svalue_id::dump_node_name_to_pp): Delete. (svalue_id::validate): Delete. (region_id::print): Delete. (region_id::dump_node_name_to_pp): Delete. (region_id::validate): Delete. (region_id_set::region_id_set): Delete. (svalue_id_set::svalue_id_set): Delete. (svalue::operator==): Delete. (svalue::hash): Delete. (svalue::print): Delete. (svalue::dump_dot_to_pp): Delete. (svalue::remap_region_ids): Delete. (svalue::walk_for_canonicalization): Delete. (svalue::get_child_sid): Delete. (svalue::maybe_get_constant): Delete. (region_svalue::compare_fields): Delete. (region_svalue::add_to_hash): Delete. (region_svalue::print_details): Delete. (region_svalue::dump_dot_to_pp): Delete. (region_svalue::remap_region_ids): Delete. (region_svalue::merge_values): Delete. (region_svalue::walk_for_canonicalization): Delete. (region_svalue::eval_condition): Delete. (constant_svalue::compare_fields): Delete. (constant_svalue::add_to_hash): Delete. (constant_svalue::merge_values): Delete. (constant_svalue::eval_condition): Move to svalue.cc. (constant_svalue::print_details): Delete. (constant_svalue::get_child_sid): Delete. (unknown_svalue::compare_fields): Delete. (unknown_svalue::add_to_hash): Delete. (unknown_svalue::print_details): Delete. (poison_kind_to_str): Move to svalue.cc. (poisoned_svalue::compare_fields): Delete. (poisoned_svalue::add_to_hash): Delete. (poisoned_svalue::print_details): Delete. (region_kind_to_str): Move to region.cc and reimplement. (region::operator==): Delete. (region::get_parent_region): Delete. (region::set_value): Delete. (region::become_active_view): Delete. (region::deactivate_any_active_view): Delete. (region::deactivate_view): Delete. (region::get_value): Delete. (region::get_inherited_child_sid): Delete. (region_model::copy_region): Delete. (region_model::copy_struct_region): Delete. (region_model::copy_union_region): Delete. (region_model::copy_array_region): Delete. (region::hash): Delete. (region::print): Delete. (region::dump_dot_to_pp): Delete. (region::dump_to_pp): Delete. (region::dump_child_label): Delete. (region::validate): Delete. (region::remap_svalue_ids): Delete. (region::remap_region_ids): Delete. (region::add_view): Delete. (region::get_view): Delete. (region::region): Move to region.cc. (region::add_to_hash): Delete. (region::print_fields): Delete. (region::non_null_p): Delete. (primitive_region::clone): Delete. (primitive_region::walk_for_canonicalization): Delete. (map_region::map_region): Delete. (map_region::compare_fields): Delete. (map_region::print_fields): Delete. (map_region::validate): Delete. (map_region::dump_dot_to_pp): Delete. (map_region::dump_child_label): Delete. (map_region::get_or_create): Delete. (map_region::get): Delete. (map_region::add_to_hash): Delete. (map_region::remap_region_ids): Delete. (map_region::unbind): Delete. (map_region::get_tree_for_child_region): Delete. (map_region::get_tree_for_child_region): Delete. (tree_cmp): Move to region.cc. (map_region::can_merge_p): Delete. (map_region::walk_for_canonicalization): Delete. (map_region::get_value_by_name): Delete. (struct_or_union_region::valid_key_p): Delete. (struct_or_union_region::compare_fields): Delete. (struct_region::clone): Delete. (struct_region::compare_fields): Delete. (union_region::clone): Delete. (union_region::compare_fields): Delete. (frame_region::compare_fields): Delete. (frame_region::clone): Delete. (frame_region::valid_key_p): Delete. (frame_region::print_fields): Delete. (frame_region::add_to_hash): Delete. (globals_region::compare_fields): Delete. (globals_region::clone): Delete. (globals_region::valid_key_p): Delete. (code_region::compare_fields): Delete. (code_region::clone): Delete. (code_region::valid_key_p): Delete. (array_region::array_region): Delete. (array_region::get_element): Delete. (array_region::clone): Delete. (array_region::compare_fields): Delete. (array_region::print_fields): Delete. (array_region::validate): Delete. (array_region::dump_dot_to_pp): Delete. (array_region::dump_child_label): Delete. (array_region::get_or_create): Delete. (array_region::get): Delete. (array_region::add_to_hash): Delete. (array_region::remap_region_ids): Delete. (array_region::get_key_for_child_region): Delete. (array_region::key_cmp): Delete. (array_region::walk_for_canonicalization): Delete. (array_region::key_from_constant): Delete. (array_region::constant_from_key): Delete. (function_region::compare_fields): Delete. (function_region::clone): Delete. (function_region::valid_key_p): Delete. (stack_region::stack_region): Delete. (stack_region::compare_fields): Delete. (stack_region::clone): Delete. (stack_region::print_fields): Delete. (stack_region::dump_child_label): Delete. (stack_region::validate): Delete. (stack_region::push_frame): Delete. (stack_region::get_current_frame_id): Delete. (stack_region::pop_frame): Delete. (stack_region::add_to_hash): Delete. (stack_region::remap_region_ids): Delete. (stack_region::can_merge_p): Delete. (stack_region::walk_for_canonicalization): Delete. (stack_region::get_value_by_name): Delete. (heap_region::heap_region): Delete. (heap_region::compare_fields): Delete. (heap_region::clone): Delete. (heap_region::walk_for_canonicalization): Delete. (root_region::root_region): Delete. (root_region::compare_fields): Delete. (root_region::clone): Delete. (root_region::print_fields): Delete. (root_region::validate): Delete. (root_region::dump_child_label): Delete. (root_region::push_frame): Delete. (root_region::get_current_frame_id): Delete. (root_region::pop_frame): Delete. (root_region::ensure_stack_region): Delete. (root_region::get_stack_region): Delete. (root_region::ensure_globals_region): Delete. (root_region::get_code_region): Delete. (root_region::ensure_code_region): Delete. (root_region::get_globals_region): Delete. (root_region::ensure_heap_region): Delete. (root_region::get_heap_region): Delete. (root_region::remap_region_ids): Delete. (root_region::can_merge_p): Delete. (root_region::add_to_hash): Delete. (root_region::walk_for_canonicalization): Delete. (root_region::get_value_by_name): Delete. (symbolic_region::symbolic_region): Delete. (symbolic_region::compare_fields): Delete. (symbolic_region::clone): Delete. (symbolic_region::walk_for_canonicalization): Delete. (symbolic_region::print_fields): Delete. (region_model::region_model): Add region_model_manager * param. Reimplement in terms of store, dropping impl_constraint_manager subclass. (region_model::operator=): Reimplement in terms of store (region_model::operator==): Likewise. (region_model::hash): Likewise. (region_model::print): Delete. (region_model::print_svalue): Delete. (region_model::dump_dot_to_pp): Delete. (region_model::dump_dot_to_file): Delete. (region_model::dump_dot): Delete. (region_model::dump_to_pp): Replace "summarize" param with "simple" and "multiline". Port to store-based implementation. (region_model::dump): Replace "summarize" param with "simple" and "multiline". (dump_vec_of_tree): Delete. (region_model::dump_summary_of_rep_path_vars): Delete. (region_model::validate): Delete. (svalue_id_cmp_by_constant_svalue_model): Delete. (svalue_id_cmp_by_constant_svalue): Delete. (region_model::canonicalize): Drop "ctxt" param. Reimplement in terms of store and constraints. (region_model::canonicalized_p): Remove NULL arg to canonicalize. (region_model::loop_replay_fixup): New. (poisoned_value_diagnostic::emit): Tweak wording of warnings. (region_model::check_for_poison): Delete. (region_model::get_gassign_result): New. (region_model::on_assignment): Port to store-based implementation. (region_model::on_call_pre): Delete calls to check_for_poison. Move implementations to region-model-impl-calls.c and port to store-based implementation. (region_model::on_call_post): Likewise. (class reachable_regions): Move to region-model-reachability.h/cc and port to store-based implementation. (region_model::handle_unrecognized_call): Port to store-based implementation. (region_model::get_reachable_svalues): New. (region_model::on_setjmp): Port to store-based implementation. (region_model::on_longjmp): Likewise. (region_model::handle_phi): Drop is_back_edge param and the logic using it. (region_model::get_lvalue_1): Port from region_id to const region *. (region_model::make_region_for_unexpected_tree_code): Delete. (assert_compat_types): If the check fails, use internal_error to show the types. (region_model::get_lvalue): Port from region_id to const region *. (region_model::get_rvalue_1): Port from svalue_id to const svalue *. (region_model::get_rvalue): Likewise. (region_model::get_or_create_ptr_svalue): Delete. (region_model::get_or_create_constant_svalue): Delete. (region_model::get_svalue_for_fndecl): Delete. (region_model::get_region_for_fndecl): Delete. (region_model::get_svalue_for_label): Delete. (region_model::get_region_for_label): Delete. (build_cast): Delete. (region_model::maybe_cast_1): Delete. (region_model::maybe_cast): Delete. (region_model::get_field_region): Delete. (region_model::get_store_value): New. (region_model::region_exists_p): New. (region_model::deref_rvalue): Port from svalue_id to const svalue *. (region_model::set_value): Likewise. (region_model::clobber_region): New. (region_model::purge_region): New. (region_model::zero_fill_region): New. (region_model::mark_region_as_unknown): New. (region_model::eval_condition): Port from svalue_id to const svalue *. (region_model::eval_condition_without_cm): Likewise. (region_model::compare_initial_and_pointer): New. (region_model::add_constraint): Port from svalue_id to const svalue *. (region_model::maybe_get_constant): Delete. (region_model::get_representative_path_var): New. (region_model::add_new_malloc_region): Delete. (region_model::get_representative_tree): Port to const svalue *. (region_model::get_representative_path_var): Port to const region *. (region_model::get_path_vars_for_svalue): Delete. (region_model::set_to_new_unknown_value): Delete. (region_model::update_for_phis): Don't pass is_back_edge to handle_phi. (region_model::update_for_call_superedge): Port from svalue_id to const svalue *. (region_model::update_for_return_superedge): Port to store-based implementation. (region_model::update_for_call_summary): Replace set_to_new_unknown_value with mark_region_as_unknown. (region_model::get_root_region): Delete. (region_model::get_stack_region_id): Delete. (region_model::push_frame): Delete. (region_model::get_current_frame_id): Delete. (region_model::get_current_function): Delete. (region_model::pop_frame): Delete. (region_model::on_top_level_param): New. (region_model::get_stack_depth): Delete. (region_model::get_function_at_depth): Delete. (region_model::get_globals_region_id): Delete. (region_model::add_svalue): Delete. (region_model::replace_svalue): Delete. (region_model::add_region): Delete. (region_model::get_svalue): Delete. (region_model::get_region): Delete. (make_region_for_type): Delete. (region_model::add_region_for_type): Delete. (region_model::on_top_level_param): New. (class restrict_to_used_svalues): Delete. (region_model::purge_unused_svalues): Delete. (region_model::push_frame): New. (region_model::remap_svalue_ids): Delete. (region_model::remap_region_ids): Delete. (region_model::purge_regions): Delete. (region_model::get_descendents): Delete. (region_model::delete_region_and_descendents): Delete. (region_model::poison_any_pointers_to_bad_regions): Delete. (region_model::can_merge_with_p): Delete. (region_model::get_current_function): New. (region_model::get_value_by_name): Delete. (region_model::convert_byte_offset_to_array_index): Delete. (region_model::pop_frame): New. (region_model::get_or_create_mem_ref): Delete. (region_model::get_stack_depth): New. (region_model::get_frame_at_index): New. (region_model::unbind_region_and_descendents): New. (struct bad_pointer_finder): New. (region_model::get_or_create_pointer_plus_expr): Delete. (region_model::poison_any_pointers_to_descendents): New. (region_model::get_or_create_view): Delete. (region_model::can_merge_with_p): New. (region_model::get_fndecl_for_call): Port from svalue_id to const svalue *. (struct append_ssa_names_cb_data): New. (get_ssa_name_regions_for_current_frame): New. (region_model::append_ssa_names_cb): New. (model_merger::dump_to_pp): Add "simple" param. Drop dumping of remappings. (model_merger::dump): Add "simple" param to both overloads. (model_merger::can_merge_values_p): Delete. (model_merger::record_regions): Delete. (model_merger::record_svalues): Delete. (svalue_id_merger_mapping::svalue_id_merger_mapping): Delete. (svalue_id_merger_mapping::dump_to_pp): Delete. (svalue_id_merger_mapping::dump): Delete. (region_model::create_region_for_heap_alloc): New. (region_model::create_region_for_alloca): New. (region_model::record_dynamic_extents): New. (canonicalization::canonicalization): Delete. (canonicalization::walk_rid): Delete. (canonicalization::walk_sid): Delete. (canonicalization::dump_to_pp): Delete. (canonicalization::dump): Delete. (inchash::add): Delete overloads for svalue_id and region_id. (engine::log_stats): New. (assert_condition): Add overload comparing svalues. (assert_dump_eq): Pass "true" for multiline. (selftest::test_dump): Update for rewrite of region_model. (selftest::test_dump_2): Rename to... (selftest::test_struct): ...this. Provide a region_model_manager when creating region_model instance. Remove dump test. Add checks for get_offset. (selftest::test_dump_3): Rename to... (selftest::test_array_1): ...this. Provide a region_model_manager when creating region_model instance. Remove dump test. (selftest::test_get_representative_tree): Port from svalue_id to new API. Add test coverage for various expressions. (selftest::test_unique_constants): Provide a region_model_manager for the region_model. Add test coverage for comparing const vs non-const. (selftest::test_svalue_equality): Delete. (selftest::test_region_equality): Delete. (selftest::test_unique_unknowns): New. (class purge_all_svalue_ids): Delete. (class purge_one_svalue_id): Delete. (selftest::test_purging_by_criteria): Delete. (selftest::test_initial_svalue_folding): New. (selftest::test_unaryop_svalue_folding): New. (selftest::test_binop_svalue_folding): New. (selftest::test_sub_svalue_folding): New. (selftest::test_purge_unused_svalues): Delete. (selftest::test_descendent_of_p): New. (selftest::test_assignment): Provide a region_model_manager for the region_model. Drop the dump test. (selftest::test_compound_assignment): Likewise. (selftest::test_stack_frames): Port to new implementation. (selftest::test_get_representative_path_var): Likewise. (selftest::test_canonicalization_1): Rename to... (selftest::test_equality_1): ...this. Port to new API, and add (selftest::test_canonicalization_2): Provide a region_model_manager when creating region_model instances. Remove redundant canicalization. (selftest::test_canonicalization_3): Provide a region_model_manager when creating region_model instances. Remove param from calls to region_model::canonicalize. (selftest::test_canonicalization_4): Likewise. (selftest::assert_region_models_merge): Constify out_merged_svalue. Port to new API. (selftest::test_state_merging): Provide a region_model_manager when creating region_model instances. Provide a program_point point when merging them. Replace set_to_new_unknown_value with usage of placeholder_svalues. Drop get_value_by_name. Port from svalue_id to const svalue *. Add test of heap allocation. (selftest::test_constraint_merging): Provide a region_model_manager when creating region_model instances. Provide a program_point point when merging them. Eliminate use of set_to_new_unknown_value. (selftest::test_widening_constraints): New. (selftest::test_iteration_1): New. (selftest::test_malloc_constraints): Port to store-based implementation. (selftest::test_var): New test. (selftest::test_array_2): New test. (selftest::test_mem_ref): New test. (selftest::test_POINTER_PLUS_EXPR_then_MEM_REF): New. (selftest::test_malloc): New. (selftest::test_alloca): New. (selftest::analyzer_region_model_cc_tests): Update for renamings. Call new functions. * region-model.h (class path_var): Move to analyzer.h. (class svalue_id): Delete. (class region_id): Delete. (class id_map): Delete. (svalue_id_map): Delete. (region_id_map): Delete. (id_map<T>::id_map): Delete. (id_map<T>::put): Delete. (id_map<T>::get_dst_for_src): Delete. (id_map<T>::get_src_for_dst): Delete. (id_map<T>::dump_to_pp): Delete. (id_map<T>::dump): Delete. (id_map<T>::update): Delete. (one_way_svalue_id_map): Delete. (one_way_region_id_map): Delete. (class region_id_set): Delete. (class svalue_id_set): Delete. (struct complexity): New. (class visitor): New. (enum svalue_kind): Add SK_SETJMP, SK_INITIAL, SK_UNARYOP, SK_BINOP, SK_SUB,SK_UNMERGEABLE, SK_PLACEHOLDER, SK_WIDENING, SK_COMPOUND, and SK_CONJURED. (svalue::operator==): Delete. (svalue::operator!=): Delete. (svalue::clone): Delete. (svalue::hash): Delete. (svalue::dump_dot_to_pp): Delete. (svalue::dump_to_pp): New. (svalue::dump): New. (svalue::get_desc): New. (svalue::dyn_cast_initial_svalue): New. (svalue::dyn_cast_unaryop_svalue): New. (svalue::dyn_cast_binop_svalue): New. (svalue::dyn_cast_sub_svalue): New. (svalue::dyn_cast_unmergeable_svalue): New. (svalue::dyn_cast_widening_svalue): New. (svalue::dyn_cast_compound_svalue): New. (svalue::dyn_cast_conjured_svalue): New. (svalue::maybe_undo_cast): New. (svalue::unwrap_any_unmergeable): New. (svalue::remap_region_ids): Delete (svalue::can_merge_p): New. (svalue::walk_for_canonicalization): Delete (svalue::get_complexity): New. (svalue::get_child_sid): Delete (svalue::accept): New. (svalue::live_p): New. (svalue::implicitly_live_p): New. (svalue::svalue): Add complexity param. (svalue::add_to_hash): Delete (svalue::print_details): Delete (svalue::m_complexity): New field. (region_svalue::key_t): New struct. (region_svalue::region_svalue): Port from region_id to const region_id *. Add complexity. (region_svalue::compare_fields): Delete. (region_svalue::clone): Delete. (region_svalue::dump_dot_to_pp): Delete. (region_svalue::get_pointee): Port from region_id to const region_id *. (region_svalue::remap_region_ids): Delete. (region_svalue::merge_values): Delete. (region_svalue::dump_to_pp): New. (region_svalue::accept): New. (region_svalue::walk_for_canonicalization): Delete. (region_svalue::eval_condition): Make params const. (region_svalue::add_to_hash): Delete. (region_svalue::print_details): Delete. (region_svalue::m_rid): Replace with... (region_svalue::m_reg): ...this. (is_a_helper <region_svalue *>::test): Convert to... (is_a_helper <const region_svalue *>::test): ...this. (template <> struct default_hash_traits<region_svalue::key_t>): New. (constant_svalue::constant_svalue): Add complexity. (constant_svalue::compare_fields): Delete. (constant_svalue::clone): Delete. (constant_svalue::add_to_hash): Delete. (constant_svalue::dump_to_pp): New. (constant_svalue::accept): New. (constant_svalue::implicitly_live_p): New. (constant_svalue::merge_values): Delete. (constant_svalue::eval_condition): Make params const. (constant_svalue::get_child_sid): Delete. (constant_svalue::print_details): Delete. (is_a_helper <constant_svalue *>::test): Convert to... (is_a_helper <const constant_svalue *>::test): ...this. (class unknown_svalue): Update leading comment. (unknown_svalue::unknown_svalue): Add complexity. (unknown_svalue::compare_fields): Delete. (unknown_svalue::add_to_hash): Delete. (unknown_svalue::dyn_cast_unknown_svalue): Delete. (unknown_svalue::print_details): Delete. (unknown_svalue::dump_to_pp): New. (unknown_svalue::accept): New. (poisoned_svalue::key_t): New struct. (poisoned_svalue::poisoned_svalue): Add complexity. (poisoned_svalue::compare_fields): Delete. (poisoned_svalue::clone): Delete. (poisoned_svalue::add_to_hash): Delete. (poisoned_svalue::dump_to_pp): New. (poisoned_svalue::accept): New. (poisoned_svalue::print_details): Delete. (is_a_helper <poisoned_svalue *>::test): Convert to... (is_a_helper <const poisoned_svalue *>::test): ...this. (template <> struct default_hash_traits<poisoned_svalue::key_t>): New. (setjmp_record::add_to_hash): New. (setjmp_svalue::key_t): New struct. (setjmp_svalue::compare_fields): Delete. (setjmp_svalue::clone): Delete. (setjmp_svalue::add_to_hash): Delete. (setjmp_svalue::setjmp_svalue): Add complexity. (setjmp_svalue::dump_to_pp): New. (setjmp_svalue::accept): New. (setjmp_svalue::void print_details): Delete. (is_a_helper <const setjmp_svalue *>::test): New. (template <> struct default_hash_traits<setjmp_svalue::key_t>): New. (class initial_svalue : public svalue): New. (is_a_helper <const initial_svalue *>::test): New. (class unaryop_svalue): New. (is_a_helper <const unaryop_svalue *>::test): New. (template <> struct default_hash_traits<unaryop_svalue::key_t>): New. (class binop_svalue): New. (is_a_helper <const binop_svalue *>::test): New. (template <> struct default_hash_traits<binop_svalue::key_t>): New. (class sub_svalue): New. (is_a_helper <const sub_svalue *>::test): New. (template <> struct default_hash_traits<sub_svalue::key_t>): New. (class unmergeable_svalue): New. (is_a_helper <const unmergeable_svalue *>::test): New. (class placeholder_svalue): New. (is_a_helper <placeholder_svalue *>::test): New. (class widening_svalue): New. (is_a_helper <widening_svalue *>::test): New. (template <> struct default_hash_traits<widening_svalue::key_t>): New. (class compound_svalue): New. (is_a_helper <compound_svalue *>::test): New. (template <> struct default_hash_traits<compound_svalue::key_t>): New. (class conjured_svalue): New. (is_a_helper <conjured_svalue *>::test): New. (template <> struct default_hash_traits<conjured_svalue::key_t>): New. (enum region_kind): Delete RK_PRIMITIVE, RK_STRUCT, RK_UNION, and RK_ARRAY. Add RK_LABEL, RK_DECL, RK_FIELD, RK_ELEMENT, RK_OFFSET, RK_CAST, RK_HEAP_ALLOCATED, RK_ALLOCA, RK_STRING, and RK_UNKNOWN. (region_kind_to_str): Delete. (region::~region): Move implementation to region.cc. (region::operator==): Delete. (region::operator!=): Delete. (region::clone): Delete. (region::get_id): New. (region::cmp_ids): New. (region::dyn_cast_map_region): Delete. (region::dyn_cast_array_region): Delete. (region::region_id get_parent): Delete. (region::get_parent_region): Convert to a simple accessor. (region::void set_value): Delete. (region::svalue_id get_value): Delete. (region::svalue_id get_value_direct): Delete. (region::svalue_id get_inherited_child_sid): Delete. (region::dyn_cast_frame_region): New. (region::dyn_cast_function_region): New. (region::dyn_cast_decl_region): New. (region::dyn_cast_field_region): New. (region::dyn_cast_element_region): New. (region::dyn_cast_offset_region): New. (region::dyn_cast_cast_region): New. (region::dyn_cast_string_region): New. (region::accept): New. (region::get_base_region): New. (region::base_region_p): New. (region::descendent_of_p): New. (region::maybe_get_frame_region): New. (region::maybe_get_decl): New. (region::hash): Delete. (region::rint): Delete. (region::dump_dot_to_pp): Delete. (region::get_desc): New. (region::dump_to_pp): Convert to vfunc, changing signature. (region::dump_child_label): Delete. (region::remap_svalue_ids): Delete. (region::remap_region_ids): Delete. (region::dump): New. (region::walk_for_canonicalization): Delete. (region::non_null_p): Drop region_model param. (region::add_view): Delete. (region::get_view): Delete. (region::get_active_view): Delete. (region::is_view_p): Delete. (region::cmp_ptrs): New. (region::validate): Delete. (region::get_offset): New. (region::get_byte_size): New. (region::get_bit_size): New. (region::get_subregions_for_binding): New. (region::region): Add complexity param. Convert parent from region_id to const region *. Drop svalue_id. Drop copy ctor. (region::symbolic_for_unknown_ptr_p): New. (region::add_to_hash): Delete. (region::print_fields): Delete. (region::get_complexity): New accessor. (region::become_active_view): Delete. (region::deactivate_any_active_view): Delete. (region::deactivate_view): Delete. (region::calc_offset): New. (region::m_parent_rid): Delete. (region::m_sval_id): Delete. (region::m_complexity): New. (region::m_id): New. (region::m_parent): New. (region::m_view_rids): Delete. (region::m_is_view): Delete. (region::m_active_view_rid): Delete. (region::m_cached_offset): New. (is_a_helper <region *>::test): Convert to... (is_a_helper <const region *>::test): ... this. (class primitive_region): Delete. (class space_region): New. (class map_region): Delete. (is_a_helper <map_region *>::test): Delete. (class frame_region): Reimplement. (template <> struct default_hash_traits<frame_region::key_t>): New. (class globals_region): Reimplement. (is_a_helper <globals_region *>::test): Convert to... (is_a_helper <const globals_region *>::test): ...this. (class struct_or_union_region): Delete. (is_a_helper <struct_or_union_region *>::test): Delete. (class code_region): Reimplement. (is_a_helper <const code_region *>::test): New. (class struct_region): Delete. (is_a_helper <struct_region *>::test): Delete. (class function_region): Reimplement. (is_a_helper <function_region *>::test): Convert to... (is_a_helper <const function_region *>::test): ...this. (class union_region): Delete. (is_a_helper <union_region *>::test): Delete. (class label_region): New. (is_a_helper <const label_region *>::test): New. (class scope_region): Delete. (class stack_region): Reimplement. (is_a_helper <stack_region *>::test): Convert to... (is_a_helper <const stack_region *>::test): ...this. (class heap_region): Reimplement. (is_a_helper <heap_region *>::test): Convert to... (is_a_helper <const heap_region *>::test): ...this. (class root_region): Reimplement. (is_a_helper <root_region *>::test): Convert to... (is_a_helper <const root_region *>::test): ...this. (class symbolic_region): Reimplement. (is_a_helper <const symbolic_region *>::test): New. (template <> struct default_hash_traits<symbolic_region::key_t>): New. (class decl_region): New. (is_a_helper <const decl_region *>::test): New. (class field_region): New. (template <> struct default_hash_traits<field_region::key_t>): New. (class array_region): Delete. (class element_region): New. (is_a_helper <array_region *>::test): Delete. (is_a_helper <const element_region *>::test): New. (template <> struct default_hash_traits<element_region::key_t>): New. (class offset_region): New. (is_a_helper <const offset_region *>::test): New. (template <> struct default_hash_traits<offset_region::key_t>): New. (class cast_region): New. (is_a_helper <const cast_region *>::test): New. (template <> struct default_hash_traits<cast_region::key_t>): New. (class heap_allocated_region): New. (class alloca_region): New. (class string_region): New. (is_a_helper <const string_region *>::test): New. (class unknown_region): New. (class region_model_manager): New. (struct append_ssa_names_cb_data): New. (class call_details): New. (region_model::region_model): Add region_model_manager param. (region_model::print_svalue): Delete. (region_model::dump_dot_to_pp): Delete. (region_model::dump_dot_to_file): Delete. (region_model::dump_dot): Delete. (region_model::dump_to_pp): Drop summarize param in favor of simple and multiline. (region_model::dump): Likewise. (region_model::summarize_to_pp): Delete. (region_model::summarize): Delete. (region_model::void canonicalize): Drop ctxt param. (region_model::void check_for_poison): Delete. (region_model::get_gassign_result): New. (region_model::impl_call_alloca): New. (region_model::impl_call_analyzer_describe): New. (region_model::impl_call_analyzer_eval): New. (region_model::impl_call_builtin_expect): New. (region_model::impl_call_calloc): New. (region_model::impl_call_free): New. (region_model::impl_call_malloc): New. (region_model::impl_call_memset): New. (region_model::impl_call_strlen): New. (region_model::get_reachable_svalues): New. (region_model::handle_phi): Drop is_back_edge param. (region_model::region_id get_root_rid): Delete. (region_model::root_region *get_root_region): Delete. (region_model::region_id get_stack_region_id): Delete. (region_model::push_frame): Convert from region_id and svalue_id to const region * and const svalue *. (region_model::get_current_frame_id): Replace with... (region_model::get_current_frame): ...this. (region_model::pop_frame): Convert from region_id to const region *. Drop purge and stats param. Add out_result. (region_model::function *get_function_at_depth): Delete. (region_model::get_globals_region_id): Delete. (region_model::add_svalue): Delete. (region_model::replace_svalue): Delete. (region_model::add_region): Delete. (region_model::add_region_for_type): Delete. (region_model::get_svalue): Delete. (region_model::get_region): Delete. (region_model::get_lvalue): Convert from region_id to const region *. (region_model::get_rvalue): Convert from svalue_id to const svalue *. (region_model::get_or_create_ptr_svalue): Delete. (region_model::get_or_create_constant_svalue): Delete. (region_model::get_svalue_for_fndecl): Delete. (region_model::get_svalue_for_label): Delete. (region_model::get_region_for_fndecl): Delete. (region_model::get_region_for_label): Delete. (region_model::get_frame_at_index (int index) const;): New. (region_model::maybe_cast): Delete. (region_model::maybe_cast_1): Delete. (region_model::get_field_region): Delete. (region_model::id deref_rvalue): Convert from region_id and svalue_id to const region * and const svalue *. Drop overload, passing in both a tree and an svalue. (region_model::set_value): Convert from region_id and svalue_id to const region * and const svalue *. (region_model::set_to_new_unknown_value): Delete. (region_model::clobber_region (const region *reg);): New. (region_model::purge_region (const region *reg);): New. (region_model::zero_fill_region (const region *reg);): New. (region_model::mark_region_as_unknown (const region *reg);): New. (region_model::copy_region): Convert from region_id to const region *. (region_model::eval_condition): Convert from svalue_id to const svalue *. (region_model::eval_condition_without_cm): Likewise. (region_model::compare_initial_and_pointer): New. (region_model:maybe_get_constant): Delete. (region_model::add_new_malloc_region): Delete. (region_model::get_representative_tree): Convert from svalue_id to const svalue *. (region_model::get_representative_path_var): Delete decl taking a region_id in favor of two decls, for svalue vs region, with an svalue_set to ensure termination. (region_model::get_path_vars_for_svalue): Delete. (region_model::create_region_for_heap_alloc): New. (region_model::create_region_for_alloca): New. (region_model::purge_unused_svalues): Delete. (region_model::remap_svalue_ids): Delete. (region_model::remap_region_ids): Delete. (region_model::purge_regions): Delete. (region_model::get_num_svalues): Delete. (region_model::get_num_regions): Delete. (region_model::get_descendents): Delete. (region_model::get_store): New. (region_model::delete_region_and_descendents): Delete. (region_model::get_manager): New. (region_model::unbind_region_and_descendents): New. (region_model::can_merge_with_p): Add point param. Drop svalue_id_merger_mapping. (region_model::get_value_by_name): Delete. (region_model::convert_byte_offset_to_array_index): Delete. (region_model::get_or_create_mem_ref): Delete. (region_model::get_or_create_pointer_plus_expr): Delete. (region_model::get_or_create_view): Delete. (region_model::get_lvalue_1): Convert from region_id to const region *. (region_model::get_rvalue_1): Convert from svalue_id to const svalue *. (region_model::get_ssa_name_regions_for_current_frame): New. (region_model::append_ssa_names_cb): New. (region_model::get_store_value): New. (region_model::copy_struct_region): Delete. (region_model::copy_union_region): Delete. (region_model::copy_array_region): Delete. (region_model::region_exists_p): New. (region_model::make_region_for_unexpected_tree_code): Delete. (region_model::loop_replay_fixup): New. (region_model::poison_any_pointers_to_bad_regions): Delete. (region_model::poison_any_pointers_to_descendents): New. (region_model::dump_summary_of_rep_path_vars): Delete. (region_model::on_top_level_param): New. (region_model::record_dynamic_extents): New. (region_model::m_mgr;): New. (region_model::m_store;): New. (region_model::m_svalues;): Delete. (region_model::m_regions;): Delete. (region_model::m_root_rid;): Delete. (region_model::m_current_frame;): New. (region_model_context::remap_svalue_ids): Delete. (region_model_context::can_purge_p): Delete. (region_model_context::on_svalue_leak): New. (region_model_context::on_svalue_purge): Delete. (region_model_context::on_liveness_change): New. (region_model_context::on_inherited_svalue): Delete. (region_model_context::on_cast): Delete. (region_model_context::on_unknown_change): Convert from svalue_id to const svalue * and add is_mutable. (class noop_region_model_context): Update for region_model_context changes. (model_merger::model_merger): Add program_point. Drop svalue_id_merger_mapping. (model_merger::dump_to_pp): Add "simple" param. (model_merger::dump): Likewise. (model_merger::get_region_a): Delete. (model_merger::get_region_b): Delete. (model_merger::can_merge_values_p): Delete. (model_merger::record_regions): Delete. (model_merger::record_svalues): Delete. (model_merger::m_point): New field. (model_merger::m_map_regions_from_a_to_m): Delete. (model_merger::m_map_regions_from_b_to_m): Delete. (model_merger::m_sid_mapping): Delete. (struct svalue_id_merger_mapping): Delete. (class engine): New. (struct canonicalization): Delete. (inchash::add): Delete decls for hashing svalue_id and region_id. (test_region_model_context::on_unexpected_tree_code): Require t to be non-NULL. (selftest::assert_condition): Add overload comparing a pair of const svalue *. * sm-file.cc: Include "tristate.h", "selftest.h", "analyzer/call-string.h", "analyzer/program-point.h", "analyzer/store.h", and "analyzer/region-model.h". (fileptr_state_machine::get_default_state): New. (fileptr_state_machine::on_stmt): Remove calls to get_readable_tree in favor of get_diagnostic_tree. * sm-malloc.cc: Include "tristate.h", "selftest.h", "analyzer/call-string.h", "analyzer/program-point.h", "analyzer/store.h", and "analyzer/region-model.h". (malloc_state_machine::get_default_state): New. (malloc_state_machine::reset_when_passed_to_unknown_fn_p): New. (malloc_diagnostic::describe_state_change): Handle change.m_expr being NULL. (null_arg::emit): Avoid printing "NULL '0'". (null_arg::describe_final_event): Avoid printing "(0) NULL". (malloc_leak::emit): Handle m_arg being NULL. (malloc_leak::describe_final_event): Handle ev.m_expr being NULL. (malloc_state_machine::on_stmt): Don't call get_readable_tree. Call get_diagnostic_tree when creating pending diagnostics. Update for is_zero_assignment becoming a member function of sm_ctxt. Don't transition to m_non_heap for ADDR_EXPR(MEM_REF()). (malloc_state_machine::reset_when_passed_to_unknown_fn_p): New vfunc implementation. * sm-sensitive.cc (sensitive_state_machine::warn_for_any_exposure): Call get_diagnostic_tree and pass the result to warn_for_state. * sm-signal.cc: Move includes of "analyzer/call-string.h" and "analyzer/program-point.h" to before "analyzer/region-model.h", and also include "analyzer/store.h" before it. (signal_unsafe_call::describe_state_change): Use get_dest_function to get handler. (update_model_for_signal_handler): Pass manager to region_model ctor. (register_signal_handler::impl_transition): Update for changes to get_or_create_node and add_edge. * sm-taint.cc (taint_state_machine::on_stmt): Remove calls to get_readable_tree, replacing them when calling warn_for_state with calls to get_diagnostic_tree. * sm.cc (is_zero_assignment): Delete. (any_pointer_p): Move to within namespace ana. * sm.h (is_zero_assignment): Remove decl. (any_pointer_p): Move decl to within namespace ana. (state_machine::get_default_state): New vfunc. (state_machine::reset_when_passed_to_unknown_fn_p): New vfunc. (sm_context::get_readable_tree): Rename to... (sm_context::get_diagnostic_tree): ...this. (sm_context::is_zero_assignment): New vfunc. * store.cc: New file. * store.h: New file. * svalue.cc: New file. gcc/testsuite/ChangeLog: PR analyzer/93032 PR analyzer/93938 PR analyzer/94011 PR analyzer/94099 PR analyzer/94399 PR analyzer/94458 PR analyzer/94503 PR analyzer/94640 PR analyzer/94688 PR analyzer/94689 PR analyzer/94839 PR analyzer/95026 PR analyzer/95042 PR analyzer/95240 * g++.dg/analyzer/pr93212.C: Add dg-warning for dangling reference. * g++.dg/analyzer/pr93950.C: Remove xfail. * g++.dg/analyzer/pr94011.C: New test. * g++.dg/analyzer/pr94028.C: Remove leak false positives; mark as failing on C++98. * g++.dg/analyzer/pr94503.C: New test. * g++.dg/analyzer/pr95042.C: New test. * gcc.dg/analyzer/CVE-2005-1689-dedupe-issue-2.c: New test. * gcc.dg/analyzer/CVE-2005-1689-dedupe-issue.c: Add xfail. * gcc.dg/analyzer/CVE-2005-1689-minimal.c: Include "analyzer-decls.h". (test_4, test_5, test_6, test_7, test_8): New tests. * gcc.dg/analyzer/abs-1.c: New test. * gcc.dg/analyzer/aliasing-1.c: New test. * gcc.dg/analyzer/aliasing-2.c: New test. * gcc.dg/analyzer/analyzer-decls.h (__analyzer_describe): New decl. (__analyzer_dump_num_heap_regions): Remove. * gcc.dg/analyzer/attribute-nonnull.c: Add dg-warnings for cases where NULL is directly used as an argument. * gcc.dg/analyzer/bzero-1.c: New test. * gcc.dg/analyzer/casts-1.c: New test. * gcc.dg/analyzer/casts-2.c: New test. * gcc.dg/analyzer/compound-assignment-1.c (test_4): Remove xfail from leak false positive. (called_by_test_5a): Add "allocated here" expected message. (called_by_test_5b): Make expected leak message more precise. * gcc.dg/analyzer/compound-assignment-3.c: Update expected leak message. * gcc.dg/analyzer/compound-assignment-4.c: New test. * gcc.dg/analyzer/compound-assignment-5.c: New test. * gcc.dg/analyzer/conditionals-notrans.c: Remove xfails. * gcc.dg/analyzer/data-model-1.c (test_12d): Update expected results. (test_13): Remove xfail. (test_14): Remove xfail. (test_15): Remove xfail. (test_16): Remove xfails. Add out-of-bounds access. (test_16_alt): Remove xfails. (test_23): Remove xfail. (test_24): Remove xfail. (test_25): Remove xfail. (test_26): Update expected result. Remove xfail. Add xfail. (test_27): Remove xfails. (test_29): Add __analyzer_eval pointer comparisons. (test_41): Generalize expected output for u.ptr comparison with NULL for targets where this could be known to be false. (test_42): Remove xfail. (test_51): Remove xfails. * gcc.dg/analyzer/data-model-13.c: Update for improvements to source location and wording of leak message. * gcc.dg/analyzer/data-model-14.c: Remove -fanalyzer-fine-grained. (test_1): Update for improvement to expected message. (test_2): Remove xfail. * gcc.dg/analyzer/data-model-18.c: Remove xfail. * gcc.dg/analyzer/data-model-20.c: New test. * gcc.dg/analyzer/data-model-5.c: Add dg-warning for deref of NULL. Add xfailing false leak. * gcc.dg/analyzer/data-model-5b.c: Add xfailing false leak. * gcc.dg/analyzer/data-model-5c.c: Update xfailing false leak. * gcc.dg/analyzer/data-model-5d.c: Reimplement. * gcc.dg/analyzer/data-model-6.c: Delete test. * gcc.dg/analyzer/data-model-8.c: Remove xfail. * gcc.dg/analyzer/describe-1.c: New test. * gcc.dg/analyzer/dot-output.c: Remove xfail. * gcc.dg/analyzer/explode-1.c: Add expected leak warning. * gcc.dg/analyzer/explode-2.c: Add expected leak warnings. Mark double-free warnings as xfail for now. * gcc.dg/analyzer/feasibility-1.c: New test. * gcc.dg/analyzer/first-field-1.c: New test. * gcc.dg/analyzer/first-field-2.c: New test. * gcc.dg/analyzer/init.c: New test. * gcc.dg/analyzer/leak-2.c: New test. * gcc.dg/analyzer/loop-0-up-to-n-by-1-with-iter-obj.c: New test. * gcc.dg/analyzer/loop-0-up-to-n-by-1.c: New test. * gcc.dg/analyzer/loop-2a.c: Update expected behavior. * gcc.dg/analyzer/loop-3.c: Mark use-after-free as xfail. Add expected warning about deref of unchecked pointer. * gcc.dg/analyzer/loop-4.c: Remove -fno-analyzer-state-purge. Update expected behavior. * gcc.dg/analyzer/loop-n-down-to-1-by-1.c: New test. * gcc.dg/analyzer/loop-start-down-to-end-by-1.c: New test. * gcc.dg/analyzer/loop-start-down-to-end-by-step.c: New test. * gcc.dg/analyzer/loop-start-to-end-by-step.c: New test. * gcc.dg/analyzer/loop-start-up-to-end-by-1.c: New test. * gcc.dg/analyzer/loop.c: Remove -fno-analyzer-state-purge. Update expected behavior. * gcc.dg/analyzer/malloc-1.c: Remove xfails from leak false positives. Update expected wording of global_link.m_ptr leak. (test_49): New test. * gcc.dg/analyzer/malloc-4.c: Remove leak false positive. Update expected wording of leak warning. * gcc.dg/analyzer/malloc-in-loop.c: New test. * gcc.dg/analyzer/malloc-ipa-8-double-free.c: Update expected path to show call to wrapped_malloc. * gcc.dg/analyzer/malloc-ipa-8-unchecked.c: Remove -fanalyzer-verbose-state-changes. * gcc.dg/analyzer/malloc-paths-9.c: Remove comment about duplicate warnings. Remove duplicate use-after-free paths. * gcc.dg/analyzer/malloc-vs-local-1a.c: Add dg-warning for deref of unchecked pointer. Update expected number of enodes. * gcc.dg/analyzer/malloc-vs-local-2.c: Likewise. * gcc.dg/analyzer/malloc-vs-local-3.c: Add dg-warning for deref of unchecked pointer. Update expected number of enodes. Avoid overspecifying the leak message. * gcc.dg/analyzer/memset-1.c: New test. * gcc.dg/analyzer/paths-3.c: Update expected number of enodes. * gcc.dg/analyzer/paths-4.c: Likewise. * gcc.dg/analyzer/paths-6.c: Likewise. * gcc.dg/analyzer/paths-7.c: Likewise. * gcc.dg/analyzer/pr93032-mztools-simplified.c: New test. * gcc.dg/analyzer/pr93032-mztools.c: New test. * gcc.dg/analyzer/pr93382.c: Mark taint tests as failing. * gcc.dg/analyzer/pr93938.c: New test. * gcc.dg/analyzer/pr94099.c: Replace uninit dg-warning with dg-warning for NULL dereference. * gcc.dg/analyzer/pr94399.c: New test. * gcc.dg/analyzer/pr94447.c: Add dg-warning for NULL dereference. * gcc.dg/analyzer/pr94458.c: New test. * gcc.dg/analyzer/pr94640.c: New test. * gcc.dg/analyzer/pr94688.c: New test. * gcc.dg/analyzer/pr94689.c: New test. * gcc.dg/analyzer/pr94839.c: New test. * gcc.dg/analyzer/pr95026.c: New test. * gcc.dg/analyzer/pr95240.c: New test. * gcc.dg/analyzer/refcounting-1.c: New test. * gcc.dg/analyzer/single-field.c: New test. * gcc.dg/analyzer/stale-frame-1.c: New test. * gcc.dg/analyzer/symbolic-1.c: New test. * gcc.dg/analyzer/symbolic-2.c: New test. * gcc.dg/analyzer/symbolic-3.c: New test. * gcc.dg/analyzer/symbolic-4.c: New test. * gcc.dg/analyzer/symbolic-5.c: New test. * gcc.dg/analyzer/symbolic-6.c: New test. * gcc.dg/analyzer/taint-1.c: Mark the "gets unchecked value" events as failing for now. Update dg-message directives to avoid relying on numbering. * gcc.dg/analyzer/torture/loop-inc-ptr-1.c: New test. * gcc.dg/analyzer/torture/loop-inc-ptr-2.c: New test. * gcc.dg/analyzer/torture/loop-inc-ptr-3.c: New test. * gcc.dg/analyzer/unknown-fns-2.c: New test. * gcc.dg/analyzer/unknown-fns-3.c: New test. * gcc.dg/analyzer/unknown-fns-4.c: New test. * gcc.dg/analyzer/unknown-fns.c: Update dg-warning to reflect fixed source location for leak diagnostic. * gcc.dg/analyzer/use-after-free.c: New test. * gcc.dg/analyzer/vla-1.c: New test. * gcc.dg/analyzer/zlib-4.c: Rewrite to avoid "exit" calls. Add expected leak warnings. * gfortran.dg/analyzer/pr93993.f90: Remove leak of tm warning, which seems to have been a false positive.
Diffstat (limited to 'gcc/testsuite')
-rw-r--r--gcc/testsuite/g++.dg/analyzer/pr93212.C5
-rw-r--r--gcc/testsuite/g++.dg/analyzer/pr93950.C4
-rw-r--r--gcc/testsuite/g++.dg/analyzer/pr94011.C16
-rw-r--r--gcc/testsuite/g++.dg/analyzer/pr94028.C4
-rw-r--r--gcc/testsuite/g++.dg/analyzer/pr94503.C25
-rw-r--r--gcc/testsuite/g++.dg/analyzer/pr95042.C28
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/CVE-2005-1689-dedupe-issue-2.c30
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/CVE-2005-1689-dedupe-issue.c2
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/CVE-2005-1689-minimal.c61
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/abs-1.c22
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/aliasing-1.c25
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/aliasing-2.c32
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/analyzer-decls.h7
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/attribute-nonnull.c12
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/bzero-1.c11
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/casts-1.c49
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/casts-2.c15
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/compound-assignment-1.c9
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/compound-assignment-3.c2
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/compound-assignment-4.c28
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/compound-assignment-5.c142
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/conditionals-notrans.c33
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/data-model-1.c96
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/data-model-13.c7
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/data-model-14.c9
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/data-model-18.c4
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/data-model-20.c25
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/data-model-5.c13
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/data-model-5b.c12
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/data-model-5c.c13
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/data-model-5d.c61
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/data-model-6.c14
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/data-model-8.c3
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/describe-1.c11
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/dot-output.c3
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/explode-1.c2
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/explode-2.c16
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/feasibility-1.c62
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/first-field-1.c24
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/first-field-2.c33
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/init.c136
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/leak-2.c9
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/loop-0-up-to-n-by-1-with-iter-obj.c73
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/loop-0-up-to-n-by-1.c31
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/loop-2a.c10
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/loop-3.c7
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/loop-4.c13
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/loop-n-down-to-1-by-1.c35
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/loop-start-down-to-end-by-1.c35
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/loop-start-down-to-end-by-step.c30
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/loop-start-to-end-by-step.c36
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/loop-start-up-to-end-by-1.c34
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/loop.c9
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/malloc-1.c26
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/malloc-4.c4
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/malloc-in-loop.c19
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/malloc-ipa-8-double-free.c82
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/malloc-ipa-8-unchecked.c6
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/malloc-paths-9.c59
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/malloc-vs-local-1a.c22
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/malloc-vs-local-2.c16
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/malloc-vs-local-3.c7
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/memset-1.c100
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/paths-3.c4
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/paths-4.c10
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/paths-6.c4
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/paths-7.c3
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/pr93032-mztools-simplified.c22
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/pr93032-mztools.c331
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/pr93382.c6
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/pr93938.c13
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/pr94099.c3
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/pr94399.c13
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/pr94447.c2
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/pr94458.c23
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/pr94640.c17
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/pr94688.c6
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/pr94689.c8
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/pr94839.c20
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/pr95026.c17
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/pr95240.c27
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/refcounting-1.c31
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/single-field.c37
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/stale-frame-1.c15
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/symbolic-1.c43
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/symbolic-2.c32
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/symbolic-3.c12
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/symbolic-4.c20
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/symbolic-5.c29
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/symbolic-6.c24
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/taint-1.c22
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/torture/loop-inc-ptr-1.c15
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/torture/loop-inc-ptr-2.c17
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/torture/loop-inc-ptr-3.c18
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/unknown-fns-2.c238
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/unknown-fns-3.c67
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/unknown-fns-4.c15
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/unknown-fns.c8
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/use-after-free.c12
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/vla-1.c13
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/zlib-4.c10
-rw-r--r--gcc/testsuite/gfortran.dg/analyzer/pr93993.f902
102 files changed, 2605 insertions, 373 deletions
diff --git a/gcc/testsuite/g++.dg/analyzer/pr93212.C b/gcc/testsuite/g++.dg/analyzer/pr93212.C
index 0d76d839378..1029e8d547b 100644
--- a/gcc/testsuite/g++.dg/analyzer/pr93212.C
+++ b/gcc/testsuite/g++.dg/analyzer/pr93212.C
@@ -5,8 +5,11 @@ auto lol()
{
int aha = 3;
return [&aha] {
- return aha;
+ return aha; // { dg-warning "dereferencing pointer '.*' to within stale stack frame" }
};
+ /* TODO: may be worth special-casing the reporting of dangling
+ references from lambdas, to highlight the declaration, and maybe fix
+ the wording (it's a reference, not a pointer, for one thing). */
}
int main()
diff --git a/gcc/testsuite/g++.dg/analyzer/pr93950.C b/gcc/testsuite/g++.dg/analyzer/pr93950.C
index e2808173407..129690c82be 100644
--- a/gcc/testsuite/g++.dg/analyzer/pr93950.C
+++ b/gcc/testsuite/g++.dg/analyzer/pr93950.C
@@ -9,9 +9,7 @@ struct d
};
void h (e * i)
{
- void *j = nullptr; // { dg-bogus "NULL" "" { xfail *-*-* } }
- // TODO(xfail): we report "'i' is NULL" above, which is the wrong location
-
+ void *j = nullptr; // { dg-bogus "NULL" }
i->f = *i->g; // { dg-warning "dereference of NULL 'i'" }
}
virtual void c (int, int)
diff --git a/gcc/testsuite/g++.dg/analyzer/pr94011.C b/gcc/testsuite/g++.dg/analyzer/pr94011.C
new file mode 100644
index 00000000000..2642aa40c1b
--- /dev/null
+++ b/gcc/testsuite/g++.dg/analyzer/pr94011.C
@@ -0,0 +1,16 @@
+// { dg-do compile { target c++11 } }
+// { dg-additional-options "-O1" }
+
+template <typename DV> DV
+vu (DV j4)
+{
+ return [j4] () { return j4 () ? j4 : throw j4 (); } ();
+}
+
+void
+foo ()
+{
+ auto n1 = [] { return nullptr; };
+
+ vu (n1);
+}
diff --git a/gcc/testsuite/g++.dg/analyzer/pr94028.C b/gcc/testsuite/g++.dg/analyzer/pr94028.C
index 5c8e2c9b286..0573d309ba2 100644
--- a/gcc/testsuite/g++.dg/analyzer/pr94028.C
+++ b/gcc/testsuite/g++.dg/analyzer/pr94028.C
@@ -19,11 +19,11 @@ struct j
throw()
#endif
{
- return calloc (b, sizeof (int)); // { dg-warning "leak" }
+ return calloc (b, sizeof (int)); // { dg-bogus "leak" "" { xfail c++98_only } }
}
j (B *, int)
{
- } // { dg-warning "leak" }
+ }
};
j *
diff --git a/gcc/testsuite/g++.dg/analyzer/pr94503.C b/gcc/testsuite/g++.dg/analyzer/pr94503.C
new file mode 100644
index 00000000000..9432ac40764
--- /dev/null
+++ b/gcc/testsuite/g++.dg/analyzer/pr94503.C
@@ -0,0 +1,25 @@
+template <typename> class allocator {
+public:
+ allocator(const allocator &);
+ allocator();
+};
+
+template <typename> struct allocator_traits;
+template <typename _Tp> struct allocator_traits<allocator<_Tp> > {
+ static allocator<_Tp> select_on_container_copy_construction() {
+ return allocator<_Tp>();
+ }
+ static allocator<_Tp> _S_select_on_copy() {
+ return select_on_container_copy_construction();
+ }
+};
+
+class basic_string {
+ struct _Alloc_hider {
+ _Alloc_hider(allocator<char>);
+ } _M_dataplus;
+
+public:
+ basic_string(basic_string &)
+ : _M_dataplus(allocator_traits<allocator<char> >::_S_select_on_copy()) {}
+} xxx(xxx);
diff --git a/gcc/testsuite/g++.dg/analyzer/pr95042.C b/gcc/testsuite/g++.dg/analyzer/pr95042.C
new file mode 100644
index 00000000000..0e745529f45
--- /dev/null
+++ b/gcc/testsuite/g++.dg/analyzer/pr95042.C
@@ -0,0 +1,28 @@
+// { dg-additional-options "-O1" }
+
+class kz {
+public:
+ kz ();
+
+private:
+ int yu;
+};
+
+const kz vl;
+kz ax;
+
+void
+c1 (bool va, bool ze)
+{
+ kz ny, fb = vl;
+
+ if (va)
+ {
+ if (ze)
+ ny = vl;
+
+ fb = ny;
+ }
+
+ ax = fb;
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/CVE-2005-1689-dedupe-issue-2.c b/gcc/testsuite/gcc.dg/analyzer/CVE-2005-1689-dedupe-issue-2.c
new file mode 100644
index 00000000000..57fd30add7b
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/CVE-2005-1689-dedupe-issue-2.c
@@ -0,0 +1,30 @@
+/* { dg-additional-options "-fanalyzer-show-duplicate-count" } */
+
+#include <stdlib.h>
+
+typedef struct _krb5_data {
+ char *data;
+} krb5_data;
+
+/* Ensure that we de-duplicate the various paths to reach here,
+ and only emit one diagnostic. */
+
+void
+recvauth_common(krb5_data common)
+{
+ free(common.data);
+ free(common.data); /* { dg-warning "double-'free' of 'common.data'" "inner warning" } */
+ /* { dg-warning "double-'free' of 'inbuf_a.data' " "inbuf_a warning" { target *-*-* } .-1 } */
+ /* { dg-warning "double-'free' of 'inbuf_b.data' " "inbuf_b warning" { target *-*-* } .-2 } */
+ /* { dg-message "2 duplicates" "duplicates notification" { xfail *-*-* } .-3 } */
+}
+
+void krb5_recvauth(krb5_data inbuf_a)
+{
+ recvauth_common(inbuf_a);
+}
+
+void krb5_recvauth_version(krb5_data inbuf_b)
+{
+ recvauth_common(inbuf_b);
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/CVE-2005-1689-dedupe-issue.c b/gcc/testsuite/gcc.dg/analyzer/CVE-2005-1689-dedupe-issue.c
index b43148cb4a7..0fc865f599b 100644
--- a/gcc/testsuite/gcc.dg/analyzer/CVE-2005-1689-dedupe-issue.c
+++ b/gcc/testsuite/gcc.dg/analyzer/CVE-2005-1689-dedupe-issue.c
@@ -14,7 +14,7 @@ recvauth_common(krb5_data inbuf)
{
free(inbuf.data);
free(inbuf.data); /* { dg-warning "double-'free'" "warning" } */
- /* { dg-message "2 duplicates" "duplicates notification" { target *-*-* } .-1 } */
+ /* { dg-message "2 duplicates" "duplicates notification" { xfail *-*-* } .-1 } */
}
void krb5_recvauth(krb5_data inbuf)
diff --git a/gcc/testsuite/gcc.dg/analyzer/CVE-2005-1689-minimal.c b/gcc/testsuite/gcc.dg/analyzer/CVE-2005-1689-minimal.c
index aa9deb3be19..5edbdb1cc3e 100644
--- a/gcc/testsuite/gcc.dg/analyzer/CVE-2005-1689-minimal.c
+++ b/gcc/testsuite/gcc.dg/analyzer/CVE-2005-1689-minimal.c
@@ -1,4 +1,5 @@
#include <stdlib.h>
+#include "analyzer-decls.h"
typedef struct _krb5_data {
char *data;
@@ -28,3 +29,63 @@ test_3 (krb5_data inbuf, int flag)
}
free((char *)inbuf.data); /* { dg-warning "double-'free' of 'inbuf.data'" } */
}
+
+extern void unknown_fn (void *);
+
+void
+test_4 (krb5_data inbuf)
+{
+ unknown_fn (NULL);
+ free(inbuf.data); /* { dg-message "first 'free' here" } */
+ free(inbuf.data); /* { dg-warning "double-'free' of 'inbuf.data'" } */
+}
+
+void
+test_5 (krb5_data inbuf)
+{
+ unknown_fn (&inbuf);
+ free(inbuf.data); /* { dg-message "first 'free' here" } */
+ free(inbuf.data); /* { dg-warning "double-'free' of 'inbuf.data'" "inbuf.data" } */
+ /* { dg-bogus "double-'free' of 'inbuf'" "inbuf" { target *-*-* } .-1 } */
+}
+
+typedef struct _padded_krb5_data {
+ int pad;
+ char *data;
+} padded_krb5_data;
+
+void
+test_6 (padded_krb5_data inbuf)
+{
+ unknown_fn (&inbuf.data);
+ free((char *)inbuf.data); /* { dg-message "first 'free' here" } */
+ free((char *)inbuf.data); /* { dg-warning "double-'free' of 'inbuf.data'" "inbuf.data" } */
+}
+
+void
+test_7 (padded_krb5_data inbuf)
+{
+ unknown_fn (&inbuf.data);
+ free((char *)inbuf.data);
+
+ unknown_fn (&inbuf.data);
+ free((char *)inbuf.data);
+}
+
+void
+test_8 (padded_krb5_data inbuf, int flag)
+{
+ if (flag)
+ {
+ unknown_fn (&inbuf.data);
+ free((char *)inbuf.data);
+ }
+ /* Should have two enodes, one for the explicit "freed" state, and one
+ for the implicit "start" state. */
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 processed enodes" } */
+
+ unknown_fn (&inbuf.data);
+
+ /* Should have just one enode, for the implicit "start" state. */
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 processed enode" } */
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/abs-1.c b/gcc/testsuite/gcc.dg/analyzer/abs-1.c
new file mode 100644
index 00000000000..d6ce8d6afad
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/abs-1.c
@@ -0,0 +1,22 @@
+#include "analyzer-decls.h"
+
+extern long int labs (long int x)
+ __attribute__ ((__nothrow__ , __leaf__))
+ __attribute__ ((__const__));
+
+long int test_1 (long int x)
+{
+ return labs (x);
+}
+
+static long __attribute__((noinline))
+hide_long (long x)
+{
+ return x;
+}
+
+long int test_2 (long int x)
+{
+ __analyzer_eval (labs (hide_long (42)) == 42); /* { dg-warning "TRUE" } */
+ __analyzer_eval (labs (hide_long (-17)) == 17); /* { dg-warning "TRUE" } */
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/aliasing-1.c b/gcc/testsuite/gcc.dg/analyzer/aliasing-1.c
new file mode 100644
index 00000000000..26050f1eee3
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/aliasing-1.c
@@ -0,0 +1,25 @@
+#include "analyzer-decls.h"
+
+int a;
+void test (int *p, int x)
+{
+ int y;
+
+ a = 17;
+ x = 42;
+ y = 13;
+
+ __analyzer_eval (a == 17); /* { dg-warning "TRUE" } */
+ __analyzer_eval (x == 42); /* { dg-warning "TRUE" } */
+ __analyzer_eval (y == 13); /* { dg-warning "TRUE" } */
+
+ __analyzer_eval (p == &a); /* { dg-warning "UNKNOWN" } */
+ __analyzer_eval (p == &x); /* { dg-warning "FALSE" } */
+ __analyzer_eval (p == &y); /* { dg-warning "FALSE" } */
+
+ *p = 73;
+
+ __analyzer_eval (a == 17); /* { dg-warning "UNKNOWN" } */
+ __analyzer_eval (x == 42); /* { dg-warning "TRUE" } */
+ __analyzer_eval (y == 13); /* { dg-warning "TRUE" } */
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/aliasing-2.c b/gcc/testsuite/gcc.dg/analyzer/aliasing-2.c
new file mode 100644
index 00000000000..38ceeffbe37
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/aliasing-2.c
@@ -0,0 +1,32 @@
+#include "analyzer-decls.h"
+
+extern void escape (int *p);
+
+int a;
+void test (int *p, int x)
+{
+ int y;
+
+ a = 17;
+ x = 42;
+ y = 13;
+
+ __analyzer_eval (a == 17); /* { dg-warning "TRUE" } */
+ __analyzer_eval (x == 42); /* { dg-warning "TRUE" } */
+ __analyzer_eval (y == 13); /* { dg-warning "TRUE" } */
+
+ escape (&x);
+ __analyzer_eval (a == 17); /* { dg-warning "UNKNOWN" } */
+ __analyzer_eval (x == 42); /* { dg-warning "UNKNOWN" } */
+ __analyzer_eval (y == 13); /* { dg-warning "TRUE" } */
+
+ __analyzer_eval (p == &a); /* { dg-warning "UNKNOWN" } */
+ __analyzer_eval (p == &x); /* { dg-warning "FALSE" } */
+ __analyzer_eval (p == &y); /* { dg-warning "FALSE" } */
+
+ *p = 73;
+
+ __analyzer_eval (a == 17); /* { dg-warning "UNKNOWN" } */
+ __analyzer_eval (x == 42); /* { dg-warning "UNKNOWN" } */
+ __analyzer_eval (y == 13); /* { dg-warning "TRUE" } */
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/analyzer-decls.h b/gcc/testsuite/gcc.dg/analyzer/analyzer-decls.h
index 180e873b67c..d96b3f2f29a 100644
--- a/gcc/testsuite/gcc.dg/analyzer/analyzer-decls.h
+++ b/gcc/testsuite/gcc.dg/analyzer/analyzer-decls.h
@@ -7,6 +7,11 @@
/* Trigger a breakpoint in the analyzer when reached. */
extern void __analyzer_break (void);
+/* Emit a warning describing the 2nd argument (which can be of any
+ type), at the given verbosity level. This is for use when
+ debugging, and may be of use in DejaGnu tests. */
+extern void __analyzer_describe (int verbosity, ...);
+
/* Dump copious information about the analyzer’s state when reached. */
extern void __analyzer_dump (void);
@@ -20,8 +25,6 @@ extern void __analyzer_dump (void);
will also dump all of the states within those nodes. */
extern void __analyzer_dump_exploded_nodes (int);
-extern void __analyzer_dump_num_heap_regions (void);
-
/* Emit a placeholder "note" diagnostic with a path to this call site,
if the analyzer finds a feasible path to it. */
extern void __analyzer_dump_path (void);
diff --git a/gcc/testsuite/gcc.dg/analyzer/attribute-nonnull.c b/gcc/testsuite/gcc.dg/analyzer/attribute-nonnull.c
index e0bf1f4aa1c..70bb921e742 100644
--- a/gcc/testsuite/gcc.dg/analyzer/attribute-nonnull.c
+++ b/gcc/testsuite/gcc.dg/analyzer/attribute-nonnull.c
@@ -12,9 +12,10 @@ extern void bar(void *ptrA, void *ptrB, void *ptrC) /* { dg-message "argument 1
void test_1 (void *p, void *q, void *r)
{
foo(p, q, r);
- foo(NULL, q, r);
+ foo(NULL, q, r); /* { dg-warning "use of NULL where non-null expected" "warning" } */
+ /* { dg-message "argument 1 NULL where non-null expected" "note" { target *-*-* } .-1 } */
foo(p, NULL, r);
- foo(p, q, NULL);
+ foo(p, q, NULL); /* { dg-warning "use of NULL where non-null expected" } */
}
void test_1a (void *q, void *r)
@@ -27,9 +28,10 @@ void test_1a (void *q, void *r)
void test_2 (void *p, void *q, void *r)
{
bar(p, q, r);
- bar(NULL, q, r);
- bar(p, NULL, r);
- bar(p, q, NULL);
+ bar(NULL, q, r); /* { dg-warning "use of NULL where non-null expected" "warning" } */
+ bar(p, NULL, r); /* { dg-warning "use of NULL where non-null expected" "warning" } */
+ /* { dg-message "argument 2 NULL where non-null expected" "note" { target *-*-* } .-1 } */
+ bar(p, q, NULL); /* { dg-warning "use of NULL where non-null expected" "warning" } */
}
void test_3 (void *q, void *r)
diff --git a/gcc/testsuite/gcc.dg/analyzer/bzero-1.c b/gcc/testsuite/gcc.dg/analyzer/bzero-1.c
new file mode 100644
index 00000000000..894508611fe
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/bzero-1.c
@@ -0,0 +1,11 @@
+#include "analyzer-decls.h"
+
+extern void bzero(void *s, __SIZE_TYPE__ n);
+
+void test_1 (void)
+{
+ char tmp[1024];
+ bzero (tmp, 1024);
+ __analyzer_eval (tmp[0] == 0); /* { dg-warning "TRUE" } */
+ __analyzer_eval (tmp[1023] == 0); /* { dg-warning "TRUE" } */
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/casts-1.c b/gcc/testsuite/gcc.dg/analyzer/casts-1.c
new file mode 100644
index 00000000000..15cd85f77cf
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/casts-1.c
@@ -0,0 +1,49 @@
+#include "analyzer-decls.h"
+
+struct s1
+{
+ char a;
+ char b;
+ char c;
+ char d;
+};
+
+struct s2
+{
+ char arr[4];
+};
+
+void test_1 ()
+{
+ struct s1 x = {'A', 'B', 'C', 'D'};
+ __analyzer_eval (x.a == 'A'); /* { dg-warning "TRUE" } */
+ __analyzer_eval (x.b == 'B'); /* { dg-warning "TRUE" } */
+ __analyzer_eval (x.c == 'C'); /* { dg-warning "TRUE" } */
+ __analyzer_eval (x.d == 'D'); /* { dg-warning "TRUE" } */
+ __analyzer_eval (((struct s2 *)&x)->arr[0] == 'A'); /* { dg-warning "TRUE" } */
+ __analyzer_eval (((struct s2 *)&x)->arr[1] == 'B'); /* { dg-warning "TRUE" } */
+ __analyzer_eval (((struct s2 *)&x)->arr[2] == 'C'); /* { dg-warning "TRUE" } */
+ __analyzer_eval (((struct s2 *)&x)->arr[3] == 'D'); /* { dg-warning "TRUE" } */
+
+ ((struct s2 *)&x)->arr[1] = '#';
+ __analyzer_eval (((struct s2 *)&x)->arr[1] == '#'); /* { dg-warning "TRUE" } */
+ __analyzer_eval (x.b == '#'); /* { dg-warning "TRUE" } */
+}
+
+void test_2 ()
+{
+ struct s2 x = {{'A', 'B', 'C', 'D'}};
+ __analyzer_eval (x.arr[0] == 'A'); /* { dg-warning "TRUE" } */
+ __analyzer_eval (x.arr[1] == 'B'); /* { dg-warning "TRUE" } */
+ __analyzer_eval (x.arr[2] == 'C'); /* { dg-warning "TRUE" } */
+ __analyzer_eval (x.arr[3] == 'D'); /* { dg-warning "TRUE" } */
+ struct s1 *p = (struct s1 *)&x;
+ __analyzer_eval (p->a == 'A'); /* { dg-warning "TRUE" "true" { xfail *-*-* } } */
+ /* { dg-bogus "UNKNOWN" "unknown" { xfail *-*-* } .-1 } */
+ __analyzer_eval (p->b == 'B'); /* { dg-warning "TRUE" "true" { xfail *-*-* } } */
+ /* { dg-bogus "UNKNOWN" "unknown" { xfail *-*-* } .-1 } */
+ __analyzer_eval (p->c == 'C'); /* { dg-warning "TRUE" "true" { xfail *-*-* } } */
+ /* { dg-bogus "UNKNOWN" "unknown" { xfail *-*-* } .-1 } */
+ __analyzer_eval (p->d == 'D'); /* { dg-warning "TRUE" "true" { xfail *-*-* } } */
+ /* { dg-bogus "UNKNOWN" "unknown" { xfail *-*-* } .-1 } */
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/casts-2.c b/gcc/testsuite/gcc.dg/analyzer/casts-2.c
new file mode 100644
index 00000000000..3eef71726ba
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/casts-2.c
@@ -0,0 +1,15 @@
+#include "analyzer-decls.h"
+
+void test_1 (int i)
+{
+ char c1 = i;
+ char c2 = i;
+ __analyzer_eval (c1 == i); /* { dg-warning "UNKNOWN" } */
+ __analyzer_eval (c1 == c2); /* { dg-warning "TRUE" } */
+}
+
+void test_2 (char c)
+{
+ int i = c;
+ __analyzer_eval (i == c); /* { dg-warning "TRUE" } */
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/compound-assignment-1.c b/gcc/testsuite/gcc.dg/analyzer/compound-assignment-1.c
index bb03487f23e..0f07818dcdd 100644
--- a/gcc/testsuite/gcc.dg/analyzer/compound-assignment-1.c
+++ b/gcc/testsuite/gcc.dg/analyzer/compound-assignment-1.c
@@ -40,13 +40,13 @@ void test_4 (void)
struct ptr_wrapper r;
r.ptr = malloc (sizeof (int)); /* { dg-message "allocated here" } */
} /* { dg-warning "leak of 'r.ptr'" } */
-/* { dg-bogus "leak of '<unknown>'" "unknown leak" { xfail *-*-* } .-1 } */
+/* { dg-bogus "leak of '<unknown>'" "unknown leak" { target *-*-* } .-1 } */
static struct ptr_wrapper __attribute__((noinline))
called_by_test_5a (void)
{
struct ptr_wrapper r;
- r.ptr = malloc (sizeof (int));
+ r.ptr = malloc (sizeof (int)); /* { dg-message "allocated here" } */
return r;
}
@@ -54,15 +54,14 @@ void test_5a (void)
{
struct ptr_wrapper q = called_by_test_5a ();
} /* { dg-warning "leak of 'q.ptr'" } */
-/* TODO: show the allocation point. */
static struct ptr_wrapper __attribute__((noinline))
called_by_test_5b (void)
{
struct ptr_wrapper r;
r.ptr = malloc (sizeof (int));
- return r; /* { dg-warning "leak" } */
- /* TODO: show the allocation point. */
+ return r; /* { dg-warning "leak of '<return-value>.ptr'" } */
+ /* TODO: show the allocation point; improve above message. */
}
void test_5b (void)
diff --git a/gcc/testsuite/gcc.dg/analyzer/compound-assignment-3.c b/gcc/testsuite/gcc.dg/analyzer/compound-assignment-3.c
index 5083faa9ce2..49259262c4a 100644
--- a/gcc/testsuite/gcc.dg/analyzer/compound-assignment-3.c
+++ b/gcc/testsuite/gcc.dg/analyzer/compound-assignment-3.c
@@ -22,4 +22,4 @@ test_2 (void)
{
struct union_wrapper uw2;
uw2.u.ptr = malloc (1024);
-} /* { dg-warning "leak of '\\(void \\*\\)uw2.u'" } */
+} /* { dg-warning "leak of 'uw2.u.ptr'" } */
diff --git a/gcc/testsuite/gcc.dg/analyzer/compound-assignment-4.c b/gcc/testsuite/gcc.dg/analyzer/compound-assignment-4.c
new file mode 100644
index 00000000000..5c0a5f9d47c
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/compound-assignment-4.c
@@ -0,0 +1,28 @@
+#include "analyzer-decls.h"
+
+struct coord
+{
+ int x;
+ int y;
+};
+
+void test_1 (void)
+{
+ struct coord arr[16];
+
+ arr[2].y = 4;
+ arr[3].x = 5;
+ arr[3].y = 6;
+ arr[4].x = 7;
+ arr[6].y = 8;
+ arr[8].x = 9;
+
+ arr[7] = arr[3];
+
+ __analyzer_eval (arr[7].x == 5); /* { dg-warning "TRUE" } */
+ __analyzer_eval (arr[7].y == 6); /* { dg-warning "TRUE" } */
+
+ /* Make sure we don't touch the neighbors. */
+ __analyzer_eval (arr[6].y == 8); /* { dg-warning "TRUE" } */
+ __analyzer_eval (arr[8].x == 9); /* { dg-warning "TRUE" } */
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/compound-assignment-5.c b/gcc/testsuite/gcc.dg/analyzer/compound-assignment-5.c
new file mode 100644
index 00000000000..ccf8fe392bf
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/compound-assignment-5.c
@@ -0,0 +1,142 @@
+#include "analyzer-decls.h"
+
+struct coord
+{
+ int x;
+ int y;
+};
+
+/* Copying from one on-stack array to another. */
+
+void test_1 (void)
+{
+ struct coord arr_a[16];
+ struct coord arr_b[16];
+ arr_a[3].x = 5;
+ arr_a[3].y = 6;
+
+ arr_b[7] = arr_a[3];
+
+ __analyzer_eval (arr_b[7].x == 5); /* { dg-warning "TRUE" } */
+ __analyzer_eval (arr_b[7].y == 6); /* { dg-warning "TRUE" } */
+}
+
+/* Copying from an on-stack array to a global array. */
+
+struct coord glob_arr[16];
+
+void test_2 (void)
+{
+ struct coord arr[16];
+ arr[3].x = 5;
+ arr[3].y = 6;
+
+ glob_arr[7] = arr[3];
+
+ __analyzer_eval (glob_arr[7].x == 5); /* { dg-warning "TRUE" } */
+ __analyzer_eval (glob_arr[7].y == 6); /* { dg-warning "TRUE" } */
+}
+
+/* Copying from a partially initialized on-stack array to a global array. */
+
+struct coord glob_arr[16];
+
+void test_3 (void)
+{
+ struct coord arr[16];
+ arr[3].y = 6;
+
+ glob_arr[7] = arr[3]; // or should the uninit warning be here?
+
+ __analyzer_eval (glob_arr[7].x); /* { dg-warning "uninitialized" "uninit" { xfail *-*-* } } */
+ /* { dg-bogus "UNKNOWN" "unknown" { xfail *-*-* } .-1 } */
+ __analyzer_eval (glob_arr[7].y == 6); /* { dg-warning "TRUE" } */
+}
+
+/* Symbolic bindings: copying from one array to another. */
+
+struct coord glob_arr[16];
+
+void test_4 (int i)
+{
+ struct coord arr_a[16];
+ struct coord arr_b[16];
+ arr_a[i].x = 5;
+ arr_a[i].y = 6;
+ __analyzer_eval (arr_a[i].x == 5); /* { dg-warning "TRUE" "TRUE" { xfail *-*-* } } */
+ /* { dg-bogus "UNKNOWN" "UNKNOWN" { xfail *-*-* } .-1 } */
+ __analyzer_eval (arr_a[i].y == 6); /* { dg-warning "TRUE" } */
+
+ arr_b[i] = arr_a[i];
+
+ __analyzer_eval (arr_b[i].x == 5); /* { dg-warning "TRUE" "TRUE" { xfail *-*-* } } */
+ /* { dg-bogus "UNKNOWN" "UNKNOWN" { xfail *-*-* } .-1 } */
+ __analyzer_eval (arr_b[i].y == 6); /* { dg-warning "TRUE" "TRUE" { xfail *-*-* } } */
+ /* { dg-bogus "UNKNOWN" "UNKNOWN" { xfail *-*-* } .-1 } */
+}
+
+/* Symbolic bindings: copying within an array: symbolic src and dest */
+
+struct coord glob_arr[16];
+
+void test_5a (int i, int j)
+{
+ struct coord arr[16];
+ arr[i].x = 5;
+ arr[i].y = 6;
+
+ arr[j] = arr[i];
+
+ __analyzer_eval (arr[j].x == 5); /* { dg-warning "TRUE" "TRUE" { xfail *-*-* } } */
+ /* { dg-bogus "UNKNOWN" "UNKNOWN" { xfail *-*-* } .-1 } */
+ __analyzer_eval (arr[j].y == 6); /* { dg-warning "TRUE" "TRUE" { xfail *-*-* } } */
+ /* { dg-bogus "UNKNOWN" "UNKNOWN" { xfail *-*-* } .-1 } */
+}
+
+/* Symbolic bindings: copying within an array: symbolic src, concrete dest. */
+
+struct coord glob_arr[16];
+
+void test_5b (int i)
+{
+ struct coord arr[16];
+ arr[i].x = 5;
+ arr[i].y = 6;
+
+ arr[3] = arr[i];
+
+ __analyzer_eval (arr[3].x == 5); /* { dg-warning "TRUE" "TRUE" { xfail *-*-* } } */
+ /* { dg-bogus "UNKNOWN" "UNKNOWN" { xfail *-*-* } .-1 } */
+ __analyzer_eval (arr[3].y == 6); /* { dg-warning "TRUE" "TRUE" { xfail *-*-* } } */
+ /* { dg-bogus "UNKNOWN" "UNKNOWN" { xfail *-*-* } .-1 } */
+}
+
+/* Symbolic bindings: copying within an array: concrete src, symbolic dest. */
+
+struct coord glob_arr[16];
+
+void test_5c (int i)
+{
+ struct coord arr[16];
+ arr[3].x = 5;
+ arr[3].y = 6;
+
+ arr[i] = arr[3];
+
+ __analyzer_eval (arr[i].x == 5); /* { dg-warning "TRUE" "TRUE" { xfail *-*-* } } */
+ /* { dg-bogus "UNKNOWN" "UNKNOWN" { xfail *-*-* } .-1 } */
+ __analyzer_eval (arr[i].y == 6); /* { dg-warning "TRUE" "TRUE" { xfail *-*-* } } */
+ /* { dg-bogus "UNKNOWN" "UNKNOWN" { xfail *-*-* } .-1 } */
+}
+
+/* No info on the subregion being copied, and hence
+ binding_cluster2::maybe_get_compound_binding should return NULL. */
+
+void test_6 (void)
+{
+ struct coord arr[16];
+ arr[7] = glob_arr[3];
+
+ __analyzer_eval (arr[7].x == 5); /* { dg-warning "UNKNOWN" } */
+ __analyzer_eval (arr[7].y == 6); /* { dg-warning "UNKNOWN" } */
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/conditionals-notrans.c b/gcc/testsuite/gcc.dg/analyzer/conditionals-notrans.c
index a00127b1a7f..b1ac541920f 100644
--- a/gcc/testsuite/gcc.dg/analyzer/conditionals-notrans.c
+++ b/gcc/testsuite/gcc.dg/analyzer/conditionals-notrans.c
@@ -7,15 +7,12 @@ void test (int i, int j)
{
__analyzer_eval (i > 4); /* { dg-warning "TRUE" } */
__analyzer_eval (i <= 4); /* { dg-warning "FALSE" } */
- __analyzer_eval (i > 3); /* { dg-warning "TRUE" "desired" { xfail *-*-* } } */
- /* { dg-bogus "UNKNOWN" "status quo" { xfail *-*-* } .-1 } */
+ __analyzer_eval (i > 3); /* { dg-warning "TRUE" } */
__analyzer_eval (i > 5); /* { dg-warning "UNKNOWN" } */
- __analyzer_eval (i != 3); /* { dg-warning "TRUE" "desired" { xfail *-*-* } } */
- /* { dg-bogus "UNKNOWN" "status quo" { xfail *-*-* } .-1 } */
+ __analyzer_eval (i != 3); /* { dg-warning "TRUE" } */
- __analyzer_eval (i == 3); /* { dg-warning "FALSE" "desired" { xfail *-*-* } } */
- /* { dg-bogus "UNKNOWN" "status quo" { xfail *-*-* } .-1 } */
+ __analyzer_eval (i == 3); /* { dg-warning "FALSE" } */
__analyzer_eval (i != 4); /* { dg-warning "TRUE" } */
__analyzer_eval (i == 4); /* { dg-warning "FALSE" } */
@@ -43,21 +40,17 @@ void test (int i, int j)
__analyzer_eval (i <= 4); /* { dg-warning "TRUE" } */
__analyzer_eval (i > 3); /* { dg-warning "UNKNOWN" } */
- __analyzer_eval (i > 5); /* { dg-warning "FALSE" "desired" { xfail *-*-* } } */
- /* { dg-bogus "UNKNOWN" "status quo" { xfail *-*-* } .-1 } */
+ __analyzer_eval (i > 5); /* { dg-warning "FALSE" } */
__analyzer_eval (i != 3); /* { dg-warning "UNKNOWN" } */
__analyzer_eval (i == 3); /* { dg-warning "UNKNOWN" } */
__analyzer_eval (i != 4); /* { dg-warning "UNKNOWN" } */
__analyzer_eval (i == 4); /* { dg-warning "UNKNOWN" } */
- __analyzer_eval (i == 5); /* { dg-warning "FALSE" "desired" { xfail *-*-* } } */
- /* { dg-bogus "UNKNOWN" "status quo" { xfail *-*-* } .-1 } */
- __analyzer_eval (i != 5); /* { dg-warning "TRUE" "desired" { xfail *-*-* } } */
- /* { dg-bogus "UNKNOWN" "status quo" { xfail *-*-* } .-1 } */
+ __analyzer_eval (i == 5); /* { dg-warning "FALSE" } */
+ __analyzer_eval (i != 5); /* { dg-warning "TRUE" } */
__analyzer_eval (i < 5); /* { dg-warning "TRUE" } */
- __analyzer_eval (i <= 5); /* { dg-warning "TRUE" "desired" { xfail *-*-* } } */
- /* { dg-bogus "UNKNOWN" "status quo" { xfail *-*-* } .-1 } */
+ __analyzer_eval (i <= 5); /* { dg-warning "TRUE" } */
}
}
@@ -101,8 +94,7 @@ void test_range_int_gt_lt (int i)
{
if (i > 3)
if (i < 5)
- __analyzer_eval (i == 4); /* { dg-warning "TRUE" "desired" { xfail *-*-* } } */
- /* { dg-bogus "UNKNOWN" "status quo" { xfail *-*-* } .-1 } */
+ __analyzer_eval (i == 4); /* { dg-warning "TRUE" } */
}
void test_range_float_gt_lt (float f)
@@ -116,8 +108,7 @@ void test_range_int_ge_lt (int i)
{
if (i >= 4)
if (i < 5)
- __analyzer_eval (i == 4); /* { dg-warning "TRUE" "desired" { xfail *-*-* } } */
- /* { dg-bogus "UNKNOWN" "status quo" { xfail *-*-* } .-1 } */
+ __analyzer_eval (i == 4); /* { dg-warning "TRUE" } */
}
void test_range_float_ge_lt (float f)
@@ -131,8 +122,7 @@ void test_range_int_gt_le (int i)
{
if (i > 3)
if (i <= 4)
- __analyzer_eval (i == 4); /* { dg-warning "TRUE" "desired" { xfail *-*-* } } */
- /* { dg-bogus "UNKNOWN" "status quo" { xfail *-*-* } .-1 } */
+ __analyzer_eval (i == 4); /* { dg-warning "TRUE" } */
}
void test_range_float_gt_le (float f)
@@ -146,8 +136,7 @@ void test_range_int_ge_le (int i)
{
if (i >= 4)
if (i <= 4)
- __analyzer_eval (i == 4); /* { dg-warning "TRUE" "desired" { xfail *-*-* } } */
- /* { dg-bogus "UNKNOWN" "status quo" { xfail *-*-* } .-1 } */
+ __analyzer_eval (i == 4); /* { dg-warning "TRUE" } */
}
void test_range_float_ge_le (float f)
diff --git a/gcc/testsuite/gcc.dg/analyzer/data-model-1.c b/gcc/testsuite/gcc.dg/analyzer/data-model-1.c
index 0a906e5b62e..3f16a38ab14 100644
--- a/gcc/testsuite/gcc.dg/analyzer/data-model-1.c
+++ b/gcc/testsuite/gcc.dg/analyzer/data-model-1.c
@@ -181,15 +181,8 @@ int test_12d (struct coord c)
{
struct coord d;
d = c;
- __analyzer_eval (d.x == c.x); /* { dg-warning "TRUE" "desired" { xfail *-*-* } } */
- /* { dg-warning "UNKNOWN" "actual" { target *-*-* } .-1 } */
- /* TODO(xfail): c and d share the same unknown value of type "coord", but
- attempts to access the fields lead to different unknown values. */
-
- __analyzer_eval (d.y == c.y); /* { dg-warning "TRUE" "desired" { xfail *-*-* } } */
- /* { dg-warning "UNKNOWN" "actual" { target *-*-* } .-1 } */
- // TODO(xfail): likewise
-
+ __analyzer_eval (d.x == c.x); /* { dg-warning "TRUE" } */
+ __analyzer_eval (d.y == c.y); /* { dg-warning "TRUE" } */
__analyzer_eval (d.x == d.y); /* { dg-warning "UNKNOWN" } */
/* d and c share an unknown value of type "struct coord".
But d.x and d.y should be different unknown values (although they inherit
@@ -211,25 +204,21 @@ void test_13 (struct outer *o)
{
__analyzer_eval (o->mid.in.f == 0.f); /* { dg-warning "UNKNOWN" } */
o->mid.in.f = 0.f;
- __analyzer_eval (o->mid.in.f == 0.f); /* { dg-warning "TRUE" "PR 93356" { xfail *-*-* } } */
- /* { dg-warning "UNKNOWN" "disabled float comparisons" { target *-*-* } .-1 } */
+ __analyzer_eval (o->mid.in.f == 0.f); /* { dg-warning "TRUE" } */
}
void test_14 (struct outer o)
{
__analyzer_eval (o.mid.in.f == 0.f); /* { dg-warning "UNKNOWN" } */
o.mid.in.f = 0.f;
- __analyzer_eval (o.mid.in.f == 0.f); /* { dg-warning "TRUE" "PR 93356" { xfail *-*-* } } */
- /* { dg-warning "UNKNOWN" "disabled float comparisons" { target *-*-* } .-1 } */
+ __analyzer_eval (o.mid.in.f == 0.f); /* { dg-warning "TRUE" } */
}
void test_15 (const char *str)
{
char ch = str[0];
__analyzer_eval (ch == 'a'); /* { dg-warning "UNKNOWN" } */
- __analyzer_eval (ch == str[0]); /* { dg-warning "TRUE" "desired" { xfail *-*-* } } */
- /* { dg-warning "UNKNOWN" "status quo" { target *-*-* } .-1 } */
- // TODO(xfail)
+ __analyzer_eval (ch == str[0]); /* { dg-warning "TRUE" } */
ch = 'a';
__analyzer_eval (ch == 'a'); /* { dg-warning "TRUE" } */
@@ -242,15 +231,15 @@ void test_16 (void)
__analyzer_eval (msg != NULL); /* { dg-warning "TRUE" } */
- __analyzer_eval (msg[0] == 'h'); /* { dg-warning "TRUE" "desired" { xfail *-*-* } } */
- /* { dg-warning "UNKNOWN" "status quo" { target *-*-* } .-1 } */
- // TODO(xfail)
+ __analyzer_eval (msg[0] == 'h'); /* { dg-warning "TRUE" } */
- __analyzer_eval (msg[1] == 'e'); /* { dg-warning "TRUE" "desired" { xfail *-*-* } } */
- /* { dg-warning "UNKNOWN" "status quo" { target *-*-* } .-1 } */
- // TODO(xfail)
+ __analyzer_eval (msg[1] == 'e'); /* { dg-warning "TRUE" } */
__analyzer_eval (strlen (msg) == 11); /* { dg-warning "TRUE" } */
+
+ /* Out-of-bounds. */
+ __analyzer_eval (msg[100] == 'e'); /* { dg-warning "UNKNOWN" } */
+ // TODO: some kind of warning for the out-of-bounds access
}
static const char *__attribute__((noinline))
@@ -265,13 +254,9 @@ void test_16_alt (void)
__analyzer_eval (msg != NULL); /* { dg-warning "TRUE" } */
- __analyzer_eval (msg[0] == 'h'); /* { dg-warning "TRUE" "desired" { xfail *-*-* } } */
- /* { dg-warning "UNKNOWN" "status quo" { target *-*-* } .-1 } */
- // TODO(xfail)
+ __analyzer_eval (msg[0] == 'h'); /* { dg-warning "TRUE" } */
- __analyzer_eval (msg[1] == 'e'); /* { dg-warning "TRUE" "desired" { xfail *-*-* } } */
- /* { dg-warning "UNKNOWN" "status quo" { target *-*-* } .-1 } */
- // TODO(xfail)
+ __analyzer_eval (msg[1] == 'e'); /* { dg-warning "TRUE" } */
__analyzer_eval (strlen (msg) == 11); /* { dg-warning "TRUE" } */
}
@@ -334,9 +319,6 @@ void test_16e (int i)
__analyzer_eval (j == i); /* { dg-warning "UNKNOWN" } */
}
-/* TODO: and more complicated graph-like examples, where anything that's
- reachable from the pointer might be modified. */
-
void test_17 (int i)
{
int j = 42;
@@ -477,13 +459,7 @@ void test_23 (struct foo *f, struct foo *g)
i = f->i + g->i;
j = f->i + g->i;
k = f->i * g->i;
- __analyzer_eval (i == j); /* { dg-warning "TRUE" "desired" { xfail *-*-* } } */
- /* { dg-warning "UNKNOWN" "status quo" { target *-*-* } .-1 } */
- /* TODO(xfail): we'd need to record that the two unknown values are both
- the sum of the two unknown input values (and thus are the same); not
- yet sure if we want arbitrary expression trees in the representation
- (analysis termination concerns). */
-
+ __analyzer_eval (i == j); /* { dg-warning "TRUE" } */
__analyzer_eval (i == k); /* { dg-warning "UNKNOWN" } */
}
@@ -496,9 +472,7 @@ void test_24 (struct foo *f)
/* Overwriting a whole struct should invalidate our knowledge
about fields within it. */
g = *f;
- __analyzer_eval (g.i == 42); /* { dg-warning "UNKNOWN" "desired" { xfail *-*-* } } */
- /* { dg-warning "TRUE" "status quo" { target *-*-* } .-1 } */
- // TODO(xfail)
+ __analyzer_eval (g.i == 42); /* { dg-warning "UNKNOWN" } */
}
void test_25 (struct foo *f)
@@ -513,16 +487,14 @@ void test_25 (struct foo *f)
source value should update our knowledge about fields within
the dest value. */
g = *f;
- __analyzer_eval (g.i == 43); /* { dg-warning "TRUE" "desired" { xfail *-*-* } } */
- /* { dg-warning "FALSE" "status quo" { target *-*-* } .-1 } */
- // TODO(xfail)
+ __analyzer_eval (g.i == 43); /* { dg-warning "TRUE" } */
}
void test_26 (struct coord *p, struct coord *q)
{
p->x = 42;
- q->y = 17;
- __analyzer_eval (p->x == 42); /* { dg-warning "TRUE" } */
+ q->y = 17; /* could clobber p->x. */
+ __analyzer_eval (p->x == 42); /* { dg-warning "UNKNOWN" } */
__analyzer_eval (p->y); /* { dg-warning "UNKNOWN" } */
__analyzer_eval (q->x); /* { dg-warning "UNKNOWN" } */
__analyzer_eval (q->y == 17); /* { dg-warning "TRUE" } */
@@ -531,26 +503,21 @@ void test_26 (struct coord *p, struct coord *q)
source value should update our knowledge about fields within
the dest value. */
*p = *q;
- __analyzer_eval (p->x); /* { dg-warning "UNKNOWN" "desired" { xfail *-*-* } } */
- /* { dg-warning "TRUE" "status quo" { target *-*-* } .-1 } */
- // TODO(xfail): should have been overwritten
+ __analyzer_eval (p->x); /* { dg-warning "UNKNOWN" } */
__analyzer_eval (p->y == 17); /* { dg-warning "TRUE" "desired" { xfail *-*-* } } */
/* { dg-warning "UNKNOWN" "status quo" { target *-*-* } .-1 } */
// TODO(xfail): should have been overwritten with q->y
__analyzer_eval (q->x); /* { dg-warning "UNKNOWN" } */
- __analyzer_eval (q->y == 17); /* { dg-warning "TRUE" } */
+ __analyzer_eval (q->y == 17); /* { dg-warning "TRUE" "desired" { xfail *-*-* } } */
+ /* { dg-warning "UNKNOWN" "status quo" { target *-*-* } .-1 } */
}
void test_27 (struct coord *p)
{
memset (p, 0, sizeof (struct coord));
- __analyzer_eval (p->x == 0); /* { dg-warning "TRUE" "desired" { xfail *-*-* } } */
- /* { dg-warning "UNKNOWN" "status quo" { target *-*-* } .-1 } */
- // TODO(xfail):
- __analyzer_eval (p->y == 0); /* { dg-warning "TRUE" "desired" { xfail *-*-* } } */
- /* { dg-warning "UNKNOWN" "status quo" { target *-*-* } .-1 } */
- // TODO(xfail):
+ __analyzer_eval (p->x == 0); /* { dg-warning "TRUE" } */
+ __analyzer_eval (p->y == 0); /* { dg-warning "TRUE" } */
}
void test_28 (struct coord *p)
@@ -663,6 +630,8 @@ void test_29a (struct coord p[])
__analyzer_eval (q[-2].y == 107025); /* { dg-warning "TRUE" } */
q -= 2;
+ __analyzer_eval (q == &p[7]); /* { dg-warning "UNKNOWN" } */
+ // TODO: make this be TRUE
__analyzer_eval (q->x == 107024); /* { dg-warning "TRUE" } */
__analyzer_eval (q->y == 107025); /* { dg-warning "TRUE" } */
@@ -711,6 +680,7 @@ void test_29b (void)
__analyzer_eval (q[-2].y == 107025); /* { dg-warning "TRUE" } */
q -= 2;
+ __analyzer_eval (q == &p[7]); /* { dg-warning "TRUE" } */
__analyzer_eval (q->x == 107024); /* { dg-warning "TRUE" } */
__analyzer_eval (q->y == 107025); /* { dg-warning "TRUE" } */
@@ -759,6 +729,7 @@ void test_29c (int len)
__analyzer_eval (q[-2].y == 107025); /* { dg-warning "TRUE" } */
q -= 2;
+ __analyzer_eval (q == &p[7]); /* { dg-warning "TRUE" } */
__analyzer_eval (q->x == 107024); /* { dg-warning "TRUE" } */
__analyzer_eval (q->y == 107025); /* { dg-warning "TRUE" } */
@@ -936,12 +907,12 @@ void test_41 (void)
union u u;
u.i = 42;
__analyzer_eval (u.i == 42); /* { dg-warning "TRUE" } */
- __analyzer_eval (u.ptr == NULL); /* { dg-warning "UNKNOWN" } */
+ __analyzer_eval (u.ptr == NULL); /* { dg-warning "UNKNOWN|FALSE" } */
/* Writes to a union member should invalidate knowledge about other members. */
u.ptr = NULL;
__analyzer_eval (u.ptr == NULL); /* { dg-warning "TRUE" } */
- __analyzer_eval (u.i == 42); /* { dg-warning "UNKNOWN" } */
+ __analyzer_eval (u.i == 42); /* { dg-warning "UNKNOWN|FALSE" } */
}
void test_42 (void)
@@ -950,8 +921,7 @@ void test_42 (void)
float f;
i = 42;
f = i;
- __analyzer_eval (f == 42.0); /* { dg-warning "TRUE" "PR 93356" { xfail *-*-* } } */
- /* { dg-warning "UNKNOWN" "disabled float comparisons" { target *-*-* } .-1 } */
+ __analyzer_eval (f == 42.0); /* { dg-warning "TRUE" } */
}
void test_43 (void)
@@ -1063,10 +1033,8 @@ void test_51 (struct coord c)
{
struct coord d;
memcpy (&d, &c, sizeof (struct coord));
- __analyzer_eval (c.x == d.x); /* { dg-warning "TRUE" "desired" { xfail *-*-* } } */
- /* { dg-warning "UNKNOWN" "status quo" { target *-*-* } .-1 } */
- __analyzer_eval (c.y == d.y); /* { dg-warning "TRUE" "desired" { xfail *-*-* } } */
- /* { dg-warning "UNKNOWN" "status quo" { target *-*-* } .-1 } */
+ __analyzer_eval (c.x == d.x); /* { dg-warning "TRUE" } */
+ __analyzer_eval (c.y == d.y); /* { dg-warning "TRUE" } */
}
struct big
diff --git a/gcc/testsuite/gcc.dg/analyzer/data-model-13.c b/gcc/testsuite/gcc.dg/analyzer/data-model-13.c
index e7cb8455b7d..31c1896b0c2 100644
--- a/gcc/testsuite/gcc.dg/analyzer/data-model-13.c
+++ b/gcc/testsuite/gcc.dg/analyzer/data-model-13.c
@@ -14,8 +14,5 @@ void test_1 (void)
void test_2 (void)
{
global_union.ptr_val = malloc (1024); /* { dg-message "allocated here" } */
- global_union.int_val = 0;
-} /* { dg-warning "leak of '<unknown>' " } */
-/* TODO: something better than "<unknown>". */
-/* TODO: better location for the leak. */
-
+ global_union.int_val = 0; /* { dg-warning "leak of 'global_union.ptr_val' " } */
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/data-model-14.c b/gcc/testsuite/gcc.dg/analyzer/data-model-14.c
index f9bb540b022..1dbcb701123 100644
--- a/gcc/testsuite/gcc.dg/analyzer/data-model-14.c
+++ b/gcc/testsuite/gcc.dg/analyzer/data-model-14.c
@@ -1,6 +1,3 @@
-/* FIXME: we shouldn't need this. */
-/* { dg-additional-options "-fanalyzer-fine-grained" } */
-
#include <stdlib.h>
void *global_ptr;
@@ -8,14 +5,12 @@ void *global_ptr;
void test_1 (int i)
{
global_ptr = malloc (1024); /* { dg-message "allocated here" } */
- *(int *)&global_ptr = i; /* { dg-warning "leak of '<unknown>'" } */
- // TODO: something better than "<unknown>" here ^^^
+ *(int *)&global_ptr = i; /* { dg-warning "leak of 'global_ptr'" } */
}
void test_2 (int i)
{
- void *p = malloc (1024); /* { dg-message "allocated here" "" { xfail *-*-* } } */
- // TODO(xfail)
+ void *p = malloc (1024); /* { dg-message "allocated here" } */
global_ptr = p;
*(int *)&p = i;
p = global_ptr;
diff --git a/gcc/testsuite/gcc.dg/analyzer/data-model-18.c b/gcc/testsuite/gcc.dg/analyzer/data-model-18.c
index 0a9ae9ff4d4..86e8110ccf6 100644
--- a/gcc/testsuite/gcc.dg/analyzer/data-model-18.c
+++ b/gcc/testsuite/gcc.dg/analyzer/data-model-18.c
@@ -16,7 +16,5 @@ void test (int *p, int i, int j)
__analyzer_eval (p[3] == 42); /* { dg-warning "UNKNOWN" } */
__analyzer_eval (p[i] == 17); /* { dg-warning "TRUE" } */
- __analyzer_eval (p[j] == 17); /* { dg-warning "UNKNOWN" "desired" { xfail *-*-* } } */
- /* { dg-bogus "TRUE" "status quo" { xfail *-*-* } .-1 } */
- // FIXME(xfails) ^^^
+ __analyzer_eval (p[j] == 17); /* { dg-warning "UNKNOWN" } */
}
diff --git a/gcc/testsuite/gcc.dg/analyzer/data-model-20.c b/gcc/testsuite/gcc.dg/analyzer/data-model-20.c
new file mode 100644
index 00000000000..8fdbb6b3df6
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/data-model-20.c
@@ -0,0 +1,25 @@
+/* { dg-additional-options "-Wno-analyzer-too-complex" } */
+
+#include <stdlib.h>
+
+struct foo { int dummy; };
+
+struct foo **
+test (int n) {
+ struct foo **arr;
+ int i;
+
+ if ((arr = (struct foo **)malloc(n * sizeof(struct foo *))) == NULL)
+ return NULL;
+
+ for (i = 0; i < n; i++) {
+ if ((arr[i] = (struct foo *)malloc(sizeof(struct foo))) == NULL) {
+ for (; i >= 0; i++) {
+ free(arr[i]); /* { dg-bogus "double-'free'" } */
+ }
+ free(arr);
+ return NULL;
+ }
+ }
+ return arr;
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/data-model-5.c b/gcc/testsuite/gcc.dg/analyzer/data-model-5.c
index 5cf334768d8..2135c70eb8d 100644
--- a/gcc/testsuite/gcc.dg/analyzer/data-model-5.c
+++ b/gcc/testsuite/gcc.dg/analyzer/data-model-5.c
@@ -90,11 +90,20 @@ void unref (base_obj *obj)
{
if (--obj->ob_refcnt == 0) /* { dg-bogus "dereference of uninitialized pointer 'obj'" } */
obj->ob_type->tp_dealloc (obj);
+ /* { dg-warning "dereference of NULL 'obj'" "deref of NULL" { target *-*-* } .-2 } */
+ /* FIXME: ideally we wouldn't issue this, as we've already issued a
+ warning about str_obj which is now in the "stop" state; the cast
+ confuses things. */
}
void test_1 (const char *str)
{
base_obj *obj = new_string_obj (str);
- //__analyzer_dump();
unref (obj);
-} /* { dg-bogus "leak" } */
+} /* { dg-bogus "leak" "" { xfail *-*-* } } */
+/* XFAIL (false leak):
+ Given that we only know "len" symbolically, this line:
+ str_obj->str_buf[len] = '\0';
+ is a symbolic write which could clobber the ob_type or ob_refcnt.
+ It reports a leak when following the path where the refcount is clobbered
+ to be a value that leads to the deallocator not being called. */
diff --git a/gcc/testsuite/gcc.dg/analyzer/data-model-5b.c b/gcc/testsuite/gcc.dg/analyzer/data-model-5b.c
index 76e351d86cb..cd6a4df00dd 100644
--- a/gcc/testsuite/gcc.dg/analyzer/data-model-5b.c
+++ b/gcc/testsuite/gcc.dg/analyzer/data-model-5b.c
@@ -86,8 +86,10 @@ void test_1 (const char *str)
//__analyzer_dump();
if (obj)
unref (obj);
-} /* { dg-bogus "leak of 'obj'" "" { xfail *-*-* } } */
-/* TODO(xfail): the false leak report involves the base_obj.ob_refcnt
- being 1, but the string_obj.str_base.ob_refcnt being unknown (when
- they ought to be the same region), thus allowing for a path in which
- the object is allocated but not freed. */
+} /* { dg-bogus "leak" "" { xfail *-*-* } } */
+/* XFAIL (false leak):
+ Given that we only know "len" symbolically, this line:
+ str_obj->str_buf[len] = '\0';
+ is a symbolic write which could clobber the ob_type or ob_refcnt.
+ It reports a leak when following the path where the refcount is clobbered
+ to be a value that leads to the deallocator not being called. */
diff --git a/gcc/testsuite/gcc.dg/analyzer/data-model-5c.c b/gcc/testsuite/gcc.dg/analyzer/data-model-5c.c
index 3aba7bdc2aa..ad4e1d2644d 100644
--- a/gcc/testsuite/gcc.dg/analyzer/data-model-5c.c
+++ b/gcc/testsuite/gcc.dg/analyzer/data-model-5c.c
@@ -75,9 +75,10 @@ void test_1 (const char *str)
string_obj *obj = new_string_obj (str);
if (obj)
unref (obj);
-} /* { dg-bogus "leak of 'obj'" "" { xfail *-*-* } } */
-/* TODO(xfail): the false leak report involves the base_obj.ob_refcnt
- being 1, but the string_obj.str_base.ob_refcnt being unknown (when
- they ought to be the same region), thus allowing for a path in which
- the object is allocated but not freed. */
-
+} /* { dg-bogus "leak" "" { xfail *-*-* } } */
+/* XFAIL (false leak):
+ Given that we only know "len" symbolically, this line:
+ str_obj->str_buf[len] = '\0';
+ is a symbolic write which could clobber the ob_type or ob_refcnt.
+ It reports a leak when following the path where the refcount is clobbered
+ to be a value that leads to the deallocator not being called. */
diff --git a/gcc/testsuite/gcc.dg/analyzer/data-model-5d.c b/gcc/testsuite/gcc.dg/analyzer/data-model-5d.c
index 8c7bfa91a96..b4d77a939bc 100644
--- a/gcc/testsuite/gcc.dg/analyzer/data-model-5d.c
+++ b/gcc/testsuite/gcc.dg/analyzer/data-model-5d.c
@@ -5,34 +5,33 @@
#include <stdlib.h>
#include "analyzer-decls.h"
-typedef struct base_obj base_obj;
-typedef struct type_obj type_obj;
-typedef struct string_obj string_obj;
-
-struct base_obj
+typedef struct base_obj
{
struct type_obj *ob_type;
int ob_refcnt;
-};
+} base_obj;
-struct type_obj
+typedef struct type_obj
{
base_obj tp_base;
-};
+ void (*tp_dealloc) (base_obj *);
+} type_obj;
-struct string_obj
+typedef struct boxed_int_obj
{
- base_obj str_base;
- size_t str_len;
- char str_buf[];
-};
+ base_obj int_base;
+ int int_val;
+} boxed_int_obj;
+
+extern void int_del (base_obj *);
type_obj type_type = {
- { &type_type, 1},
+ { &type_type, 1}
};
-type_obj str_type = {
- { &str_type, 1},
+type_obj boxed_int_type = {
+ { &type_type, 1},
+ int_del
};
base_obj *alloc_obj (type_obj *ob_type, size_t sz)
@@ -45,20 +44,28 @@ base_obj *alloc_obj (type_obj *ob_type, size_t sz)
return obj;
}
+base_obj *new_int_obj (int val)
+{
+ boxed_int_obj *int_obj
+ = (boxed_int_obj *)alloc_obj (&boxed_int_type, sizeof (boxed_int_obj));
+ if (!int_obj)
+ return NULL;
+ int_obj->int_val = val;
+ return (base_obj *)int_obj;
+}
+
void unref (base_obj *obj)
{
- //__analyzer_dump();
if (--obj->ob_refcnt == 0)
- free (obj);
+ obj->ob_type->tp_dealloc (obj);
}
-void test_1 ()
+void test_1 (const char *str)
{
- base_obj *obj = alloc_obj (&str_type, sizeof (string_obj));
- if (obj)
- {
- __analyzer_dump_num_heap_regions (); /* { dg-warning "num heap regions: '1'" } */
- unref (obj);
- __analyzer_dump_num_heap_regions (); /* { dg-warning "num heap regions: '0'" } */
- }
-}
+ base_obj *obj = new_int_obj (42);
+ if (!obj)
+ return;
+ __analyzer_eval (((boxed_int_obj *)obj)->int_val == 42); /* { dg-warning "TRUE" } */
+ __analyzer_eval (obj->ob_refcnt == 1); /* { dg-warning "TRUE" } */
+ unref (obj);
+} /* { dg-bogus "leak" "" } */
diff --git a/gcc/testsuite/gcc.dg/analyzer/data-model-6.c b/gcc/testsuite/gcc.dg/analyzer/data-model-6.c
deleted file mode 100644
index 78a797ead76..00000000000
--- a/gcc/testsuite/gcc.dg/analyzer/data-model-6.c
+++ /dev/null
@@ -1,14 +0,0 @@
-#include <stdlib.h>
-#include "analyzer-decls.h"
-
-/* Verify that we don't accumulate state after a malloc/free pair. */
-
-void test (void)
-{
- void *ptr;
- __analyzer_dump_num_heap_regions (); /* { dg-warning "num heap regions: '0'" } */
- ptr = malloc (1024);
- __analyzer_dump_num_heap_regions (); /* { dg-warning "num heap regions: '1'" } */
- free (ptr);
- __analyzer_dump_num_heap_regions (); /* { dg-warning "num heap regions: '0'" } */
-}
diff --git a/gcc/testsuite/gcc.dg/analyzer/data-model-8.c b/gcc/testsuite/gcc.dg/analyzer/data-model-8.c
index 939b4c275d8..3e69d0f8aa5 100644
--- a/gcc/testsuite/gcc.dg/analyzer/data-model-8.c
+++ b/gcc/testsuite/gcc.dg/analyzer/data-model-8.c
@@ -21,6 +21,5 @@ void test (void)
struct base *bp = (struct base *)&s;
- __analyzer_eval (bp->i == 3); /* { dg-warning "TRUE" "desired" { xfail *-*-* } } */
- /* { dg-warning "UNKNOWN" "status quo" { target *-*-* } .-1 } */
+ __analyzer_eval (bp->i == 3); /* { dg-warning "TRUE" } */
}
diff --git a/gcc/testsuite/gcc.dg/analyzer/describe-1.c b/gcc/testsuite/gcc.dg/analyzer/describe-1.c
new file mode 100644
index 00000000000..bc57c6ecef2
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/describe-1.c
@@ -0,0 +1,11 @@
+/* Smoketest for __analyzer_describe. */
+
+#include "analyzer-decls.h"
+
+void test (int i)
+{
+ __analyzer_describe (0, 42); /* { dg-warning "svalue: '\\(int\\)42'" } */
+ __analyzer_describe (0, i); /* { dg-warning "svalue: 'INIT_VAL\\(i.*\\)'" } */
+ __analyzer_describe (0, &i); /* { dg-warning "svalue: '&i'" } */
+ /* Further cases would risk overspecifying things. */
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/dot-output.c b/gcc/testsuite/gcc.dg/analyzer/dot-output.c
index 7b69c626f4e..ff418b156c0 100644
--- a/gcc/testsuite/gcc.dg/analyzer/dot-output.c
+++ b/gcc/testsuite/gcc.dg/analyzer/dot-output.c
@@ -18,8 +18,7 @@ int *test (int *buf, int n, int *out)
/* A loop, to ensure we have phi nodes. */
for (i = 0; i < n; i++)
- result[i] = buf[i] + i; /* { dg-warning "possibly-NULL" "" { xfail *-*-* } } */
- /* TODO(xfail): why isn't the warning appearing? */
+ result[i] = buf[i] + i; /* { dg-warning "possibly-NULL" } */
/* Example of a "'" (to test quoting). */
*out = some_call (i, 'a');
diff --git a/gcc/testsuite/gcc.dg/analyzer/explode-1.c b/gcc/testsuite/gcc.dg/analyzer/explode-1.c
index 6b62e8e871c..9b95afd9a03 100644
--- a/gcc/testsuite/gcc.dg/analyzer/explode-1.c
+++ b/gcc/testsuite/gcc.dg/analyzer/explode-1.c
@@ -47,7 +47,7 @@ void test (void)
{
default:
case 0:
- *pp = malloc (16);
+ *pp = malloc (16); /* { dg-warning "leak" } */
break;
case 1:
free (*pp);
diff --git a/gcc/testsuite/gcc.dg/analyzer/explode-2.c b/gcc/testsuite/gcc.dg/analyzer/explode-2.c
index d786aa93896..70d8fecae8f 100644
--- a/gcc/testsuite/gcc.dg/analyzer/explode-2.c
+++ b/gcc/testsuite/gcc.dg/analyzer/explode-2.c
@@ -19,31 +19,31 @@ void test (void)
{
default:
case 0:
- p0 = malloc (16);
+ p0 = malloc (16); /* { dg-warning "leak" } */
break;
case 1:
- free (p0); /* { dg-warning "double-'free' of 'p0'" } */
+ free (p0); /* { dg-warning "double-'free' of 'p0'" "" { xfail *-*-* } } */
break;
case 2:
- p1 = malloc (16);
+ p1 = malloc (16); /* { dg-warning "leak" } */
break;
case 3:
- free (p1); /* { dg-warning "double-'free' of 'p1'" } */
+ free (p1); /* { dg-warning "double-'free' of 'p1'" "" { xfail *-*-* } } */
break;
case 4:
- p2 = malloc (16);
+ p2 = malloc (16); /* { dg-warning "leak" } */
break;
case 5:
- free (p2); /* { dg-warning "double-'free' of 'p2'" } */
+ free (p2); /* { dg-warning "double-'free' of 'p2'" "" { xfail *-*-* } } */
break;
case 6:
- p3 = malloc (16);
+ p3 = malloc (16); /* { dg-warning "leak" } */
break;
case 7:
- free (p3); /* { dg-warning "double-'free' of 'p3'" } */
+ free (p3); /* { dg-warning "double-'free' of 'p3'" "" { xfail *-*-* } } */
break;
}
}
diff --git a/gcc/testsuite/gcc.dg/analyzer/feasibility-1.c b/gcc/testsuite/gcc.dg/analyzer/feasibility-1.c
new file mode 100644
index 00000000000..f2a8a4cb0be
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/feasibility-1.c
@@ -0,0 +1,62 @@
+#include "analyzer-decls.h"
+
+void test_1 (void)
+{
+ __analyzer_dump_path (); /* { dg-message "path" } */
+}
+
+void test_2 (int flag)
+{
+ if (flag)
+ __analyzer_dump_path (); /* { dg-message "path" } */
+}
+
+void test_3 (int flag)
+{
+ if (flag)
+ if (!flag)
+ __analyzer_dump_path (); /* { dg-bogus "path" } */
+}
+
+int global_for_test_4;
+static void __attribute__((noinline)) called_by_test_4 () {}
+void test_4 (void)
+{
+ /* Verify that a state change that happens in a stmt that
+ isn't the first within its BB can affect path feasibility. */
+ global_for_test_4 = 0;
+ global_for_test_4 = 1;
+ /* Thwart the optimizer. */
+ called_by_test_4 ();
+ if (global_for_test_4)
+ __analyzer_dump_path (); /* { dg-message "path" } */
+}
+
+/* Verify that loops don't confuse the feasibility checker. */
+
+void test_5 (void)
+{
+ for (int i = 0; i < 1024; i++)
+ {
+ }
+ __analyzer_dump_path (); /* { dg-message "path" } */
+}
+
+/* Reproducer for an issue seen with CVE-2005-1689 (PR analyzer/96374): if we
+ take the shortest path and update state and check feasibility per-edge, we
+ can erroneously reject valid diagnostics. */
+
+int test_6 (int a, int b)
+{
+ int problem = 0;
+ if (a)
+ problem = 1;
+ if (b)
+ {
+ if (!problem)
+ problem = 2;
+ __analyzer_dump_path (); /* { dg-message "path" "" { xfail *-*-* } } */
+ /* XFAIL is PR analyzer/96374. */
+ }
+ return problem;
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/first-field-1.c b/gcc/testsuite/gcc.dg/analyzer/first-field-1.c
new file mode 100644
index 00000000000..8b71e1abcae
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/first-field-1.c
@@ -0,0 +1,24 @@
+#include "analyzer-decls.h"
+
+typedef struct base_obj
+{
+ int m_first;
+ int m_second;
+} base_obj;
+
+typedef struct sub_obj
+{
+ base_obj base;
+} sub_obj;
+
+void test (sub_obj *sub)
+{
+ sub->base.m_first = 1;
+ sub->base.m_second = 2;
+ __analyzer_eval (sub->base.m_first == 1); /* { dg-warning "TRUE" } */
+ __analyzer_eval (sub->base.m_second == 2); /* { dg-warning "TRUE" } */
+
+ base_obj *base = (struct base_obj *)sub;
+ __analyzer_eval (base->m_first == 1); /* { dg-warning "TRUE" } */
+ __analyzer_eval (base->m_second == 2); /* { dg-warning "TRUE" } */
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/first-field-2.c b/gcc/testsuite/gcc.dg/analyzer/first-field-2.c
new file mode 100644
index 00000000000..2fb98d3c9d7
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/first-field-2.c
@@ -0,0 +1,33 @@
+/* A toy re-implementation of CPython's object model. */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "analyzer-decls.h"
+
+typedef struct base_obj base_obj;
+typedef struct string_obj string_obj;
+
+struct base_obj
+{
+ int ob_refcnt;
+};
+
+struct string_obj
+{
+ base_obj str_base;
+ size_t str_len;
+ char str_buf[];
+};
+
+base_obj *alloc_obj (const char *str)
+{
+ size_t len = strlen (str);
+ base_obj *obj = (base_obj *)malloc (sizeof (string_obj) + len + 1);
+ if (!obj)
+ return NULL;
+ obj->ob_refcnt = 1;
+ string_obj *str_obj = (string_obj *)obj;
+ __analyzer_eval (str_obj->str_base.ob_refcnt == 1); /* { dg-warning "TRUE" } */
+ return obj;
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/init.c b/gcc/testsuite/gcc.dg/analyzer/init.c
new file mode 100644
index 00000000000..e51d88e9ff9
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/init.c
@@ -0,0 +1,136 @@
+/* Tests of brace-enclosed initializers
+ Some of these use the CONSTRUCTOR tree code, but it appears
+ only for a full zero-init; it appears that by the time the analyzer
+ runs that this initialization has been converted into field-wise
+ gimple assign stmts, with just "zero-init everything" CONSTRUCTORs
+ and "clobber" CONSTRUCTORs. */
+
+#include "analyzer-decls.h"
+
+struct coord
+{
+ int x;
+ int y;
+};
+
+struct tri
+{
+ struct coord v[3];
+};
+
+union iap
+{
+ int i;
+ void *p;
+};
+
+void test_1 (void)
+{
+ struct coord c = {3, 4};
+ __analyzer_eval (c.x == 3); /* { dg-warning "TRUE" } */
+ __analyzer_eval (c.y == 4); /* { dg-warning "TRUE" } */
+}
+
+void test_2 (void)
+{
+ struct coord c = {3};
+ __analyzer_eval (c.x == 3); /* { dg-warning "TRUE" } */
+ __analyzer_eval (c.y == 0); /* { dg-warning "TRUE" } */
+}
+
+void test_3 (void)
+{
+ struct coord c = {};
+ __analyzer_eval (c.x == 0); /* { dg-warning "TRUE" } */
+ __analyzer_eval (c.y == 0); /* { dg-warning "TRUE" } */
+}
+
+void test_4 (void)
+{
+ int c[2] = {3, 4};
+ __analyzer_eval (c[0] == 3); /* { dg-warning "TRUE" } */
+ __analyzer_eval (c[1] == 4); /* { dg-warning "TRUE" } */
+}
+
+void test_5 (void)
+{
+ int c[2] = {3};
+ __analyzer_eval (c[0] == 3); /* { dg-warning "TRUE" } */
+ __analyzer_eval (c[1] == 0); /* { dg-warning "TRUE" } */
+}
+
+void test_6 (void)
+{
+ int c[2] = {};
+ __analyzer_eval (c[0] == 0); /* { dg-warning "TRUE" } */
+ __analyzer_eval (c[1] == 0); /* { dg-warning "TRUE" } */
+}
+
+void test_7 (void)
+{
+ struct coord c[2] = {{3, 4}, {5, 6}};
+ __analyzer_eval (c[0].x == 3); /* { dg-warning "TRUE" } */
+ __analyzer_eval (c[0].y == 4); /* { dg-warning "TRUE" } */
+ __analyzer_eval (c[1].x == 5); /* { dg-warning "TRUE" } */
+ __analyzer_eval (c[1].y == 6); /* { dg-warning "TRUE" } */
+}
+
+void test_8 (void)
+{
+ struct coord c[2] = {{3}, {5}};
+ __analyzer_eval (c[0].x == 3); /* { dg-warning "TRUE" } */
+ __analyzer_eval (c[0].y == 0); /* { dg-warning "TRUE" } */
+ __analyzer_eval (c[1].x == 5); /* { dg-warning "TRUE" } */
+ __analyzer_eval (c[1].y == 0); /* { dg-warning "TRUE" } */
+}
+
+void test_9 (void)
+{
+ struct coord c[2] = {{}, {}};
+ __analyzer_eval (c[0].x == 0); /* { dg-warning "TRUE" } */
+ __analyzer_eval (c[0].y == 0); /* { dg-warning "TRUE" } */
+ __analyzer_eval (c[1].x == 0); /* { dg-warning "TRUE" } */
+ __analyzer_eval (c[1].y == 0); /* { dg-warning "TRUE" } */
+}
+
+void test_10 (void)
+{
+ struct coord c[2] = {{.y = 4, .x = 3}, {5, 6}};
+ __analyzer_eval (c[0].x == 3); /* { dg-warning "TRUE" } */
+ __analyzer_eval (c[0].y == 4); /* { dg-warning "TRUE" } */
+ __analyzer_eval (c[1].x == 5); /* { dg-warning "TRUE" } */
+ __analyzer_eval (c[1].y == 6); /* { dg-warning "TRUE" } */
+}
+
+void test_11 (void)
+{
+ struct coord c[2] = {{.y = 4}, {5, 6}};
+ __analyzer_eval (c[0].x == 0); /* { dg-warning "TRUE" } */
+ __analyzer_eval (c[0].y == 4); /* { dg-warning "TRUE" } */
+ __analyzer_eval (c[1].x == 5); /* { dg-warning "TRUE" } */
+ __analyzer_eval (c[1].y == 6); /* { dg-warning "TRUE" } */
+}
+
+void test_12 (void)
+{
+ struct tri t = {};
+ __analyzer_eval (t.v[0].x == 0); /* { dg-warning "TRUE" } */
+ __analyzer_eval (t.v[2].y == 0); /* { dg-warning "TRUE" } */
+}
+
+void test_13 (void)
+{
+ struct tri t = {3, 4, 5, 6, 7, 8};
+ __analyzer_eval (t.v[0].x == 3); /* { dg-warning "TRUE" } */
+ __analyzer_eval (t.v[0].y == 4); /* { dg-warning "TRUE" } */
+ __analyzer_eval (t.v[1].x == 5); /* { dg-warning "TRUE" } */
+ __analyzer_eval (t.v[1].y == 6); /* { dg-warning "TRUE" } */
+ __analyzer_eval (t.v[2].x == 7); /* { dg-warning "TRUE" } */
+ __analyzer_eval (t.v[2].y == 8); /* { dg-warning "TRUE" } */
+}
+
+void test_14 (void)
+{
+ union iap u = {};
+ __analyzer_eval (u.i == 0); /* { dg-warning "TRUE" } */
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/leak-2.c b/gcc/testsuite/gcc.dg/analyzer/leak-2.c
new file mode 100644
index 00000000000..bba3e816db7
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/leak-2.c
@@ -0,0 +1,9 @@
+#include <stdlib.h>
+
+void *ptr;
+
+void *test (void)
+{
+ ptr = malloc (1024);
+ ptr = NULL; /* { dg-warning "leak of 'ptr'" } */
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/loop-0-up-to-n-by-1-with-iter-obj.c b/gcc/testsuite/gcc.dg/analyzer/loop-0-up-to-n-by-1-with-iter-obj.c
new file mode 100644
index 00000000000..0172c9b324c
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/loop-0-up-to-n-by-1-with-iter-obj.c
@@ -0,0 +1,73 @@
+#include <stdlib.h>
+
+#include "analyzer-decls.h"
+
+struct iter
+{
+ int start;
+ int end;
+ int step;
+ int val;
+};
+
+struct iter * __attribute__((noinline))
+iter_new (int start, int end, int step)
+{
+ struct iter *it = (struct iter *)malloc (sizeof (struct iter));
+ if (!it)
+ abort ();
+ it->start = start;
+ it->end = end;
+ it->step = step;
+ it->val = start;
+ return it;
+}
+
+int __attribute__((noinline))
+iter_done_p (struct iter *it)
+{
+ return it->val >= it->end;
+}
+
+void __attribute__((noinline))
+iter_next (struct iter *it)
+{
+ it->val += it->step;
+}
+
+/* Example of an iterator object, to see how well we cope with a well-disguised
+ iteration from 0 to n with a step of 1. */
+
+void test(int n)
+{
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 processed enode" } */
+
+ struct iter *it = iter_new (0, n, 1);
+ while (!iter_done_p (it))
+ {
+ __analyzer_eval (it->val < n); /* { dg-warning "TRUE" "true" { xfail *-*-* } } */
+ /* { dg-bogus "UNKNOWN" "unknown" { xfail *-*-* } .-1 } */
+ /* TODO(xfail^^^): ideally we ought to figure out i > 0 after 1st iteration. */
+
+ __analyzer_eval (it->val == 0); /* { dg-warning "TRUE" "true on 1st iter" } */
+ /* { dg-warning "UNKNOWN" "unknown" { target *-*-* } .-1 } */
+ /* TODO: should we ought to figure out i > 0 after 1st iteration? */
+
+ __analyzer_eval (it->val >= 0); /* { dg-warning "TRUE" } */
+
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 processed enodes" } */
+
+ iter_next (it);
+ }
+
+ __analyzer_eval (it->val >= n); /* { dg-warning "TRUE" "true" { xfail *-*-* } } */
+ /* { dg-bogus "UNKNOWN" "unknown" { xfail *-*-* } .-1 } */
+
+ __analyzer_eval (it->val == n); /* { dg-warning "TRUE" "desired" { xfail *-*-* } } */
+ /* { dg-warning "UNKNOWN" "status quo" { target *-*-* } .-1 } */
+ /* TODO(xfail^^^): it only figures out i >= 256, rather than i == 256. */
+
+ free (it);
+
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 processed enode" } */
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/loop-0-up-to-n-by-1.c b/gcc/testsuite/gcc.dg/analyzer/loop-0-up-to-n-by-1.c
new file mode 100644
index 00000000000..d49ed130488
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/loop-0-up-to-n-by-1.c
@@ -0,0 +1,31 @@
+#include "analyzer-decls.h"
+
+void test(int n)
+{
+ int i;
+
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 processed enode" } */
+
+ for (i = 0; i < n; i++) {
+ __analyzer_eval (i < n); /* { dg-warning "TRUE" } */
+ /* (should report TRUE twice). */
+
+ __analyzer_eval (i == 0); /* { dg-warning "TRUE" "1st" } */
+ /* { dg-warning "FALSE" "2nd" { xfail *-*-* } .-1 } */
+ /* { dg-warning "UNKNOWN" "status quo" { target *-*-* } .-2 } */
+ /* TODO(xfail^^^): ideally we ought to figure out i > 0 after 1st iteration. */
+
+ __analyzer_eval (i >= 0); /* { dg-warning "TRUE" } */
+
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 processed enodes" } */
+ }
+
+ __analyzer_eval (i >= n); /* { dg-warning "TRUE" "true" { xfail *-*-* } } */
+ /* { dg-bogus "UNKNOWN" "unknown" { xfail *-*-* } .-1 } */
+
+ __analyzer_eval (i == n); /* { dg-warning "TRUE" "true" { xfail *-*-* } } */
+ /* { dg-bogus "UNKNOWN" "unknown" { xfail *-*-* } .-1 } */
+ /* TODO(xfail^^^): it only figures out i >= 256, rather than i == 256. */
+
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 processed enode" } */
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/loop-2a.c b/gcc/testsuite/gcc.dg/analyzer/loop-2a.c
index a392ffc1a63..16b64497cb7 100644
--- a/gcc/testsuite/gcc.dg/analyzer/loop-2a.c
+++ b/gcc/testsuite/gcc.dg/analyzer/loop-2a.c
@@ -14,10 +14,7 @@ void test(void)
for (u.i=0; u.i<256; u.i++) {
- __analyzer_eval (u.i < 256); /* { dg-warning "TRUE" "1st" } */
- /* { dg-warning "TRUE" "2nd" { xfail *-*-* } .-1 } */
- /* { dg-bogus "UNKNOWN" "status quo" { xfail *-*-* } .-2 } */
- /* (should report TRUE twice). */
+ __analyzer_eval (u.i < 256); /* { dg-warning "TRUE" } */
__analyzer_dump_exploded_nodes (0); /* { dg-warning "2 processed enodes" } */
@@ -26,11 +23,10 @@ void test(void)
/* TODO(xfail^^^): we're only capturing the first iteration, so
we erroneously get i == 0. */
- //__analyzer_eval (u.i >= 0); /* { d-todo-g-warning "TRUE" } */
+ __analyzer_eval (u.i >= 0); /* { dg-warning "TRUE" } */
}
- __analyzer_eval (u.i >= 256); /* { dg-warning "TRUE" "desired" { xfail *-*-* } } */
- /* { dg-warning "UNKNOWN" "status quo" { target *-*-* } .-1 } */
+ __analyzer_eval (u.i >= 256); /* { dg-warning "TRUE" } */
__analyzer_eval (u.i == 256); /* { dg-warning "TRUE" "desired" { xfail *-*-* } } */
/* { dg-warning "UNKNOWN" "status quo" { target *-*-* } .-1 } */
diff --git a/gcc/testsuite/gcc.dg/analyzer/loop-3.c b/gcc/testsuite/gcc.dg/analyzer/loop-3.c
index 1d01771d7cb..0bcf7074623 100644
--- a/gcc/testsuite/gcc.dg/analyzer/loop-3.c
+++ b/gcc/testsuite/gcc.dg/analyzer/loop-3.c
@@ -6,11 +6,8 @@ void test(int c)
char *buffer = (char*)malloc(256);
for (i=0; i<255; i++) {
- buffer[i] = c; /* { dg-warning "use after 'free' of 'buffer'" } */
- /* BUG: the malloc could have failed
- TODO: the checker doesn't yet pick up on this, perhaps
- due to the pointer arithmetic not picking up on the
- state */
+ buffer[i] = c; /* { dg-warning "use after 'free' of 'buffer'" "use after free" { xfail *-*-* } } */
+ /* { dg-warning "possibly-NULL 'buffer'" "deref of unchecked" { target *-*-* } .-1 } */
free(buffer); /* { dg-warning "double-'free' of 'buffer'" } */
}
diff --git a/gcc/testsuite/gcc.dg/analyzer/loop-4.c b/gcc/testsuite/gcc.dg/analyzer/loop-4.c
index e5767de1f96..b66a3459437 100644
--- a/gcc/testsuite/gcc.dg/analyzer/loop-4.c
+++ b/gcc/testsuite/gcc.dg/analyzer/loop-4.c
@@ -1,6 +1,3 @@
-// FIXME:
-/* { dg-additional-options "-fno-analyzer-state-purge" } */
-
/* Example of nested loops. */
#include "analyzer-decls.h"
@@ -13,8 +10,7 @@ void test(void)
for (i=0; i<256; i++) {
- __analyzer_eval (i >= 0); /* { dg-warning "TRUE" "true" } */
- /* { dg-warning "UNKNOWN" "unknown" { target *-*-* } .-1 } */
+ __analyzer_eval (i >= 0); /* { dg-warning "TRUE" } */
__analyzer_eval (i < 256); /* { dg-warning "TRUE" } */
@@ -23,7 +19,9 @@ void test(void)
__analyzer_eval (j >= 0); /* { dg-warning "TRUE" "true" } */
/* { dg-warning "UNKNOWN" "unknown" { target *-*-* } .-1 } */
- __analyzer_eval (j < 256); /* { dg-warning "TRUE" } */
+ __analyzer_eval (j < 256); /* { dg-warning "TRUE" "true" } */
+ /* { dg-bogus "UNKNOWN" "unknown" { xfail *-*-* } .-1 } */
+ /* TODO(xfail^^^): should report TRUE twice. */
__analyzer_dump_exploded_nodes (0); /* { dg-warning "3 processed enodes" } */
@@ -32,7 +30,8 @@ void test(void)
__analyzer_eval (k >= 0); /* { dg-warning "TRUE" "true" } */
/* { dg-warning "UNKNOWN" "unknown" { target *-*-* } .-1 } */
- __analyzer_eval (k < 256); /* { dg-warning "TRUE" } */
+ __analyzer_eval (k < 256); /* { dg-warning "TRUE" "true" } */
+ /* { dg-bogus "UNKNOWN" "unknown" { xfail *-*-* } .-1 } */
__analyzer_dump_exploded_nodes (0); /* { dg-warning "4 processed enodes" } */
}
diff --git a/gcc/testsuite/gcc.dg/analyzer/loop-n-down-to-1-by-1.c b/gcc/testsuite/gcc.dg/analyzer/loop-n-down-to-1-by-1.c
new file mode 100644
index 00000000000..e02a849d4bb
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/loop-n-down-to-1-by-1.c
@@ -0,0 +1,35 @@
+#include "analyzer-decls.h"
+
+void test(int n)
+{
+ int i;
+
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 processed enode" } */
+
+ for (i = n; i > 0; i--) {
+ __analyzer_eval (i > 0); /* { dg-warning "TRUE" "true" } */
+ /* { dg-bogus "UNKNOWN" "unknown" { xfail *-*-* } .-1 } */
+ /* TODO(xfail^^^): should report TRUE twice. */
+
+ __analyzer_eval (i == n); /* { dg-warning "TRUE" "1st" } */
+ /* { dg-warning "FALSE" "2nd" { xfail *-*-* } .-1 } */
+ /* { dg-warning "UNKNOWN" "status quo" { target *-*-* } .-2 } */
+ /* TODO(xfail^^^): ideally we ought to figure out i > 0 after 1st iteration. */
+
+ __analyzer_eval (i <= n); /* { dg-warning "TRUE" "1st" } */
+ /* { dg-warning "TRUE" "2nd" { xfail *-*-* } } */
+ /* { dg-warning "UNKNOWN" "status quo" { target *-*-* } .-2 } */
+ /* TODO(xfail^^^): ideally we ought to figure out i >= 0 for all iterations. */
+
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 processed enodes" } */
+ }
+
+ __analyzer_eval (i <= 0); /* { dg-warning "TRUE" "true" { xfail *-*-* } } */
+ /* { dg-bogus "UNKNOWN" "unknown" { xfail *-*-* } .-1 } */
+
+ __analyzer_eval (i == 0); /* { dg-warning "TRUE" "desired" { xfail *-*-* } } */
+ /* { dg-warning "UNKNOWN" "status quo" { target *-*-* } .-1 } */
+ /* TODO(xfail^^^): it only figures out i >= 256, rather than i == 256. */
+
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 processed enode" } */
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/loop-start-down-to-end-by-1.c b/gcc/testsuite/gcc.dg/analyzer/loop-start-down-to-end-by-1.c
new file mode 100644
index 00000000000..3513bf42347
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/loop-start-down-to-end-by-1.c
@@ -0,0 +1,35 @@
+#include "analyzer-decls.h"
+
+void test(int start, int end, int step)
+{
+ int i;
+
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 processed enode" } */
+
+ for (i = start; i > end; i --) {
+ __analyzer_eval (i > end); /* { dg-warning "TRUE" "true" } */
+ /* { dg-bogus "UNKNOWN" "unknown" { xfail *-*-* } .-1 } */
+ /* TODO(xfail^^^): should report TRUE twice. */
+
+ __analyzer_eval (i == start); /* { dg-warning "TRUE" "1st" } */
+ /* { dg-warning "FALSE" "2nd" { xfail *-*-* } .-1 } */
+ /* { dg-warning "UNKNOWN" "status quo" { target *-*-* } .-2 } */
+ /* TODO(xfail^^^): ideally we ought to figure out i > 0 after 1st iteration. */
+
+ __analyzer_eval (i <= start); /* { dg-warning "TRUE" "true" } */
+ /* { dg-bogus "UNKNOWN" "unknown" { xfail *-*-* } .-1 } */
+ /* TODO(xfail^^^): should report TRUE twice. */
+
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 processed enodes" } */
+ }
+
+ __analyzer_eval (i >= end); /* { dg-warning "TRUE" "true" { xfail *-*-* } } */
+ /* { dg-bogus "UNKNOWN" "unknown" { xfail *-*-* } .-1 } */
+
+ // FIXME: do we know this? What if we overshoot?
+ __analyzer_eval (i == end); /* { dg-warning "TRUE" "desired" { xfail *-*-* } } */
+ /* { dg-warning "UNKNOWN" "status quo" { target *-*-* } .-1 } */
+ /* TODO(xfail^^^): it only figures out i >= 256, rather than i == 256. */
+
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 processed enode" } */
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/loop-start-down-to-end-by-step.c b/gcc/testsuite/gcc.dg/analyzer/loop-start-down-to-end-by-step.c
new file mode 100644
index 00000000000..2692c503b5c
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/loop-start-down-to-end-by-step.c
@@ -0,0 +1,30 @@
+#include "analyzer-decls.h"
+
+void test(int start, int end, int step)
+{
+ int i;
+
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 processed enode" } */
+
+ for (i = start; i > end; i -= step) {
+ __analyzer_eval (i > end); /* { dg-warning "TRUE" "true" } */
+ /* { dg-bogus "UNKNOWN" "unknown" { xfail *-*-* } .-1 } */
+ /* TODO(xfail^^^): should report TRUE twice. */
+
+ __analyzer_eval (i == start); /* { dg-warning "TRUE" "1st" } */
+ /* { dg-warning "FALSE" "2nd" { xfail *-*-* } .-1 } */
+ /* { dg-warning "UNKNOWN" "status quo" { target *-*-* } .-2 } */
+ /* TODO(xfail^^^): ideally we ought to figure out i > 0 after 1st iteration. */
+
+ /* We don't know the direction of step. */
+ __analyzer_eval (i <= start); /* { dg-warning "TRUE" "true" } */
+ /* { dg-warning "UNKNOWN" "unknown" { target *-*-* } .-1 } */
+
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 processed enodes" } */
+ }
+
+ __analyzer_eval (i <= end); /* { dg-warning "TRUE" "true" { xfail *-*-* } } */
+ /* { dg-bogus "UNKNOWN" "unknown" { xfail *-*-* } .-1 } */
+
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 processed enode" } */
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/loop-start-to-end-by-step.c b/gcc/testsuite/gcc.dg/analyzer/loop-start-to-end-by-step.c
new file mode 100644
index 00000000000..3fc1362d1cf
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/loop-start-to-end-by-step.c
@@ -0,0 +1,36 @@
+#include "analyzer-decls.h"
+
+void test(int start, int end, int step)
+{
+ int i;
+
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 processed enode" } */
+
+ for (i = start; i < end; i += step) {
+ __analyzer_eval (i < end); /* { dg-warning "TRUE" "true" } */
+ /* { dg-bogus "UNKNOWN" "unknown" { xfail *-*-* } .-1 } */
+ /* TODO(xfail^^^): should report TRUE twice. */
+
+ __analyzer_eval (i == start); /* { dg-warning "TRUE" "1st" } */
+ /* { dg-warning "FALSE" "2nd" { xfail *-*-* } .-1 } */
+ /* { dg-warning "UNKNOWN" "status quo" { target *-*-* } .-2 } */
+ /* TODO(xfail^^^): ideally we ought to figure out i > 0 after 1st iteration. */
+
+ /* We don't know the direction of step. */
+ __analyzer_eval (i >= start); /* { dg-warning "TRUE" "true" } */
+ /* { dg-warning "UNKNOWN" "unknown" { target *-*-* } .-1 } */
+
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 processed enodes" } */
+ }
+
+ // FIXME: do we know this? What about direction of step?
+ __analyzer_eval (i >= end); /* { dg-warning "TRUE" "true" { xfail *-*-* } } */
+ /* { dg-bogus "UNKNOWN" "unknown" { xfail *-*-* } .-1 } */
+
+ // FIXME: do we know this? What if we overshoot?
+ __analyzer_eval (i == end); /* { dg-warning "TRUE" "desired" { xfail *-*-* } } */
+ /* { dg-warning "UNKNOWN" "status quo" { target *-*-* } .-1 } */
+ /* TODO(xfail^^^): it only figures out i >= 256, rather than i == 256. */
+
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 processed enode" } */
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/loop-start-up-to-end-by-1.c b/gcc/testsuite/gcc.dg/analyzer/loop-start-up-to-end-by-1.c
new file mode 100644
index 00000000000..ca6a862092c
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/loop-start-up-to-end-by-1.c
@@ -0,0 +1,34 @@
+#include "analyzer-decls.h"
+
+void test(int start, int end)
+{
+ int i;
+
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 processed enode" } */
+
+ for (i = start; i < end; i++) {
+ __analyzer_eval (i < end); /* { dg-warning "TRUE" "true" } */
+ /* { dg-bogus "UNKNOWN" "unknown" { xfail *-*-* } .-1 } */
+ /* TODO(xfail^^^): should report TRUE twice. */
+
+ __analyzer_eval (i == start); /* { dg-warning "TRUE" "1st" } */
+ /* { dg-warning "FALSE" "2nd" { xfail *-*-* } .-1 } */
+ /* { dg-warning "UNKNOWN" "status quo" { target *-*-* } .-2 } */
+ /* TODO(xfail^^^): ideally we ought to figure out i > 0 after 1st iteration. */
+
+ __analyzer_eval (i >= start); /* { dg-warning "TRUE" "true" } */
+ /* { dg-bogus "UNKNOWN" "unknown" { xfail *-*-* } .-1 } */
+ /* TODO(xfail^^^): should report TRUE twice. */
+
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 processed enodes" } */
+ }
+
+ __analyzer_eval (i >= end); /* { dg-warning "TRUE" "true" { xfail *-*-* } } */
+ /* { dg-bogus "UNKNOWN" "unknown" { xfail *-*-* } .-1 } */
+
+ __analyzer_eval (i == end); /* { dg-warning "TRUE" "desired" { xfail *-*-* } } */
+ /* { dg-warning "UNKNOWN" "status quo" { target *-*-* } .-1 } */
+ /* TODO(xfail^^^): it only figures out i >= end, rather than i == end. */
+
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 processed enode" } */
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/loop.c b/gcc/testsuite/gcc.dg/analyzer/loop.c
index 37b757bf8f4..c4cfd88c912 100644
--- a/gcc/testsuite/gcc.dg/analyzer/loop.c
+++ b/gcc/testsuite/gcc.dg/analyzer/loop.c
@@ -1,5 +1,3 @@
-/* { dg-additional-options "-fno-analyzer-state-purge" } */
-
#include "analyzer-decls.h"
void test(void)
@@ -12,15 +10,12 @@ void test(void)
__analyzer_eval (i < 256); /* { dg-warning "TRUE" } */
/* (should report TRUE twice). */
- __analyzer_eval (i == 0); /* { dg-warning "TRUE" "1st" } */
+ __analyzer_eval (i == 0); /* { dg-warning "TRUE" } */
/* { dg-warning "FALSE" "2nd" { xfail *-*-* } .-1 } */
/* { dg-warning "UNKNOWN" "status quo" { target *-*-* } .-2 } */
/* TODO(xfail^^^): ideally we ought to figure out i > 0 after 1st iteration. */
- __analyzer_eval (i >= 0); /* { dg-warning "TRUE" "1st" } */
- /* { dg-warning "TRUE" "2nd" { xfail *-*-* } } */
- /* { dg-warning "UNKNOWN" "status quo" { target *-*-* } .-2 } */
- /* TODO(xfail^^^): ideally we ought to figure out i >= 0 for all iterations. */
+ __analyzer_eval (i >= 0); /* { dg-warning "TRUE" } */
__analyzer_dump_exploded_nodes (0); /* { dg-warning "2 processed enodes" } */
}
diff --git a/gcc/testsuite/gcc.dg/analyzer/malloc-1.c b/gcc/testsuite/gcc.dg/analyzer/malloc-1.c
index 4d9afa483c9..c3e1330ec6a 100644
--- a/gcc/testsuite/gcc.dg/analyzer/malloc-1.c
+++ b/gcc/testsuite/gcc.dg/analyzer/malloc-1.c
@@ -360,7 +360,7 @@ void test_30 (void)
struct link tmp;
tmp.m_ptr = (struct link *)malloc (sizeof (struct link)); /* { dg-message "allocated here" } */
} /* { dg-warning "leak of 'tmp.m_ptr'" } */
-/* { dg-bogus "leak of '<unknown>'" "" { xfail *-*-* } .-1 } */
+/* { dg-bogus "leak of '<unknown>'" "leak of unknown" { target *-*-* } .-1 } */
void test_31 (void)
{
@@ -368,7 +368,7 @@ void test_31 (void)
void *ptr = malloc (sizeof (struct link)); /* { dg-message "allocated here" } */
tmp.m_ptr = (struct link *)ptr;
} /* { dg-warning "leak of 'ptr'" } */
-/* { dg-bogus "leak of 'tmp.m_ptr'" "" { xfail *-*-* } .-1 } */
+/* { dg-bogus "leak of 'tmp.m_ptr'" "" { target *-*-* } .-1 } */
void test_32 (void)
{
@@ -507,8 +507,7 @@ void test_42c (void)
void *p = malloc (1024);
void *q = p + 64;
free (q - 64); /* this is probably OK. */
-} /* { dg-bogus "leak of 'p'" "" { xfail *-*-* } } */
-// TODO(xfail)
+} /* { dg-bogus "leak of 'p'" } */
#if 0
void test_31 (void *p)
@@ -531,10 +530,8 @@ struct link global_link;
void test_43 (void)
{
global_link.m_ptr = malloc (sizeof (struct link)); /* { dg-message "allocated here" } */
- global_link.m_ptr = NULL;
-} /* { dg-warning "leak of '<unknown>'" } */
-/* TODO: should be more precise than just '<unknown>', and
- ideally would be at the assigment to NULL. */
+ global_link.m_ptr = NULL; /* { dg-warning "leak of 'global_link.m_ptr'" } */
+}
struct link *global_ptr;
@@ -591,3 +588,16 @@ void test_48 (void)
int *p = NULL; /* { dg-message "'p' is NULL" } */
*p = 1; /* { dg-warning "dereference of NULL 'p'" } */
}
+
+/* As test_48, but where the assignment of NULL is not at the start of a BB. */
+
+int test_49 (int i)
+{
+ int *p;
+ int x;
+
+ x = i * 2;
+ p = NULL; /* { dg-message "'p' is NULL" } */
+ *p = 1; /* { dg-warning "dereference of NULL 'p'" } */
+ return x;
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/malloc-4.c b/gcc/testsuite/gcc.dg/analyzer/malloc-4.c
index c9c275aa491..908bb28ee50 100644
--- a/gcc/testsuite/gcc.dg/analyzer/malloc-4.c
+++ b/gcc/testsuite/gcc.dg/analyzer/malloc-4.c
@@ -10,11 +10,11 @@ void *hv (struct foo **tm)
*tm = p;
if (!p)
abort ();
- return p; /* { dg-warning "leak of 'tm'" } */
+ return p;
}
void a5 (void)
{
struct bar *qb = NULL;
hv (&qb);
-} /* { dg-warning "leak of '\\(struct foo \\*\\)qb'" } */
+} /* { dg-warning "leak of 'qb'" } */
diff --git a/gcc/testsuite/gcc.dg/analyzer/malloc-in-loop.c b/gcc/testsuite/gcc.dg/analyzer/malloc-in-loop.c
new file mode 100644
index 00000000000..a8c85a9c618
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/malloc-in-loop.c
@@ -0,0 +1,19 @@
+#include <stdlib.h>
+#include "analyzer-decls.h"
+
+extern void foo (int *);
+
+void test (int n)
+{
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 processed enode" } */
+
+ for (int i = 0; i < n; i++)
+ {
+ int *ptr = (int *)malloc (sizeof (int) * i);
+ foo (ptr);
+ free (ptr);
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 processed enodes" } */
+ }
+
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 processed enode" } */
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-8-double-free.c b/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-8-double-free.c
index f86e8ac72d7..cdf5ac18324 100644
--- a/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-8-double-free.c
+++ b/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-8-double-free.c
@@ -61,111 +61,133 @@ void test (int i)
| | |
| | (2) calling 'make_boxed_int' from 'test'
|
- +--> 'make_boxed_int': events 3-6
+ +--> 'make_boxed_int': events 3-4
|
| NN | make_boxed_int (int i)
| | ^~~~~~~~~~~~~~
| | |
| | (3) entry to 'make_boxed_int'
- |......
+ | NN | {
+ | NN | boxed_int *result = (boxed_int *)wrapped_malloc (sizeof (boxed_int));
+ | | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ | | |
+ | | (4) calling 'wrapped_malloc' from 'make_boxed_int'
+ |
+ +--> 'wrapped_malloc': events 5-6
+ |
+ | NN | void *wrapped_malloc (size_t size)
+ | | ^~~~~~~~~~~~~~
+ | | |
+ | | (5) entry to 'wrapped_malloc'
+ | NN | {
+ | NN | return malloc (size);
+ | | ~~~~~~~~~~~~~
+ | | |
+ | | (6) allocated here (state of '<unknown>': 'start' -> 'unchecked', NULL origin)
+ |
+ <------+
+ |
+ 'make_boxed_int': events 7-10
+ |
+ | NN | boxed_int *result = (boxed_int *)wrapped_malloc (sizeof (boxed_int));
+ | | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ | | |
+ | | (7) returning to 'make_boxed_int' from 'wrapped_malloc'
| NN | if (!result)
- | | ~
+ | | ~
| | |
- | | (4) following 'false' branch (when 'result' is non-NULL)...
+ | | (8) assuming 'result' is non-NULL (state of 'result': 'unchecked' -> 'nonnull', NULL origin)
+ | | (9) following 'false' branch (when 'result' is non-NULL)...
| NN | abort ();
| NN | result->i = i;
- | | ~~~~~~~~~~~~~
+ | | ~~~~~~~~~~~~~
| | |
- | | (5) ...to here
- | NN | return result;
- | | ~~~~~~
- | | |
- | | (6) state of '<return-value>': 'start' -> 'nonnull' (origin: NULL)
+ | | (10) ...to here
|
<------+
|
- 'test': events 7-8
+ 'test': events 11-12
|
| NN | boxed_int *obj = make_boxed_int (i);
| | ^~~~~~~~~~~~~~~~~~
| | |
- | | (7) returning to 'test' from 'make_boxed_int'
+ | | (11) returning to 'test' from 'make_boxed_int'
| NN |
| NN | free_boxed_int (obj);
| | ~~~~~~~~~~~~~~~~~~~~
| | |
- | | (8) calling 'free_boxed_int' from 'test'
+ | | (12) calling 'free_boxed_int' from 'test'
|
- +--> 'free_boxed_int': events 9-10
+ +--> 'free_boxed_int': events 13-14
|
| NN | free_boxed_int (boxed_int *bi)
| | ^~~~~~~~~~~~~~
| | |
- | | (9) entry to 'free_boxed_int'
+ | | (13) entry to 'free_boxed_int'
| NN | {
| NN | wrapped_free (bi);
| | ~~~~~~~~~~~~~~~~~
| | |
- | | (10) calling 'wrapped_free' from 'free_boxed_int'
+ | | (14) calling 'wrapped_free' from 'free_boxed_int'
|
- +--> 'wrapped_free': events 11-12
+ +--> 'wrapped_free': events 15-16
|
| NN | void wrapped_free (void *ptr)
| | ^~~~~~~~~~~~
| | |
- | | (11) entry to 'wrapped_free'
+ | | (15) entry to 'wrapped_free'
| NN | {
| NN | free (ptr);
| | ~~~~~~~~~~
| | |
- | | (12) first 'free' here (state of 'ptr': 'nonnull' -> 'freed', origin: NULL)
+ | | (16) first 'free' here (state of 'ptr': 'nonnull' -> 'freed', NULL origin)
|
<------+
|
- 'free_boxed_int': event 13
+ 'free_boxed_int': event 17
|
| NN | wrapped_free (bi);
| | ^~~~~~~~~~~~~~~~~
| | |
- | | (13) returning to 'free_boxed_int' from 'wrapped_free'
+ | | (17) returning to 'free_boxed_int' from 'wrapped_free'
|
<------+
|
- 'test': events 14-15
+ 'test': events 18-19
|
| NN | free_boxed_int (obj);
| | ^~~~~~~~~~~~~~~~~~~~
| | |
- | | (14) returning to 'test' from 'free_boxed_int'
+ | | (18) returning to 'test' from 'free_boxed_int'
| NN |
| NN | free_boxed_int (obj);
| | ~~~~~~~~~~~~~~~~~~~~
| | |
- | | (15) passing freed pointer 'obj' in call to 'free_boxed_int' from 'test'
+ | | (19) passing freed pointer 'obj' in call to 'free_boxed_int' from 'test'
|
- +--> 'free_boxed_int': events 16-17
+ +--> 'free_boxed_int': events 20-21
|
| NN | free_boxed_int (boxed_int *bi)
| | ^~~~~~~~~~~~~~
| | |
- | | (16) entry to 'free_boxed_int'
+ | | (20) entry to 'free_boxed_int'
| NN | {
| NN | wrapped_free (bi);
| | ~~~~~~~~~~~~~~~~~
| | |
- | | (17) passing freed pointer 'bi' in call to 'wrapped_free' from 'free_boxed_int'
+ | | (21) passing freed pointer 'bi' in call to 'wrapped_free' from 'free_boxed_int'
|
- +--> 'wrapped_free': events 18-19
+ +--> 'wrapped_free': events 22-23
|
| NN | void wrapped_free (void *ptr)
| | ^~~~~~~~~~~~
| | |
- | | (18) entry to 'wrapped_free'
+ | | (22) entry to 'wrapped_free'
| NN | {
| NN | free (ptr);
| | ~~~~~~~~~~
| | |
- | | (19) second 'free' here; first 'free' was at (12) ('ptr' is in state 'freed')
+ | | (23) second 'free' here; first 'free' was at (16) ('ptr' is in state 'freed')
|
{ dg-end-multiline-output "" } */
diff --git a/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-8-unchecked.c b/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-8-unchecked.c
index a778a29852f..320044753a1 100644
--- a/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-8-unchecked.c
+++ b/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-8-unchecked.c
@@ -1,6 +1,6 @@
/* Example of a multilevel wrapper around malloc, with an unchecked write. */
-/* { dg-additional-options "-fdiagnostics-show-line-numbers -fdiagnostics-path-format=inline-events -fanalyzer-checker=malloc -fdiagnostics-show-caret -fanalyzer-verbose-state-changes" } */
+/* { dg-additional-options "-fdiagnostics-show-line-numbers -fdiagnostics-path-format=inline-events -fanalyzer-checker=malloc -fdiagnostics-show-caret" } */
/* { dg-enable-nn-line-numbers "" } */
#include <stdlib.h>
@@ -49,7 +49,7 @@ make_boxed_int (int i)
| NN | return malloc (size);
| | ~~~~~~~~~~~~~
| | |
- | | (4) this call could return NULL (state of '<return-value>': 'start' -> 'unchecked', origin: NULL)
+ | | (4) this call could return NULL
|
<------+
|
@@ -62,6 +62,6 @@ make_boxed_int (int i)
| NN | result->i = i;
| | ~~~~~~~~~~~~~
| | |
- | | (6) 'result' could be NULL: unchecked value from (4) ('result' is in state 'unchecked')
+ | | (6) 'result' could be NULL: unchecked value from (4)
|
{ dg-end-multiline-output "" } */
diff --git a/gcc/testsuite/gcc.dg/analyzer/malloc-paths-9.c b/gcc/testsuite/gcc.dg/analyzer/malloc-paths-9.c
index 51190c92391..a3cacc07fab 100644
--- a/gcc/testsuite/gcc.dg/analyzer/malloc-paths-9.c
+++ b/gcc/testsuite/gcc.dg/analyzer/malloc-paths-9.c
@@ -112,8 +112,7 @@ int test_3 (int x, int y)
free (ptr); /* No double-'free' warning: we've already attempted
to dereference it above. */
return *ptr; /* { dg-warning "use after 'free' of 'ptr'" "use-after-free" } */
- // TODO: two warnings here: one is from sm-malloc, the other from region model
- /* { dg-warning "leak of 'ptr'" "leak" { target *-*-* } .-2 } */
+ /* { dg-warning "leak of 'ptr'" "leak" { target *-*-* } .-1 } */
}
/* "dereference of possibly-NULL 'ptr'". */
@@ -241,59 +240,3 @@ int test_3 (int x, int y)
| | (7) 'ptr' leaks here; was allocated at (1)
|
{ dg-end-multiline-output "" } */
-
-/* "use after 'free' of 'ptr'". */
-/* { dg-begin-multiline-output "" }
- NN | *ptr = 19;
- | ~~~~~^~~~
- 'test_3': events 1-3
- |
- | NN | if (x)
- | | ^
- | | |
- | | (1) following 'true' branch (when 'x != 0')...
- | NN | free (ptr);
- | | ~~~~~~~~~~
- | | |
- | | (2) ...to here
- | NN |
- | NN | *ptr = 19;
- | | ~~~~~~~~~
- | | |
- | | (3) use after 'free' of 'ptr' here
- |
- { dg-end-multiline-output "" } */
-
-/* "use after 'free' of 'ptr'". */
-/* { dg-begin-multiline-output "" }
- NN | return *ptr;
- | ^~~~
- 'test_3': events 1-5
- |
- | NN | if (x)
- | | ^
- | | |
- | | (1) following 'false' branch (when 'x == 0')...
- |......
- | NN | *ptr = 19;
- | | ~~~~~~~~~
- | | |
- | | (2) ...to here
- |......
- | NN | if (y)
- | | ~
- | | |
- | | (3) following 'true' branch (when 'y != 0')...
- | NN | free (ptr);
- | | ~~~~~~~~~~
- | | |
- | | (4) ...to here
- | NN | to dereference it above
- | NN | return *ptr;
- | | ~~~~
- | | |
- | | (5) use after 'free' of 'ptr' here
- |
- { dg-end-multiline-output "" } */
-/* TODO: this is really a duplicate; can we either eliminate it, or
- improve the path? */
diff --git a/gcc/testsuite/gcc.dg/analyzer/malloc-vs-local-1a.c b/gcc/testsuite/gcc.dg/analyzer/malloc-vs-local-1a.c
index d47dfa8b2c0..4e70694a33d 100644
--- a/gcc/testsuite/gcc.dg/analyzer/malloc-vs-local-1a.c
+++ b/gcc/testsuite/gcc.dg/analyzer/malloc-vs-local-1a.c
@@ -11,7 +11,7 @@ do_stuff (int *p, int n)
int sum = 0;
int i;
for (i = 0; i < n; i++)
- p[i] = i;
+ p[i] = i; /* { dg-warning "dereference of possibly-NULL 'p'" } */
for (i = 0; i < n; i++)
sum += foo (p[i]); /* { dg-bogus "uninitialized" } */
return sum;
@@ -48,10 +48,10 @@ int test_repeated_predicate_1 (int n)
result = do_stuff (ptr, n);
- __analyzer_dump_exploded_nodes (0); /* { dg-warning "3 processed enodes" } */
- // FIXME: why 3 here?
- __analyzer_dump_exploded_nodes (0); /* { dg-warning "3 processed enodes" } */
- // FIXME: why 3 here?
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "5 processed enodes" } */
+ // FIXME: why 5 here?
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "5 processed enodes" } */
+ // FIXME: why 5 here?
if (n > 10)
free (ptr); /* { dg-bogus "not on the heap" } */
@@ -105,8 +105,8 @@ int test_explicit_flag (int n)
result = do_stuff (ptr, n);
- __analyzer_dump_exploded_nodes (0); /* { dg-warning "3 processed enodes" } */
- // FIXME: why 3 here?
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "5 processed enodes" } */
+ // FIXME: why 5 here?
if (need_to_free)
free (ptr); /* { dg-bogus "not on the heap" } */
@@ -131,8 +131,8 @@ int test_pointer_comparison (int n)
result = do_stuff (ptr, n);
- __analyzer_dump_exploded_nodes (0); /* { dg-warning "3 processed enodes" } */
- // FIXME: why 3 here?
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "5 processed enodes" } */
+ // FIXME: why 5 here?
if (ptr != buf)
free (ptr); /* { dg-bogus "not on the heap" } */
@@ -169,8 +169,8 @@ int test_initial_flag (int n)
result = do_stuff (ptr, n);
- __analyzer_dump_exploded_nodes (0); /* { dg-warning "5 processed enodes" } */
- // FIXME: why 5 here?
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "6 processed enodes" } */
+ // FIXME: why 6 here?
if (n > 10)
free (ptr); /* { dg-bogus "not on the heap" } */
diff --git a/gcc/testsuite/gcc.dg/analyzer/malloc-vs-local-2.c b/gcc/testsuite/gcc.dg/analyzer/malloc-vs-local-2.c
index 89bd511549f..9001fe66cd7 100644
--- a/gcc/testsuite/gcc.dg/analyzer/malloc-vs-local-2.c
+++ b/gcc/testsuite/gcc.dg/analyzer/malloc-vs-local-2.c
@@ -31,13 +31,13 @@ int test_repeated_predicate_1 (int n)
int sum = 0;
int i;
for (i = 0; i < n; i++)
- p[i] = i;
+ p[i] = i; /* { dg-warning "dereference of possibly-NULL" } */
for (i = 0; i < n; i++)
sum += foo (p[i]); /* { dg-bogus "uninitialized" } */
result = sum;
}
- __analyzer_dump_exploded_nodes (0); /* { dg-warning "3 processed enodes" } */
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 processed enodes" } */
if (n > 10)
free (ptr); /* { dg-bogus "not on the heap" } */
@@ -65,11 +65,11 @@ int test_repeated_predicate_1a (int n)
int sum = 0;
int i;
for (i = 0; i < n; i++)
- p[i] = i;
+ p[i] = i; /* { dg-warning "dereference of possibly-NULL" } */
result = sum;
}
- __analyzer_dump_exploded_nodes (0); /* { dg-warning "3 processed enodes" } */
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 processed enodes" } */
if (n > 10)
free (ptr); /* { dg-bogus "not on the heap" } */
@@ -126,13 +126,13 @@ int test_explicit_flag (int n)
int sum = 0;
int i;
for (i = 0; i < n; i++)
- p[i] = i;
+ p[i] = i; /* { dg-warning "dereference of possibly-NULL" } */
for (i = 0; i < n; i++)
sum += foo (p[i]); /* { dg-bogus "uninitialized" } */
result = sum;
}
- __analyzer_dump_exploded_nodes (0); /* { dg-warning "3 processed enodes" } */
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 processed enodes" } */
if (need_to_free)
free (ptr); /* { dg-bogus "not on the heap" } */
@@ -160,13 +160,13 @@ int test_pointer_comparison (int n)
int sum = 0;
int i;
for (i = 0; i < n; i++)
- p[i] = i;
+ p[i] = i; /* { dg-warning "dereference of possibly-NULL" } */
for (i = 0; i < n; i++)
sum += foo (p[i]); /* { dg-bogus "uninitialized" } */
result = sum;
}
- __analyzer_dump_exploded_nodes (0); /* { dg-warning "3 processed enodes" } */
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 processed enodes" } */
if (ptr != buf)
free (ptr); /* { dg-bogus "not on the heap" } */
diff --git a/gcc/testsuite/gcc.dg/analyzer/malloc-vs-local-3.c b/gcc/testsuite/gcc.dg/analyzer/malloc-vs-local-3.c
index d20a2753bb8..0196389d3a5 100644
--- a/gcc/testsuite/gcc.dg/analyzer/malloc-vs-local-3.c
+++ b/gcc/testsuite/gcc.dg/analyzer/malloc-vs-local-3.c
@@ -30,16 +30,15 @@ int test_1 (int n)
int sum = 0;
int i;
for (i = 0; i < n; i++)
- p[i] = i;
+ p[i] = i; /* { dg-warning "dereference of possibly-NULL" } */
for (i = 0; i < n; i++)
sum += foo (p[i]); /* { dg-bogus "uninitialized" } */
result = sum;
}
- __analyzer_dump_exploded_nodes (0); /* { dg-warning "3 processed enodes" } */
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 processed enodes" } */
- return result; /* { dg-message "leak of 'p'" } */
- /* FIXME: should this be 'ptr'? */
+ return result; /* { dg-message "leak of 'p'|leak of 'ptr'" } */
}
/* A simpler version of the above. */
diff --git a/gcc/testsuite/gcc.dg/analyzer/memset-1.c b/gcc/testsuite/gcc.dg/analyzer/memset-1.c
new file mode 100644
index 00000000000..830c1105f46
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/memset-1.c
@@ -0,0 +1,100 @@
+#include <string.h>
+#include "analyzer-decls.h"
+
+/* Zero-fill of uninitialized buffer. */
+
+void test_1 (void)
+{
+ char buf[256];
+ memset (buf, 0, 256);
+ __analyzer_eval (buf[42] == 0); /* { dg-warning "TRUE" } */
+}
+
+/* As above, but with __builtin_memset. */
+
+void test_1a (void)
+{
+ char buf[256];
+ __builtin_memset (buf, 0, 256);
+ __analyzer_eval (buf[42] == 0); /* { dg-warning "TRUE" } */
+}
+
+/* Zero-fill of partially initialized buffer. */
+
+void test_2 (void)
+{
+ char buf[256];
+ buf[42] = 'A';
+ __analyzer_eval (buf[42] == 'A'); /* { dg-warning "TRUE" } */
+ memset (buf, 0, 256);
+ __analyzer_eval (buf[42] == '\0'); /* { dg-warning "TRUE" } */
+}
+
+/* A "memset" with known non-zero value. */
+
+void test_3 (int val)
+{
+ char buf[256];
+ memset (buf, 'A', 256);
+ /* We currently merely mark such regions as "unknown", so querying
+ values within them yields UNKNOWN when ideally it would be TRUE. */
+ __analyzer_eval (buf[42] == 'A'); /* { dg-warning "TRUE" "known nonzero" { xfail *-*-* } } */
+ /* { dg-bogus "UNKNOWN" "status quo" { xfail *-*-* } .-1 } */
+}
+
+/* A "memset" with unknown value. */
+
+void test_4 (int val)
+{
+ char buf[256];
+ memset (buf, val, 256);
+ /* We currently merely mark such regions as "unknown", so querying
+ values within them yields UNKNOWN when ideally it would be TRUE. */
+ __analyzer_eval (buf[42] == (char)val); /* { dg-warning "TRUE" "known nonzero" { xfail *-*-* } } */
+ /* { dg-bogus "UNKNOWN" "status quo" { xfail *-*-* } .-1 } */
+}
+
+/* A "memset" with unknown num bytes. */
+
+void test_5 (int n)
+{
+ char buf[256];
+ buf[42] = 'A';
+ __analyzer_eval (buf[42] == 'A'); /* { dg-warning "TRUE" } */
+ memset (buf, 0, n);
+
+ /* We can't know if buf[42] was written to or not. */
+ __analyzer_eval (buf[42] == 'A'); /* { dg-warning "UNKNOWN" } */
+ __analyzer_eval (buf[42] == '\0'); /* { dg-warning "UNKNOWN" } */
+}
+
+/* A "memset" with unknown value, but with zero size. */
+
+static size_t __attribute__((noinline))
+get_zero (void)
+{
+ return 0;
+}
+
+void test_6 (int val)
+{
+ char buf[256];
+ buf[42] = 'A';
+ memset (buf, 'B', get_zero ());
+ __analyzer_eval (buf[42] == 'A'); /* { dg-warning "TRUE" } */
+}
+
+/* A "memset" of known size that's not the full buffer. */
+
+void test_7 (void)
+{
+ char buf[256];
+ buf[128] = 'A';
+ memset (buf, 0, 128);
+ /* We currently merely mark the whole region as "unknown", so querying
+ values within them yields UNKNOWN. */
+ __analyzer_eval (buf[127] == '\0'); /* { dg-warning "TRUE" "known nonzero" { xfail *-*-* } } */
+ /* { dg-bogus "UNKNOWN" "status quo" { xfail *-*-* } .-1 } */
+ __analyzer_eval (buf[128] == 'A'); /* { dg-warning "TRUE" "known nonzero" { xfail *-*-* } } */
+ /* { dg-bogus "UNKNOWN" "status quo" { xfail *-*-* } .-1 } */
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/paths-3.c b/gcc/testsuite/gcc.dg/analyzer/paths-3.c
index 9bd30301690..b5ab810e536 100644
--- a/gcc/testsuite/gcc.dg/analyzer/paths-3.c
+++ b/gcc/testsuite/gcc.dg/analyzer/paths-3.c
@@ -13,7 +13,7 @@ int test_1 (int a, int b)
else
p = malloc (32);
- __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 processed enodes" } */
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "3 processed enodes" } */
if (a > 5)
{
@@ -34,7 +34,7 @@ int test_2 (int a, int b)
else
p = malloc (32);
- __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 processed enodes" } */
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "3 processed enodes" } */
if (a > 6) /* different condition */
{
diff --git a/gcc/testsuite/gcc.dg/analyzer/paths-4.c b/gcc/testsuite/gcc.dg/analyzer/paths-4.c
index 2f58763886c..b72e658739e 100644
--- a/gcc/testsuite/gcc.dg/analyzer/paths-4.c
+++ b/gcc/testsuite/gcc.dg/analyzer/paths-4.c
@@ -13,8 +13,8 @@ int test_1 (struct state *s)
__analyzer_dump_exploded_nodes (0); /* { dg-warning "1 processed enode" } */
while (1)
{
- __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 processed enodes" } */
- __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 processed enode" } */
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 processed enode" } */
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 processed enode" } */
/* TODO: why does the above need an extra stmt to merge state? */
do_stuff (s, s->mode);
}
@@ -25,13 +25,13 @@ int test_2 (struct state *s)
__analyzer_dump_exploded_nodes (0); /* { dg-warning "1 processed enode" } */
while (1)
{
- __analyzer_dump_exploded_nodes (0); /* { dg-warning "3 processed enodes" } */
- __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 processed enode" } */
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 processed enode" } */
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 processed enode" } */
/* TODO: why does the above need an extra stmt to merge state? */
switch (s->mode)
{
case 0:
- __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 processed enode" } */
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 processed enode" } */
do_stuff (s, 0);
break;
case 1:
diff --git a/gcc/testsuite/gcc.dg/analyzer/paths-6.c b/gcc/testsuite/gcc.dg/analyzer/paths-6.c
index 8220b8eff82..ef1a4e6c04d 100644
--- a/gcc/testsuite/gcc.dg/analyzer/paths-6.c
+++ b/gcc/testsuite/gcc.dg/analyzer/paths-6.c
@@ -89,7 +89,7 @@ void test_3 (int i)
break;
}
- __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 processed enode" } */
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 processed enode" } */
__analyzer_eval (f == 3); /* { dg-warning "TRUE" } */
__analyzer_eval (g == 4); /* { dg-warning "TRUE" } */
__analyzer_eval (h == 5); /* { dg-warning "TRUE" } */
@@ -108,7 +108,7 @@ void test_4 (int flag)
q = malloc (256);
p = malloc (256);
}
- __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 processed enode" } */
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 processed enode" } */
free (p);
free (q);
}
diff --git a/gcc/testsuite/gcc.dg/analyzer/paths-7.c b/gcc/testsuite/gcc.dg/analyzer/paths-7.c
index 243b9635364..8caaee8a3fd 100644
--- a/gcc/testsuite/gcc.dg/analyzer/paths-7.c
+++ b/gcc/testsuite/gcc.dg/analyzer/paths-7.c
@@ -50,8 +50,7 @@ int test_2 (int flag, int *p, int n)
sum += foo (p[i]); /* { dg-bogus "uninitialized" } */
result = sum;
- __analyzer_dump_exploded_nodes (0); /* { dg-warning "5 processed enodes" } */
- // FIXME: why 5 here?
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 processed enodes" } */
free (ptr); /* { dg-warning "double-'free' of 'ptr'" } */
return result;
diff --git a/gcc/testsuite/gcc.dg/analyzer/pr93032-mztools-simplified.c b/gcc/testsuite/gcc.dg/analyzer/pr93032-mztools-simplified.c
new file mode 100644
index 00000000000..4a08f0f1f50
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/pr93032-mztools-simplified.c
@@ -0,0 +1,22 @@
+/* { dg-do "compile" } */
+
+/* Minimal replacement of system headers. */
+#define NULL ((void *) 0)
+typedef struct _IO_FILE FILE;
+extern FILE *fopen(const char *__restrict __filename,
+ const char *__restrict __modes);
+extern int fclose (FILE *__stream);
+
+extern void unzRepair(const char* file, const char* fileOut, const char* fileOutTmp)
+{
+ FILE* fpZip = fopen(file, "rb");
+ FILE* fpOut = fopen(fileOut, "wb");
+ FILE* fpOutCD = fopen(fileOutTmp, "wb");
+ if (fpZip != NULL && fpOut != NULL) {
+ fclose(fpOutCD);
+ fclose(fpZip);
+ fclose(fpOut);
+ }
+} /* { dg-warning "leak of FILE 'fpZip'" "leak of fpZip" } */
+ /* { dg-warning "leak of FILE 'fpOut'" "leak of fpOut" { target *-*-* } .-1 } */
+ /* { dg-warning "leak of FILE 'fpOutCD'" "leak of fpOutCD" { target *-*-* } .-2 } */
diff --git a/gcc/testsuite/gcc.dg/analyzer/pr93032-mztools.c b/gcc/testsuite/gcc.dg/analyzer/pr93032-mztools.c
new file mode 100644
index 00000000000..88ab5bf8c18
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/pr93032-mztools.c
@@ -0,0 +1,331 @@
+/* Integration test to ensure we issue FILE * leak diagnostics for
+ this particular non-trivial case.
+ Adapted from zlib/contrib/minizip/mztools.c, with all #includes
+ removed. */
+
+/* { dg-do "compile" } */
+
+/* Minimal replacement of system headers. */
+
+typedef __SIZE_TYPE__ size_t;
+#define NULL ((void *) 0)
+
+typedef struct _IO_FILE FILE;
+extern FILE *fopen(const char *__restrict __filename,
+ const char *__restrict __modes);
+extern size_t fread (void *__restrict __ptr, size_t __size,
+ size_t __n, FILE *__restrict __stream);
+extern size_t fwrite (const void *__restrict __ptr, size_t __size,
+ size_t __n, FILE *__restrict __s);
+extern int fclose (FILE *__stream);
+extern int remove (const char *__filename)
+ __attribute__ ((__nothrow__ , __leaf__));
+
+extern void *malloc (size_t __size)
+ __attribute__ ((__nothrow__ , __leaf__))
+ __attribute__ ((__malloc__));
+extern void free (void *__ptr)
+ __attribute__ ((__nothrow__ , __leaf__));
+
+extern size_t strlen (const char *__s)
+ __attribute__ ((__nothrow__ , __leaf__))
+ __attribute__ ((__pure__))
+ __attribute__ ((__nonnull__ (1)));
+
+/* Minimal replacement of zlib headers. */
+
+#define ZEXPORT
+typedef unsigned long uLong; /* 32 bits or more */
+#define Z_OK 0
+#define Z_ERRNO (-1)
+#define Z_STREAM_ERROR (-2)
+#define Z_MEM_ERROR (-4)
+
+/*
+ Additional tools for Minizip
+ Code: Xavier Roche '2004
+ License: Same as ZLIB (www.gzip.org)
+*/
+
+/* Code */
+
+#define READ_8(adr) ((unsigned char)*(adr))
+#define READ_16(adr) ( READ_8(adr) | (READ_8(adr+1) << 8) )
+#define READ_32(adr) ( READ_16(adr) | (READ_16((adr)+2) << 16) )
+
+#define WRITE_8(buff, n) do { \
+ *((unsigned char*)(buff)) = (unsigned char) ((n) & 0xff); \
+} while(0)
+#define WRITE_16(buff, n) do { \
+ WRITE_8((unsigned char*)(buff), n); \
+ WRITE_8(((unsigned char*)(buff)) + 1, (n) >> 8); \
+} while(0)
+#define WRITE_32(buff, n) do { \
+ WRITE_16((unsigned char*)(buff), (n) & 0xffff); \
+ WRITE_16((unsigned char*)(buff) + 2, (n) >> 16); \
+} while(0)
+
+extern int ZEXPORT unzRepair(file, fileOut, fileOutTmp, nRecovered, bytesRecovered)
+const char* file;
+const char* fileOut;
+const char* fileOutTmp;
+uLong* nRecovered;
+uLong* bytesRecovered;
+{
+ int err = Z_OK;
+ FILE* fpZip = fopen(file, "rb");
+ FILE* fpOut = fopen(fileOut, "wb");
+ FILE* fpOutCD = fopen(fileOutTmp, "wb");
+ if (fpZip != NULL && fpOut != NULL) {
+ int entries = 0;
+ uLong totalBytes = 0;
+ char header[30];
+ char filename[1024];
+ char extra[1024];
+ int offset = 0;
+ int offsetCD = 0;
+ while ( fread(header, 1, 30, fpZip) == 30 ) {
+ int currentOffset = offset;
+
+ /* File entry */
+ if (READ_32(header) == 0x04034b50) {
+ unsigned int version = READ_16(header + 4);
+ unsigned int gpflag = READ_16(header + 6);
+ unsigned int method = READ_16(header + 8);
+ unsigned int filetime = READ_16(header + 10);
+ unsigned int filedate = READ_16(header + 12);
+ unsigned int crc = READ_32(header + 14); /* crc */
+ unsigned int cpsize = READ_32(header + 18); /* compressed size */
+ unsigned int uncpsize = READ_32(header + 22); /* uncompressed sz */
+ unsigned int fnsize = READ_16(header + 26); /* file name length */
+ unsigned int extsize = READ_16(header + 28); /* extra field length */
+ filename[0] = extra[0] = '\0';
+
+ /* Header */
+ if (fwrite(header, 1, 30, fpOut) == 30) {
+ offset += 30;
+ } else {
+ err = Z_ERRNO;
+ break;
+ }
+
+ /* Filename */
+ if (fnsize > 0) {
+ if (fnsize < sizeof(filename)) {
+ if (fread(filename, 1, fnsize, fpZip) == fnsize) {
+ if (fwrite(filename, 1, fnsize, fpOut) == fnsize) {
+ offset += fnsize;
+ } else {
+ err = Z_ERRNO;
+ break;
+ }
+ } else {
+ err = Z_ERRNO;
+ break;
+ }
+ } else {
+ err = Z_ERRNO;
+ break;
+ }
+ } else {
+ err = Z_STREAM_ERROR;
+ break;
+ }
+
+ /* Extra field */
+ if (extsize > 0) {
+ if (extsize < sizeof(extra)) {
+ if (fread(extra, 1, extsize, fpZip) == extsize) {
+ if (fwrite(extra, 1, extsize, fpOut) == extsize) {
+ offset += extsize;
+ } else {
+ err = Z_ERRNO;
+ break;
+ }
+ } else {
+ err = Z_ERRNO;
+ break;
+ }
+ } else {
+ err = Z_ERRNO;
+ break;
+ }
+ }
+
+ /* Data */
+ {
+ int dataSize = cpsize;
+ if (dataSize == 0) {
+ dataSize = uncpsize;
+ }
+ if (dataSize > 0) {
+ char* data = malloc(dataSize);
+ if (data != NULL) {
+ if ((int)fread(data, 1, dataSize, fpZip) == dataSize) {
+ if ((int)fwrite(data, 1, dataSize, fpOut) == dataSize) {
+ offset += dataSize;
+ totalBytes += dataSize;
+ } else {
+ err = Z_ERRNO;
+ }
+ } else {
+ err = Z_ERRNO;
+ }
+ free(data);
+ if (err != Z_OK) {
+ break;
+ }
+ } else {
+ err = Z_MEM_ERROR;
+ break;
+ }
+ }
+ }
+
+ /* Central directory entry */
+ {
+ char header[46];
+ char* comment = "";
+ int comsize = (int) strlen(comment);
+ WRITE_32(header, 0x02014b50);
+ WRITE_16(header + 4, version);
+ WRITE_16(header + 6, version);
+ WRITE_16(header + 8, gpflag);
+ WRITE_16(header + 10, method);
+ WRITE_16(header + 12, filetime);
+ WRITE_16(header + 14, filedate);
+ WRITE_32(header + 16, crc);
+ WRITE_32(header + 20, cpsize);
+ WRITE_32(header + 24, uncpsize);
+ WRITE_16(header + 28, fnsize);
+ WRITE_16(header + 30, extsize);
+ WRITE_16(header + 32, comsize);
+ WRITE_16(header + 34, 0); /* disk # */
+ WRITE_16(header + 36, 0); /* int attrb */
+ WRITE_32(header + 38, 0); /* ext attrb */
+ WRITE_32(header + 42, currentOffset);
+ /* Header */
+ if (fwrite(header, 1, 46, fpOutCD) == 46) {
+ offsetCD += 46;
+
+ /* Filename */
+ if (fnsize > 0) {
+ if (fwrite(filename, 1, fnsize, fpOutCD) == fnsize) {
+ offsetCD += fnsize;
+ } else {
+ err = Z_ERRNO;
+ break;
+ }
+ } else {
+ err = Z_STREAM_ERROR;
+ break;
+ }
+
+ /* Extra field */
+ if (extsize > 0) {
+ if (fwrite(extra, 1, extsize, fpOutCD) == extsize) {
+ offsetCD += extsize;
+ } else {
+ err = Z_ERRNO;
+ break;
+ }
+ }
+
+ /* Comment field */
+ if (comsize > 0) {
+ if ((int)fwrite(comment, 1, comsize, fpOutCD) == comsize) {
+ offsetCD += comsize;
+ } else {
+ err = Z_ERRNO;
+ break;
+ }
+ }
+
+
+ } else {
+ err = Z_ERRNO;
+ break;
+ }
+ }
+
+ /* Success */
+ entries++;
+
+ } else {
+ break;
+ }
+ }
+
+ /* Final central directory */
+ {
+ int entriesZip = entries;
+ char header[22];
+ char* comment = ""; // "ZIP File recovered by zlib/minizip/mztools";
+ int comsize = (int) strlen(comment);
+ if (entriesZip > 0xffff) {
+ entriesZip = 0xffff;
+ }
+ WRITE_32(header, 0x06054b50);
+ WRITE_16(header + 4, 0); /* disk # */
+ WRITE_16(header + 6, 0); /* disk # */
+ WRITE_16(header + 8, entriesZip); /* hack */
+ WRITE_16(header + 10, entriesZip); /* hack */
+ WRITE_32(header + 12, offsetCD); /* size of CD */
+ WRITE_32(header + 16, offset); /* offset to CD */
+ WRITE_16(header + 20, comsize); /* comment */
+
+ /* Header */
+ if (fwrite(header, 1, 22, fpOutCD) == 22) {
+
+ /* Comment field */
+ if (comsize > 0) {
+ if ((int)fwrite(comment, 1, comsize, fpOutCD) != comsize) {
+ err = Z_ERRNO;
+ }
+ }
+
+ } else {
+ err = Z_ERRNO;
+ }
+ }
+
+ /* Final merge (file + central directory) */
+ fclose(fpOutCD);
+ if (err == Z_OK) {
+ fpOutCD = fopen(fileOutTmp, "rb");
+ if (fpOutCD != NULL) {
+ int nRead;
+ char buffer[8192];
+ while ( (nRead = (int)fread(buffer, 1, sizeof(buffer), fpOutCD)) > 0) {
+ if ((int)fwrite(buffer, 1, nRead, fpOut) != nRead) {
+ err = Z_ERRNO;
+ break;
+ }
+ }
+ fclose(fpOutCD);
+ }
+ }
+
+ /* Close */
+ fclose(fpZip);
+ fclose(fpOut);
+
+ /* Wipe temporary file */
+ (void)remove(fileOutTmp);
+
+ /* Number of recovered entries */
+ if (err == Z_OK) {
+ if (nRecovered != NULL) {
+ *nRecovered = entries;
+ }
+ if (bytesRecovered != NULL) {
+ *bytesRecovered = totalBytes;
+ }
+ }
+ } else {
+ err = Z_STREAM_ERROR;
+ }
+ return err; /* { dg-warning "leak of FILE 'fpZip'" "leak of fpZip" } */
+ /* { dg-warning "leak of FILE 'fpOut'" "leak of fpOut" { target *-*-* } .-1 } */
+ /* { dg-warning "leak of FILE 'fpOutCD'" "leak of fpOutCD" { target *-*-* } .-2 } */
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/pr93382.c b/gcc/testsuite/gcc.dg/analyzer/pr93382.c
index c55696df0ba..210b97d1a47 100644
--- a/gcc/testsuite/gcc.dg/analyzer/pr93382.c
+++ b/gcc/testsuite/gcc.dg/analyzer/pr93382.c
@@ -13,8 +13,8 @@ ql (void)
{
int n1[1];
- fread (n1, sizeof (n1[0]), 1, fp); /* { dg-message "'n1' gets an unchecked value here" } */
- idx = n1[0]; /* { dg-message "'idx' has an unchecked value here \\\(from 'n1'\\\)" } */
+ fread (n1, sizeof (n1[0]), 1, fp); /* { dg-message "'n1' gets an unchecked value here" "" { xfail *-*-* } } */
+ idx = n1[0]; /* { dg-message "'idx' has an unchecked value here \\\(from 'n1'\\\)" "" { xfail *-*-* } } */
}
int arr[10];
@@ -23,5 +23,5 @@ int
pl (void)
{
ql ();
- return arr[idx]; /* { dg-warning "use of tainted value 'idx' in array lookup without bounds checking" } */
+ return arr[idx]; /* { dg-warning "use of tainted value 'idx' in array lookup without bounds checking" "" { xfail *-*-* } } */
}
diff --git a/gcc/testsuite/gcc.dg/analyzer/pr93938.c b/gcc/testsuite/gcc.dg/analyzer/pr93938.c
new file mode 100644
index 00000000000..81d9983083f
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/pr93938.c
@@ -0,0 +1,13 @@
+/* Taken from gcc.dg/pr70022.c, adding -O1 to the options
+ (and -fanalyzer, implicitly). */
+
+/* { dg-do compile } */
+/* { dg-options "-w -Wno-psabi -O1" } */
+
+typedef int v4si __attribute__ ((vector_size (16)));
+
+int
+foo (v4si v)
+{
+ return v[~0UL];
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/pr94099.c b/gcc/testsuite/gcc.dg/analyzer/pr94099.c
index a116c49f105..1d7a5d771d6 100644
--- a/gcc/testsuite/gcc.dg/analyzer/pr94099.c
+++ b/gcc/testsuite/gcc.dg/analyzer/pr94099.c
@@ -21,7 +21,8 @@ pl (void)
for (sc = 0; sc < 1; ++sc)
{
th.gk.hk = 0;
- th.gk.bg[sc] = 0; /* { dg-warning "uninitialized" "uninit-warning-removed" { xfail *-*-* } } */
+ th.gk.bg[sc] = 0; /* { dg-warning "dereference of NULL '0'" } */
+ // TODO: above message could be improved
l3 (&th);
}
}
diff --git a/gcc/testsuite/gcc.dg/analyzer/pr94399.c b/gcc/testsuite/gcc.dg/analyzer/pr94399.c
new file mode 100644
index 00000000000..e897c04a007
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/pr94399.c
@@ -0,0 +1,13 @@
+#include <stdlib.h>
+
+#define _cleanup_(f) __attribute__((cleanup(f)))
+
+static inline void freep(void **p) {
+ free(*p);
+}
+
+void test(void) {
+ _cleanup_(freep) void *ptr;
+
+ ptr = malloc(3);
+} /* { dg-bogus "leak" } */
diff --git a/gcc/testsuite/gcc.dg/analyzer/pr94447.c b/gcc/testsuite/gcc.dg/analyzer/pr94447.c
index 1aecebba4ef..7c61a203067 100644
--- a/gcc/testsuite/gcc.dg/analyzer/pr94447.c
+++ b/gcc/testsuite/gcc.dg/analyzer/pr94447.c
@@ -6,5 +6,5 @@ struct foo
int test (void)
{
struct foo f = {};
- return *f.v;
+ return *f.v; /* { dg-warning "dereference of NULL" } */
}
diff --git a/gcc/testsuite/gcc.dg/analyzer/pr94458.c b/gcc/testsuite/gcc.dg/analyzer/pr94458.c
new file mode 100644
index 00000000000..ad9bfc94ad4
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/pr94458.c
@@ -0,0 +1,23 @@
+#include <stdlib.h>
+
+struct ret
+{
+ int **array;
+};
+
+struct ret *allocate_stuff(void)
+{
+ struct ret *ret;
+
+ ret = calloc(1, sizeof (struct ret));
+ if (!ret) {
+ abort();
+ }
+
+ ret->array = calloc (10, sizeof(int *));
+ if (!ret->array) {
+ abort();
+ }
+
+ return ret;
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/pr94640.c b/gcc/testsuite/gcc.dg/analyzer/pr94640.c
new file mode 100644
index 00000000000..9722a179fcd
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/pr94640.c
@@ -0,0 +1,17 @@
+#include <stdio.h>
+
+int debug;
+
+int opencfgfile(const char *cfgfile, FILE **fd)
+{
+ if (cfgfile[0] != '\0') {
+
+ if ((*fd = fopen(cfgfile, "r")) != NULL) {
+ if (debug)
+ printf("Config file: --config\n");
+ }
+
+ }
+
+ return 2;
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/pr94688.c b/gcc/testsuite/gcc.dg/analyzer/pr94688.c
new file mode 100644
index 00000000000..f553b8cfdad
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/pr94688.c
@@ -0,0 +1,6 @@
+int a, b;
+void d();
+void c()
+{
+ d((void (*)()) & a + b);
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/pr94689.c b/gcc/testsuite/gcc.dg/analyzer/pr94689.c
new file mode 100644
index 00000000000..09802a7ecf5
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/pr94689.c
@@ -0,0 +1,8 @@
+typedef void (*F) (void);
+void bar (F);
+
+void
+foo (void *a, int b)
+{
+ bar ((F) a + b);
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/pr94839.c b/gcc/testsuite/gcc.dg/analyzer/pr94839.c
new file mode 100644
index 00000000000..46c8bb98bd2
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/pr94839.c
@@ -0,0 +1,20 @@
+struct bitmap
+{
+ int min;
+ int max;
+ int *vec;
+};
+
+int bitmap_create(struct bitmap *bm, int min, int max)
+{
+ int sz;
+
+ sz = (max / sizeof(int)) + 1;
+
+ bm->min = min;
+ bm->max = max;
+ bm->vec = __builtin_calloc(sz, sizeof(int));
+ if (!bm->vec)
+ return (-12);
+ return 0; /* { dg-bogus "leak" } */
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/pr95026.c b/gcc/testsuite/gcc.dg/analyzer/pr95026.c
new file mode 100644
index 00000000000..1845f15ae59
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/pr95026.c
@@ -0,0 +1,17 @@
+struct _IO_FILE;
+typedef struct _IO_FILE FILE;
+typedef struct _message
+{
+ FILE *fp;
+} MESSAGE;
+extern FILE *fopen (const char *__restrict __filename,
+ const char *__restrict __modes);
+FILE *f (void);
+int imap_fetch_message (int i, MESSAGE *msg, char *p)
+{
+ if ((msg->fp = i ? 0 : f ()))
+ return 0;
+ if (p)
+ msg->fp = fopen (p, "r");
+ return -1;
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/pr95240.c b/gcc/testsuite/gcc.dg/analyzer/pr95240.c
new file mode 100644
index 00000000000..c84c64de8b8
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/pr95240.c
@@ -0,0 +1,27 @@
+typedef __SIZE_TYPE__ size_t;
+
+extern void *calloc(size_t nmemb, size_t size);
+extern void free(void *ptr);
+
+static char *activeTroubleArray;
+
+int
+initActiveTroubleArray ()
+{
+ activeTroubleArray = calloc (1, 1);
+ return activeTroubleArray ? 0 : 1;
+}
+
+void
+freeActiveTroubleArray ()
+{
+ free (activeTroubleArray);
+}
+
+int main (int argc, char *argv[])
+{
+ initActiveTroubleArray ();
+ freeActiveTroubleArray ();
+
+ return 1;
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/refcounting-1.c b/gcc/testsuite/gcc.dg/analyzer/refcounting-1.c
new file mode 100644
index 00000000000..4eb3a3e6ff2
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/refcounting-1.c
@@ -0,0 +1,31 @@
+#include "analyzer-decls.h"
+
+typedef struct obj {
+ int ob_refcnt;
+} PyObject;
+
+extern void Py_Dealloc (PyObject *op);
+
+#define Py_INCREF(op) \
+ do { \
+ ((PyObject*)(op))->ob_refcnt++; \
+ } while (0)
+
+#define Py_DECREF(op) \
+ do { \
+ if (--((PyObject*)(op))->ob_refcnt == 0) \
+ { \
+ /*Py_Dealloc((PyObject *)(op));*/ \
+ } \
+ } while (0)
+
+void test_1 (PyObject *obj)
+{
+ int orig_refcnt = obj->ob_refcnt;
+ Py_INCREF (obj);
+ Py_INCREF (obj);
+ Py_DECREF (obj);
+ Py_INCREF (obj);
+ __analyzer_eval (obj->ob_refcnt == orig_refcnt + 2); /* { dg-warning "TRUE" } */
+}
+/* TODO: uncomment the Py_Dealloc, which leads to two paths. */
diff --git a/gcc/testsuite/gcc.dg/analyzer/single-field.c b/gcc/testsuite/gcc.dg/analyzer/single-field.c
new file mode 100644
index 00000000000..d54cfb0c4d1
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/single-field.c
@@ -0,0 +1,37 @@
+#include <stdlib.h>
+#include "analyzer-decls.h"
+
+struct foo
+{
+ char *ptr;
+};
+
+void test_1 (struct foo f)
+{
+ __analyzer_describe (0, f.ptr); /* { dg-warning "svalue: 'INIT_VAL\\(f.ptr\\)'" } */
+}
+
+static void called_by_test_2 (struct foo f_inner)
+{
+ free (f_inner.ptr);
+ free (f_inner.ptr); /* { dg-warning "double-'free' of 'f_outer.ptr'" } */
+}
+void test_2 (struct foo f_outer)
+{
+ called_by_test_2 (f_outer);
+}
+
+struct nested
+{
+ struct foo f;
+};
+
+static void called_by_test_3 (struct nested n_inner)
+{
+ free (n_inner.f.ptr);
+ free (n_inner.f.ptr); /* { dg-warning "double-'free' of 'n_outer.f.ptr'" } */
+}
+void test_3 (struct nested n_outer)
+{
+ called_by_test_3 (n_outer);
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/stale-frame-1.c b/gcc/testsuite/gcc.dg/analyzer/stale-frame-1.c
new file mode 100644
index 00000000000..04221479bf9
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/stale-frame-1.c
@@ -0,0 +1,15 @@
+
+int *global_ptr;
+
+static void __attribute__((noinline))
+called_by_test_1 (void)
+{
+ int i = 42;
+ global_ptr = &i;
+}
+
+int test_1 (void)
+{
+ called_by_test_1 ();
+ return *global_ptr; /* { dg-warning "dereferencing pointer 'global_ptr' to within stale stack frame" } */
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/symbolic-1.c b/gcc/testsuite/gcc.dg/analyzer/symbolic-1.c
new file mode 100644
index 00000000000..9d228e6331c
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/symbolic-1.c
@@ -0,0 +1,43 @@
+#include "analyzer-decls.h"
+
+/* The example from store2.h */
+
+void test_1 (char a, char b, char c, char d, char e, char f,
+ int i, int j)
+{
+ char arr[1024];
+ arr[2] = a; /* (1) */
+ arr[3] = b; /* (2) */
+
+ __analyzer_eval (arr[2] == a); /* { dg-warning "TRUE" } */
+ __analyzer_eval (arr[3] == b); /* { dg-warning "TRUE" } */
+ __analyzer_eval (arr[4]); /* { dg-warning "UNKNOWN" } */ // TODO: report uninit
+
+ /* Replace one concrete binding's value with a different value. */
+ arr[3] = c; /* (3) */
+ __analyzer_eval (arr[2] == a); /* { dg-warning "TRUE" } */
+ __analyzer_eval (arr[3] == c); /* { dg-warning "TRUE" } */
+ __analyzer_eval (arr[3] == b); /* { dg-warning "UNKNOWN" } */
+ __analyzer_eval (arr[4]); /* { dg-warning "UNKNOWN" } */ // TODO: report uninit
+
+ /* Symbolic binding. */
+ arr[i] = d; /* (4) */
+ __analyzer_eval (arr[i] == d); /* { dg-warning "TRUE" } */
+ __analyzer_eval (arr[2] == a); /* { dg-warning "UNKNOWN" } */
+ __analyzer_eval (arr[3] == c); /* { dg-warning "UNKNOWN" } */
+ __analyzer_eval (arr[4]); /* { dg-warning "UNKNOWN" } */ /* Don't report uninit. */
+
+ /* Replace symbolic binding with a different one. */
+ arr[j] = e; /* (5) */
+ __analyzer_eval (arr[j] == e); /* { dg-warning "TRUE" } */
+ __analyzer_eval (arr[i] == d); /* { dg-warning "UNKNOWN" } */
+ __analyzer_eval (arr[4]); /* { dg-warning "UNKNOWN" } */ /* Don't report uninit. */
+
+ /* Add a concrete binding. */
+ arr[3] = f; /* (6) */
+ __analyzer_eval (arr[3] == f); /* { dg-warning "TRUE" } */
+ __analyzer_eval (arr[j] == e); /* { dg-warning "UNKNOWN" } */
+ __analyzer_eval (arr[4]); /* { dg-warning "UNKNOWN" } */ /* Don't report uninit. */
+}
+
+// TODO: as above, but with int rather than char so there's a cast
diff --git a/gcc/testsuite/gcc.dg/analyzer/symbolic-2.c b/gcc/testsuite/gcc.dg/analyzer/symbolic-2.c
new file mode 100644
index 00000000000..70c00ce3867
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/symbolic-2.c
@@ -0,0 +1,32 @@
+#include "analyzer-decls.h"
+
+struct foo
+{
+ int ival;
+ int iarr[10];
+};
+
+void test_1 (int i, int j)
+{
+ struct foo fooarr[4];
+ fooarr[1].ival = 42;
+ fooarr[1].iarr[3] = 27;
+ fooarr[2].iarr[1] = 17;
+ __analyzer_eval (fooarr[1].ival == 42); /* { dg-warning "TRUE" } */
+ __analyzer_eval (fooarr[1].iarr[3] == 27); /* { dg-warning "TRUE" } */
+ __analyzer_eval (fooarr[2].iarr[1] == 17); /* { dg-warning "TRUE" } */
+
+ /* Symbolic binding. */
+ fooarr[2].iarr[i] = j;
+ __analyzer_eval (fooarr[2].iarr[i] == j); /* { dg-warning "TRUE" } */
+
+ /* We should have lost our knowledge about fooarr[2].
+ It's not clear to me if we should also lose our knowledge about
+ fooarr[1] (for the case where i is negative). For now, we do. */
+ __analyzer_eval (fooarr[1].ival == 42); /* { dg-warning "UNKNOWN" } */
+ __analyzer_eval (fooarr[1].iarr[3] == 27); /* { dg-warning "UNKNOWN" } */
+ __analyzer_eval (fooarr[2].iarr[1] == 17); /* { dg-warning "UNKNOWN" } */
+ /* Should also be safe to read from fooarr[2];
+ it isn't known to be uninit anymore. */
+ __analyzer_eval (fooarr[2].iarr[10] == 17); /* { dg-warning "UNKNOWN" } */
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/symbolic-3.c b/gcc/testsuite/gcc.dg/analyzer/symbolic-3.c
new file mode 100644
index 00000000000..da4cdbbc54c
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/symbolic-3.c
@@ -0,0 +1,12 @@
+#include "analyzer-decls.h"
+
+int iarr[16];
+
+void test_1 (int i, int j)
+{
+ int init_el_8 = iarr[8];
+ __analyzer_eval (init_el_8 == iarr[8]); /* { dg-warning "TRUE" } */
+
+ iarr[i] = j;
+ __analyzer_eval (init_el_8 == iarr[8]); /* { dg-warning "UNKNOWN" } */
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/symbolic-4.c b/gcc/testsuite/gcc.dg/analyzer/symbolic-4.c
new file mode 100644
index 00000000000..a466f912007
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/symbolic-4.c
@@ -0,0 +1,20 @@
+#include <string.h>
+#include "analyzer-decls.h"
+
+void test_1 (int i, int j, int k)
+{
+ int iarr[16];
+ iarr[i] = j;
+ __analyzer_eval (iarr[i] == j); /* { dg-warning "TRUE" } */
+ __analyzer_eval (iarr[k] == j); /* { dg-warning "UNKNOWN" } */
+
+ memset (iarr, 0, sizeof (iarr));
+ __analyzer_eval (iarr[0] == 0); /* { dg-warning "TRUE" } */
+ __analyzer_eval (iarr[i] == 0); /* { dg-warning "TRUE" } */
+ __analyzer_eval (iarr[i] == j); /* { dg-warning "UNKNOWN" } */
+
+ iarr[i] = j;
+ __analyzer_eval (iarr[i] == j); /* { dg-warning "TRUE" } */
+ __analyzer_eval (iarr[0] == 0); /* { dg-warning "UNKNOWN" } */
+ __analyzer_eval (iarr[i] == 0); /* { dg-warning "UNKNOWN" } */
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/symbolic-5.c b/gcc/testsuite/gcc.dg/analyzer/symbolic-5.c
new file mode 100644
index 00000000000..3f696503606
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/symbolic-5.c
@@ -0,0 +1,29 @@
+#include "analyzer-decls.h"
+
+int a[1024];
+int b[1024];
+
+extern void escape (void *ptr);
+
+void test_1 (int *p)
+{
+ int c, d;
+ escape (&c);
+ a[16] = 42;
+ b[16] = 17;
+ c = 33;
+ d = 44;
+ __analyzer_eval (a[16] == 42); /* { dg-warning "TRUE" } */
+ __analyzer_eval (b[16] == 17); /* { dg-warning "TRUE" } */
+ __analyzer_eval (c == 33); /* { dg-warning "TRUE" } */
+ __analyzer_eval (d == 44); /* { dg-warning "TRUE" } */
+
+ /* Write through an externally-provided pointer. */
+ *p = 100;
+ /* It could clobber our writes to the global arrays... */
+ __analyzer_eval (a[16] == 42); /* { dg-warning "UNKNOWN" } */
+ __analyzer_eval (b[16] == 17); /* { dg-warning "UNKNOWN" } */
+ /* ...but can't clobber locals, even ones like "c" that have escaped. */
+ __analyzer_eval (c == 33); /* { dg-warning "TRUE" } */
+ __analyzer_eval (d == 44); /* { dg-warning "TRUE" } */
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/symbolic-6.c b/gcc/testsuite/gcc.dg/analyzer/symbolic-6.c
new file mode 100644
index 00000000000..10d4e972299
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/symbolic-6.c
@@ -0,0 +1,24 @@
+#include "analyzer-decls.h"
+
+int a[1024];
+int b[1024];
+
+extern void escape (void *ptr);
+
+void test_1 (int *p)
+{
+ int c, d;
+ escape (&c);
+
+ *p = 42;
+ __analyzer_eval (*p == 42); /* { dg-warning "TRUE" } */
+
+ /* These writes shouldn't affect *p. */
+ c = 33;
+ d = 44;
+ __analyzer_eval (*p == 42); /* { dg-warning "TRUE" } */
+
+ /* This write could affect *p. */
+ a[16] = 55;
+ __analyzer_eval (*p == 42); /* { dg-warning "UNKNOWN" } */
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/taint-1.c b/gcc/testsuite/gcc.dg/analyzer/taint-1.c
index 549e2660284..cd46dd5fc14 100644
--- a/gcc/testsuite/gcc.dg/analyzer/taint-1.c
+++ b/gcc/testsuite/gcc.dg/analyzer/taint-1.c
@@ -14,14 +14,14 @@ char test_1(FILE *f)
{
struct foo tmp;
- if (1 == fread(&tmp, sizeof(tmp), 1, f)) { /* { dg-message "\\(1\\) 'tmp' gets an unchecked value here" "event 1" } */
- /* { dg-message "\\(2\\) following 'true' branch\\.\\.\\." "event 2" { target *-*-* } .-1 } */
+ if (1 == fread(&tmp, sizeof(tmp), 1, f)) { /* { dg-message "\\(\[0-9\]+\\) 'tmp' gets an unchecked value here" "event: tmp gets unchecked value" { xfail *-*-* } } */
+ /* { dg-message "\\(\[0-9\]+\\) following 'true' branch\\.\\.\\." "event: following true branch" { target *-*-* } .-1 } */
/* BUG: the following array lookup trusts that the input data's index is
in the range 0 <= i < 256; otherwise it's accessing the stack */
return tmp.buf[tmp.i]; // { dg-warning "use of tainted value 'tmp.i' in array lookup without bounds checking" "warning" } */
- /* { dg-message "23: \\(3\\) \\.\\.\\.to here" "event 3" { target *-*-* } .-1 } */
- /* { dg-message "23: \\(4\\) 'tmp.i' has an unchecked value here \\(from 'tmp'\\)" "event 4" { target *-*-* } .-2 } */
- /* { dg-message "\\(5\\) use of tainted value 'tmp.i' in array lookup without bounds checking" "event 5" { target *-*-* } .-3 } */
+ /* { dg-message "23: \\(\[0-9\]+\\) \\.\\.\\.to here" "event: to here" { target *-*-* } .-1 } */
+ /* { dg-message "23: \\(\[0-9\]+\\) 'tmp.i' has an unchecked value here \\(from 'tmp'\\)" "event: tmp.i has an unchecked value" { xfail *-*-* } .-2 } */
+ /* { dg-message "\\(\[0-9\]+\\) use of tainted value 'tmp.i' in array lookup without bounds checking" "final event" { target *-*-* } .-3 } */
// TOOD: better messages for state changes
}
@@ -52,9 +52,9 @@ char test_4(FILE *f)
struct foo tmp;
if (1 == fread(&tmp, sizeof(tmp), 1, f)) {
- if (tmp.i >= 0) { /* { dg-message "'tmp.i' has an unchecked value here \\(from 'tmp'\\)" "warning" } */
- /* { dg-message "'tmp.i' has its lower bound checked here" "event" { target *-*-* } .-1 } */
- return tmp.buf[tmp.i]; /* { dg-warning "use of tainted value 'tmp.i' in array lookup without upper-bounds checking" } */
+ if (tmp.i >= 0) { /* { dg-message "'tmp.i' has an unchecked value here \\(from 'tmp'\\)" "event: tmp.i has an unchecked value" { xfail *-*-* } } */
+ /* { dg-message "'tmp.i' has its lower bound checked here" "event: lower bound checked" { target *-*-* } .-1 } */
+ return tmp.buf[tmp.i]; /* { dg-warning "use of tainted value 'tmp.i' in array lookup without upper-bounds checking" "warning" } */
}
}
return 0;
@@ -65,9 +65,9 @@ char test_5(FILE *f)
struct foo tmp;
if (1 == fread(&tmp, sizeof(tmp), 1, f)) {
- if (tmp.i < 256) { /* { dg-message "'tmp.i' has an unchecked value here \\(from 'tmp'\\)" "warning" } */
- /* { dg-message "'tmp.i' has its upper bound checked here" "event" { target *-*-* } .-1 } */
- return tmp.buf[tmp.i]; /* { dg-warning "use of tainted value 'tmp.i' in array lookup without lower-bounds checking" } */
+ if (tmp.i < 256) { /* { dg-message "'tmp.i' has an unchecked value here \\(from 'tmp'\\)" "event: tmp.i has an unchecked value" { xfail *-*-* } } */
+ /* { dg-message "'tmp.i' has its upper bound checked here" "event: upper bound checked" { target *-*-* } .-1 } */
+ return tmp.buf[tmp.i]; /* { dg-warning "use of tainted value 'tmp.i' in array lookup without lower-bounds checking" "warning" } */
}
}
return 0;
diff --git a/gcc/testsuite/gcc.dg/analyzer/torture/loop-inc-ptr-1.c b/gcc/testsuite/gcc.dg/analyzer/torture/loop-inc-ptr-1.c
new file mode 100644
index 00000000000..afb27185f1b
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/torture/loop-inc-ptr-1.c
@@ -0,0 +1,15 @@
+/* { dg-skip-if "" { *-*-* } { "-fno-fat-lto-objects" } { "" } } */
+
+#include "../analyzer-decls.h"
+
+void test (int *p)
+{
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 processed enode" } */
+
+ while (*p)
+ {
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 processed enode" } */
+ p++;
+ }
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 processed enode" } */
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/torture/loop-inc-ptr-2.c b/gcc/testsuite/gcc.dg/analyzer/torture/loop-inc-ptr-2.c
new file mode 100644
index 00000000000..95d8c53ade1
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/torture/loop-inc-ptr-2.c
@@ -0,0 +1,17 @@
+/* { dg-skip-if "" { *-*-* } { "-fno-fat-lto-objects" } { "" } } */
+
+#include "../analyzer-decls.h"
+
+void test (int *p, int val, int count)
+{
+ int n = count;
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 processed enode" } */
+
+ while (n--)
+ {
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 processed enode" } */
+ *p++ = val;
+ }
+
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 processed enode" } */
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/torture/loop-inc-ptr-3.c b/gcc/testsuite/gcc.dg/analyzer/torture/loop-inc-ptr-3.c
new file mode 100644
index 00000000000..1d3576d9308
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/torture/loop-inc-ptr-3.c
@@ -0,0 +1,18 @@
+/* { dg-skip-if "" { *-*-* } { "-fno-fat-lto-objects" } { "" } } */
+
+#include "../analyzer-decls.h"
+
+void test (int *p, int a, int b, int count)
+{
+ int n = count;
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 processed enode" } */
+
+ while (n--)
+ {
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 processed enode" } */
+ *p++ = a;
+ *p++ = b;
+ }
+
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 processed enode" } */
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/unknown-fns-2.c b/gcc/testsuite/gcc.dg/analyzer/unknown-fns-2.c
new file mode 100644
index 00000000000..1c4bdd6b51b
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/unknown-fns-2.c
@@ -0,0 +1,238 @@
+/* Tests for data model handling of unknown fns. */
+
+#include <stddef.h>
+#include "analyzer-decls.h"
+
+void unknown_fn (void *);
+
+void test_1 (void)
+{
+ int i;
+
+ i = 42;
+ __analyzer_eval (i == 42); /* { dg-warning "TRUE" } */
+
+ unknown_fn (NULL);
+ __analyzer_eval (i == 42); /* { dg-warning "TRUE" } */
+
+ unknown_fn (&i);
+ __analyzer_eval (i == 42); /* { dg-warning "UNKNOWN" } */
+
+ i = 17;
+ __analyzer_eval (i == 17); /* { dg-warning "TRUE" } */
+
+ /* Even though we're not passing &i to unknown_fn, it escaped
+ above, so unknown_fn could write to it. */
+ unknown_fn (NULL);
+ __analyzer_eval (i == 17); /* { dg-warning "UNKNOWN" } */
+}
+
+/* As test_1, but with an unknown fn_ptr. */
+
+void test_1a (void (*fn_ptr) (void *))
+{
+ int i;
+
+ i = 42;
+ __analyzer_eval (i == 42); /* { dg-warning "TRUE" } */
+
+ fn_ptr (NULL);
+ __analyzer_eval (i == 42); /* { dg-warning "TRUE" } */
+
+ fn_ptr (&i);
+ __analyzer_eval (i == 42); /* { dg-warning "UNKNOWN" } */
+
+ i = 17;
+ __analyzer_eval (i == 17); /* { dg-warning "TRUE" } */
+
+ /* Even though we're not passing &i to unknown_fn, it escaped
+ above, so fn_ptr (NULL) could write to it. */
+ fn_ptr (NULL);
+ __analyzer_eval (i == 17); /* { dg-warning "UNKNOWN" } */
+}
+
+int *global_for_test_2;
+
+void test_2 (void)
+{
+ int i;
+
+ i = 42;
+ __analyzer_eval (i == 42); /* { dg-warning "TRUE" } */
+
+ global_for_test_2 = &i;
+ unknown_fn (NULL);
+ __analyzer_eval (i == 42); /* { dg-warning "UNKNOWN" } */
+
+ global_for_test_2 = NULL;
+
+ i = 17;
+ __analyzer_eval (i == 17); /* { dg-warning "TRUE" } */
+
+ /* Even though the global no longer points to i, it escaped
+ above, so unknown_fn could write to it. */
+ unknown_fn (NULL);
+ __analyzer_eval (i == 17); /* { dg-warning "UNKNOWN" } */
+}
+
+struct used_by_test_3
+{
+ int *int_ptr;
+};
+
+void test_3 (void)
+{
+ int i;
+
+ struct used_by_test_3 s;
+ s.int_ptr = &i;
+
+ i = 42;
+ __analyzer_eval (i == 42); /* { dg-warning "TRUE" } */
+
+ unknown_fn (NULL);
+ __analyzer_eval (i == 42); /* { dg-warning "TRUE" } */
+ __analyzer_eval (s.int_ptr == &i); /* { dg-warning "TRUE" } */
+
+ /* i should escape here. */
+ unknown_fn (&s);
+ __analyzer_eval (i == 42); /* { dg-warning "UNKNOWN" } */
+ __analyzer_eval (s.int_ptr == &i); /* { dg-warning "UNKNOWN" } */
+
+ s.int_ptr = NULL;
+ __analyzer_eval (s.int_ptr == NULL); /* { dg-warning "TRUE" } */
+
+ i = 17;
+ __analyzer_eval (i == 17); /* { dg-warning "TRUE" } */
+
+ /* Even though nothing we know about points to i, it escaped
+ above, so unknown_fn could write to it. */
+ unknown_fn (NULL);
+ __analyzer_eval (i == 17); /* { dg-warning "UNKNOWN" } */
+}
+
+struct used_by_test_4
+{
+ int *int_ptr;
+};
+
+void test_4 (struct used_by_test_4 *st4_ptr)
+{
+ /* Something unknown called "test_4", and hence *st4_ptr has
+ effectively already escaped. */
+
+ int i = 42;
+ __analyzer_eval (i == 42); /* { dg-warning "TRUE" } */
+
+ unknown_fn (NULL);
+ __analyzer_eval (i == 42); /* { dg-warning "TRUE" } */
+
+ /* Given that *st4_ptr has effectively already escaped, calling
+ an unknown fn should invalidate our knowledge of i". */
+ st4_ptr->int_ptr = &i;
+ unknown_fn (NULL);
+ __analyzer_eval (i == 42); /* { dg-warning "UNKNOWN" } */
+
+ /* ...and "&i" should now be treated as having escaped. */
+ i = 17;
+ __analyzer_eval (i == 17); /* { dg-warning "TRUE" } */
+ st4_ptr->int_ptr = NULL;
+ unknown_fn (NULL);
+ __analyzer_eval (i == 17); /* { dg-warning "UNKNOWN" } */
+}
+
+static void __attribute__((noinline))
+known_fn (void *ptr)
+{
+ /* Empty. */
+}
+
+void test_5 (void)
+{
+ int i;
+
+ i = 42;
+ __analyzer_eval (i == 42); /* { dg-warning "TRUE" } */
+
+ known_fn (&i);
+ __analyzer_eval (i == 42); /* { dg-warning "TRUE" } */
+
+ i = 17;
+ __analyzer_eval (i == 17); /* { dg-warning "TRUE" } */
+
+ /* Ensure that we don't consider &i to have escaped. */
+ unknown_fn (NULL);
+ __analyzer_eval (i == 17); /* { dg-warning "TRUE" } */
+}
+
+extern int __attribute__ ((__pure__))
+unknown_pure_fn (void *);
+
+void test_6 (void)
+{
+ int i;
+
+ i = 42;
+ __analyzer_eval (i == 42); /* { dg-warning "TRUE" } */
+
+ unknown_pure_fn (&i);
+ __analyzer_eval (i == 42); /* { dg-warning "TRUE" } */
+
+ i = 17;
+ __analyzer_eval (i == 17); /* { dg-warning "TRUE" } */
+
+ /* Ensure that we don't consider &i to have escaped. */
+ unknown_fn (NULL);
+ __analyzer_eval (i == 17); /* { dg-warning "TRUE" } */
+}
+
+extern void unknown_const_fn (const void *);
+
+void test_7 (void)
+{
+ int i;
+
+ i = 42;
+ __analyzer_eval (i == 42); /* { dg-warning "TRUE" } */
+
+ /* &i is passed as a const void *, so i shouldn't be clobbered by
+ the call. */
+ unknown_const_fn (&i);
+ __analyzer_eval (i == 42); /* { dg-warning "TRUE" } */
+
+ i = 17;
+ __analyzer_eval (i == 17); /* { dg-warning "TRUE" } */
+
+ /* Ensure that we don't consider &i to have escaped. */
+ unknown_fn (NULL);
+ __analyzer_eval (i == 17); /* { dg-warning "TRUE" } */
+}
+
+struct used_by_test_8
+{
+ int *int_ptr;
+};
+
+void test_8 (void)
+{
+ int i;
+
+ i = 42;
+ __analyzer_eval (i == 42); /* { dg-warning "TRUE" } */
+
+ struct used_by_test_8 st8;
+ st8.int_ptr = &i;
+
+ /* Although unknown_const_fn takes a const void *, the
+ int_ptr is a non-const int *, and so &i should be considered
+ writable. */
+ unknown_const_fn (&st8);
+ __analyzer_eval (i == 42); /* { dg-warning "UNKNOWN" } */
+
+ i = 17;
+ __analyzer_eval (i == 17); /* { dg-warning "TRUE" } */
+
+ /* &i should be considered to have escaped. */
+ unknown_fn (NULL);
+ __analyzer_eval (i == 17); /* { dg-warning "UNKNOWN" } */
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/unknown-fns-3.c b/gcc/testsuite/gcc.dg/analyzer/unknown-fns-3.c
new file mode 100644
index 00000000000..095df5e77a7
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/unknown-fns-3.c
@@ -0,0 +1,67 @@
+/* Tests for handling constraints on results of unknown fns. */
+
+#include <stddef.h>
+#include "analyzer-decls.h"
+
+void unknown_fn (void *);
+
+void test_1 (void)
+{
+ int i;
+ unknown_fn (&i);
+ if (i)
+ __analyzer_eval (i); /* { dg-warning "TRUE" } */
+ else
+ __analyzer_eval (i); /* { dg-warning "FALSE" } */
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 processed enode" } */
+}
+
+struct foo
+{
+ int i;
+ int j;
+};
+
+void test_2 (void)
+{
+ struct foo f;
+ unknown_fn (&f);
+ if (f.j)
+ __analyzer_eval (f.j); /* { dg-warning "TRUE" } */
+ else
+ __analyzer_eval (f.j); /* { dg-warning "FALSE" } */
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 processed enode" } */
+}
+
+void test_3 (int flag)
+{
+ int i;
+ unknown_fn (&i);
+ if (i)
+ {
+ __analyzer_eval (i); /* { dg-warning "TRUE" } */
+ if (flag)
+ __analyzer_eval (flag); /* { dg-warning "TRUE" } */
+ else
+ __analyzer_eval (flag); /* { dg-warning "FALSE" } */
+ }
+ else
+ __analyzer_eval (i); /* { dg-warning "FALSE" } */
+ if (flag)
+ __analyzer_eval (flag); /* { dg-warning "TRUE" } */
+ else
+ __analyzer_eval (flag); /* { dg-warning "FALSE" } */
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 processed enode" } */
+}
+
+void test_4 (int y)
+{
+ int x;
+ unknown_fn (&x);
+ if (x)
+ {
+ __analyzer_eval (x); /* { dg-warning "TRUE" } */
+ x = 0;
+ }
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 processed enode" } */
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/unknown-fns-4.c b/gcc/testsuite/gcc.dg/analyzer/unknown-fns-4.c
new file mode 100644
index 00000000000..3d8f82ee290
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/unknown-fns-4.c
@@ -0,0 +1,15 @@
+#include "analyzer-decls.h"
+
+int get(void);
+void test (void)
+{
+ int got = 0;
+ while (1)
+ {
+ if (get ())
+ got = 1;
+ else
+ if (got)
+ __analyzer_dump_path (); /* { dg-message "path" "" { xfail *-*-* } } */
+ }
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/unknown-fns.c b/gcc/testsuite/gcc.dg/analyzer/unknown-fns.c
index 355c8b3d92a..fd1ecbb7005 100644
--- a/gcc/testsuite/gcc.dg/analyzer/unknown-fns.c
+++ b/gcc/testsuite/gcc.dg/analyzer/unknown-fns.c
@@ -76,10 +76,10 @@ void test_4a (void)
node_b.ptr = malloc (sizeof (int));
global_ptr = &node_a;
*node_b.ptr = 42; /* { dg-warning "possibly-NULL" "possibly-NULL" } */
- /* { dg-warning "leak" "leak" { target *-*-* } .-1 } */
- /* FIXME: the above leak report is correct, but is reported at the wrong
- location. */
-} /* { dg-warning "leak" } */
+ /* Although there's a chain of pointers to the allocation, pointed to
+ by global_ptr, the chain goes through the stack frame and thus
+ there's a leak when it is popped. */
+} /* { dg-warning "leak of 'node_b.ptr'" } */
/* With a call to an unknown function. */
diff --git a/gcc/testsuite/gcc.dg/analyzer/use-after-free.c b/gcc/testsuite/gcc.dg/analyzer/use-after-free.c
new file mode 100644
index 00000000000..d7e4bc2c6ca
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/use-after-free.c
@@ -0,0 +1,12 @@
+#include <stdlib.h>
+#include "analyzer-decls.h"
+
+struct link { struct link *next; };
+
+int free_a_list_badly (struct link *n)
+{
+ while (n) {
+ free(n); /* { dg-message "freed here" } */
+ n = n->next; /* { dg-warning "use after 'free' of 'n'" } */
+ }
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/vla-1.c b/gcc/testsuite/gcc.dg/analyzer/vla-1.c
new file mode 100644
index 00000000000..0488ff282a4
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/vla-1.c
@@ -0,0 +1,13 @@
+#include "analyzer-decls.h"
+
+void test_1 (int n)
+{
+ struct
+ {
+ char a[n], b;
+ } s;
+ s.a[0] = 42;
+ __analyzer_eval (s.a[0] == 42); /* { dg-warning "TRUE" } */
+ s.b = 17;
+ __analyzer_eval (s.b == 17); /* { dg-warning "TRUE" } */
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/zlib-4.c b/gcc/testsuite/gcc.dg/analyzer/zlib-4.c
index 08261442128..ae2f6c9f8ad 100644
--- a/gcc/testsuite/gcc.dg/analyzer/zlib-4.c
+++ b/gcc/testsuite/gcc.dg/analyzer/zlib-4.c
@@ -7,14 +7,18 @@ typedef unsigned long uLong;
#define Z_NULL 0
-void test ()
+int test ()
{
uLong comprLen = 10000*sizeof(int);
uLong uncomprLen = comprLen;
Byte *compr = (Byte*)calloc((uInt)comprLen, 1);
Byte *uncompr = (Byte*)calloc((uInt)uncomprLen, 1);
if (compr == Z_NULL || uncompr == Z_NULL)
- exit (1);
+ {
+ return 1; /* { dg-warning "leak of 'uncompr'" "uncompr leak" } */
+ /* { dg-warning "leak of 'compr'" "compr leak" { target *-*-* } .-1 } */
+ }
strcpy((char*)uncompr, "garbage");
- exit (0);
+ return 0; /* { dg-warning "leak of 'uncompr'" "uncompr leak" } */
+ /* { dg-warning "leak of 'compr'" "compr leak" { target *-*-* } .-1 } */
}
diff --git a/gcc/testsuite/gfortran.dg/analyzer/pr93993.f90 b/gcc/testsuite/gfortran.dg/analyzer/pr93993.f90
index 7a01095bfdc..230b99e4fcd 100644
--- a/gcc/testsuite/gfortran.dg/analyzer/pr93993.f90
+++ b/gcc/testsuite/gfortran.dg/analyzer/pr93993.f90
@@ -18,7 +18,7 @@ contains
allocate (tm) ! { dg-bogus "dereference of possibly-NULL" }
ce => tm
- end function hv ! { dg-warning "leak of 'tm'" }
+ end function hv
end module gg