aboutsummaryrefslogtreecommitdiff
path: root/gcc/analyzer
diff options
context:
space:
mode:
authorbenjamin priour <vultkayn@gcc.gnu.org>2023-08-27 14:36:14 +0200
committerbenjamin priour <vultkayn@gcc.gnu.org>2023-08-27 14:50:43 +0200
commit55f6a7d949abc708d1c6ebc01eb3053f96d1472b (patch)
treecf251e1e9b38eb84c9cfa0fbf98404e2688779ff /gcc/analyzer
parent7997f0d35efca8a24d1b0ceae5066b1019d633d7 (diff)
downloadgcc-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.h22
-rw-r--r--gcc/analyzer/kf.cc210
-rw-r--r--gcc/analyzer/known-function-manager.cc7
-rw-r--r--gcc/analyzer/known-function-manager.h2
-rw-r--r--gcc/analyzer/region-model.cc56
-rw-r--r--gcc/analyzer/region-model.h4
-rw-r--r--gcc/analyzer/sm-fd.cc25
-rw-r--r--gcc/analyzer/sm-malloc.cc133
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