As part of PR analyzer/96395, these patches moved testcases from
gcc.dg/analyzer to c-c++-common/analyzer:
-
r14-3503-g55f6a7d949abc7
-
r14-3823-g50b5199cff6908
-
r14-6564-gae034b9106fbdd
Unfortunately this led to numerous g++ testsuite failures on Solaris,
tracked as PR analyzer/111475.
Almost all of the failures are due to standard library differences where
including a C standard library on C++ e.g. <stdlib.h> leads to the plain
symbols referencing the symbols "std::" via a "using" declaration,
whereas I had written the code expecting them to use symbols in the root
namespace.
The analyzer has special-case handling of many functions by name.
This patch generalizes such handling to also match against functions
in "std::" for all of the cases I found in the testsuite (via manual
inspection of the preprocessed test cases against Solaris headers).
This fixes cases where the analyzer was failing to "know about" the
behavior of such functions.
Other such failures are due to "std::" prefixes appearing in names of
functions in the output, leading to mismatches against expected output.
The patch adds regexes to some cases, and moves some other cases back
from c-c++-common to gcc.dg where the dg-multiline syntax isn't
expressive enough.
Various "fd-*.c" failures relate to Solaris's socket-handling functions
not being marked with "noexcept", where due to PR analyzer/97111 we
mishandle the exception-handling edges in the CFG, leading to leak
false positives. The patch works around this by adding -fno-exceptions
to these cases, pending a proper fix for PR analyzer/97111.
gcc/analyzer/ChangeLog:
PR analyzer/111475
* analyzer.cc (is_special_named_call_p): Add "look_in_std" param.
(is_std_function_p): Make non-static.
* analyzer.h (is_special_named_call_p): Add optional "look_in_std"
param.
(is_std_function_p): New decl.
* engine.cc (stmt_requires_new_enode_p): Look for both "signal"
and "std::signal".
* kf.cc (register_known_functions): Add various "std::" copies
of the known functions.
* known-function-manager.cc
(known_function_manager::~known_function_manager): Clean up
m_std_ns_map_id_to_kf.
(known_function_manager::add_std_ns): New.
(known_function_manager::get_match): Also look for known "std::"
functions.
(known_function_manager::get_by_identifier_in_std_ns): New.
* known-function-manager.h
(known_function_manager::add_std_ns): New decl.
(known_function_manager::get_by_identifier_in_std_ns): New decl.
(known_function_manager::m_std_ns_map_id_to_kf): New field.
* sm-file.cc (register_known_file_functions): Add various "std::"
copies of the known functions.
* sm-malloc.cc (malloc_state_machine::on_stmt): Handle
"std::realloc".
* sm-signal.cc (signal_unsafe_p): Consider "std::" copies of the
functions as also being async-signal-unsafe.
(signal_state_machine::on_stmt): Consider "std::signal".
gcc/testsuite/ChangeLog:
PR analyzer/111475
* c-c++-common/analyzer/fd-glibc-byte-stream-socket.c: Add
-fno-exceptions for now.
* c-c++-common/analyzer/fd-manpage-getaddrinfo-client.c: Likewise.
* c-c++-common/analyzer/fd-mappage-getaddrinfo-server.c: Rename to...
* c-c++-common/analyzer/fd-manpage-getaddrinfo-server.c: ...this, and
add -fno-exceptions for now.
* c-c++-common/analyzer/fd-socket-meaning.c: Add -fno-exceptions
for now.
* c-c++-common/analyzer/fd-symbolic-socket.c: Likewise.
* c-c++-common/analyzer/flexible-array-member-1.c: Use regexp to
handle C vs C++ differences in spelling of function name, which
could have a "std::" prefix on some targets.
* c-c++-common/analyzer/pr106539.c: Likewise.
* c-c++-common/analyzer/malloc-ipa-8-unchecked.c: Move back to...
* gcc.dg/analyzer/malloc-ipa-8-unchecked.c: ...here, dropping
attempt to generalize output for C vs C++.
* c-c++-common/analyzer/signal-4a.c: Move back to...
* gcc.dg/analyzer/signal-4a.c: ...here, dropping attempt to
generalize output for C vs C++.
* c-c++-common/analyzer/signal-4b.c: Move back to...
* gcc.dg/analyzer/signal-4b.c: ...here, dropping attempt to
generalize output for C vs C++.
Signed-off-by: David Malcolm <dmalcolm@redhat.com>
is_named_call_p should be used instead, using a fndecl from
get_fndecl_for_call; this function should only be used for special cases
where it's not practical to get at the region model, or for special
- analyzer functions such as __analyzer_dump. */
+ analyzer functions such as __analyzer_dump.
+
+ If LOOK_IN_STD is true, then also look for within std:: for the name. */
bool
is_special_named_call_p (const gcall *call, const char *funcname,
- unsigned int num_args)
+ unsigned int num_args, bool look_in_std)
{
gcc_assert (funcname);
if (!fndecl)
return false;
- return is_named_call_p (fndecl, funcname, call, num_args);
+ if (is_named_call_p (fndecl, funcname, call, num_args))
+ return true;
+ if (look_in_std)
+ if (is_std_named_call_p (fndecl, funcname, call, num_args))
+ return true;
+ return false;
}
/* Helper function for checkers. Is FNDECL an extern fndecl at file scope
Compare with cp/typeck.cc: decl_in_std_namespace_p, but this doesn't
rely on being the C++ FE (or handle inline namespaces inside of std). */
-static inline bool
+bool
is_std_function_p (const_tree fndecl)
{
tree name_decl = DECL_NAME (fndecl);
} // namespace ana
extern bool is_special_named_call_p (const gcall *call, const char *funcname,
- unsigned int num_args);
+ unsigned int num_args,
+ bool look_in_std = false);
extern bool is_named_call_p (const_tree fndecl, const char *funcname);
extern bool is_named_call_p (const_tree fndecl, const char *funcname,
const gcall *call, unsigned int num_args);
+extern bool is_std_function_p (const_tree fndecl);
extern bool is_std_named_call_p (const_tree fndecl, const char *funcname);
extern bool is_std_named_call_p (const_tree fndecl, const char *funcname,
const gcall *call, unsigned int num_args);
regular next state, which defeats the "detect state change" logic
in process_node. Work around this via special-casing, to ensure
we split the enode immediately before any "signal" call. */
- if (is_special_named_call_p (call, "signal", 2))
+ if (is_special_named_call_p (call, "signal", 2, true))
return true;
}
/* Language-specific support functions. */
register_known_functions_lang_cp (kfm);
+
+ /* Some C++ implementations use the std:: copies of these functions
+ from <cstdlib> etc for the C spellings of these headers (e.g. <stdlib.h>),
+ so we must match against these too. */
+ {
+ kfm.add_std_ns ("malloc", make_unique<kf_malloc> ());
+ kfm.add_std_ns ("free", make_unique<kf_free> ());
+ kfm.add_std_ns ("realloc", make_unique<kf_realloc> ());
+ kfm.add_std_ns ("calloc", make_unique<kf_calloc> ());
+ kfm.add_std_ns
+ ("memcpy",
+ make_unique<kf_memcpy_memmove> (kf_memcpy_memmove::KF_MEMCPY));
+ kfm.add_std_ns
+ ("memmove",
+ make_unique<kf_memcpy_memmove> (kf_memcpy_memmove::KF_MEMMOVE));
+ kfm.add_std_ns ("memset", make_unique<kf_memset> (false));
+ kfm.add_std_ns ("strcat", make_unique<kf_strcat> (2, false));
+ kfm.add_std_ns ("strcpy", make_unique<kf_strcpy> (2, false));
+ kfm.add_std_ns ("strlen", make_unique<kf_strlen> ());
+ kfm.add_std_ns ("strncpy", make_unique<kf_strncpy> ());
+ kfm.add_std_ns ("strtok", make_unique<kf_strtok> (rmm));
+ }
}
} // namespace ana
/* Delete all owned kfs. */
for (auto iter : m_map_id_to_kf)
delete iter.second;
+ for (auto iter : m_std_ns_map_id_to_kf)
+ delete iter.second;
for (auto iter : m_combined_fns_arr)
delete iter;
}
m_map_id_to_kf.put (id, kf.release ());
}
+void
+known_function_manager::add_std_ns (const char *name,
+ std::unique_ptr<known_function> kf)
+{
+ LOG_FUNC_1 (get_logger (), "registering std::%s", name);
+ tree id = get_identifier (name);
+ m_std_ns_map_id_to_kf.put (id, kf.release ());
+}
+
void
known_function_manager::add (enum built_in_function name,
std::unique_ptr<known_function> kf)
/* Look for a match by name. */
- /* Reject fndecls that aren't in the root namespace. */
+ if (is_std_function_p (fndecl))
+ {
+ if (tree identifier = DECL_NAME (fndecl))
+ if (const known_function *candidate
+ = get_by_identifier_in_std_ns (identifier))
+ if (candidate->matches_call_types_p (cd))
+ return candidate;
+ return nullptr;
+ }
+
if (DECL_CONTEXT (fndecl)
&& TREE_CODE (DECL_CONTEXT (fndecl)) != TRANSLATION_UNIT_DECL)
return NULL;
return NULL;
}
+/* Get any known_function in C++ std:: namespace matching IDENTIFIER, without
+ type-checking.
+ Return nullptr if there isn't one. */
+
+const known_function *
+known_function_manager::get_by_identifier_in_std_ns (tree identifier) const
+{
+ known_function_manager *mut_this = const_cast<known_function_manager *>(this);
+ known_function **slot = mut_this->m_std_ns_map_id_to_kf.get (identifier);
+ if (slot)
+ return *slot;
+ else
+ return nullptr;
+}
+
+
} // namespace ana
#endif /* #if ENABLE_ANALYZER */
~known_function_manager ();
void add (const char *name, std::unique_ptr<known_function> kf);
+ void add_std_ns (const char *name, std::unique_ptr<known_function> kf);
void add (enum built_in_function name, std::unique_ptr<known_function> kf);
void add (enum internal_fn ifn, std::unique_ptr<known_function> kf);
const known_function *
get_normal_builtin (const builtin_known_function *builtin_kf) const;
const known_function *get_by_identifier (tree identifier) const;
+ const known_function *get_by_identifier_in_std_ns (tree identifier) const;
/* Map from identifier to known_function instance.
Has ownership of the latter. */
hash_map<tree, known_function *> m_map_id_to_kf;
+ /* Likewise for C++'s std:: namespace. */
+ hash_map<tree, known_function *> m_std_ns_map_id_to_kf;
+
/* Array of known builtins. */
known_function *m_combined_fns_arr[CFN_LAST];
};
kfm.add ("fread", make_unique<kf_fread> ());
kfm.add ("getc", make_unique<kf_getc> ());
kfm.add ("getchar", make_unique<kf_getchar> ());
+
+ /* Some C++ implementations use the std:: copies of these functions
+ from <cstdio> for <stdio.h>, so we must match against these too. */
+ kfm.add_std_ns ("ferror", make_unique<kf_ferror> ());
+ kfm.add_std_ns ("fgets", make_unique<kf_fgets> ());
+ kfm.add_std_ns ("fread", make_unique<kf_fread> ());
+ kfm.add_std_ns ("getc", make_unique<kf_getc> ());
+ kfm.add_std_ns ("getchar", make_unique<kf_getchar> ());
}
#if CHECKING_P
}
if (is_named_call_p (callee_fndecl, "realloc", call, 2)
+ || is_std_named_call_p (callee_fndecl, "realloc", call, 2)
|| is_named_call_p (callee_fndecl, "__builtin_realloc", call, 2))
{
on_realloc_call (sm_ctxt, node, call);
signal_unsafe_p (tree fndecl)
{
function_set fs = get_async_signal_unsafe_fns ();
- return fs.contains_decl_p (fndecl);
+ if (fs.contains_decl_p (fndecl))
+ return true;
+ if (is_std_function_p (fndecl)
+ && fs.contains_name_p (IDENTIFIER_POINTER (DECL_NAME (fndecl))))
+ return true;
+
+ return false;
}
/* Implementation of state_machine::on_stmt vfunc for signal_state_machine. */
{
if (const gcall *call = dyn_cast <const gcall *> (stmt))
if (tree callee_fndecl = sm_ctxt->get_fndecl_for_call (call))
- if (is_named_call_p (callee_fndecl, "signal", call, 2))
+ if (is_named_call_p (callee_fndecl, "signal", call, 2)
+ || is_std_named_call_p (callee_fndecl, "signal", call, 2))
{
tree handler = gimple_call_arg (call, 1);
if (TREE_CODE (handler) == ADDR_EXPR
/* Example from glibc manual (16.9.6). */
/* { dg-require-effective-target sockets } */
+
+/* Needed on some targets until we have exception-handling working (PR 111475). */
+/* { dg-additional-options "-fno-exceptions" } */
+
/* { dg-skip-if "" { hppa*-*-hpux* powerpc*-*-aix* } } */
#include <stdio.h>
/* { dg-require-effective-target sockets } */
/* { dg-additional-options "-Wno-analyzer-too-complex" } */
+
+/* Needed on some targets until we have exception-handling working (PR 111475). */
+/* { dg-additional-options "-fno-exceptions" } */
+
/* { dg-skip-if "" { hppa*-*-hpux* powerpc*-*-aix* } } */
#include <sys/types.h>
*/
/* { dg-require-effective-target sockets } */
+
+/* Needed on some targets until we have exception-handling working (PR 111475). */
+/* { dg-additional-options "-fno-exceptions" } */
+
/* { dg-skip-if "" { hppa*-*-hpux* powerpc*-*-aix* } } */
#include <sys/types.h>
/* { dg-require-effective-target sockets } */
+
+/* Needed on some targets until we have exception-handling working (PR 111475). */
+/* { dg-additional-options "-fno-exceptions" } */
+
/* { dg-additional-options "-fanalyzer-verbose-state-changes" } */
#include <sys/socket.h>
/* { dg-require-effective-target sockets } */
+
+/* Needed on some targets until we have exception-handling working (PR 111475). */
+/* { dg-additional-options "-fno-exceptions" } */
+
/* { dg-skip-if "" { hppa*-*-hpux* powerpc*-*-aix* } } */
#include <string.h>
if (str) {
str->len = 10;
memset(str->data, 'x', 10); /* { dg-warning "heap-based buffer overflow" "Wanalyzer-out-of-bounds" } */
- /* { dg-warning "'memset' writing 10 bytes into a region of size 0 overflows the destination" "Wstringop-overflow" { target c } .-1 } */
- /* { dg-warning "'void\\* memset\\(void\\*, int, size_t\\)' writing 10 bytes into a region of size 0 overflows the destination" "Wstringop-overflow" { target c++ } .-2 } */
+ /* { dg-warning "'\[^\n\r\]*memset\[^\n\r\]*' writing 10 bytes into a region of size 0 overflows the destination" "Wstringop-overflow" { target *-*-* } .-1 } */
return str;
}
return NULL;
str->len = 10;
/* Using the wrong size here. */
memset(str->data, 'x', 11); /* { dg-warning "heap-based buffer overflow" "Wanalyzer-out-of-bounds" } */
- /* { dg-warning "'memset' writing 11 bytes into a region of size 10 overflows the destination" "Wstringop-overflow" { target c } .-1 } */
- /* { dg-warning "'void\\* memset\\(void\\*, int, size_t\\)' writing 11 bytes into a region of size 10 overflows the destination" "Wstringop-overflow" { target c++ } .-2 } */
+ /* { dg-warning "'\[^\n\r\]*memset\[^\n\r\]*' writing 11 bytes into a region of size 10 overflows the destination" "Wstringop-overflow" { target *-*-* } .-1 } */
return str;
}
return NULL;
p[0] = malloc(10);
p[1] = malloc(20); /* { dg-message "allocated here" } */
- void *q = realloc (p, sizeof (void *)); /* { dg-message "when 'realloc' succeeds, moving buffer" } */
+ void *q = realloc (p, sizeof (void *)); /* { dg-message "when '\[^\n\r\]*realloc\[^\n\r\]*' succeeds, moving buffer" } */
if (!q)
/* { dg-warning "leak of '<unknown>'" "leak of unknown" { target *-*-* } .-1 } */
return p;
/* { dg-additional-options "-fdiagnostics-show-line-numbers -fdiagnostics-path-format=inline-events -fanalyzer-checker=malloc -fdiagnostics-show-caret" } */
/* { dg-enable-nn-line-numbers "" } */
+/* C only; attempting to generalize it for C++ leads
+ to an explosion of possibilities for the multiline output. */
+
#include <stdlib.h>
void *wrapped_malloc (size_t size)
| | |
| | (6) 'result' could be NULL: unchecked value from (4)
|
- { dg-end-multiline-output "" { target c } } */
-/* { dg-begin-multiline-output "" }
- NN | result->i = i;
- | ~~~~~~~~~~^~~
- 'boxed_int* make_boxed_int(int)': events 1-2
- |
- | NN | make_boxed_int (int i)
- | | ^~~~~~~~~~~~~~
- | | |
- | | (1) entry to 'make_boxed_int'
- | NN | {
- | NN | boxed_int *result = (boxed_int *)wrapped_malloc (sizeof (boxed_int));
- | | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- | | |
- | | (2) calling 'wrapped_malloc' from 'make_boxed_int'
- |
- +--> 'void* wrapped_malloc(size_t)': events 3-4
- |
- | NN | void *wrapped_malloc (size_t size)
- | | ^~~~~~~~~~~~~~
- | | |
- | | (3) entry to 'wrapped_malloc'
- | NN | {
- | NN | return malloc (size);
- | | ~~~~~~~~~~~~~
- | | |
- | | (4) this call could return NULL
- |
- <------+
- |
- 'boxed_int* make_boxed_int(int)': events 5-6
- |
- | NN | boxed_int *result = (boxed_int *)wrapped_malloc (sizeof (boxed_int));
- | | ~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~
- | | |
- | | (5) possible return of NULL to 'make_boxed_int' from 'wrapped_malloc'
- | NN | result->i = i;
- | | ~~~~~~~~~~~~~
- | | |
- | | (6) 'result' could be NULL: unchecked value from (4)
- |
- { dg-end-multiline-output "" { target c++ } } */
+ { dg-end-multiline-output "" } */
/* Verify how paths are printed for signal-handler diagnostics. */
+/* C only; attempting to generalize it for C++ leads
+ to an explosion of possibilities for the multiline output. */
+
/* { dg-options "-fanalyzer -fdiagnostics-show-line-numbers -fdiagnostics-path-format=inline-events -fdiagnostics-show-caret" } */
/* { dg-enable-nn-line-numbers "" } */
/* { dg-require-effective-target signal } */
void custom_logger(const char *msg)
{
- fprintf(stderr, "LOG: %s", msg); /* { dg-warning "call to 'fprintf' from within signal handler" "" { target c } } */
- /* { dg-warning "call to 'int fprintf\\(FILE\\*, const char\\*, ...\\)' from within signal handler" "" { target c++ } .-1 } */
+ fprintf(stderr, "LOG: %s", msg); /* { dg-warning "call to 'fprintf' from within signal handler" } */
}
static void int_handler(int signum)
| | |
| | (7) call to 'fprintf' from within signal handler
|
- { dg-end-multiline-output "" { target c } } */
-/* { dg-begin-multiline-output "" }
- NN | fprintf(stderr, "LOG: %s", msg);
- | ~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~
- 'void test()': events 1-2
- |
- | NN | void test (void)
- | | ^~~~
- | | |
- | | (1) entry to 'test'
- |......
- | NN | signal(SIGINT, int_handler);
- | | ~~~~~~~~~~~~~~~~~~~~~~~~~~~
- | | |
- | | (2) registering 'void int_handler(int)' as signal handler
- |
- event 3
- |
- |cc1plus:
- | (3): later on, when the signal is delivered to the process
- |
- +--> 'void int_handler(int)': events 4-5
- |
- | NN | static void int_handler(int signum)
- | | ^~~~~~~~~~~
- | | |
- | | (4) entry to 'int_handler'
- | NN | {
- | NN | custom_logger("got signal");
- | | ~~~~~~~~~~~~~~~~~~~~~~~~~~~
- | | |
- | | (5) calling 'custom_logger' from 'int_handler'
- |
- +--> 'void custom_logger(const char*)': events 6-7
- |
- | NN | void custom_logger(const char *msg)
- | | ^~~~~~~~~~~~~
- | | |
- | | (6) entry to 'custom_logger'
- | NN | {
- | NN | fprintf(stderr, "LOG: %s", msg);
- | | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- | | |
- | | (7) call to 'int fprintf(FILE*, const char*, ...)' from within signal handler
- |
- { dg-end-multiline-output "" { target c++ } } */
+ { dg-end-multiline-output "" } */
/* Verify how paths are printed for signal-handler diagnostics. */
+/* C only; attempting to generalize it for C++ leads
+ to an explosion of possibilities for the multiline output. */
+
/* { dg-options "-fanalyzer -fdiagnostics-show-line-numbers -fdiagnostics-path-format=inline-events -fdiagnostics-show-caret" } */
/* { dg-enable-nn-line-numbers "" } */
/* { dg-require-effective-target signal } */
void custom_logger(const char *msg)
{
- fprintf(stderr, "LOG: %s", msg); /* { dg-warning "call to 'fprintf' from within signal handler" "" { target c } } */
- /* { dg-warning "call to 'int fprintf\\(FILE\\*, const char\\*, ...\\)' from within signal handler" "" { target c++ } .-1 } */
+ fprintf(stderr, "LOG: %s", msg); /* { dg-warning "call to 'fprintf' from within signal handler" } */
}
static void int_handler(int signum)
| | |
| | (9) call to 'fprintf' from within signal handler
|
- { dg-end-multiline-output "" { target c } } */
-/* { dg-begin-multiline-output "" }
- NN | fprintf(stderr, "LOG: %s", msg);
- | ~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~
- 'void test()': events 1-2
- |
- | NN | void test (void)
- | | ^~~~
- | | |
- | | (1) entry to 'test'
- | NN | {
- | NN | __analyzer_register_handler ();
- | | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- | | |
- | | (2) calling '__analyzer_register_handler' from 'test'
- |
- +--> 'void __analyzer_register_handler()': events 3-4
- |
- | NN | static void __analyzer_register_handler ()
- | | ^~~~~~~~~~~~~~~~~~~~~~~~~~~
- | | |
- | | (3) entry to '__analyzer_register_handler'
- | NN | {
- | NN | signal(SIGINT, int_handler);
- | | ~~~~~~~~~~~~~~~~~~~~~~~~~~~
- | | |
- | | (4) registering 'void int_handler(int)' as signal handler
- |
- event 5
- |
- |cc1plus:
- | (5): later on, when the signal is delivered to the process
- |
- +--> 'void int_handler(int)': events 6-7
- |
- | NN | static void int_handler(int signum)
- | | ^~~~~~~~~~~
- | | |
- | | (6) entry to 'int_handler'
- | NN | {
- | NN | custom_logger("got signal");
- | | ~~~~~~~~~~~~~~~~~~~~~~~~~~~
- | | |
- | | (7) calling 'custom_logger' from 'int_handler'
- |
- +--> 'void custom_logger(const char*)': events 8-9
- |
- | NN | void custom_logger(const char *msg)
- | | ^~~~~~~~~~~~~
- | | |
- | | (8) entry to 'custom_logger'
- | NN | {
- | NN | fprintf(stderr, "LOG: %s", msg);
- | | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- | | |
- | | (9) call to 'int fprintf(FILE*, const char*, ...)' from within signal handler
- |
- { dg-end-multiline-output "" { target c++ } } */
+ { dg-end-multiline-output "" } */