diff options
author | benjamin priour <vultkayn@gcc.gnu.org> | 2023-08-27 14:36:14 +0200 |
---|---|---|
committer | benjamin priour <vultkayn@gcc.gnu.org> | 2023-08-27 14:50:43 +0200 |
commit | 55f6a7d949abc708d1c6ebc01eb3053f96d1472b (patch) | |
tree | cf251e1e9b38eb84c9cfa0fbf98404e2688779ff /gcc/analyzer | |
parent | 7997f0d35efca8a24d1b0ceae5066b1019d633d7 (diff) | |
download | gcc-55f6a7d949abc708d1c6ebc01eb3053f96d1472b.zip gcc-55f6a7d949abc708d1c6ebc01eb3053f96d1472b.tar.gz gcc-55f6a7d949abc708d1c6ebc01eb3053f96d1472b.tar.bz2 |
analyzer: Move gcc.dg/analyzer tests to c-c++-common (1) [PR96395]
First batch of moving tests from under gcc.dg/analyzer into
c-c++-common/analyzer.
C builtins are not recognized as such by C++, therefore
this patch no longer uses tree.h:fndecl_built_in_p to recognize
a builtin function, but rather the function names.
Thus functions named as C builtins - such as calloc, sprintf ... -
are recognized as such both in C and C++ sources by the analyzer.
For user-declared functions named after builtins, the latters' function_decl
tree are now preferred over the function_decl the user declared, even
when the FE consider their declaration to mismatch
(Wbuiltin-declaration-mismatch emitted). This mainly comes into account
in the handling of these function attributes : the analyzer uses
the builtin's attributes defined in gcc/builtins.def.
Signed-off-by: benjamin priour <priour.be@gmail.com>
gcc/analyzer/ChangeLog:
PR analyzer/96395
* analyzer.h (class known_function): Add virtual casts
to builtin_known_function.
(class builtin_known_function): New subclass of known_function
for builtins.
* kf.cc (class kf_alloca): Now derived from
builtin_known_function.
(class kf_calloc): Likewise.
(class kf_free): Likewise.
(class kf_malloc): Likewise.
(class kf_memcpy_memmove): Likewise.
(class kf_memset): Likewise.
(class kf_realloc): Likewise.
(class kf_strchr): Likewise.
(class kf_sprintf): Likewise.
(class kf_strcat): Likewise.
(class kf_strcpy): Likewise.
(class kf_strdup): Likewise.
(class kf_strlen): Likewise.
(class kf_strndup): Likewise.
(register_known_functions): Builtins are now registered as
known_functions by name rather than by their BUILTIN_CODE.
* known-function-manager.cc (get_normal_builtin): New overload.
* known-function-manager.h: New overload declaration.
* region-model.cc (region_model::get_builtin_kf): New function.
* region-model.h (class region_model): Add declaration of
get_builtin_kf.
* sm-fd.cc: For called recognized as builtins, use the
attributes of that builtin as defined in gcc/builtins.def
rather than the user's.
* sm-malloc.cc (malloc_state_machine::on_stmt): Likewise.
gcc/testsuite/ChangeLog:
PR analyzer/96395
* gcc.dg/analyzer/aliasing-3.c: Moved to...
* c-c++-common/analyzer/aliasing-3.c: ...here.
* gcc.dg/analyzer/aliasing-pr106473.c: Moved to...
* c-c++-common/analyzer/aliasing-pr106473.c: ...here.
* gcc.dg/analyzer/asm-x86-dyndbg-2.c: Moved to...
* c-c++-common/analyzer/asm-x86-dyndbg-2.c: ...here.
* gcc.dg/analyzer/asm-x86-lp64-2.c: Moved to...
* c-c++-common/analyzer/asm-x86-lp64-2.c: ...here.
* gcc.dg/analyzer/atomic-builtins-haproxy-proxy.c: Moved to...
* c-c++-common/analyzer/atomic-builtins-haproxy-proxy.c: ...here.
* gcc.dg/analyzer/atomic-builtins-qemu-sockets.c: Moved to...
* c-c++-common/analyzer/atomic-builtins-qemu-sockets.c: ...here.
* gcc.dg/analyzer/attr-malloc-6.c: Moved to...
* c-c++-common/analyzer/attr-malloc-6.c: ...here.
* gcc.dg/analyzer/attr-malloc-CVE-2019-19078-usb-leak.c: Moved to...
* c-c++-common/analyzer/attr-malloc-CVE-2019-19078-usb-leak.c: ...here.
* gcc.dg/analyzer/attr-tainted_args-1.c: Moved to...
* c-c++-common/analyzer/attr-tainted_args-1.c: ...here.
* gcc.dg/analyzer/call-summaries-pr107158.c: Moved to...
* c-c++-common/analyzer/call-summaries-pr107158.c: ...here.
* gcc.dg/analyzer/calloc-1.c: Moved to...
* c-c++-common/analyzer/calloc-1.c: ...here.
* gcc.dg/analyzer/compound-assignment-5.c: Moved to...
* c-c++-common/analyzer/compound-assignment-5.c: ...here.
* gcc.dg/analyzer/coreutils-cksum-pr108664.c: Moved to...
* c-c++-common/analyzer/coreutils-cksum-pr108664.c: ...here.
* gcc.dg/analyzer/coreutils-sum-pr108666.c: Moved to...
* c-c++-common/analyzer/coreutils-sum-pr108666.c: ...here.
* gcc.dg/analyzer/deref-before-check-pr108455-1.c: Moved to...
* c-c++-common/analyzer/deref-before-check-pr108455-1.c: ...here.
* gcc.dg/analyzer/deref-before-check-pr108455-git-pack-revindex.c: Moved to...
* c-c++-common/analyzer/deref-before-check-pr108455-git-pack-revindex.c: ...here.
* gcc.dg/analyzer/deref-before-check-pr108475-1.c: Moved to...
* c-c++-common/analyzer/deref-before-check-pr108475-1.c: ...here.
* gcc.dg/analyzer/deref-before-check-pr108475-haproxy-tcpcheck.c: Moved to...
* c-c++-common/analyzer/deref-before-check-pr108475-haproxy-tcpcheck.c: ...here.
* gcc.dg/analyzer/deref-before-check-pr109060-haproxy-cfgparse.c: Moved to...
* c-c++-common/analyzer/deref-before-check-pr109060-haproxy-cfgparse.c: ...here.
* gcc.dg/analyzer/deref-before-check-pr109239-linux-bus.c: Moved to...
* c-c++-common/analyzer/deref-before-check-pr109239-linux-bus.c: ...here.
* gcc.dg/analyzer/deref-before-check-pr77425.c: Moved to...
* c-c++-common/analyzer/deref-before-check-pr77425.c: ...here.
* gcc.dg/analyzer/exec-1.c: Moved to...
* c-c++-common/analyzer/exec-1.c: ...here.
* gcc.dg/analyzer/feasibility-3.c: Moved to...
* c-c++-common/analyzer/feasibility-3.c: ...here.
* gcc.dg/analyzer/fields.c: Moved to...
* c-c++-common/analyzer/fields.c: ...here.
* gcc.dg/analyzer/function-ptr-5.c: Moved to...
* c-c++-common/analyzer/function-ptr-5.c: ...here.
* gcc.dg/analyzer/infinite-recursion-pr108524-1.c: Moved to...
* c-c++-common/analyzer/infinite-recursion-pr108524-1.c: ...here.
* gcc.dg/analyzer/infinite-recursion-pr108524-2.c: Moved to...
* c-c++-common/analyzer/infinite-recursion-pr108524-2.c: ...here.
* gcc.dg/analyzer/infinite-recursion-pr108524-qobject-json-parser.c: Moved to...
* c-c++-common/analyzer/infinite-recursion-pr108524-qobject-json-parser.c: ...here.
* gcc.dg/analyzer/init.c: Moved to...
* c-c++-common/analyzer/init.c: ...here.
* gcc.dg/analyzer/inlining-3-multiline.c: Moved to...
* c-c++-common/analyzer/inlining-3-multiline.c: ...here.
* gcc.dg/analyzer/inlining-3.c: Moved to...
* c-c++-common/analyzer/inlining-3.c: ...here.
* gcc.dg/analyzer/inlining-4-multiline.c: Moved to...
* c-c++-common/analyzer/inlining-4-multiline.c: ...here.
* gcc.dg/analyzer/inlining-4.c: Moved to...
* c-c++-common/analyzer/inlining-4.c: ...here.
* gcc.dg/analyzer/leak-pr105906.c: Moved to...
* c-c++-common/analyzer/leak-pr105906.c: ...here.
* gcc.dg/analyzer/leak-pr108045-with-call-summaries.c: Moved to...
* c-c++-common/analyzer/leak-pr108045-with-call-summaries.c: ...here.
* gcc.dg/analyzer/leak-pr108045-without-call-summaries.c: Moved to...
* c-c++-common/analyzer/leak-pr108045-without-call-summaries.c: ...here.
* gcc.dg/analyzer/leak-pr109059-1.c: Moved to...
* c-c++-common/analyzer/leak-pr109059-1.c: ...here.
* gcc.dg/analyzer/leak-pr109059-2.c: Moved to...
* c-c++-common/analyzer/leak-pr109059-2.c: ...here.
* gcc.dg/analyzer/malloc-2.c: Moved to...
* c-c++-common/analyzer/malloc-2.c: ...here.
* gcc.dg/analyzer/memcpy-2.c: Moved to...
* c-c++-common/analyzer/memcpy-2.c: ...here.
* gcc.dg/analyzer/null-deref-pr108251-smp_fetch_ssl_fc_has_early-O2.c: Moved to...
* c-c++-common/analyzer/null-deref-pr108251-smp_fetch_ssl_fc_has_early-O2.c: ...here.
* gcc.dg/analyzer/null-deref-pr108251-smp_fetch_ssl_fc_has_early.c: Moved to...
* c-c++-common/analyzer/null-deref-pr108251-smp_fetch_ssl_fc_has_early.c: ...here.
* gcc.dg/analyzer/null-deref-pr108806-qemu.c: Moved to...
* c-c++-common/analyzer/null-deref-pr108806-qemu.c: ...here.
* gcc.dg/analyzer/null-deref-pr108830.c: Moved to...
* c-c++-common/analyzer/null-deref-pr108830.c: ...here.
* gcc.dg/analyzer/pr101962.c: Moved to...
* c-c++-common/analyzer/pr101962.c: ...here.
* gcc.dg/analyzer/pr103217-2.c: Moved to...
* c-c++-common/analyzer/pr103217-2.c: ...here.
* gcc.dg/analyzer/pr103217.c: Moved to...
* c-c++-common/analyzer/pr103217.c: ...here.
* gcc.dg/analyzer/pr104029.c: Moved to...
* c-c++-common/analyzer/pr104029.c: ...here.
* gcc.dg/analyzer/pr104062.c: Moved to...
* c-c++-common/analyzer/pr104062.c: ...here.
* gcc.dg/analyzer/pr105783.c: Moved to...
* c-c++-common/analyzer/pr105783.c: ...here.
* gcc.dg/analyzer/pr107345.c: Moved to...
* c-c++-common/analyzer/pr107345.c: ...here.
* gcc.dg/analyzer/pr93695-1.c: Moved to...
* c-c++-common/analyzer/pr93695-1.c: ...here.
* gcc.dg/analyzer/pr94596.c: Moved to...
* c-c++-common/analyzer/pr94596.c: ...here.
* gcc.dg/analyzer/pr94839.c: Moved to...
* c-c++-common/analyzer/pr94839.c: ...here.
* gcc.dg/analyzer/pr95152-4.c: C only.
* gcc.dg/analyzer/pr95152-5.c: C only.
* gcc.dg/analyzer/pr95240.c: Moved to...
* c-c++-common/analyzer/pr95240.c: ...here.
* gcc.dg/analyzer/pr96639.c: Moved to...
* c-c++-common/analyzer/pr96639.c: ...here.
* gcc.dg/analyzer/pr96653.c: Moved to...
* c-c++-common/analyzer/pr96653.c: ...here.
* gcc.dg/analyzer/pr96792.c: Moved to...
* c-c++-common/analyzer/pr96792.c: ...here.
* gcc.dg/analyzer/pr96841.c: Moved to...
* c-c++-common/analyzer/pr96841.c: ...here.
* gcc.dg/analyzer/pr98564.c: Moved to...
* c-c++-common/analyzer/pr98564.c: ...here.
* gcc.dg/analyzer/pr98628.c: Moved to...
* c-c++-common/analyzer/pr98628.c: ...here.
* gcc.dg/analyzer/pr98969.c: Moved to...
* c-c++-common/analyzer/pr98969.c: ...here.
* gcc.dg/analyzer/pr99193-2.c: Moved to...
* c-c++-common/analyzer/pr99193-2.c: ...here.
* gcc.dg/analyzer/pr99193-3.c: Moved to...
* c-c++-common/analyzer/pr99193-3.c: ...here.
* gcc.dg/analyzer/pr99716-1.c: Moved to...
* c-c++-common/analyzer/pr99716-1.c: ...here.
* gcc.dg/analyzer/pr99774-1.c: Moved to...
* c-c++-common/analyzer/pr99774-1.c: ...here.
* gcc.dg/analyzer/realloc-1.c: Moved to...
* c-c++-common/analyzer/realloc-1.c: ...here.
* gcc.dg/analyzer/realloc-2.c: Moved to...
* c-c++-common/analyzer/realloc-2.c: ...here.
* gcc.dg/analyzer/realloc-3.c: Moved to...
* c-c++-common/analyzer/realloc-3.c: ...here.
* gcc.dg/analyzer/realloc-4.c: Moved to...
* c-c++-common/analyzer/realloc-4.c: ...here.
* gcc.dg/analyzer/realloc-5.c: Moved to...
* c-c++-common/analyzer/realloc-5.c: ...here.
* gcc.dg/analyzer/realloc-pr110014.c: Moved to...
* c-c++-common/analyzer/realloc-pr110014.c: ...here.
* gcc.dg/analyzer/snprintf-concat.c: Moved to...
* c-c++-common/analyzer/snprintf-concat.c: ...here.
* gcc.dg/analyzer/sock-1.c: Moved to...
* c-c++-common/analyzer/sock-1.c: ...here.
* gcc.dg/analyzer/sprintf-concat.c: Moved to...
* c-c++-common/analyzer/sprintf-concat.c: ...here.
* gcc.dg/analyzer/string-ops-concat-pair.c: Moved to...
* c-c++-common/analyzer/string-ops-concat-pair.c: ...here.
* gcc.dg/analyzer/string-ops-dup.c: Moved to...
* c-c++-common/analyzer/string-ops-dup.c: ...here.
* gcc.dg/analyzer/switch-enum-pr105273-git-vreportf-2.c: Moved to...
* c-c++-common/analyzer/switch-enum-pr105273-git-vreportf-2.c: ...here.
* gcc.dg/analyzer/symbolic-12.c: Moved to...
* c-c++-common/analyzer/symbolic-12.c: ...here.
* gcc.dg/analyzer/uninit-alloca.c: Moved to...
* c-c++-common/analyzer/uninit-alloca.c: ...here.
* gcc.dg/analyzer/untracked-2.c: Moved to...
* c-c++-common/analyzer/untracked-2.c: ...here.
* gcc.dg/analyzer/vasprintf-1.c: Moved to...
* c-c++-common/analyzer/vasprintf-1.c: ...here.
* gcc.dg/analyzer/write-to-const-1.c: Moved to...
* c-c++-common/analyzer/write-to-const-1.c: ...here.
* gcc.dg/analyzer/write-to-function-1.c: C only.
* gcc.dg/analyzer/write-to-string-literal-1.c: Moved to...
* c-c++-common/analyzer/write-to-string-literal-1.c: ...here.
* gcc.dg/analyzer/write-to-string-literal-4-disabled.c: Moved to...
* c-c++-common/analyzer/write-to-string-literal-4-disabled.c: ...here.
* gcc.dg/analyzer/write-to-string-literal-5.c: Moved to...
* c-c++-common/analyzer/write-to-string-literal-5.c: ...here.
* g++.dg/analyzer/analyzer.exp: Now also run tests under
c-c++-common/analyzer.
* gcc.dg/analyzer/analyzer-decls.h: Add NULL definition.
* gcc.dg/analyzer/analyzer.exp: Now also run tests under
c-c++-common/analyzer.
* gcc.dg/analyzer/pr104369-1.c: C only.
* gcc.dg/analyzer/pr104369-2.c: Likewise.
* gcc.dg/analyzer/pr93355-localealias-feasibility-2.c: Likewise.
* gcc.dg/analyzer/sprintf-1.c: Split into C-only and
C++-friendly bits.
* gcc.dg/analyzer/allocation-size-multiline-1.c: Removed.
* gcc.dg/analyzer/allocation-size-multiline-2.c: Removed.
* gcc.dg/analyzer/allocation-size-multiline-3.c: Removed.
* gcc.dg/analyzer/data-model-11.c: Removed.
* gcc.dg/analyzer/pr61861.c: C only.
* gcc.dg/analyzer/pr93457.c: Removed.
* gcc.dg/analyzer/pr97568.c: Removed.
* gcc.dg/analyzer/write-to-string-literal-4.c: Removed.
* c-c++-common/analyzer/allocation-size-multiline-1.c: New test.
* c-c++-common/analyzer/allocation-size-multiline-2.c: New test.
* c-c++-common/analyzer/allocation-size-multiline-3.c: New test.
* c-c++-common/analyzer/data-model-11.c: New test.
* c-c++-common/analyzer/pr93457.c: New test.
* c-c++-common/analyzer/pr97568.c: New test.
* c-c++-common/analyzer/sprintf-2.c: C++-friendly bit of
previous gcc.dg/analyzer/sprintf-1.c.
* c-c++-common/analyzer/write-to-string-literal-4.c: New test.
Diffstat (limited to 'gcc/analyzer')
-rw-r--r-- | gcc/analyzer/analyzer.h | 22 | ||||
-rw-r--r-- | gcc/analyzer/kf.cc | 210 | ||||
-rw-r--r-- | gcc/analyzer/known-function-manager.cc | 7 | ||||
-rw-r--r-- | gcc/analyzer/known-function-manager.h | 2 | ||||
-rw-r--r-- | gcc/analyzer/region-model.cc | 56 | ||||
-rw-r--r-- | gcc/analyzer/region-model.h | 4 | ||||
-rw-r--r-- | gcc/analyzer/sm-fd.cc | 25 | ||||
-rw-r--r-- | gcc/analyzer/sm-malloc.cc | 133 |
8 files changed, 351 insertions, 108 deletions
diff --git a/gcc/analyzer/analyzer.h b/gcc/analyzer/analyzer.h index 93a28b4..9b351b5 100644 --- a/gcc/analyzer/analyzer.h +++ b/gcc/analyzer/analyzer.h @@ -128,6 +128,10 @@ struct interesting_t; class feasible_node; +class known_function; + class builtin_known_function; + class internal_known_function; + /* Forward decls of functions. */ extern void dump_tree (pretty_printer *pp, tree t); @@ -279,6 +283,24 @@ public: { return; } + + virtual const builtin_known_function * + dyn_cast_builtin_kf () const { return NULL; } +}; + +/* Subclass of known_function for builtin functions. */ + +class builtin_known_function : public known_function +{ +public: + virtual enum built_in_function builtin_code () const = 0; + tree builtin_decl () const { + gcc_assert (builtin_code () < END_BUILTINS); + return builtin_info[builtin_code ()].decl; + } + + const builtin_known_function * + dyn_cast_builtin_kf () const final override { return this; } }; /* Subclass of known_function for IFN_* functions. */ diff --git a/gcc/analyzer/kf.cc b/gcc/analyzer/kf.cc index 36d9d10..333ffd9 100644 --- a/gcc/analyzer/kf.cc +++ b/gcc/analyzer/kf.cc @@ -53,13 +53,17 @@ impl_call_pre (const call_details &cd) const /* Handler for "alloca". */ -class kf_alloca : public known_function +class kf_alloca : public builtin_known_function { public: bool matches_call_types_p (const call_details &cd) const final override { return cd.num_args () == 1; } + enum built_in_function builtin_code () const final override + { + return BUILT_IN_ALLOCA; + } void impl_call_pre (const call_details &cd) const final override; }; @@ -322,7 +326,7 @@ public: /* Handler for "calloc". */ -class kf_calloc : public known_function +class kf_calloc : public builtin_known_function { public: bool matches_call_types_p (const call_details &cd) const final override @@ -331,6 +335,11 @@ public: && cd.arg_is_size_p (0) && cd.arg_is_size_p (1)); } + enum built_in_function builtin_code () const final override + { + return BUILT_IN_CALLOC; + } + void impl_call_pre (const call_details &cd) const final override; }; @@ -462,12 +471,16 @@ public: all pointers to the region to the "freed" state together, regardless of casts. */ -class kf_free : public known_function +class kf_free : public builtin_known_function { public: bool matches_call_types_p (const call_details &cd) const final override { - return (cd.num_args () == 0 && cd.arg_is_pointer_p (0)); + return (cd.num_args () == 1 && cd.arg_is_pointer_p (0)); + } + enum built_in_function builtin_code () const final override + { + return BUILT_IN_FREE; } void impl_call_post (const call_details &cd) const final override; }; @@ -488,7 +501,7 @@ kf_free::impl_call_post (const call_details &cd) const /* Handle the on_call_pre part of "malloc". */ -class kf_malloc : public known_function +class kf_malloc : public builtin_known_function { public: bool matches_call_types_p (const call_details &cd) const final override @@ -496,6 +509,10 @@ public: return (cd.num_args () == 1 && cd.arg_is_size_p (0)); } + enum built_in_function builtin_code () const final override + { + return BUILT_IN_MALLOC; + } void impl_call_pre (const call_details &cd) const final override; }; @@ -520,9 +537,18 @@ kf_malloc::impl_call_pre (const call_details &cd) const /* TODO: complain about overlapping src and dest for the memcpy variants. */ -class kf_memcpy_memmove : public known_function +class kf_memcpy_memmove : public builtin_known_function { public: + enum kf_memcpy_memmove_variant + { + KF_MEMCPY, + KF_MEMCPY_CHK, + KF_MEMMOVE, + KF_MEMMOVE_CHK, + }; + kf_memcpy_memmove (enum kf_memcpy_memmove_variant variant) + : m_variant (variant) {}; bool matches_call_types_p (const call_details &cd) const final override { return (cd.num_args () == 3 @@ -530,7 +556,25 @@ public: && cd.arg_is_pointer_p (1) && cd.arg_is_size_p (2)); } + enum built_in_function builtin_code () const final override + { + switch (m_variant) + { + case KF_MEMCPY: + return BUILT_IN_MEMCPY; + case KF_MEMCPY_CHK: + return BUILT_IN_MEMCPY_CHK; + case KF_MEMMOVE: + return BUILT_IN_MEMMOVE; + case KF_MEMMOVE_CHK: + return BUILT_IN_MEMMOVE_CHK; + default: + gcc_unreachable (); + } + } void impl_call_pre (const call_details &cd) const final override; +private: + const enum kf_memcpy_memmove_variant m_variant; }; void @@ -557,15 +601,21 @@ kf_memcpy_memmove::impl_call_pre (const call_details &cd) const /* Handler for "memset" and "__builtin_memset". */ -class kf_memset : public known_function +class kf_memset : public builtin_known_function { public: + kf_memset (bool chk_variant) : m_chk_variant (chk_variant) {} bool matches_call_types_p (const call_details &cd) const final override { return (cd.num_args () == 3 && cd.arg_is_pointer_p (0)); } - + enum built_in_function builtin_code () const final override + { + return m_chk_variant ? BUILT_IN_MEMSET_CHK : BUILT_IN_MEMSET; + } void impl_call_pre (const call_details &cd) const final override; +private: + const bool m_chk_variant; }; void @@ -747,7 +797,7 @@ public: Each of these has a custom_edge_info subclass, which updates the region_model and sm-state of the destination state. */ -class kf_realloc : public known_function +class kf_realloc : public builtin_known_function { public: bool matches_call_types_p (const call_details &cd) const final override @@ -756,6 +806,12 @@ public: && cd.arg_is_pointer_p (0) && cd.arg_is_size_p (1)); } + + enum built_in_function builtin_code () const final override + { + return BUILT_IN_REALLOC; + } + void impl_call_post (const call_details &cd) const final override; }; @@ -968,7 +1024,7 @@ kf_realloc::impl_call_post (const call_details &cd) const /* Handler for "strchr" and "__builtin_strchr". */ -class kf_strchr : public known_function +class kf_strchr : public builtin_known_function { public: bool matches_call_types_p (const call_details &cd) const final override @@ -979,6 +1035,11 @@ public: { cd.check_for_null_terminated_string_arg (0); } + + enum built_in_function builtin_code () const final override + { + return BUILT_IN_STRCHR; + } void impl_call_post (const call_details &cd) const final override; }; @@ -1055,7 +1116,7 @@ kf_strchr::impl_call_post (const call_details &cd) const int sprintf(char *str, const char *format, ...); */ -class kf_sprintf : public known_function +class kf_sprintf : public builtin_known_function { public: bool matches_call_types_p (const call_details &cd) const final override @@ -1065,6 +1126,11 @@ public: && cd.arg_is_pointer_p (1)); } + enum built_in_function builtin_code () const final override + { + return BUILT_IN_SPRINTF; + } + void impl_call_pre (const call_details &cd) const final override { /* For now, merely assume that the destination buffer gets set to a @@ -1108,10 +1174,12 @@ public: /* Handler for "strcat" and "__builtin_strcat_chk". */ -class kf_strcat : public known_function +class kf_strcat : public builtin_known_function { public: - kf_strcat (unsigned int num_args) : m_num_args (num_args) {} + kf_strcat (unsigned int num_args, bool chk_variant) + : m_num_args (num_args), + m_chk_variant (chk_variant) {} bool matches_call_types_p (const call_details &cd) const final override { return (cd.num_args () == m_num_args @@ -1119,6 +1187,11 @@ public: && cd.arg_is_pointer_p (1)); } + enum built_in_function builtin_code () const final override + { + return m_chk_variant ? BUILT_IN_STRCAT_CHK : BUILT_IN_STRCAT; + } + void impl_call_pre (const call_details &cd) const final override { region_model *model = cd.get_model (); @@ -1159,25 +1232,32 @@ public: private: unsigned int m_num_args; + const bool m_chk_variant; }; /* Handler for "strcpy" and "__builtin_strcpy_chk". */ -class kf_strcpy : public known_function +class kf_strcpy : public builtin_known_function { public: - kf_strcpy (unsigned int num_args) : m_num_args (num_args) {} + kf_strcpy (unsigned int num_args, bool chk_variant) + : m_num_args (num_args), + m_chk_variant (chk_variant) {} bool matches_call_types_p (const call_details &cd) const final override { return (cd.num_args () == m_num_args && cd.arg_is_pointer_p (0) && cd.arg_is_pointer_p (1)); } - + enum built_in_function builtin_code () const final override + { + return m_chk_variant ? BUILT_IN_STRCPY_CHK : BUILT_IN_STRCPY; + } void impl_call_pre (const call_details &cd) const final override; private: unsigned int m_num_args; + const bool m_chk_variant; }; void @@ -1207,13 +1287,17 @@ kf_strcpy::impl_call_pre (const call_details &cd) const /* Handler for "strdup" and "__builtin_strdup". */ -class kf_strdup : public known_function +class kf_strdup : public builtin_known_function { public: bool matches_call_types_p (const call_details &cd) const final override { return (cd.num_args () == 1 && cd.arg_is_pointer_p (0)); } + enum built_in_function builtin_code () const final override + { + return BUILT_IN_STRDUP; + } void impl_call_pre (const call_details &cd) const final override { region_model *model = cd.get_model (); @@ -1234,13 +1318,18 @@ public: /* Handler for "strlen" and for "__analyzer_get_strlen". */ -class kf_strlen : public known_function +class kf_strlen : public builtin_known_function { public: bool matches_call_types_p (const call_details &cd) const final override { return (cd.num_args () == 1 && cd.arg_is_pointer_p (0)); } + enum built_in_function builtin_code () const final override + { + return BUILT_IN_STRLEN; + } + void impl_call_pre (const call_details &cd) const final override { if (const svalue *strlen_sval @@ -1266,13 +1355,17 @@ make_kf_strlen () /* Handler for "strndup" and "__builtin_strndup". */ -class kf_strndup : public known_function +class kf_strndup : public builtin_known_function { public: bool matches_call_types_p (const call_details &cd) const final override { return (cd.num_args () == 2 && cd.arg_is_pointer_p (0)); } + enum built_in_function builtin_code () const final override + { + return BUILT_IN_STRNDUP; + } void impl_call_pre (const call_details &cd) const final override { region_model *model = cd.get_model (); @@ -1445,44 +1538,73 @@ register_known_functions (known_function_manager &kfm) kfm.add (IFN_UBSAN_BOUNDS, make_unique<kf_ubsan_bounds> ()); } - /* Built-ins the analyzer has known_functions for. */ + /* GCC built-ins that do not correspond to a function + in the standard library. */ { - kfm.add (BUILT_IN_ALLOCA, make_unique<kf_alloca> ()); - kfm.add (BUILT_IN_ALLOCA_WITH_ALIGN, make_unique<kf_alloca> ()); - kfm.add (BUILT_IN_CALLOC, make_unique<kf_calloc> ()); kfm.add (BUILT_IN_EXPECT, make_unique<kf_expect> ()); kfm.add (BUILT_IN_EXPECT_WITH_PROBABILITY, make_unique<kf_expect> ()); - kfm.add (BUILT_IN_FREE, make_unique<kf_free> ()); - kfm.add (BUILT_IN_MALLOC, make_unique<kf_malloc> ()); - kfm.add (BUILT_IN_MEMCPY, make_unique<kf_memcpy_memmove> ()); - kfm.add (BUILT_IN_MEMCPY_CHK, make_unique<kf_memcpy_memmove> ()); - kfm.add (BUILT_IN_MEMMOVE, make_unique<kf_memcpy_memmove> ()); - kfm.add (BUILT_IN_MEMMOVE_CHK, make_unique<kf_memcpy_memmove> ()); - kfm.add (BUILT_IN_MEMSET, make_unique<kf_memset> ()); - kfm.add (BUILT_IN_MEMSET_CHK, make_unique<kf_memset> ()); - kfm.add (BUILT_IN_REALLOC, make_unique<kf_realloc> ()); - kfm.add (BUILT_IN_SPRINTF, make_unique<kf_sprintf> ()); + kfm.add (BUILT_IN_ALLOCA_WITH_ALIGN, make_unique<kf_alloca> ()); kfm.add (BUILT_IN_STACK_RESTORE, make_unique<kf_stack_restore> ()); kfm.add (BUILT_IN_STACK_SAVE, make_unique<kf_stack_save> ()); - kfm.add (BUILT_IN_STRCAT, make_unique<kf_strcat> (2)); - kfm.add (BUILT_IN_STRCAT_CHK, make_unique<kf_strcat> (3)); - kfm.add (BUILT_IN_STRCHR, make_unique<kf_strchr> ()); - kfm.add (BUILT_IN_STRCPY, make_unique<kf_strcpy> (2)); - kfm.add (BUILT_IN_STRCPY_CHK, make_unique<kf_strcpy> (3)); - kfm.add (BUILT_IN_STRDUP, make_unique<kf_strdup> ()); - kfm.add (BUILT_IN_STRNDUP, make_unique<kf_strndup> ()); - kfm.add (BUILT_IN_STRLEN, make_kf_strlen ()); register_atomic_builtins (kfm); register_varargs_builtins (kfm); } - /* Known builtins and C standard library functions. */ + /* Known builtins and C standard library functions + the analyzer has known functions for. */ { - kfm.add ("memset", make_unique<kf_memset> ()); - kfm.add ("strcat", make_unique<kf_strcat> (2)); + kfm.add ("alloca", make_unique<kf_alloca> ()); + kfm.add ("__builtin_alloca", make_unique<kf_alloca> ()); + kfm.add ("calloc", make_unique<kf_calloc> ()); + kfm.add ("__builtin_calloc", make_unique<kf_calloc> ()); + kfm.add ("free", make_unique<kf_free> ()); + kfm.add ("__builtin_free", make_unique<kf_free> ()); + kfm.add ("malloc", make_unique<kf_malloc> ()); + kfm.add ("__builtin_malloc", make_unique<kf_malloc> ()); + kfm.add ("memcpy", + make_unique<kf_memcpy_memmove> (kf_memcpy_memmove::KF_MEMCPY)); + kfm.add ("__builtin_memcpy", + make_unique<kf_memcpy_memmove> (kf_memcpy_memmove::KF_MEMCPY)); + kfm.add ("__memcpy_chk", make_unique<kf_memcpy_memmove> + (kf_memcpy_memmove::KF_MEMCPY_CHK)); + kfm.add ("__builtin___memcpy_chk", make_unique<kf_memcpy_memmove> + (kf_memcpy_memmove::KF_MEMCPY_CHK)); + kfm.add ("memmove", + make_unique<kf_memcpy_memmove> (kf_memcpy_memmove::KF_MEMMOVE)); + kfm.add ("__builtin_memmove", + make_unique<kf_memcpy_memmove> (kf_memcpy_memmove::KF_MEMMOVE)); + kfm.add ("__memmove_chk", make_unique<kf_memcpy_memmove> + (kf_memcpy_memmove::KF_MEMMOVE_CHK)); + kfm.add ("__builtin___memmove_chk", make_unique<kf_memcpy_memmove> + (kf_memcpy_memmove::KF_MEMMOVE_CHK)); + kfm.add ("memset", make_unique<kf_memset> (false)); + kfm.add ("__builtin_memset", make_unique<kf_memset> (false)); + kfm.add ("__memset_chk", make_unique<kf_memset> (true)); + kfm.add ("__builtin___memset_chk", make_unique<kf_memset> (true)); + kfm.add ("realloc", make_unique<kf_realloc> ()); + kfm.add ("__builtin_realloc", make_unique<kf_realloc> ()); + kfm.add ("sprintf", make_unique<kf_sprintf> ()); + kfm.add ("__builtin_sprintf", make_unique<kf_sprintf> ()); + kfm.add ("strchr", make_unique<kf_strchr> ()); + kfm.add ("__builtin_strchr", make_unique<kf_strchr> ()); + kfm.add ("strcpy", make_unique<kf_strcpy> (2, false)); + kfm.add ("__builtin_strcpy", make_unique<kf_strcpy> (2, false)); + kfm.add ("__strcpy_chk", make_unique<kf_strcpy> (3, true)); + kfm.add ("__builtin___strcpy_chk", make_unique<kf_strcpy> (3, true)); + kfm.add ("strcat", make_unique<kf_strcat> (2, false)); + kfm.add ("__builtin_strcat", make_unique<kf_strcat> (2, false)); + kfm.add ("__strcat_chk", make_unique<kf_strcat> (3, true)); + kfm.add ("__builtin___strcat_chk", make_unique<kf_strcat> (3, true)); kfm.add ("strdup", make_unique<kf_strdup> ()); + kfm.add ("__builtin_strdup", make_unique<kf_strdup> ()); kfm.add ("strndup", make_unique<kf_strndup> ()); + kfm.add ("__builtin_strndup", make_unique<kf_strndup> ()); + kfm.add ("strlen", make_unique<kf_strlen> ()); + kfm.add ("__builtin_strlen", make_unique<kf_strlen> ()); + + register_atomic_builtins (kfm); + register_varargs_builtins (kfm); } /* Known POSIX functions, and some non-standard extensions. */ diff --git a/gcc/analyzer/known-function-manager.cc b/gcc/analyzer/known-function-manager.cc index 4a2cf52..615c495 100644 --- a/gcc/analyzer/known-function-manager.cc +++ b/gcc/analyzer/known-function-manager.cc @@ -137,6 +137,13 @@ known_function_manager::get_normal_builtin (enum built_in_function name) const return m_combined_fns_arr[name]; } +const known_function * +known_function_manager:: +get_normal_builtin (const builtin_known_function *builtin_kf) const +{ + return get_normal_builtin (builtin_kf->builtin_code ()); +} + /* Get any known_function matching IDENTIFIER, without type-checking. Return NULL if there isn't one. */ diff --git a/gcc/analyzer/known-function-manager.h b/gcc/analyzer/known-function-manager.h index 1432e54..04f49ce 100644 --- a/gcc/analyzer/known-function-manager.h +++ b/gcc/analyzer/known-function-manager.h @@ -54,6 +54,8 @@ private: DISABLE_COPY_AND_ASSIGN (known_function_manager); const known_function *get_normal_builtin (enum built_in_function name) const; + const known_function * + get_normal_builtin (const builtin_known_function *builtin_kf) const; const known_function *get_by_identifier (tree identifier) const; /* Map from identifier to known_function instance. diff --git a/gcc/analyzer/region-model.cc b/gcc/analyzer/region-model.cc index 02c073c..4f31a6d 100644 --- a/gcc/analyzer/region-model.cc +++ b/gcc/analyzer/region-model.cc @@ -1514,6 +1514,62 @@ region_model::get_known_function (enum internal_fn ifn) const return known_fn_mgr->get_internal_fn (ifn); } +/* Get any builtin_known_function for CALL and emit any warning to CTXT + if not NULL. + + The call must match all assumptions made by the known_function (such as + e.g. "argument 1's type must be a pointer type"). + + Return NULL if no builtin_known_function is found, or it does + not match the assumption(s). + + Internally calls get_known_function to find a known_function and cast it + to a builtin_known_function. + + For instance, calloc is a C builtin, defined in gcc/builtins.def + by the DEF_LIB_BUILTIN macro. Such builtins are recognized by the + analyzer by their name, so that even in C++ or if the user redeclares + them but mismatch their signature, they are still recognized as builtins. + + Cases when a supposed builtin is not flagged as one by the FE: + + The C++ FE does not recognize calloc as a builtin if it has not been + included from a standard header, but the C FE does. Hence in C++ if + CALL comes from a calloc and stdlib is not included, + gcc/tree.h:fndecl_built_in_p (CALL) would be false. + + In C code, a __SIZE_TYPE__ calloc (__SIZE_TYPE__, __SIZE_TYPE__) user + declaration has obviously a mismatching signature from the standard, and + its function_decl tree won't be unified by + gcc/c-decl.cc:match_builtin_function_types. + + Yet in both cases the analyzer should treat the calls as a builtin calloc + so that extra attributes unspecified by the standard but added by GCC + (e.g. sprintf attributes in gcc/builtins.def), useful for the detection of + dangerous behavior, are indeed processed. + + Therefore for those cases when a "builtin flag" is not added by the FE, + builtins' kf are derived from builtin_known_function, whose method + builtin_known_function::builtin_decl returns the builtin's + function_decl tree as defined in gcc/builtins.def, with all the extra + attributes. */ + +const builtin_known_function * +region_model::get_builtin_kf (const gcall *call, + region_model_context *ctxt /* = NULL */) const +{ + region_model *mut_this = const_cast <region_model *> (this); + tree callee_fndecl = mut_this->get_fndecl_for_call (call, ctxt); + if (! callee_fndecl) + return NULL; + + call_details cd (call, mut_this, ctxt); + if (const known_function *kf = get_known_function (callee_fndecl, cd)) + return kf->dyn_cast_builtin_kf (); + + return NULL; +} + /* Update this model for the CALL stmt, using CTXT to report any diagnostics - the first half. diff --git a/gcc/analyzer/region-model.h b/gcc/analyzer/region-model.h index 4025962..10b2a59 100644 --- a/gcc/analyzer/region-model.h +++ b/gcc/analyzer/region-model.h @@ -528,6 +528,10 @@ class region_model bool include_terminator, const svalue **out_sval); + const builtin_known_function * + get_builtin_kf (const gcall *call, + region_model_context *ctxt = NULL) const; + private: const region *get_lvalue_1 (path_var pv, region_model_context *ctxt) const; const svalue *get_rvalue_1 (path_var pv, region_model_context *ctxt) const; diff --git a/gcc/analyzer/sm-fd.cc b/gcc/analyzer/sm-fd.cc index c75744f..34bbd84 100644 --- a/gcc/analyzer/sm-fd.cc +++ b/gcc/analyzer/sm-fd.cc @@ -1294,8 +1294,19 @@ fd_state_machine::check_for_fd_attrs ( const gcall *call, const tree callee_fndecl, const char *attr_name, access_directions fd_attr_access_dir) const { - - tree attrs = TYPE_ATTRIBUTES (TREE_TYPE (callee_fndecl)); + /* Handle interesting fd attributes of the callee_fndecl, + or prioritize those of the builtin that callee_fndecl is + expected to be. + Might want this to be controlled by a flag. */ + tree fndecl = callee_fndecl; + /* If call is recognized as a builtin known_function, + use that builtin's function_decl. */ + if (const region_model *old_model = sm_ctxt->get_old_region_model ()) + if (const builtin_known_function *builtin_kf + = old_model->get_builtin_kf (call)) + fndecl = builtin_kf->builtin_decl (); + + tree attrs = TYPE_ATTRIBUTES (TREE_TYPE (fndecl)); attrs = lookup_attribute (attr_name, attrs); if (!attrs) return; @@ -1325,13 +1336,15 @@ fd_state_machine::check_for_fd_attrs ( // attributes { + /* Do use the fndecl that caused the warning so that the + misused attributes are printed and the user not confused. */ if (is_closed_fd_p (state)) { sm_ctxt->warn (node, stmt, arg, make_unique<fd_use_after_close> (*this, diag_arg, - callee_fndecl, attr_name, + fndecl, attr_name, arg_idx)); continue; } @@ -1343,7 +1356,7 @@ fd_state_machine::check_for_fd_attrs ( sm_ctxt->warn (node, stmt, arg, make_unique<fd_use_without_check> (*this, diag_arg, - callee_fndecl, attr_name, + fndecl, attr_name, arg_idx)); continue; } @@ -1361,7 +1374,7 @@ fd_state_machine::check_for_fd_attrs ( node, stmt, arg, make_unique<fd_access_mode_mismatch> (*this, diag_arg, DIRS_WRITE, - callee_fndecl, + fndecl, attr_name, arg_idx)); } @@ -1375,7 +1388,7 @@ fd_state_machine::check_for_fd_attrs ( node, stmt, arg, make_unique<fd_access_mode_mismatch> (*this, diag_arg, DIRS_READ, - callee_fndecl, + fndecl, attr_name, arg_idx)); } diff --git a/gcc/analyzer/sm-malloc.cc b/gcc/analyzer/sm-malloc.cc index ec76325..2ff777d 100644 --- a/gcc/analyzer/sm-malloc.cc +++ b/gcc/analyzer/sm-malloc.cc @@ -1965,71 +1965,88 @@ malloc_state_machine::on_stmt (sm_context *sm_ctxt, malloc_state_machine *mutable_this = const_cast <malloc_state_machine *> (this); - /* Handle "__attribute__((malloc(FOO)))". */ - if (const deallocator_set *deallocators + /* Handle interesting attributes of the callee_fndecl, + or prioritize those of the builtin that callee_fndecl is expected + to be. + Might want this to be controlled by a flag. */ + { + tree fndecl = callee_fndecl; + /* If call is recognized as a builtin known_function, use that + builtin's function_decl. */ + if (const region_model *old_model = sm_ctxt->get_old_region_model ()) + if (const builtin_known_function *builtin_kf + = old_model->get_builtin_kf (call)) + fndecl = builtin_kf->builtin_decl (); + + /* Handle "__attribute__((malloc(FOO)))". */ + if (const deallocator_set *deallocators = mutable_this->get_or_create_custom_deallocator_set - (callee_fndecl)) + (fndecl)) + { + tree attrs = TYPE_ATTRIBUTES (TREE_TYPE (fndecl)); + bool returns_nonnull + = lookup_attribute ("returns_nonnull", attrs); + on_allocator_call (sm_ctxt, call, deallocators, returns_nonnull); + } + { - tree attrs = TYPE_ATTRIBUTES (TREE_TYPE (callee_fndecl)); - bool returns_nonnull - = lookup_attribute ("returns_nonnull", attrs); - on_allocator_call (sm_ctxt, call, deallocators, returns_nonnull); + /* Handle "__attribute__((nonnull))". */ + tree fntype = TREE_TYPE (fndecl); + bitmap nonnull_args = get_nonnull_args (fntype); + if (nonnull_args) + { + for (unsigned i = 0; i < gimple_call_num_args (stmt); i++) + { + tree arg = gimple_call_arg (stmt, i); + if (TREE_CODE (TREE_TYPE (arg)) != POINTER_TYPE) + continue; + /* If we have a nonnull-args, and either all pointers, or + just the specified pointers. */ + if (bitmap_empty_p (nonnull_args) + || bitmap_bit_p (nonnull_args, i)) + { + state_t state = sm_ctxt->get_state (stmt, arg); + /* Can't use a switch as the states are non-const. */ + /* Do use the fndecl that caused the warning so that the + misused attributes are printed and the user not + confused. */ + if (unchecked_p (state)) + { + tree diag_arg = sm_ctxt->get_diagnostic_tree (arg); + sm_ctxt->warn (node, stmt, arg, + make_unique<possible_null_arg> + (*this, diag_arg, fndecl, i)); + const allocation_state *astate + = as_a_allocation_state (state); + sm_ctxt->set_next_state (stmt, arg, + astate->get_nonnull ()); + } + else if (state == m_null) + { + tree diag_arg = sm_ctxt->get_diagnostic_tree (arg); + sm_ctxt->warn (node, stmt, arg, + make_unique<null_arg> + (*this, diag_arg, fndecl, i)); + sm_ctxt->set_next_state (stmt, arg, m_stop); + } + else if (state == m_start) + maybe_assume_non_null (sm_ctxt, arg, stmt); + } + } + BITMAP_FREE (nonnull_args); + } } - /* Handle "__attribute__((nonnull))". */ - { - tree fntype = TREE_TYPE (callee_fndecl); - bitmap nonnull_args = get_nonnull_args (fntype); - if (nonnull_args) + /* Check for this after nonnull, so that if we have both + then we transition to "freed", rather than "checked". */ + unsigned dealloc_argno = fndecl_dealloc_argno (fndecl); + if (dealloc_argno != UINT_MAX) { - for (unsigned i = 0; i < gimple_call_num_args (stmt); i++) - { - tree arg = gimple_call_arg (stmt, i); - if (TREE_CODE (TREE_TYPE (arg)) != POINTER_TYPE) - continue; - /* If we have a nonnull-args, and either all pointers, or just - the specified pointers. */ - if (bitmap_empty_p (nonnull_args) - || bitmap_bit_p (nonnull_args, i)) - { - state_t state = sm_ctxt->get_state (stmt, arg); - /* Can't use a switch as the states are non-const. */ - if (unchecked_p (state)) - { - tree diag_arg = sm_ctxt->get_diagnostic_tree (arg); - sm_ctxt->warn (node, stmt, arg, - make_unique<possible_null_arg> - (*this, diag_arg, callee_fndecl, i)); - const allocation_state *astate - = as_a_allocation_state (state); - sm_ctxt->set_next_state (stmt, arg, - astate->get_nonnull ()); - } - else if (state == m_null) - { - tree diag_arg = sm_ctxt->get_diagnostic_tree (arg); - sm_ctxt->warn (node, stmt, arg, - make_unique<null_arg> - (*this, diag_arg, callee_fndecl, i)); - sm_ctxt->set_next_state (stmt, arg, m_stop); - } - else if (state == m_start) - maybe_assume_non_null (sm_ctxt, arg, stmt); - } - } - BITMAP_FREE (nonnull_args); + const deallocator *d + = mutable_this->get_or_create_deallocator (fndecl); + on_deallocator_call (sm_ctxt, node, call, d, dealloc_argno); } } - - /* Check for this after nonnull, so that if we have both - then we transition to "freed", rather than "checked". */ - unsigned dealloc_argno = fndecl_dealloc_argno (callee_fndecl); - if (dealloc_argno != UINT_MAX) - { - const deallocator *d - = mutable_this->get_or_create_deallocator (callee_fndecl); - on_deallocator_call (sm_ctxt, node, call, d, dealloc_argno); - } } /* Look for pointers explicitly being compared against zero |