#include "analyzer/analyzer-selftests.h"\r
#include "tristate.h"\r
#include "selftest.h"\r
+#include "stringpool.h"\r
+#include "attribs.h"\r
#include "analyzer/call-string.h"\r
#include "analyzer/program-point.h"\r
#include "analyzer/store.h"\r
#include "analyzer/region-model.h"\r
+#include "bitmap.h"\r
\r
#if ENABLE_ANALYZER\r
\r
WRITE_ONLY\r
};\r
\r
+enum access_directions\r
+{\r
+ DIRS_READ_WRITE,\r
+ DIRS_READ,\r
+ DIRS_WRITE\r
+};\r
+\r
class fd_state_machine : public state_machine\r
{\r
public:\r
void check_for_open_fd (sm_context *sm_ctxt, const supernode *node,\r
const gimple *stmt, const gcall *call,\r
const tree callee_fndecl,\r
- enum access_direction access_fn) const;\r
+ enum access_directions access_fn) const;\r
\r
void make_valid_transitions_on_condition (sm_context *sm_ctxt,\r
const supernode *node,\r
const supernode *node,\r
const gimple *stmt,\r
const svalue *lhs) const;\r
+ void check_for_fd_attrs (sm_context *sm_ctxt, const supernode *node,\r
+ const gimple *stmt, const gcall *call,\r
+ const tree callee_fndecl, const char *attr_name,\r
+ access_directions fd_attr_access_dir) const;\r
};\r
\r
/* Base diagnostic class relative to fd_state_machine. */\r
tree m_arg;\r
};\r
\r
+class fd_param_diagnostic : public fd_diagnostic\r
+{\r
+public:\r
+ fd_param_diagnostic (const fd_state_machine &sm, tree arg, tree callee_fndecl,\r
+ const char *attr_name, int arg_idx)\r
+ : fd_diagnostic (sm, arg), m_callee_fndecl (callee_fndecl),\r
+ m_attr_name (attr_name), m_arg_idx (arg_idx)\r
+ {\r
+ }\r
+\r
+ fd_param_diagnostic (const fd_state_machine &sm, tree arg, tree callee_fndecl)\r
+ : fd_diagnostic (sm, arg), m_callee_fndecl (callee_fndecl),\r
+ m_attr_name (NULL), m_arg_idx (-1)\r
+ {\r
+ }\r
+ \r
+ bool\r
+ subclass_equal_p (const pending_diagnostic &base_other) const override\r
+ {\r
+ const fd_param_diagnostic &sub_other\r
+ = (const fd_param_diagnostic &)base_other;\r
+ return (same_tree_p (m_arg, sub_other.m_arg)\r
+ && same_tree_p (m_callee_fndecl, sub_other.m_callee_fndecl)\r
+ && m_arg_idx == sub_other.m_arg_idx\r
+ && ((m_attr_name)\r
+ ? (strcmp (m_attr_name, sub_other.m_attr_name) == 0)\r
+ : true));\r
+ }\r
+\r
+ void\r
+ inform_filedescriptor_attribute (access_directions fd_dir)\r
+ {\r
+\r
+ if (m_attr_name)\r
+ switch (fd_dir)\r
+ {\r
+ case DIRS_READ_WRITE:\r
+ inform (DECL_SOURCE_LOCATION (m_callee_fndecl),\r
+ "argument %d of %qD must be an open file descriptor, due to "\r
+ "%<__attribute__((%s(%d)))%>",\r
+ m_arg_idx + 1, m_callee_fndecl, m_attr_name, m_arg_idx + 1);\r
+ break;\r
+ case DIRS_WRITE:\r
+ inform (DECL_SOURCE_LOCATION (m_callee_fndecl),\r
+ "argument %d of %qD must be a readable file descriptor, due "\r
+ "to %<__attribute__((%s(%d)))%>",\r
+ m_arg_idx + 1, m_callee_fndecl, m_attr_name, m_arg_idx + 1);\r
+ break;\r
+ case DIRS_READ:\r
+ inform (DECL_SOURCE_LOCATION (m_callee_fndecl),\r
+ "argument %d of %qD must be a writable file descriptor, due "\r
+ "to %<__attribute__((%s(%d)))%>",\r
+ m_arg_idx + 1, m_callee_fndecl, m_attr_name, m_arg_idx + 1);\r
+ break;\r
+ }\r
+ }\r
+\r
+protected:\r
+ tree m_callee_fndecl;\r
+ const char *m_attr_name;\r
+ /* ARG_IDX is 0-based. */\r
+ int m_arg_idx;\r
+};\r
+\r
class fd_leak : public fd_diagnostic\r
{\r
public:\r
diagnostic_event_id_t m_open_event;\r
};\r
\r
-class fd_access_mode_mismatch : public fd_diagnostic\r
+class fd_access_mode_mismatch : public fd_param_diagnostic\r
{\r
public:\r
fd_access_mode_mismatch (const fd_state_machine &sm, tree arg,\r
- enum access_direction fd_dir,\r
- const tree callee_fndecl)\r
- : fd_diagnostic (sm, arg), m_fd_dir (fd_dir),\r
- m_callee_fndecl (callee_fndecl)\r
+ enum access_directions fd_dir,\r
+ const tree callee_fndecl, const char *attr_name,\r
+ int arg_idx)\r
+ : fd_param_diagnostic (sm, arg, callee_fndecl, attr_name, arg_idx),\r
+ m_fd_dir (fd_dir)\r
\r
{\r
}\r
\r
+ fd_access_mode_mismatch (const fd_state_machine &sm, tree arg,\r
+ enum access_directions fd_dir,\r
+ const tree callee_fndecl)\r
+ : fd_param_diagnostic (sm, arg, callee_fndecl), m_fd_dir (fd_dir)\r
+ {\r
+ }\r
+ \r
const char *\r
get_kind () const final override\r
{\r
bool\r
emit (rich_location *rich_loc) final override\r
{\r
+ bool warned;\r
switch (m_fd_dir)\r
{\r
- case DIR_READ:\r
- return warning_at (rich_loc, get_controlling_option (),\r
- "%qE on %<read-only%> file descriptor %qE",\r
+ case DIRS_READ:\r
+ warned = warning_at (rich_loc, get_controlling_option (),\r
+ "%qE on read-only file descriptor %qE",\r
m_callee_fndecl, m_arg);\r
- case DIR_WRITE:\r
- return warning_at (rich_loc, get_controlling_option (),\r
- "%qE on %<write-only%> file descriptor %qE",\r
+ break;\r
+ case DIRS_WRITE:\r
+ warned = warning_at (rich_loc, get_controlling_option (),\r
+ "%qE on write-only file descriptor %qE",\r
m_callee_fndecl, m_arg);\r
+ break;\r
default:\r
gcc_unreachable ();\r
}\r
- }\r
-\r
- bool\r
- subclass_equal_p (const pending_diagnostic &base_other) const override\r
- {\r
- const fd_access_mode_mismatch &sub_other\r
- = (const fd_access_mode_mismatch &)base_other;\r
- return (same_tree_p (m_arg, sub_other.m_arg)\r
- && m_callee_fndecl == sub_other.m_callee_fndecl\r
- && m_fd_dir == sub_other.m_fd_dir);\r
+ if (warned)\r
+ inform_filedescriptor_attribute (m_fd_dir);\r
+ return warned;\r
}\r
\r
label_text\r
{\r
switch (m_fd_dir)\r
{\r
- case DIR_READ:\r
- return ev.formatted_print ("%qE on %<read-only%> file descriptor %qE",\r
+ case DIRS_READ:\r
+ return ev.formatted_print ("%qE on read-only file descriptor %qE",\r
m_callee_fndecl, m_arg);\r
- case DIR_WRITE:\r
- return ev.formatted_print ("%qE on %<write-only%> file descriptor %qE",\r
+ case DIRS_WRITE:\r
+ return ev.formatted_print ("%qE on write-only file descriptor %qE",\r
m_callee_fndecl, m_arg);\r
default:\r
gcc_unreachable ();\r
}\r
\r
private:\r
- enum access_direction m_fd_dir;\r
- const tree m_callee_fndecl;\r
+ enum access_directions m_fd_dir;\r
};\r
\r
-class double_close : public fd_diagnostic\r
+class fd_double_close : public fd_diagnostic\r
{\r
public:\r
- double_close (const fd_state_machine &sm, tree arg) : fd_diagnostic (sm, arg)\r
+ fd_double_close (const fd_state_machine &sm, tree arg) : fd_diagnostic (sm, arg)\r
{\r
}\r
\r
const char *\r
get_kind () const final override\r
{\r
- return "double_close";\r
+ return "fd_double_close";\r
}\r
\r
int\r
diagnostic_event_id_t m_first_close_event;\r
};\r
\r
-class fd_use_after_close : public fd_diagnostic\r
+class fd_use_after_close : public fd_param_diagnostic\r
{\r
public:\r
fd_use_after_close (const fd_state_machine &sm, tree arg,\r
+ const tree callee_fndecl, const char *attr_name,\r
+ int arg_idx)\r
+ : fd_param_diagnostic (sm, arg, callee_fndecl, attr_name, arg_idx)\r
+ {\r
+ }\r
+\r
+ fd_use_after_close (const fd_state_machine &sm, tree arg,\r
const tree callee_fndecl)\r
- : fd_diagnostic (sm, arg), m_callee_fndecl (callee_fndecl)\r
+ : fd_param_diagnostic (sm, arg, callee_fndecl)\r
{\r
}\r
\r
bool\r
emit (rich_location *rich_loc) final override\r
{\r
- return warning_at (rich_loc, get_controlling_option (),\r
+ bool warned;\r
+ warned = warning_at (rich_loc, get_controlling_option (),\r
"%qE on closed file descriptor %qE", m_callee_fndecl,\r
m_arg);\r
+ if (warned)\r
+ inform_filedescriptor_attribute (DIRS_READ_WRITE);\r
+ return warned;\r
}\r
\r
label_text\r
describe_final_event (const evdesc::final_event &ev) final override\r
{\r
if (m_first_close_event.known_p ())\r
- return ev.formatted_print (\r
- "%qE on closed file descriptor %qE; %qs was at %@", m_callee_fndecl,\r
- m_arg, "close", &m_first_close_event);\r
- else\r
- return ev.formatted_print ("%qE on closed file descriptor %qE",\r
- m_callee_fndecl, m_arg);\r
+ return ev.formatted_print (\r
+ "%qE on closed file descriptor %qE; %qs was at %@", m_callee_fndecl,\r
+ m_arg, "close", &m_first_close_event);\r
+ else\r
+ return ev.formatted_print ("%qE on closed file descriptor %qE",\r
+ m_callee_fndecl, m_arg);\r
}\r
\r
private:\r
diagnostic_event_id_t m_first_close_event;\r
- const tree m_callee_fndecl;\r
};\r
\r
-class unchecked_use_of_fd : public fd_diagnostic\r
+class fd_use_without_check : public fd_param_diagnostic\r
{\r
public:\r
- unchecked_use_of_fd (const fd_state_machine &sm, tree arg,\r
- const tree callee_fndecl)\r
- : fd_diagnostic (sm, arg), m_callee_fndecl (callee_fndecl)\r
+ fd_use_without_check (const fd_state_machine &sm, tree arg,\r
+ const tree callee_fndecl, const char *attr_name,\r
+ int arg_idx)\r
+ : fd_param_diagnostic (sm, arg, callee_fndecl, attr_name, arg_idx)\r
+ {\r
+ }\r
+\r
+ fd_use_without_check (const fd_state_machine &sm, tree arg,\r
+ const tree callee_fndecl)\r
+ : fd_param_diagnostic (sm, arg, callee_fndecl)\r
{\r
}\r
\r
const char *\r
get_kind () const final override\r
{\r
- return "unchecked_use_of_fd";\r
+ return "fd_use_without_check";\r
}\r
\r
int\r
bool\r
emit (rich_location *rich_loc) final override\r
{\r
- return warning_at (rich_loc, get_controlling_option (),\r
- "%qE on possibly invalid file descriptor %qE",\r
- m_callee_fndecl, m_arg);\r
- }\r
-\r
- bool\r
- subclass_equal_p (const pending_diagnostic &base_other) const override\r
- {\r
- const unchecked_use_of_fd &sub_other\r
- = (const unchecked_use_of_fd &)base_other;\r
- return (same_tree_p (m_arg, sub_other.m_arg)\r
- && m_callee_fndecl == sub_other.m_callee_fndecl);\r
+ bool warned;\r
+ warned = warning_at (rich_loc, get_controlling_option (),\r
+ "%qE on possibly invalid file descriptor %qE",\r
+ m_callee_fndecl, m_arg);\r
+ if (warned)\r
+ inform_filedescriptor_attribute (DIRS_READ_WRITE);\r
+ return warned;\r
}\r
\r
label_text\r
}\r
\r
private:\r
- diagnostic_event_id_t m_first_open_event;\r
- const tree m_callee_fndecl;\r
+ diagnostic_event_id_t m_first_open_event; \r
};\r
\r
fd_state_machine::fd_state_machine (logger *logger)\r
on_read (sm_ctxt, node, stmt, call, callee_fndecl);\r
return true;\r
} // "read"\r
+\r
+ \r
+ {\r
+ // Handle __attribute__((fd_arg))\r
+\r
+ check_for_fd_attrs (sm_ctxt, node, stmt, call, callee_fndecl,\r
+ "fd_arg", DIRS_READ_WRITE);\r
+\r
+ // Handle __attribute__((fd_arg_read))\r
+\r
+ check_for_fd_attrs (sm_ctxt, node, stmt, call, callee_fndecl,\r
+ "fd_arg_read", DIRS_READ);\r
+\r
+ // Handle __attribute__((fd_arg_write))\r
+\r
+ check_for_fd_attrs (sm_ctxt, node, stmt, call, callee_fndecl,\r
+ "fd_arg_write", DIRS_WRITE);\r
+ } \r
}\r
\r
return false;\r
}\r
\r
void\r
+fd_state_machine::check_for_fd_attrs (\r
+ sm_context *sm_ctxt, const supernode *node, const gimple *stmt,\r
+ const gcall *call, const tree callee_fndecl, const char *attr_name,\r
+ access_directions fd_attr_access_dir) const\r
+{\r
+\r
+ tree attrs = TYPE_ATTRIBUTES (TREE_TYPE (callee_fndecl));\r
+ attrs = lookup_attribute (attr_name, attrs);\r
+ if (!attrs)\r
+ return;\r
+\r
+ if (!TREE_VALUE (attrs))\r
+ return;\r
+\r
+ auto_bitmap argmap;\r
+\r
+ for (tree idx = TREE_VALUE (attrs); idx; idx = TREE_CHAIN (idx))\r
+ {\r
+ unsigned int val = TREE_INT_CST_LOW (TREE_VALUE (idx)) - 1;\r
+ bitmap_set_bit (argmap, val);\r
+ }\r
+ if (bitmap_empty_p (argmap))\r
+ return;\r
+\r
+ for (unsigned arg_idx = 0; arg_idx < gimple_call_num_args (call); arg_idx++)\r
+ {\r
+ tree arg = gimple_call_arg (call, arg_idx);\r
+ tree diag_arg = sm_ctxt->get_diagnostic_tree (arg);\r
+ state_t state = sm_ctxt->get_state (stmt, arg);\r
+ bool bit_set = bitmap_bit_p (argmap, arg_idx);\r
+ if (TREE_CODE (TREE_TYPE (arg)) != INTEGER_TYPE)\r
+ continue;\r
+ if (bit_set) // Check if arg_idx is marked by any of the file descriptor\r
+ // attributes\r
+ {\r
+\r
+ if (is_closed_fd_p (state))\r
+ {\r
+\r
+ sm_ctxt->warn (node, stmt, arg,\r
+ new fd_use_after_close (*this, diag_arg,\r
+ callee_fndecl, attr_name,\r
+ arg_idx));\r
+ continue;\r
+ }\r
+\r
+ if (!(is_valid_fd_p (state) || (state == m_stop)))\r
+ {\r
+ if (!is_constant_fd_p (state))\r
+ sm_ctxt->warn (node, stmt, arg,\r
+ new fd_use_without_check (*this, diag_arg,\r
+ callee_fndecl, attr_name,\r
+ arg_idx));\r
+ }\r
+\r
+ switch (fd_attr_access_dir)\r
+ {\r
+ case DIRS_READ_WRITE:\r
+ break;\r
+ case DIRS_READ:\r
+\r
+ if (is_writeonly_fd_p (state))\r
+ {\r
+ sm_ctxt->warn (\r
+ node, stmt, arg,\r
+ new fd_access_mode_mismatch (*this, diag_arg, DIRS_WRITE,\r
+ callee_fndecl, attr_name, arg_idx));\r
+ }\r
+\r
+ break;\r
+ case DIRS_WRITE:\r
+\r
+ if (is_readonly_fd_p (state))\r
+ {\r
+ sm_ctxt->warn (\r
+ node, stmt, arg,\r
+ new fd_access_mode_mismatch (*this, diag_arg, DIRS_READ,\r
+ callee_fndecl, attr_name, arg_idx));\r
+ }\r
+\r
+ break;\r
+ }\r
+ }\r
+ }\r
+}\r
+\r
+\r
+void\r
fd_state_machine::on_open (sm_context *sm_ctxt, const supernode *node,\r
const gimple *stmt, const gcall *call) const\r
{\r
\r
if (is_closed_fd_p (state))\r
{\r
- sm_ctxt->warn (node, stmt, arg, new double_close (*this, diag_arg));\r
+ sm_ctxt->warn (node, stmt, arg, new fd_double_close (*this, diag_arg));\r
sm_ctxt->set_next_state (stmt, arg, m_stop);\r
}\r
}\r
const gimple *stmt, const gcall *call,\r
const tree callee_fndecl) const\r
{\r
- check_for_open_fd (sm_ctxt, node, stmt, call, callee_fndecl, DIR_READ);\r
+ check_for_open_fd (sm_ctxt, node, stmt, call, callee_fndecl, DIRS_READ);\r
}\r
void\r
fd_state_machine::on_write (sm_context *sm_ctxt, const supernode *node,\r
const gimple *stmt, const gcall *call,\r
const tree callee_fndecl) const\r
{\r
- check_for_open_fd (sm_ctxt, node, stmt, call, callee_fndecl, DIR_WRITE);\r
+ check_for_open_fd (sm_ctxt, node, stmt, call, callee_fndecl, DIRS_WRITE);\r
}\r
\r
void\r
fd_state_machine::check_for_open_fd (\r
sm_context *sm_ctxt, const supernode *node, const gimple *stmt,\r
const gcall *call, const tree callee_fndecl,\r
- enum access_direction callee_fndecl_dir) const\r
+ enum access_directions callee_fndecl_dir) const\r
{\r
tree arg = gimple_call_arg (call, 0);\r
tree diag_arg = sm_ctxt->get_diagnostic_tree (arg);\r
if (!is_constant_fd_p (state))\r
sm_ctxt->warn (\r
node, stmt, arg,\r
- new unchecked_use_of_fd (*this, diag_arg, callee_fndecl));\r
+ new fd_use_without_check (*this, diag_arg, callee_fndecl));\r
}\r
switch (callee_fndecl_dir)\r
{\r
- case DIR_READ:\r
+ case DIRS_READ:\r
if (is_writeonly_fd_p (state))\r
{\r
tree diag_arg = sm_ctxt->get_diagnostic_tree (arg);\r
sm_ctxt->warn (node, stmt, arg,\r
new fd_access_mode_mismatch (\r
- *this, diag_arg, DIR_WRITE, callee_fndecl));\r
+ *this, diag_arg, DIRS_WRITE, callee_fndecl));\r
}\r
\r
break;\r
- case DIR_WRITE:\r
+ case DIRS_WRITE:\r
\r
if (is_readonly_fd_p (state))\r
{\r
tree diag_arg = sm_ctxt->get_diagnostic_tree (arg);\r
sm_ctxt->warn (node, stmt, arg,\r
new fd_access_mode_mismatch (\r
- *this, diag_arg, DIR_READ, callee_fndecl));\r
+ *this, diag_arg, DIRS_READ, callee_fndecl));\r
}\r
break;\r
+ default:\r
+ gcc_unreachable ();\r
}\r
}\r
}\r