aboutsummaryrefslogtreecommitdiff
path: root/gcc/analyzer
diff options
context:
space:
mode:
authorDavid Malcolm <dmalcolm@redhat.com>2023-08-21 21:13:19 -0400
committerDavid Malcolm <dmalcolm@redhat.com>2023-08-21 21:13:19 -0400
commit3b691e0190c6e7291f8a52e1e14d8293a28ff4ce (patch)
treeeee77267219569549a3bdfabb6861abed4fd27f8 /gcc/analyzer
parent4325c82736d9e8a14b312fd1558e2788b69278cd (diff)
downloadgcc-3b691e0190c6e7291f8a52e1e14d8293a28ff4ce.zip
gcc-3b691e0190c6e7291f8a52e1e14d8293a28ff4ce.tar.gz
gcc-3b691e0190c6e7291f8a52e1e14d8293a28ff4ce.tar.bz2
analyzer: check format strings for null termination [PR105899]
This patch extends -fanalyzer to check the format strings of calls to functions marked with '__attribute__ ((format...))'. The only checking done in this patch is to check that the format string is a valid null-terminated string; this patch doesn't attempt to check the content of the format string. gcc/analyzer/ChangeLog: PR analyzer/105899 * call-details.cc (call_details::call_details): New ctor. * call-details.h (call_details::call_details): New ctor decl. (struct call_arg_details): Move here from region-model.cc. * region-model.cc (region_model::check_call_format_attr): New. (region_model::check_call_args): Call it. (struct call_arg_details): Move it to call-details.h. * region-model.h (region_model::check_call_format_attr): New decl. gcc/testsuite/ChangeLog: PR analyzer/105899 * gcc.dg/analyzer/attr-format-1.c: New test. * gcc.dg/analyzer/sprintf-1.c: Update expected results for now-passing tests. Signed-off-by: David Malcolm <dmalcolm@redhat.com>
Diffstat (limited to 'gcc/analyzer')
-rw-r--r--gcc/analyzer/call-details.cc10
-rw-r--r--gcc/analyzer/call-details.h30
-rw-r--r--gcc/analyzer/region-model.cc125
-rw-r--r--gcc/analyzer/region-model.h2
4 files changed, 137 insertions, 30 deletions
diff --git a/gcc/analyzer/call-details.cc b/gcc/analyzer/call-details.cc
index e497fc5..8f5b28c 100644
--- a/gcc/analyzer/call-details.cc
+++ b/gcc/analyzer/call-details.cc
@@ -58,6 +58,16 @@ call_details::call_details (const gcall *call, region_model *model,
}
}
+/* call_details's ctor: copy CD, but override the context,
+ using CTXT instead. */
+
+call_details::call_details (const call_details &cd,
+ region_model_context *ctxt)
+{
+ *this = cd;
+ m_ctxt = ctxt;
+}
+
/* Get the manager from m_model. */
region_model_manager *
diff --git a/gcc/analyzer/call-details.h b/gcc/analyzer/call-details.h
index 86f0e68..58b5ccd 100644
--- a/gcc/analyzer/call-details.h
+++ b/gcc/analyzer/call-details.h
@@ -30,6 +30,7 @@ class call_details
public:
call_details (const gcall *call, region_model *model,
region_model_context *ctxt);
+ call_details (const call_details &cd, region_model_context *ctxt);
region_model *get_model () const { return m_model; }
region_model_manager *get_manager () const;
@@ -83,6 +84,35 @@ private:
const region *m_lhs_region;
};
+/* A bundle of information about a problematic argument at a callsite
+ for use by pending_diagnostic subclasses for reporting and
+ for deduplication. */
+
+struct call_arg_details
+{
+public:
+ call_arg_details (const call_details &cd, unsigned arg_idx)
+ : m_call (cd.get_call_stmt ()),
+ m_called_fndecl (cd.get_fndecl_for_call ()),
+ m_arg_idx (arg_idx),
+ m_arg_expr (cd.get_arg_tree (arg_idx))
+ {
+ }
+
+ bool operator== (const call_arg_details &other) const
+ {
+ return (m_call == other.m_call
+ && m_called_fndecl == other.m_called_fndecl
+ && m_arg_idx == other.m_arg_idx
+ && pending_diagnostic::same_tree_p (m_arg_expr, other.m_arg_expr));
+ }
+
+ const gcall *m_call;
+ tree m_called_fndecl;
+ unsigned m_arg_idx; // 0-based
+ tree m_arg_expr;
+};
+
} // namespace ana
#endif /* GCC_ANALYZER_CALL_DETAILS_H */
diff --git a/gcc/analyzer/region-model.cc b/gcc/analyzer/region-model.cc
index 0fce188..99817ae 100644
--- a/gcc/analyzer/region-model.cc
+++ b/gcc/analyzer/region-model.cc
@@ -1271,14 +1271,108 @@ region_model::on_stmt_pre (const gimple *stmt,
}
}
+/* Given a call CD with function attribute FORMAT_ATTR, check that the
+ format arg to the call is a valid null-terminated string. */
+
+void
+region_model::check_call_format_attr (const call_details &cd,
+ tree format_attr) const
+{
+ /* We assume that FORMAT_ATTR has already been validated. */
+
+ /* arg0 of the attribute should be kind of format strings
+ that this function expects (e.g. "printf"). */
+ const tree arg0_tree_list = TREE_VALUE (format_attr);
+ if (!arg0_tree_list)
+ return;
+
+ /* arg1 of the attribute should be the 1-based parameter index
+ to treat as the format string. */
+ const tree arg1_tree_list = TREE_CHAIN (arg0_tree_list);
+ if (!arg1_tree_list)
+ return;
+ const tree arg1_value = TREE_VALUE (arg1_tree_list);
+ if (!arg1_value)
+ return;
+
+ unsigned format_arg_idx = TREE_INT_CST_LOW (arg1_value) - 1;
+ if (cd.num_args () <= format_arg_idx)
+ return;
+
+ /* Subclass of annotating_context that
+ adds a note about the format attr to any saved diagnostics. */
+ class annotating_ctxt : public annotating_context
+ {
+ public:
+ annotating_ctxt (const call_details &cd,
+ unsigned fmt_param_idx)
+ : annotating_context (cd.get_ctxt ()),
+ m_cd (cd),
+ m_fmt_param_idx (fmt_param_idx)
+ {
+ }
+ void add_annotations () final override
+ {
+ class reason_format_attr
+ : public pending_note_subclass<reason_format_attr>
+ {
+ public:
+ reason_format_attr (const call_arg_details &arg_details)
+ : m_arg_details (arg_details)
+ {
+ }
+
+ const char *get_kind () const final override
+ {
+ return "reason_format_attr";
+ }
+
+ void emit () const final override
+ {
+ inform (DECL_SOURCE_LOCATION (m_arg_details.m_called_fndecl),
+ "parameter %i of %qD marked as a format string"
+ " via %qs attribute",
+ m_arg_details.m_arg_idx + 1, m_arg_details.m_called_fndecl,
+ "format");
+ }
+
+ bool operator== (const reason_format_attr &other) const
+ {
+ return m_arg_details == other.m_arg_details;
+ }
+
+ private:
+ call_arg_details m_arg_details;
+ };
+
+ call_arg_details arg_details (m_cd, m_fmt_param_idx);
+ add_note (make_unique<reason_format_attr> (arg_details));
+ }
+ private:
+ const call_details &m_cd;
+ unsigned m_fmt_param_idx;
+ };
+
+ annotating_ctxt my_ctxt (cd, format_arg_idx);
+ call_details my_cd (cd, &my_ctxt);
+ my_cd.check_for_null_terminated_string_arg (format_arg_idx);
+}
+
/* Ensure that all arguments at the call described by CD are checked
- for poisoned values, by calling get_rvalue on each argument. */
+ for poisoned values, by calling get_rvalue on each argument.
+
+ Check that calls to functions with "format" attribute have valid
+ null-terminated strings for their format argument. */
void
region_model::check_call_args (const call_details &cd) const
{
for (unsigned arg_idx = 0; arg_idx < cd.num_args (); arg_idx++)
cd.get_arg_svalue (arg_idx);
+
+ /* Handle attribute "format". */
+ if (tree format_attr = cd.lookup_function_attribute ("format"))
+ check_call_format_attr (cd, format_attr);
}
/* Update this model for an outcome of a call that returns a specific
@@ -3175,35 +3269,6 @@ region_model::set_value (tree lhs, tree rhs, region_model_context *ctxt)
set_value (lhs_reg, rhs_sval, ctxt);
}
-/* A bundle of information about a problematic argument at a callsite
- for use by pending_diagnostic subclasses for reporting and
- for deduplication. */
-
-struct call_arg_details
-{
-public:
- call_arg_details (const call_details &cd, unsigned arg_idx)
- : m_call (cd.get_call_stmt ()),
- m_called_fndecl (cd.get_fndecl_for_call ()),
- m_arg_idx (arg_idx),
- m_arg_expr (cd.get_arg_tree (arg_idx))
- {
- }
-
- bool operator== (const call_arg_details &other) const
- {
- return (m_call == other.m_call
- && m_called_fndecl == other.m_called_fndecl
- && m_arg_idx == other.m_arg_idx
- && pending_diagnostic::same_tree_p (m_arg_expr, other.m_arg_expr));
- }
-
- const gcall *m_call;
- tree m_called_fndecl;
- unsigned m_arg_idx; // 0-based
- tree m_arg_expr;
-};
-
/* Issue a note specifying that a particular function parameter is expected
to be a valid null-terminated string. */
diff --git a/gcc/analyzer/region-model.h b/gcc/analyzer/region-model.h
index 63a67b3..3979bf1 100644
--- a/gcc/analyzer/region-model.h
+++ b/gcc/analyzer/region-model.h
@@ -597,6 +597,8 @@ private:
region_model_context *ctxt) const;
void check_call_args (const call_details &cd) const;
+ void check_call_format_attr (const call_details &cd,
+ tree format_attr) const;
void check_external_function_for_access_attr (const gcall *call,
tree callee_fndecl,
region_model_context *ctxt) const;