public inbox for gcc-cvs@sourceware.org
help / color / mirror / Atom feed
* [gcc r14-3741] analyzer: implement kf_strstr [PR105899]
@ 2023-09-06 13:33 David Malcolm
  0 siblings, 0 replies; only message in thread
From: David Malcolm @ 2023-09-06 13:33 UTC (permalink / raw)
  To: gcc-cvs

https://gcc.gnu.org/g:f2d7a4001a33884bc1dfd8da58e58dee18e3cd71

commit r14-3741-gf2d7a4001a33884bc1dfd8da58e58dee18e3cd71
Author: David Malcolm <dmalcolm@redhat.com>
Date:   Wed Sep 6 09:32:07 2023 -0400

    analyzer: implement kf_strstr [PR105899]
    
    gcc/analyzer/ChangeLog:
            PR analyzer/105899
            * kf.cc (class kf_strstr): New.
            (kf_strstr::impl_call_post): New.
            (register_known_functions): Register it.
    
    gcc/testsuite/ChangeLog:
            PR analyzer/105899
            * c-c++-common/analyzer/strstr-1.c: New test.
    
    Signed-off-by: David Malcolm <dmalcolm@redhat.com>

Diff:
---
 gcc/analyzer/kf.cc                             | 96 ++++++++++++++++++++++++++
 gcc/testsuite/c-c++-common/analyzer/strstr-1.c | 54 +++++++++++++++
 2 files changed, 150 insertions(+)

diff --git a/gcc/analyzer/kf.cc b/gcc/analyzer/kf.cc
index 8a45c329c282..92959891fe44 100644
--- a/gcc/analyzer/kf.cc
+++ b/gcc/analyzer/kf.cc
@@ -1585,6 +1585,100 @@ public:
   }
 };
 
