public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [pushed] analyzer: fix false +ves from -Wanalyzer-tainted-array-index with unsigned char index [PR106229]
@ 2024-01-16  0:05 David Malcolm
  0 siblings, 0 replies; only message in thread
From: David Malcolm @ 2024-01-16  0:05 UTC (permalink / raw)
  To: gcc-patches; +Cc: David Malcolm

Successfully bootstrapped & regrtested on x86_64-pc-linux-gnu.
Successful run of analyzer integration tests on x86_64-pc-linux-gnu.
Pushed to trunk as r14-7266-gce27b66d952127.

gcc/analyzer/ChangeLog:
	PR analyzer/106229
	* analyzer.h (compare_constants): New decl.
	* constraint-manager.cc (compare_constants): Make non-static.
	* sm-taint.cc: Add include "fold-const.h".
	(class concrete_range): New.
	(get_possible_range): New.
	(index_can_be_out_of_bounds_p): New.
	(region_model::check_region_for_taint): Reject
	-Wanalyzer-tainted-array-index if the type of the value makes it
	impossible for it to be out-of-bounds of the array.

gcc/testsuite/ChangeLog:
	PR analyzer/106229
	* c-c++-common/analyzer/taint-index-pr106229.c: New test.

Signed-off-by: David Malcolm <dmalcolm@redhat.com>
---
 gcc/analyzer/analyzer.h                       |   3 +
 gcc/analyzer/constraint-manager.cc            |   2 +-
 gcc/analyzer/sm-taint.cc                      | 114 +++++++++++++++++-
 .../analyzer/taint-index-pr106229.c           | 109 +++++++++++++++++
 4 files changed, 223 insertions(+), 5 deletions(-)
 create mode 100644 gcc/testsuite/c-c++-common/analyzer/taint-index-pr106229.c

diff --git a/gcc/analyzer/analyzer.h b/gcc/analyzer/analyzer.h
index 8dec9649f2fb..23e3f71df0af 100644
--- a/gcc/analyzer/analyzer.h
+++ b/gcc/analyzer/analyzer.h
@@ -427,6 +427,9 @@ bit_offset_to_json (const bit_offset_t &offset);
 extern json::value *
 byte_offset_to_json (const byte_offset_t &offset);
 
