aboutsummaryrefslogtreecommitdiff
path: root/gcc/analyzer
diff options
context:
space:
mode:
authorMartin Liska <mliska@suse.cz>2022-08-08 09:05:36 +0200
committerMartin Liska <mliska@suse.cz>2022-08-08 09:05:36 +0200
commitb3a187edd33b89acf19ba46f3b8070d7c977ac57 (patch)
tree43549f6851052eb2844ea76358af30a9fd302ec5 /gcc/analyzer
parent89eca196c99645ee1abefcf8b4a9dd84edd87ad6 (diff)
parent2633c8d8f338f1e2b53d3757f3edf4179bfcc218 (diff)
downloadgcc-b3a187edd33b89acf19ba46f3b8070d7c977ac57.zip
gcc-b3a187edd33b89acf19ba46f3b8070d7c977ac57.tar.gz
gcc-b3a187edd33b89acf19ba46f3b8070d7c977ac57.tar.bz2
Merge branch 'master' into devel/sphinx
Diffstat (limited to 'gcc/analyzer')
-rw-r--r--gcc/analyzer/ChangeLog18
-rw-r--r--gcc/analyzer/analyzer.opt4
-rw-r--r--gcc/analyzer/engine.cc49
-rw-r--r--gcc/analyzer/sm-fd.cc129
4 files changed, 197 insertions, 3 deletions
diff --git a/gcc/analyzer/ChangeLog b/gcc/analyzer/ChangeLog
index d1cb3ad..0b93219 100644
--- a/gcc/analyzer/ChangeLog
+++ b/gcc/analyzer/ChangeLog
@@ -1,3 +1,21 @@
+2022-08-05 David Malcolm <dmalcolm@redhat.com>
+
+ 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.
+
+2022-08-02 Immad Mir <mirimmad@outlook.com>
+
+ PR analyzer/106298
+ * sm-fd.cc (fd_state_machine::on_open): Add
+ creat, dup, dup2 and dup3 functions.
+ (enum dup): New.
+ (fd_state_machine::valid_to_unchecked_state): New.
+ (fd_state_machine::on_creat): New.
+ (fd_state_machine::on_dup): New.
+
2022-07-28 David Malcolm <dmalcolm@redhat.com>
PR analyzer/105893
diff --git a/gcc/analyzer/analyzer.opt b/gcc/analyzer/analyzer.opt
index 808ff36..c6d9c53 100644
--- a/gcc/analyzer/analyzer.opt
+++ b/gcc/analyzer/analyzer.opt
@@ -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.
diff --git a/gcc/analyzer/engine.cc b/gcc/analyzer/engine.cc
index 85b7c5e..e8db00d 100644
--- a/gcc/analyzer/engine.cc
+++ b/gcc/analyzer/engine.cc
@@ -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.
diff --git a/gcc/analyzer/sm-fd.cc b/gcc/analyzer/sm-fd.cc
index ed923ad..8bb76d7 100644
--- a/gcc/analyzer/sm-fd.cc
+++ b/gcc/analyzer/sm-fd.cc
@@ -69,6 +69,14 @@ enum access_directions
DIRS_WRITE
};
+/* An enum for distinguishing between dup, dup2 and dup3. */
+enum dup
+{
+ DUP_1,
+ DUP_2,
+ DUP_3
+};
+
class fd_state_machine : public state_machine
{
public:
@@ -114,7 +122,9 @@ public:
bool is_readonly_fd_p (state_t s) const;
bool is_writeonly_fd_p (state_t s) const;
enum access_mode get_access_mode_from_flag (int flag) const;
-
+ /* Function for one-to-one correspondence between valid
+ and unchecked states. */
+ state_t valid_to_unchecked_state (state_t state) const;
/* State for a constant file descriptor (>= 0) */
state_t m_constant_fd;
@@ -147,6 +157,8 @@ public:
private:
void on_open (sm_context *sm_ctxt, const supernode *node, const gimple *stmt,
const gcall *call) const;
+ void on_creat (sm_context *sm_ctxt, const supernode *node, const gimple *stmt,
+ const gcall *call) const;
void on_close (sm_context *sm_ctxt, const supernode *node, const gimple *stmt,
const gcall *call) const;
void on_read (sm_context *sm_ctxt, const supernode *node, const gimple *stmt,
@@ -170,6 +182,9 @@ private:
const gimple *stmt, const gcall *call,
const tree callee_fndecl, const char *attr_name,
access_directions fd_attr_access_dir) const;
+ void check_for_dup (sm_context *sm_ctxt, const supernode *node,
+ const gimple *stmt, const gcall *call, const tree callee_fndecl,
+ enum dup kind) const;
};
/* Base diagnostic class relative to fd_state_machine. */
@@ -723,6 +738,20 @@ fd_state_machine::is_constant_fd_p (state_t state) const
return (state == m_constant_fd);
}
+fd_state_machine::state_t
+fd_state_machine::valid_to_unchecked_state (state_t state) const
+{
+ if (state == m_valid_read_write)
+ return m_unchecked_read_write;
+ else if (state == m_valid_write_only)
+ return m_unchecked_write_only;
+ else if (state == m_valid_read_only)
+ return m_unchecked_read_only;
+ else
+ gcc_unreachable ();
+ return NULL;
+}
+
bool
fd_state_machine::on_stmt (sm_context *sm_ctxt, const supernode *node,
const gimple *stmt) const
@@ -736,6 +765,11 @@ fd_state_machine::on_stmt (sm_context *sm_ctxt, const supernode *node,
return true;
} // "open"
+ if (is_named_call_p (callee_fndecl, "creat", call, 2))
+ {
+ on_creat (sm_ctxt, node, stmt, call);
+ } // "creat"
+
if (is_named_call_p (callee_fndecl, "close", call, 1))
{
on_close (sm_ctxt, node, stmt, call);
@@ -754,6 +788,23 @@ fd_state_machine::on_stmt (sm_context *sm_ctxt, const supernode *node,
return true;
} // "read"
+ if (is_named_call_p (callee_fndecl, "dup", call, 1))
+ {
+ check_for_dup (sm_ctxt, node, stmt, call, callee_fndecl, DUP_1);
+ return true;
+ }
+
+ if (is_named_call_p (callee_fndecl, "dup2", call, 2))
+ {
+ check_for_dup (sm_ctxt, node, stmt, call, callee_fndecl, DUP_2);
+ return true;
+ }
+
+ if (is_named_call_p (callee_fndecl, "dup3", call, 3))
+ {
+ check_for_dup (sm_ctxt, node, stmt, call, callee_fndecl, DUP_3);
+ return true;
+ }
{
// Handle __attribute__((fd_arg))
@@ -900,6 +951,78 @@ fd_state_machine::on_open (sm_context *sm_ctxt, const supernode *node,
}
void
+fd_state_machine::on_creat (sm_context *sm_ctxt, const supernode *node,
+ const gimple *stmt, const gcall *call) const
+{
+ tree lhs = gimple_call_lhs (call);
+ if (lhs)
+ sm_ctxt->on_transition (node, stmt, lhs, m_start, m_unchecked_write_only);
+ else
+ sm_ctxt->warn (node, stmt, NULL_TREE, new fd_leak (*this, NULL_TREE));
+}
+
+void
+fd_state_machine::check_for_dup (sm_context *sm_ctxt, const supernode *node,
+ const gimple *stmt, const gcall *call,
+ const tree callee_fndecl, enum dup kind) const
+{
+ tree lhs = gimple_call_lhs (call);
+ tree arg_1 = gimple_call_arg (call, 0);
+ state_t state_arg_1 = sm_ctxt->get_state (stmt, arg_1);
+ if (state_arg_1 == m_stop)
+ return;
+ if (!(is_constant_fd_p (state_arg_1) || is_valid_fd_p (state_arg_1)))
+ {
+ check_for_open_fd (sm_ctxt, node, stmt, call, callee_fndecl,
+ DIRS_READ_WRITE);
+ if (kind == DUP_1)
+ return;
+ }
+ switch (kind)
+ {
+ case DUP_1:
+ if (lhs)
+ {
+ if (is_constant_fd_p (state_arg_1))
+ sm_ctxt->set_next_state (stmt, lhs, m_unchecked_read_write);
+ else
+ sm_ctxt->set_next_state (stmt, lhs,
+ valid_to_unchecked_state (state_arg_1));
+ }
+ break;
+
+ case DUP_2:
+ case DUP_3:
+ tree arg_2 = gimple_call_arg (call, 1);
+ state_t state_arg_2 = sm_ctxt->get_state (stmt, arg_2);
+ tree diag_arg_2 = sm_ctxt->get_diagnostic_tree (arg_2);
+ if (state_arg_2 == m_stop)
+ return;
+ /* Check if -1 was passed as second argument to dup2. */
+ if (!(is_constant_fd_p (state_arg_2) || is_valid_fd_p (state_arg_2)))
+ {
+ sm_ctxt->warn (
+ node, stmt, arg_2,
+ new fd_use_without_check (*this, diag_arg_2, callee_fndecl));
+ return;
+ }
+ /* dup2 returns value of its second argument on success.But, the
+ access mode of the returned file descriptor depends on the duplicated
+ file descriptor i.e the first argument. */
+ if (lhs)
+ {
+ if (is_constant_fd_p (state_arg_1))
+ sm_ctxt->set_next_state (stmt, lhs, m_unchecked_read_write);
+ else
+ sm_ctxt->set_next_state (stmt, lhs,
+ valid_to_unchecked_state (state_arg_1));
+ }
+
+ break;
+ }
+}
+
+void
fd_state_machine::on_close (sm_context *sm_ctxt, const supernode *node,
const gimple *stmt, const gcall *call) const
{
@@ -964,6 +1087,8 @@ fd_state_machine::check_for_open_fd (
}
switch (callee_fndecl_dir)
{
+ case DIRS_READ_WRITE:
+ break;
case DIRS_READ:
if (is_writeonly_fd_p (state))
{
@@ -984,8 +1109,6 @@ fd_state_machine::check_for_open_fd (
*this, diag_arg, DIRS_READ, callee_fndecl));
}
break;
- default:
- gcc_unreachable ();
}
}
}