+/* Handler for "strstr" and "__builtin_strstr".
+     extern char *strstr (const char* str, const char* substr);
+   See e.g. https://en.cppreference.com/w/c/string/byte/strstr  */
+
+class kf_strstr : public builtin_known_function
+{
+public:
+  bool matches_call_types_p (const call_details &cd) const final override
+  {
+    return (cd.num_args () == 2
+	    && cd.arg_is_pointer_p (0)
+	    && cd.arg_is_pointer_p (1));
+  }
+  enum built_in_function builtin_code () const final override
+  {
+    return BUILT_IN_STRSTR;
+  }
+  void impl_call_pre (const call_details &cd) const final override
+  {
+    cd.check_for_null_terminated_string_arg (0);
+    cd.check_for_null_terminated_string_arg (1);
+  }
+  void impl_call_post (const call_details &cd) const final override;
+};
+
+void
+kf_strstr::impl_call_post (const call_details &cd) const
+{
+  class strstr_call_info : public call_info
+  {
+  public:
+    strstr_call_info (const call_details &cd, bool found)
+    : call_info (cd), m_found (found)
+    {
+    }
+
+    label_text get_desc (bool can_colorize) const final override
+    {
+      if (m_found)
+	return make_label_text (can_colorize,
+				"when %qE returns non-NULL",
+				get_fndecl ());
+      else
+	return make_label_text (can_colorize,
+				"when %qE returns NULL",
+				get_fndecl ());
+    }
+
+    bool update_model (region_model *model,
+		       const exploded_edge *,
+		       region_model_context *ctxt) const final override
+    {
+      const call_details cd (get_call_details (model, ctxt));
+      if (tree lhs_type = cd.get_lhs_type ())
+	{
+	  region_model_manager *mgr = model->get_manager ();
+	  const svalue *result;
+	  if (m_found)
+	    {
+	      const svalue *str_sval = cd.get_arg_svalue (0);
+	      const region *str_reg
+		= model->deref_rvalue (str_sval, cd.get_arg_tree (0),
+				       cd.get_ctxt ());
+	      /* We want str_sval + OFFSET for some unknown OFFSET.
+		 Use a conjured_svalue to represent the offset,
+		 using the str_reg as the id of the conjured_svalue.  */
+	      const svalue *offset
+		= mgr->get_or_create_conjured_svalue (size_type_node,
+						      cd.get_call_stmt (),
+						      str_reg,
+						      conjured_purge (model,
+								      ctxt));
+	      result = mgr->get_or_create_binop (lhs_type, POINTER_PLUS_EXPR,
+						 str_sval, offset);
+	    }
+	  else
+	    result = mgr->get_or_create_int_cst (lhs_type, 0);
+	  cd.maybe_set_lhs (result);
+	}
+      return true;
+    }
+  private:
+    bool m_found;
+  };
+
+  /* Body of kf_strstr::impl_call_post.  */
+  if (cd.get_ctxt ())
+    {
+      cd.get_ctxt ()->bifurcate (make_unique<strstr_call_info> (cd, false));
+      cd.get_ctxt ()->bifurcate (make_unique<strstr_call_info> (cd, true));
+      cd.get_ctxt ()->terminate_path ();
+    }
+}
+
 class kf_ubsan_bounds : public internal_known_function
 {
   /* Empty.  */
@@ -1806,6 +1900,8 @@ register_known_functions (known_function_manager &kfm)
     kfm.add ("__builtin_strndup", make_unique<kf_strndup> ());
     kfm.add ("strlen", make_unique<kf_strlen> ());
     kfm.add ("__builtin_strlen", make_unique<kf_strlen> ());
+    kfm.add ("strstr", make_unique<kf_strstr> ());
+    kfm.add ("__builtin_strstr", make_unique<kf_strstr> ());
 
     register_atomic_builtins (kfm);
     register_varargs_builtins (kfm);
diff --git a/gcc/testsuite/c-c++-common/analyzer/strstr-1.c b/gcc/testsuite/c-c++-common/analyzer/strstr-1.c
new file mode 100644
index 000000000000..469e6a817d0d
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/analyzer/strstr-1.c
@@ -0,0 +1,54 @@
+/* See e.g. https://en.cppreference.com/w/c/string/byte/strstr  */
+
+/* { dg-additional-options "-fpermissive" { target c++ } } */
+
+#include "../../gcc.dg/analyzer/analyzer-decls.h"
+
+extern char *strstr (const char* str, const char* substr);
+
+char *
+test_passthrough (const char* str, const char* substr)
+{
+  return strstr (str, substr);
+}
+
+char *
+test_NULL_str (const char *substr)
+{
+  return strstr (NULL, substr); /* { dg-warning "use of NULL where non-null expected" } */
+}
+
+char *
+test_unterminated_str (const char *substr)
+{
+  char str[3] = "abc"; /* { dg-warning "initializer-string for '\[^\n\]*' is too long" "" { target c++ } } */
+  return strstr (str, substr); /* { dg-warning "stack-based buffer over-read" } */
+  /* { dg-message "while looking for null terminator for argument 1" "note" { target *-*-* } .-1 } */
+}
+
+char *
+test_uninitialized_str (const char *substr)
+{
+  char str[16];
+  return strstr (str, substr); /* { dg-warning "use of uninitialized value 'str\\\[0\\\]'" } */
+}
+
+char *
+test_NULL_substr (const char *str)
+{
+  return strstr (str, NULL); /* { dg-warning "use of NULL where non-null expected" } */
+}
+
+char *
+test_unterminated_substr (const char *str)
+{
+  char substr[3] = "abc"; /* { dg-warning "initializer-string for '\[^\n\]*' is too long" "" { target c++ } } */
+  return strstr (str, substr); /* { dg-warning "stack-based buffer over-read" } */
+  /* { dg-message "while looking for null terminator for argument 2" "note" { target *-*-* } .-1 } */
+}
+
+char *test_uninitialized_substr (const char *str)
+{
+  char substr[16];
+  return strstr (str, substr); /* { dg-warning "use of uninitialized value 'substr\\\[0\\\]'" } */
+}

^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2023-09-06 13:33 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-09-06 13:33 [gcc r14-3741] analyzer: implement kf_strstr [PR105899] David Malcolm

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).