1  // TODO: remove need for this option:
       2  /* { dg-additional-options "-fanalyzer-checker=taint" } */
       3  
       4  #include <stdio.h>
       5  #include <stdlib.h>
       6  #include <string.h>
       7  
       8  struct foo
       9  {
      10    signed int i;
      11    char buf[256];
      12  };
      13  
      14  char test_1(FILE *f)
      15  {
      16    struct foo tmp;
      17  
      18    if (1 == fread(&tmp, sizeof(tmp), 1, f)) { /* { dg-message "\\(\[0-9\]+\\) 'tmp' gets an unchecked value here" "event: tmp gets unchecked value" { xfail *-*-* } } */
      19                                               /* { dg-message "\\(\[0-9\]+\\) following 'true' branch\\.\\.\\." "event: following true branch" { target *-*-* } .-1 } */
      20      /* BUG: the following array lookup trusts that the input data's index is
      21         in the range 0 <= i < 256; otherwise it's accessing the stack */
      22      return tmp.buf[tmp.i]; // { dg-warning "use of attacker-controlled value 'tmp.i' in array lookup without bounds checking" "warning" } */
      23      /* { dg-message "23: \\(\[0-9\]+\\) \\.\\.\\.to here" "event: to here" { target *-*-* } .-1 } */
      24      /* { dg-message "23: \\(\[0-9\]+\\) 'tmp.i' has an unchecked value here \\(from 'tmp'\\)" "event: tmp.i has an unchecked value" { xfail *-*-* } .-2 } */
      25      /* { dg-message "\\(\[0-9\]+\\) use of attacker-controlled value 'tmp.i' in array lookup without bounds checking" "final event" { target *-*-* } .-3 } */
      26      
      27      // TOOD: better messages for state changes
      28    }
      29    return 0;
      30  }
      31  
      32  char test_2(struct foo *f, int i)
      33  {
      34    /* not a bug: the data is not known to be tainted: */
      35    return f->buf[f->i];
      36  }
      37  
      38  char test_3(FILE *f)
      39  {
      40    struct foo tmp;
      41  
      42    if (1 == fread(&tmp, sizeof(tmp), 1, f)) {
      43      if (tmp.i >= 0 && tmp.i < 256) {
      44        /* not a bug: the access is guarded by upper and lower bounds: */
      45        return tmp.buf[tmp.i];
      46      }
      47    }
      48    return 0;
      49  }
      50  
      51  char test_4(FILE *f)
      52  {
      53    struct foo tmp;
      54  
      55    if (1 == fread(&tmp, sizeof(tmp), 1, f)) {
      56      if (tmp.i >= 0) { /* { dg-message "'tmp.i' has an unchecked value here \\(from 'tmp'\\)" "event: tmp.i has an unchecked value" { xfail *-*-* } } */
      57        /* { dg-message "'tmp.i' has its lower bound checked here" "event: lower bound checked" { xfail *-*-* } .-1 } */
      58        return tmp.buf[tmp.i]; /* { dg-warning "use of attacker-controlled value 'tmp.i' in array lookup without upper-bounds checking" "warning" } */
      59      }
      60    }
      61    return 0;
      62  }
      63  
      64  char test_5(FILE *f)
      65  {
      66    struct foo tmp;
      67  
      68    if (1 == fread(&tmp, sizeof(tmp), 1, f)) {
      69      if (tmp.i < 256) { /* { dg-message "'tmp.i' has an unchecked value here \\(from 'tmp'\\)" "event: tmp.i has an unchecked value" { xfail *-*-* } } */
      70        /* { dg-message "'tmp.i' has its upper bound checked here" "event: upper bound checked" { xfail *-*-* } .-1 } */
      71        return tmp.buf[tmp.i]; /* { dg-warning "use of attacker-controlled value 'tmp.i' in array lookup without checking for negative" "warning" } */
      72      }
      73    }
      74    return 0;
      75  }
      76  
      77  /* unsigned types have a natural lower bound of 0 */
      78  struct bar
      79  {
      80    unsigned int i;
      81    char buf[256];
      82  };
      83  
      84  char test_6(FILE *f)
      85  {
      86    struct bar tmp;
      87  
      88    if (1 == fread(&tmp, sizeof(tmp), 1, f)) {
      89      return tmp.buf[tmp.i]; /* { dg-warning "use of attacker-controlled value 'tmp.i' in array lookup without upper-bounds checking" } */
      90    }
      91    return 0;
      92  }
      93  
      94  char test_7(FILE *f)
      95  {
      96    struct bar tmp;
      97  
      98    if (1 == fread(&tmp, sizeof(tmp), 1, f)) {
      99      if (tmp.i >= 0) {
     100        return tmp.buf[tmp.i]; /* { dg-warning "use of attacker-controlled value 'tmp.i' in array lookup without upper-bounds checking" } */
     101      }
     102    }
     103    return 0;
     104  }
     105  
     106  char test_8(FILE *f)
     107  {
     108    struct bar tmp;
     109  
     110    if (1 == fread(&tmp, sizeof(tmp), 1, f)) {
     111      if (tmp.i < 256) {
     112        /* not a bug: has an upper bound, and an implicit lower bound: */
     113        return tmp.buf[tmp.i];
     114      }
     115    }
     116    return 0;
     117  }
     118  
     119  char test_9(FILE *f)
     120  {
     121    struct foo tmp;
     122  
     123    if (1 == fread(&tmp, sizeof(tmp), 1, f)) {
     124      if (tmp.i == 42) {
     125        /* not a bug: tmp.i compared against a specific value: */
     126        return tmp.buf[tmp.i]; /* { dg-bogus "attacker-controlled" "" { xfail *-*-* } } */
     127        // TODO: xfail
     128      }
     129    }
     130    return 0;
     131  }