+extern tristate
+compare_constants (tree lhs_const, enum tree_code op, tree rhs_const);
+
 } // namespace ana
 
 extern bool is_special_named_call_p (const gcall *call, const char *funcname,
diff --git a/gcc/analyzer/constraint-manager.cc b/gcc/analyzer/constraint-manager.cc
index 2db6c1734638..e8bcabeb0cd5 100644
--- a/gcc/analyzer/constraint-manager.cc
+++ b/gcc/analyzer/constraint-manager.cc
@@ -54,7 +54,7 @@ along with GCC; see the file COPYING3.  If not see
 
 namespace ana {
 
-static tristate
+tristate
 compare_constants (tree lhs_const, enum tree_code op, tree rhs_const)
 {
   tree comparison
diff --git a/gcc/analyzer/sm-taint.cc b/gcc/analyzer/sm-taint.cc
index 3f7e5cd55837..dc4b078c411f 100644
--- a/gcc/analyzer/sm-taint.cc
+++ b/gcc/analyzer/sm-taint.cc
@@ -40,6 +40,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "digraph.h"
 #include "stringpool.h"
 #include "attribs.h"
+#include "fold-const.h"
 #include "analyzer/supergraph.h"
 #include "analyzer/call-string.h"
 #include "analyzer/program-point.h"
@@ -1369,6 +1370,104 @@ make_taint_state_machine (logger *logger)
   return new taint_state_machine (logger);
 }
 
+/* A closed concrete range.  */
+
+class concrete_range
+{
+public:
+  /* Return true iff THIS is fully within OTHER
+     i.e.
+     - m_min must be >= OTHER.m_min
+     - m_max must be <= OTHER.m_max.  */
+  bool within_p (const concrete_range &other) const
+  {
+    if (compare_constants (m_min, GE_EXPR, other.m_min).is_true ())
+      if (compare_constants (m_max, LE_EXPR, other.m_max).is_true ())
+	return true;
+    return false;
+  }
+
+  tree m_min;
+  tree m_max;
+};
+
+/* Attempt to get a closed concrete range for SVAL based on types.
+   If found, write to *OUT and return true.
+   Otherwise return false.  */
+
+static bool
+get_possible_range (const svalue *sval, concrete_range *out)
+{
+  if (const svalue *inner = sval->maybe_undo_cast ())
+    {
+      concrete_range inner_range;
+      if (!get_possible_range (inner, &inner_range))
+	return false;
+
+      if (sval->get_type ()
+	  && inner->get_type ()
+	  && INTEGRAL_TYPE_P (sval->get_type ())
+	  && INTEGRAL_TYPE_P (inner->get_type ())
+	  && TYPE_UNSIGNED (inner->get_type ())
+	  && (TYPE_PRECISION (sval->get_type ())
+	      > TYPE_PRECISION (inner->get_type ())))
+	{
+	  /* We have a cast from an unsigned type to a wider integral type.
+	     Assuming this is zero-extension, we can inherit the range from
+	     the inner type.  */
+	  enum tree_code op = ((const unaryop_svalue *)sval)->get_op ();
+	  out->m_min = fold_unary (op, sval->get_type (), inner_range.m_min);
+	  out->m_max = fold_unary (op, sval->get_type (), inner_range.m_max);
+	  return true;
+	}
+    }
+
+  if (sval->get_type ()
+      && INTEGRAL_TYPE_P (sval->get_type ()))
+    {
+      out->m_min = TYPE_MIN_VALUE (sval->get_type ());
+      out->m_max = TYPE_MAX_VALUE (sval->get_type ());
+      return true;
+    }
+
+  return false;
+}
+
+/* Determine if it's possible for tainted array access ELEMENT_REG to
+   actually be a problem.
+
+   Check here for index being from e.g. unsigned char when the array
+   contains >= 255 elements.
+
+   Return true if out-of-bounds is possible, false if it's impossible
+   (for suppressing false positives).  */
+
+static bool
+index_can_be_out_of_bounds_p (const element_region *element_reg)
+{
+  const svalue *index = element_reg->get_index ();
+  const region *array_reg = element_reg->get_parent_region ();
+
+  if (array_reg->get_type ()
+      && TREE_CODE (array_reg->get_type ()) == ARRAY_TYPE
+      && TYPE_DOMAIN (array_reg->get_type ())
+      && INTEGRAL_TYPE_P (TYPE_DOMAIN (array_reg->get_type ())))
+    {
+      concrete_range valid_index_range;
+      valid_index_range.m_min
+	= TYPE_MIN_VALUE (TYPE_DOMAIN (array_reg->get_type ()));
+      valid_index_range.m_max
+	= TYPE_MAX_VALUE (TYPE_DOMAIN (array_reg->get_type ()));
+
+      concrete_range possible_index_range;
+      if (get_possible_range (index, &possible_index_range))
+	if (possible_index_range.within_p (valid_index_range))
+	  return false;
+    }
+
+  return true;
+}
+
 /* Complain to CTXT if accessing REG leads could lead to arbitrary
    memory access under an attacker's control (due to taint).  */
 
@@ -1415,10 +1514,17 @@ region_model::check_region_for_taint (const region *reg,
 	    gcc_assert (state);
 	    enum bounds b;
 	    if (taint_sm.get_taint (state, index->get_type (), &b))
-	    {
-	      tree arg = get_representative_tree (index);
-	      ctxt->warn (make_unique<tainted_array_index> (taint_sm, arg, b));
-	    }
+	      {
+		if (index_can_be_out_of_bounds_p (element_reg))
+		  {
+		    tree arg = get_representative_tree (index);
+		    ctxt->warn (make_unique<tainted_array_index> (taint_sm,
+								  arg, b));
+		  }
+		else if (ctxt->get_logger ())
+		  ctxt->get_logger ()->log ("rejecting tainted_array_index as"
+					    " out of bounds is not possible");
+	      }
 	  }
 	  break;
 
diff --git a/gcc/testsuite/c-c++-common/analyzer/taint-index-pr106229.c b/gcc/testsuite/c-c++-common/analyzer/taint-index-pr106229.c
new file mode 100644
index 000000000000..76dca630a038
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/analyzer/taint-index-pr106229.c
@@ -0,0 +1,109 @@
+#include <stdint.h>
+
+/* Attacker-controlled 8 bit values where the array isn't
+   necessarily big enough.  We should warn about these.  */
+
+struct st_s8_field_255_elements
+{
+  int8_t idx;
+  char buf[255];
+};
+
+char __attribute__((tainted_args))
+test_s8_field_255_elements (struct st_s8_field_255_elements s)
+{
+  return s.buf[s.idx]; /* { dg-warning "tainted-array-index" } */
+}
+
+struct st_u8_field_255_elements
+{
+  uint8_t idx;
+  char buf[255];
+};
+
+char __attribute__((tainted_args))
+test_u8_field_255_elements (struct st_u8_field_255_elements s)
+{
+  return s.buf[s.idx]; /* { dg-warning "tainted-array-index" } */
+}
+
+/* Attacker-controlled 8 bit values where the array is
+   big enough, but where the value might be signed.  */
+
+struct st_s8_field_256_elements
+{
+  int8_t idx;
+  char buf[256];
+};
+
+char __attribute__((tainted_args))
+test_s8_field_256_elements (struct st_s8_field_256_elements s)
+{
+  return s.buf[s.idx]; /* { dg-warning "tainted-array-index" } */
+}
+
+struct st_u8_field_256_elements
+{
+  uint8_t idx;
+  char buf[256];
+};
+
+char __attribute__((tainted_args))
+test_u8_field_256_elements (struct st_u8_field_256_elements s)
+{
+  return s.buf[s.idx]; /* { dg-bogus "tainted-array-index" } */
+}
+
+/* Attacker-controlled 16 bit values where the array isn't
+   necessarily big enough.  We should warn about these.  */
+
+struct st_s16_field_256_elements
+{
+  int16_t idx;
+  char buf[256];
+};
+
+char __attribute__((tainted_args))
+test_s16_field_256_elements (struct st_s16_field_256_elements s)
+{
+  return s.buf[s.idx]; /* { dg-warning "tainted-array-index" } */
+}
+
+struct st_u16_field_256_elements
+{
+  uint16_t idx;
+  char buf[256];
+};
+
+char __attribute__((tainted_args))
+test_u16_field_256_elements (struct st_u16_field_256_elements s)
+{
+  return s.buf[s.idx]; /* { dg-warning "tainted-array-index" } */
+}
+
+/* Attacker-controlled 16 bit values where the array is
+   big enough, but where the value might be signed.  */
+
+struct st_s16_field_65536_elements
+{
+  int16_t idx;
+  char buf[65536];
+};
+
+char __attribute__((tainted_args))
+test_s16_field_65536_elements (struct st_s16_field_65536_elements s)
+{
+  return s.buf[s.idx]; /* { dg-warning "tainted-array-index" } */
+}
+
+struct st_u16_field_65536_elements
+{
+  uint16_t idx;
+  char buf[65536];
+};
+
+char __attribute__((tainted_args))
+test_u16_field_65536_elements (struct st_u16_field_65536_elements s)
+{
+  return s.buf[s.idx]; /* { dg-bogus "tainted-array-index" } */
+}
-- 
2.26.3


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

only message in thread, other threads:[~2024-01-16  0:05 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2024-01-16  0:05 [pushed] analyzer: fix false +ves from -Wanalyzer-tainted-array-index with unsigned char index [PR106229] 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).