New warning: -Wanalyzer-jump-through-null [PR105947]

This patch adds a new warning to -fanalyzer for jumps through NULL
function pointers.

gcc/analyzer/ChangeLog:
	PR analyzer/105947
	* analyzer.opt (Wanalyzer-jump-through-null): New option.
	* engine.cc (class jump_through_null): New.
	(exploded_graph::process_node): Complain about jumps through NULL
	function pointers.

gcc/ChangeLog:
	PR analyzer/105947
	* doc/invoke.texi: Add -Wanalyzer-jump-through-null.

gcc/testsuite/ChangeLog:
	PR analyzer/105947
	* gcc.dg/analyzer/function-ptr-5.c: New test.

Signed-off-by: David Malcolm <dmalcolm@redhat.com>
This commit is contained in:
David Malcolm 2022-08-05 19:45:41 -04:00
parent cc01a27db5
commit e1a9168153
4 changed files with 107 additions and 0 deletions

View File

@ -98,6 +98,10 @@ Wanalyzer-free-of-non-heap
Common Var(warn_analyzer_free_of_non_heap) Init(1) Warning
Warn about code paths in which a non-heap pointer is freed.
Wanalyzer-jump-through-null
Common Var(warn_analyzer_jump_through_null) Init(1) Warning
Warn about code paths in which a NULL function pointer is called.
Wanalyzer-malloc-leak
Common Var(warn_analyzer_malloc_leak) Init(1) Warning
Warn about code paths in which a heap-allocated pointer leaks.

View File

@ -3705,6 +3705,46 @@ private:
bool m_terminate_path;
};
/* A subclass of pending_diagnostic for complaining about jumps through NULL
function pointers. */
class jump_through_null : public pending_diagnostic_subclass<jump_through_null>
{
public:
jump_through_null (const gcall *call)
: m_call (call)
{}
const char *get_kind () const final override
{
return "jump_through_null";
}
bool operator== (const jump_through_null &other) const
{
return m_call == other.m_call;
}
int get_controlling_option () const final override
{
return OPT_Wanalyzer_jump_through_null;
}
bool emit (rich_location *rich_loc) final override
{
return warning_at (rich_loc, get_controlling_option (),
"jump through null pointer");
}
label_text describe_final_event (const evdesc::final_event &ev) final override
{
return ev.formatted_print ("jump through null pointer here");
}
private:
const gcall *m_call;
};
/* The core of exploded_graph::process_worklist (the main analysis loop),
handling one node in the worklist.
@ -4046,6 +4086,15 @@ exploded_graph::process_node (exploded_node *node)
logger);
if (!call_discovered)
{
/* Check for jump through NULL. */
if (tree fn_ptr = gimple_call_fn (call))
{
const svalue *fn_ptr_sval
= model->get_rvalue (fn_ptr, &ctxt);
if (fn_ptr_sval->all_zeroes_p ())
ctxt.warn (new jump_through_null (call));
}
/* An unknown function or a special function was called
at this point, in such case, don't terminate the
analysis of the current function.

View File

@ -453,6 +453,7 @@ Objective-C and Objective-C++ Dialects}.
-Wno-analyzer-fd-use-without-check @gol
-Wno-analyzer-file-leak @gol
-Wno-analyzer-free-of-non-heap @gol
-Wno-analyzer-jump-through-null @gol
-Wno-analyzer-malloc-leak @gol
-Wno-analyzer-mismatching-deallocation @gol
-Wno-analyzer-null-argument @gol
@ -9756,6 +9757,7 @@ Enabling this option effectively enables the following warnings:
-Wanalyzer-fd-use-without-check @gol
-Wanalyzer-file-leak @gol
-Wanalyzer-free-of-non-heap @gol
-Wanalyzer-jump-through-null @gol
-Wanalyzer-malloc-leak @gol
-Wanalyzer-mismatching-deallocation @gol
-Wanalyzer-null-argument @gol
@ -9942,6 +9944,16 @@ is called on a non-heap pointer (e.g. an on-stack buffer, or a global).
See @uref{https://cwe.mitre.org/data/definitions/590.html, CWE-590: Free of Memory not on the Heap}.
@item -Wno-analyzer-jump-through-null
@opindex Wanalyzer-jump-through-null
@opindex Wno-analyzer-jump-through-null
This warning requires @option{-fanalyzer}, which enables it; use
@option{-Wno-analyzer-jump-through-null}
to disable it.
This diagnostic warns for paths through the code in which a @code{NULL}
function pointer is called.
@item -Wno-analyzer-malloc-leak
@opindex Wanalyzer-malloc-leak
@opindex Wno-analyzer-malloc-leak

View File

@ -0,0 +1,42 @@
#define NULL ((void *)0)
void calling_null_fn_ptr_1 (void)
{
void (*fn_ptr) (void) = NULL;
fn_ptr (); /* { dg-warning "jump through null pointer" } */
}
int calling_null_fn_ptr_2 (void)
{
int (*fn_ptr) (void) = NULL;
return fn_ptr (); /* { dg-warning "jump through null pointer" } */
}
typedef void (*void_void_fn_ptr) (void);
void calling_const_fn_ptr (void)
{
void_void_fn_ptr fn_ptr = (void_void_fn_ptr)0xffd2;
return fn_ptr ();
}
void skipping_init (int flag)
{
void_void_fn_ptr fn_ptr = NULL;
if (flag) /* { dg-message "branch" } */
fn_ptr = (void_void_fn_ptr)0xffd2;
fn_ptr (); /* { dg-warning "jump through null pointer" } */
}
struct callbacks
{
void_void_fn_ptr on_redraw;
void_void_fn_ptr on_cleanup;
};
void test_callbacks (void)
{
struct callbacks cb;
__builtin_memset (&cb, 0, sizeof (cb));
cb.on_cleanup (); /* { dg-warning "jump through null pointer" } */
}