diff options
author | David Malcolm <dmalcolm@redhat.com> | 2022-05-16 15:32:11 -0400 |
---|---|---|
committer | David Malcolm <dmalcolm@redhat.com> | 2022-05-16 15:34:09 -0400 |
commit | 2402dc6b982c4dacac2360830f0edc123c588110 (patch) | |
tree | 95f38ecf7b3a91c1c413e2b782f1a54eb3b4b894 /gcc/analyzer/region.h | |
parent | 5eb9927aae076200bb7ba3f22c33b0a7c97c5643 (diff) | |
download | gcc-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.h | 87 |
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 |