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