1 /* PR middle-end/91582 - missing heap overflow detection for strcpy
2    { dg-do compile }
3    { dg-options "-O2 -Wall -Wno-array-bounds -ftrack-macro-expansion=0" } */
4 
5 #include "range.h"
6 
7 #define INT_MAX     __INT_MAX__
8 #define INT_MIN     (-INT_MAX - 1)
9 
10 #define ATTR(...)   __attribute__ ((__VA_ARGS__))
11 #define NOIPA       ATTR (noipa)
12 
13 extern void* alloca (size_t);
14 extern void* calloc (size_t, size_t);
15 extern void* malloc (size_t);
16 
17 extern ATTR (alloc_size (1), malloc) char* alloc1 (size_t);
18 extern ATTR (alloc_size (1, 2), malloc) char* alloc2 (size_t, size_t);
19 
20 extern char* strcpy (char*, const char*);
21 
22 void sink (void*, ...);
23 
24 
25 /* Verify warning in stores to an object of variable size N in a known
26    range, at an offset (N + I) with a constant I.  */
27 
same_size_and_offset_idx_cst(void)28 void same_size_and_offset_idx_cst (void)
29 {
30 #define T(size, off, idx) do {			\
31     size_t n_ = size;				\
32     ptrdiff_t i_ = idx;				\
33     char *p_ = alloc1 (n_);			\
34     p_ += off;					\
35     p_[i_] = 0;					\
36     sink (p_);					\
37   } while (0)
38 
39   {
40     const size_t n = UR (2, 3);
41 
42     T (n, n, -4);   // { dg-warning "writing 1 byte into a region of size 0" }
43                     // { dg-message "at offset \\\[-2, -1] to an object with size between 2 and 3 allocated by 'alloc1'" "note" { target *-*-* } .-1 }
44     T (n, n, -3);
45     T (n, n, -2);
46     T (n, n, -1);
47     T (n, n,  0);
48     T (n, n,  1);   // { dg-warning "writing 1 byte into a region of size 0" }
49                     // { dg-message "at offset \\\[3, 4] to an object with size between 2 and 3 allocated by 'alloc1'" "note" { target *-*-* } .-1 }
50   }
51 
52   {
53     const size_t n = UR (3, 4);
54 
55     T (n, n, -5);   // { dg-warning "writing 1 byte into a region of size 0" }
56                     // { dg-message "at offset \\\[-2, -1] to an object with size between 3 and 4 allocated by 'alloc1'" "note" { target *-*-* } .-1 }
57     T (n, n, -4);
58     T (n, n, -3);
59     T (n, n, -2);
60     T (n, n, -1);
61     T (n, n,  0);
62     T (n, n,  1);   // { dg-warning "writing 1 byte into a region of size 0" }
63                     // { dg-message "at offset \\\[4, 5] to an object with size between 3 and 4 allocated by 'alloc1'" "note" { target *-*-* } .-1 }
64   }
65 
66   {
67     const size_t n = UR (5, SIZE_MAX - 2);
68     T (n, n, -1);
69     T (n, n, -1);
70     T (n, n, -1);
71     T (n, n, -1);
72   }
73 }
74 
75 
76 /* Verify warning in stores to an object of variable size N in a known
77    range, at an offset (M + I) with a variable M in some range and
78    constant I.  */
79 
different_size_and_offset_idx_cst(void)80 void different_size_and_offset_idx_cst (void)
81 {
82   {
83     const size_t n = UR (2, 3);
84     const size_t i = UR (1, 2);
85 
86     T (n, i, -4);   // { dg-warning "writing 1 byte into a region of size 0" }
87                     // { dg-message "at offset \\\[-3, -2] to an object with size between 2 and 3 allocated by 'alloc1'" "note" { target *-*-* } .-1 }
88     T (n, i, -3);   // { dg-warning "writing 1 byte into a region of size 0" }
89                     // { dg-message "at offset \\\[-2, -1] to an object with size between 2 and 3 allocated by 'alloc1'" "note" { target *-*-* } .-1 }
90     T (n, i, -2);
91     T (n, i, -1);
92     T (n, i,  0);
93     T (n, i,  1);
94     T (n, i,  2);   // { dg-warning "writing 1 byte into a region of size 0" }
95                     // { dg-message "at offset \\\[3, 4] to an object with size between 2 and 3 allocated by 'alloc1'" "note" { target *-*-* } .-1 }
96   }
97 
98   {
99     const size_t n = UR (3, 4);
100     const size_t i = UR (2, 5);
101 
102     T (n, i, -6);   // { dg-warning "writing 1 byte into a region of size 0" }
103                     // { dg-message "at offset \\\[-4, -1] to an object with size between 3 and 4 allocated by 'alloc1'" "note" { target *-*-* } .-1 }
104 
105     /* The offsets -5 and -4 are both necessarily invalid even if the sum
106        (i - 5) and (i - 4) are (or could be) in bounds because they imply
107        that the intermediate offset (p + i) is out of bounds.  */
108     T (n, i, -5);   // { dg-warning "" "intermediate offset" { xfail *-*-* } }
109     T (n, i, -4);   // { dg-warning "" "intermediate offset" { xfail *-*-* } }
110     T (n, i, -3);
111     T (n, i, -2);
112     T (n, i, -1);
113     T (n, i,  0);
114     T (n, i,  1);
115     T (n, i,  2);   // { dg-warning "writing 1 byte into a region of size 0" }
116                     // { dg-message "at offset \\\[4, 7] to an object with size between 3 and 4 allocated by 'alloc1'" "note" { target *-*-* } .-1 }
117   }
118 }
119 
120 
121 /* Verify warning in stores to an object of variable size N in a known
122    range, at an offset (M + I) with a variable M in some range and
123    constant I.  */
different_size_and_offset_idx_var(void)124 void different_size_and_offset_idx_var (void)
125 {
126   {
127     const size_t n = UR (3, 4);
128     const size_t i = UR (1, 2);
129 
130     T (n, i, SR (DIFF_MIN, 0));
131     T (n, i, SR (      -3, 0));
132     T (n, i, SR (      -1, 0));
133     T (n, i, SR (       0, 1));
134     T (n, i, SR (       1, 2));
135     T (n, i, SR (       2, 3));
136     /* The warning is issued below but the offset and the size in
137        the note are wrong.  See the FIXME in compute_objsize().  */
138     T (n, i, SR (       3, 4));    // { dg-warning "\\\[-Wstringop-overflow" }
139                                    // { dg-message "at offset 4 to an object with size between 3 and 4 allocated by 'alloc1'" "pr92940 note: offset addition" { xfail *-*-* } .-1 }
140                                    // { dg-message "at offset . to an object with size . allocated by 'alloc1'" "note: offset addition" { target *-*-* } .-2 }
141  }
142 }
143 
144 
ptr_add_2(int n,int i0,int i1)145 void ptr_add_2 (int n, int i0, int i1)
146 {
147   if (n < 1 || 2 < n) n = 2;
148 
149   if (i0 < 0 || 1 < i0) i0 = 0;
150   if (i1 < 1 || 2 < i1) i1 = 1;
151 
152   char *p = (char*)__builtin_malloc (n);
153   char *q = p;
154 
155   q += i0;
156   q[0] = 0;   // p[0]
157   q += i1;
158   q[0] = 1;   // p[1]
159   q[1] = 2;   // p[2]     // { dg-warning "\\\[-Wstringop-overflow" }
160 
161   sink (p, q);
162 }
163 
ptr_add_3(int n,int i0,int i1,int i2)164 void ptr_add_3 (int n, int i0, int i1, int i2)
165 {
166   if (n < 3 || 4 < n) n = 3;
167 
168   if (i0 < 0 || 1 < i0) i0 = 0;
169   if (i1 < 1 || 2 < i1) i1 = 1;
170   if (i2 < 2 || 3 < i2) i2 = 2;
171 
172   char *p = (char*)__builtin_malloc (n);
173   char *q = p;
174 
175   q += i0;
176   q[0] = 0;   // p[0]
177   q += i1;
178   q[0] = 1;   // p[1]
179   q[1] = 2;   // p[2]
180   q += i2;
181   q[0] = 3;   // p[3]
182   q[1] = 4;   // p[4]     // { dg-warning "\\\[-Wstringop-overflow" }
183 
184   sink (p, q);
185 }
186 
ptr_add_4(int n,int i0,int i1,int i2,int i3)187 void ptr_add_4 (int n, int i0, int i1, int i2, int i3)
188 {
189   if (n < 7 || 8 < n) n = 7;
190 
191   if (i0 < 0 || 1 < i0) i0 = 0;
192   if (i1 < 1 || 2 < i1) i1 = 1;
193   if (i2 < 2 || 3 < i2) i2 = 2;
194   if (i3 < 3 || 4 < i3) i3 = 3;
195 
196   char *p = (char*)__builtin_malloc (n);
197   char *q = p;
198 
199   q += i0;
200   q[0] = 0;   // p[0]
201   q += i1;
202   q[0] = 1;   // p[1]
203   q[1] = 2;   // p[2]
204   q += i2;
205   q[0] = 3;   // p[3]
206   q[1] = 4;   // p[4]
207   q[2] = 5;   // p[5]
208   q += i3;
209   q[0] = 6;   // p[6]
210   q[1] = 7;   // p[7]
211   q[2] = 8;   // p[8]     // { dg-warning "\\\[-Wstringop-overflow" }
212 
213   sink (p, q);
214 }
215 
ptr_sub_from_end(int n,int i0,int i1,int i2,int i3)216 void ptr_sub_from_end (int n, int i0, int i1, int i2, int i3)
217 {
218   if (n < 1 || 2 < n) n = 2;
219 
220   char *p = (char*)__builtin_malloc (n);
221   char *q = p;
222 
223   // The following isn't diagnosed due to a bug/limitation.
224   q += n;      //  N=1     N=2
225   q[-1] = 0;   // p[0]    p[1]
226   q[-2] = 1;   // p[-1]   p[0]
227   q[-3] = 2;   // p[-2]   p[-1]   // { dg-warning "\\\[-Wstringop-overflow" "pr92939: negative offset from end" { xfail *-*-* } }
228 
229   /* The following isn't diagnosed because the warning doesn't recognize
230      the index below as necessarily having the same value as the size
231      argument to malloc.  All it considers is the range.  */
232   q[0] = 2;                       // { dg-warning "\\\[-Wstringop-overflow" "pr92937: store just past the end" { xfail *-*-* } }
233   q[1] = 3;                       // { dg-warning "\\\[-Wstringop-overflow" }
234 
235   sink (p, q);
236 }
237