aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gcc/analyzer/sm-taint.cc42
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/torture/taint-read-index-2.c56
2 files changed, 98 insertions, 0 deletions
diff --git a/gcc/analyzer/sm-taint.cc b/gcc/analyzer/sm-taint.cc
index 4075cf6..2de9284 100644
--- a/gcc/analyzer/sm-taint.cc
+++ b/gcc/analyzer/sm-taint.cc
@@ -848,6 +848,48 @@ taint_state_machine::on_condition (sm_context *sm_ctxt,
case LE_EXPR:
case LT_EXPR:
{
+ /* Detect where build_range_check has optimized
+ (c>=low) && (c<=high)
+ into
+ (c-low>=0) && (c-low<=high-low)
+ and thus into:
+ (unsigned)(c - low) <= (unsigned)(high-low). */
+ if (const binop_svalue *binop_sval
+ = lhs->dyn_cast_binop_svalue ())
+ {
+ const svalue *inner_lhs = binop_sval->get_arg0 ();
+ enum tree_code inner_op = binop_sval->get_op ();
+ const svalue *inner_rhs = binop_sval->get_arg1 ();
+ if (const svalue *before_cast = inner_lhs->maybe_undo_cast ())
+ inner_lhs = before_cast;
+ if (tree outer_rhs_cst = rhs->maybe_get_constant ())
+ if (tree inner_rhs_cst = inner_rhs->maybe_get_constant ())
+ if (inner_op == PLUS_EXPR
+ && TREE_CODE (inner_rhs_cst) == INTEGER_CST
+ && TREE_CODE (outer_rhs_cst) == INTEGER_CST
+ && TYPE_UNSIGNED (TREE_TYPE (inner_rhs_cst))
+ && TYPE_UNSIGNED (TREE_TYPE (outer_rhs_cst)))
+ {
+ /* We have
+ (unsigned)(INNER_LHS + CST_A) </<= UNSIGNED_CST_B
+ and thus an optimized test of INNER_LHS (before any
+ cast to unsigned) against a range.
+ Transition any of the tainted states to the stop state.
+ We have to special-case this here rather than in
+ region_model::on_condition since we can't apply
+ both conditions simultaneously (we'd have a transition
+ from the old state to has_lb, then a transition from
+ the old state *again* to has_ub). */
+ state_t old_state
+ = sm_ctxt->get_state (stmt, inner_lhs);
+ if (old_state == m_tainted
+ || old_state == m_has_lb
+ || old_state == m_has_ub)
+ sm_ctxt->set_next_state (stmt, inner_lhs, m_stop);
+ return;
+ }
+ }
+
sm_ctxt->on_transition (node, stmt, lhs, m_tainted,
m_has_ub);
sm_ctxt->on_transition (node, stmt, lhs, m_has_lb,
diff --git a/gcc/testsuite/gcc.dg/analyzer/torture/taint-read-index-2.c b/gcc/testsuite/gcc.dg/analyzer/torture/taint-read-index-2.c
new file mode 100644
index 0000000..6a4ebdb
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/torture/taint-read-index-2.c
@@ -0,0 +1,56 @@
+// TODO: remove need for the taint option:
+/* { dg-additional-options "-fanalyzer-checker=taint" } */
+/* { dg-skip-if "" { *-*-* } { "-fno-fat-lto-objects" } { "" } } */
+
+#define LOWER_LIMIT 5
+#define UPPER_LIMIT 20
+
+static int arr[UPPER_LIMIT];
+
+static int
+called_by_test_1 (int iarg)
+{
+ return arr[iarg]; /* { dg-warning "without bounds checking" } */
+}
+
+int __attribute__((tainted_args))
+test_1 (unsigned long ularg)
+{
+ return called_by_test_1 (ularg);
+}
+
+static int
+called_by_test_2 (int iarg)
+{
+ if (iarg < LOWER_LIMIT || iarg > UPPER_LIMIT)
+ return 0;
+ return arr[iarg]; /* { dg-bogus "bounds checking" } */
+}
+
+int __attribute__((tainted_args))
+test_2 (unsigned long ularg)
+{
+ return called_by_test_2 (ularg);
+}
+
+int __attribute__((tainted_args))
+test_3 (int iarg)
+{
+ if (iarg < LOWER_LIMIT || iarg > UPPER_LIMIT)
+ return 0;
+ return arr[iarg]; /* { dg-bogus "bounds checking" } */
+}
+
+static int
+called_by_test_4 (int iarg)
+{
+ if (iarg < LOWER_LIMIT || iarg > UPPER_LIMIT)
+ return 0;
+ return arr[iarg]; /* { dg-bogus "bounds checking" } */
+}
+
+int __attribute__((tainted_args))
+test_4 (unsigned uarg)
+{
+ return called_by_test_4 (uarg);
+}