aboutsummaryrefslogtreecommitdiff
path: root/gcc/analyzer/region.h
diff options
context:
space:
mode:
authorDavid Malcolm <dmalcolm@redhat.com>2022-05-16 15:32:11 -0400
committerDavid Malcolm <dmalcolm@redhat.com>2022-05-16 15:34:09 -0400
commit2402dc6b982c4dacac2360830f0edc123c588110 (patch)
tree95f38ecf7b3a91c1c413e2b782f1a54eb3b4b894 /gcc/analyzer/region.h
parent5eb9927aae076200bb7ba3f22c33b0a7c97c5643 (diff)
downloadgcc-2402dc6b982c4dacac2360830f0edc123c588110.zip
gcc-2402dc6b982c4dacac2360830f0edc123c588110.tar.gz
gcc-2402dc6b982c4dacac2360830f0edc123c588110.tar.bz2
analyzer: implement four new warnings for <stdarg.h> misuses [PR105103]
This patch adds support to the analyzer for checking usage of <stdarg.h>, with four new warnings. It adds: (a) a state-machine for tracking "started" and "ended" states on va_list instances, implementing two new warnings: -Wanalyzer-va-list-leak for complaining about missing va_end after a va_start or va_copy -Wanalyzer-va-list-use-after-va-end: for complaining about va_arg or va_copy used on a va_list that's had va_end called on it (b) interprocedural tracking of variadic parameters, tracking symbolic values, implementing two new warnings: -Wanalyzer-va-arg-type-mismatch for type-checking va_arg usage against the types of the parameters that were actually passed to the variadic call -Wanalyzer-va-list-exhausted for complaining if va_arg is used too many times on a va_list Here's an LTO example of a type mismatch in a variadic call that straddles two source files: stdarg-lto-1-a.c: In function 'called_by_test_type_mismatch_1': stdarg-lto-1-a.c:19:7: warning: 'va_arg' expected 'const char *' but received 'int' for variadic argument 1 of 'ap' [-Wanalyzer-va-arg-type-mismatch] 19 | str = va_arg (ap, const char *); | ^ 'test_type_mismatch_1': events 1-2 | |stdarg-lto-1-b.c:3:6: | 3 | void test_type_mismatch_1 (void) | | ^ | | | | | (1) entry to 'test_type_mismatch_1' | 4 | { | 5 | called_by_test_type_mismatch_1 (42, 1066); | | ~ | | | | | (2) calling 'called_by_test_type_mismatch_1' from 'test_type_mismatch_1' with 1 variadic argument | +--> 'called_by_test_type_mismatch_1': events 3-4 | |stdarg-lto-1-a.c:12:1: | 12 | called_by_test_type_mismatch_1 (int placeholder, ...) | | ^ | | | | | (3) entry to 'called_by_test_type_mismatch_1' |...... | 19 | str = va_arg (ap, const char *); | | ~ | | | | | (4) 'va_arg' expected 'const char *' but received 'int' for variadic argument 1 of 'ap' | gcc/ChangeLog: PR analyzer/105103 * Makefile.in (ANALYZER_OBJS): Add analyzer/varargs.o. * doc/invoke.texi: Add -Wanalyzer-va-arg-type-mismatch, -Wanalyzer-va-list-exhausted, -Wanalyzer-va-list-leak, and -Wanalyzer-va-list-use-after-va-end. gcc/analyzer/ChangeLog: PR analyzer/105103 * analyzer.cc (make_label_text_n): New. * analyzer.h (class var_arg_region): New forward decl. (make_label_text_n): New decl. * analyzer.opt (Wanalyzer-va-arg-type-mismatch): New option. (Wanalyzer-va-list-exhausted): New option. (Wanalyzer-va-list-leak): New option. (Wanalyzer-va-list-use-after-va-end): New option. * checker-path.cc (call_event::get_desc): Split out decl access into.. (call_event::get_caller_fndecl): ...this new function and... (call_event::get_callee_fndecl): ...this new function. * checker-path.h (call_event::get_desc): Drop "FINAL". (call_event::get_caller_fndecl): New decl. (call_event::get_callee_fndecl): New decl. (class call_event): Make fields protected. * diagnostic-manager.cc (null_assignment_sm_context::warn): New overload. (null_assignment_sm_context::get_new_program_state): New. (diagnostic_manager::add_events_for_superedge): Move case SUPEREDGE_CALL to a new pending_diagnostic::add_call_event vfunc. * engine.cc (impl_sm_context::warn): Implement new override. (impl_sm_context::get_new_program_state): New. * pending-diagnostic.cc: Include "analyzer/diagnostic-manager.h", "cpplib.h", "digraph.h", "ordered-hash-map.h", "cfg.h", "basic-block.h", "gimple.h", "gimple-iterator.h", "cgraph.h" "analyzer/supergraph.h", "analyzer/program-state.h", "alloc-pool.h", "fibonacci_heap.h", "shortest-paths.h", "sbitmap.h", "analyzer/exploded-graph.h", "diagnostic-path.h", and "analyzer/checker-path.h". (ht_ident_eq): New. (fixup_location_in_macro_p): New. (pending_diagnostic::fixup_location): New. (pending_diagnostic::add_call_event): New. * pending-diagnostic.h (pending_diagnostic::fixup_location): Drop no-op inline implementation in favor of the more complex implementation above. (pending_diagnostic::add_call_event): New vfunc. * region-model-impl-calls.cc: Include "analyzer/sm.h", "diagnostic-path.h", and "analyzer/pending-diagnostic.h". * region-model-manager.cc (region_model_manager::get_var_arg_region): New. (region_model_manager::log_stats): Log m_var_arg_regions. * region-model.cc (region_model::on_call_pre): Handle IFN_VA_ARG, BUILT_IN_VA_START, and BUILT_IN_VA_COPY. (region_model::on_call_post): Handle BUILT_IN_VA_END. (region_model::get_representative_path_var_1): Handle RK_VAR_ARG. (region_model::push_frame): Push variadic arguments. * region-model.h (region_model_manager::get_var_arg_region): New decl. (region_model_manager::m_var_arg_regions): New field. (region_model::impl_call_va_start): New decl. (region_model::impl_call_va_copy): New decl. (region_model::impl_call_va_arg): New decl. (region_model::impl_call_va_end): New decl. * region.cc (alloca_region::dump_to_pp): Dump the id. (var_arg_region::dump_to_pp): New. (var_arg_region::get_frame_region): New. * region.h (enum region_kind): Add RK_VAR_ARG. (region::dyn_cast_var_arg_region): New. (class var_arg_region): New. (is_a_helper <const var_arg_region *>::test): New. (struct default_hash_traits<var_arg_region::key_t>): New. * sm.cc (make_checkers): Call make_va_list_state_machine. * sm.h (sm_context::warn): New vfunc. (sm_context::get_old_svalue): Drop unused decl. (sm_context::get_new_program_state): New vfunc. (make_va_list_state_machine): New decl. * varargs.cc: New file. gcc/testsuite/ChangeLog: PR analyzer/105103 * gcc.dg/analyzer/stdarg-1.c: New test. * gcc.dg/analyzer/stdarg-2.c: New test. * gcc.dg/analyzer/stdarg-fmtstring-1.c: New test. * gcc.dg/analyzer/stdarg-lto-1-a.c: New test. * gcc.dg/analyzer/stdarg-lto-1-b.c: New test. * gcc.dg/analyzer/stdarg-lto-1.h: New test. * gcc.dg/analyzer/stdarg-sentinel-1.c: New test. * gcc.dg/analyzer/stdarg-types-1.c: New test. * gcc.dg/analyzer/stdarg-types-2.c: New test. Signed-off-by: David Malcolm <dmalcolm@redhat.com>
Diffstat (limited to 'gcc/analyzer/region.h')
-rw-r--r--gcc/analyzer/region.h87
1 files changed, 86 insertions, 1 deletions
diff --git a/gcc/analyzer/region.h b/gcc/analyzer/region.h
index 5150be7..d32110b 100644
--- a/gcc/analyzer/region.h
+++ b/gcc/analyzer/region.h
@@ -61,7 +61,8 @@ enum region_kind
RK_ALLOCA,
RK_STRING,
RK_BIT_RANGE,
- RK_UNKNOWN
+ RK_VAR_ARG,
+ RK_UNKNOWN,
};
/* Region and its subclasses.
@@ -90,6 +91,7 @@ enum region_kind
alloca_region (RK_ALLOCA)
string_region (RK_STRING)
bit_range_region (RK_BIT_RANGE)
+ var_arg_region (RK_VAR_ARG)
unknown_region (RK_UNKNOWN). */
/* Abstract base class for representing ways of accessing chunks of memory.
@@ -131,6 +133,8 @@ public:
dyn_cast_string_region () const { return NULL; }
virtual const bit_range_region *
dyn_cast_bit_range_region () const { return NULL; }
+ virtual const var_arg_region *
+ dyn_cast_var_arg_region () const { return NULL; }
virtual void accept (visitor *v) const;
@@ -1251,6 +1255,87 @@ template <> struct default_hash_traits<bit_range_region::key_t>
namespace ana {
+/* A region for the N-th vararg within a frame_region for a variadic call. */
+
+class var_arg_region : public region
+{
+public:
+ /* A support class for uniquifying instances of var_arg_region. */
+ struct key_t
+ {
+ key_t (const frame_region *parent, unsigned idx)
+ : m_parent (parent), m_idx (idx)
+ {
+ gcc_assert (parent);
+ }
+
+ hashval_t hash () const
+ {
+ inchash::hash hstate;
+ hstate.add_ptr (m_parent);
+ hstate.add_int (m_idx);
+ return hstate.end ();
+ }
+
+ bool operator== (const key_t &other) const
+ {
+ return (m_parent == other.m_parent
+ && m_idx == other.m_idx);
+ }
+
+ void mark_deleted ()
+ {
+ m_parent = reinterpret_cast<const frame_region *> (1);
+ }
+ void mark_empty () { m_parent = NULL; }
+ bool is_deleted () const
+ {
+ return m_parent == reinterpret_cast<const frame_region *> (1);
+ }
+ bool is_empty () const { return m_parent == NULL; }
+
+ const frame_region *m_parent;
+ unsigned m_idx;
+ };
+
+ var_arg_region (unsigned id, const frame_region *parent,
+ unsigned idx)
+ : region (complexity (parent), id, parent, NULL_TREE),
+ m_idx (idx)
+ {}
+
+ const var_arg_region *
+ dyn_cast_var_arg_region () const FINAL OVERRIDE { return this; }
+
+ enum region_kind get_kind () const FINAL OVERRIDE { return RK_VAR_ARG; }
+
+ void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE;
+
+ const frame_region *get_frame_region () const;
+ unsigned get_index () const { return m_idx; }
+
+private:
+ unsigned m_idx;
+};
+
+} // namespace ana
+
+template <>
+template <>
+inline bool
+is_a_helper <const var_arg_region *>::test (const region *reg)
+{
+ return reg->get_kind () == RK_VAR_ARG;
+}
+
+template <> struct default_hash_traits<var_arg_region::key_t>
+: public member_function_hash_traits<var_arg_region::key_t>
+{
+ static const bool empty_zero_p = true;
+};
+
+namespace ana {
+
/* An unknown region, for handling unimplemented tree codes. */
class unknown_region : public region