111
|
1 /* Test exercising -Wrawmem-overflow and -Wstringop-overflow warnings. */
|
|
2 /* { dg-do compile } */
|
|
3 /* { dg-options "-O2 -Wstringop-overflow=1" } */
|
|
4
|
|
5 #define offsetof(type, mem) __builtin_offsetof (type, mem)
|
|
6
|
|
7 /* Return the number of bytes from member MEM of TYPE to the end
|
|
8 of object OBJ. */
|
|
9 #define offsetfrom(type, obj, mem) (sizeof (obj) - offsetof (type, mem))
|
|
10
|
|
11
|
|
12 typedef __SIZE_TYPE__ size_t;
|
|
13 extern void* memcpy (void*, const void*, size_t);
|
|
14 extern void* memset (void*, int, __SIZE_TYPE__);
|
|
15
|
|
16
|
|
17 struct A { char a, b; };
|
|
18 struct B { struct A a; char c, d; };
|
|
19
|
|
20 /* Function to call to "escape" pointers from tests below to prevent
|
|
21 GCC from assuming the values of the objects they point to stay
|
|
22 the unchanged. */
|
|
23 void escape (void*, ...);
|
|
24
|
|
25 /* Function to "generate" a random number each time it's called. Declared
|
|
26 (but not defined) and used to prevent GCC from making assumptions about
|
|
27 their values based on the variables uses in the tested expressions. */
|
|
28 size_t random_unsigned_value (void);
|
|
29
|
|
30 /* Return a random unsigned value between MIN and MAX. */
|
|
31
|
|
32 static inline size_t
|
|
33 range (size_t min, size_t max)
|
|
34 {
|
|
35 const size_t val = random_unsigned_value ();
|
|
36 return val < min || max < val ? min : val;
|
|
37 }
|
|
38
|
|
39 /* Verify that writing past the end of a local array is diagnosed. */
|
|
40
|
|
41 void test_memop_warn_local (const void *src)
|
|
42 {
|
|
43 size_t n;
|
|
44
|
|
45 n = range (8, 32);
|
|
46
|
|
47 struct A a[2];
|
|
48
|
|
49 memcpy (a, src, n); /* { dg-warning "writing between 8 and 32 bytes into a region of size 4 overflows the destination" } */
|
|
50 escape (a, src);
|
|
51
|
|
52 /* At -Wrawmem-overflow=1 the destination is considered to be
|
|
53 the whole array and its size is therefore sizeof a. */
|
|
54 memcpy (&a[0], src, n); /* { dg-warning "writing between 8 and 32 bytes into a region of size 4 overflows the destination" } */
|
|
55 escape (a, src);
|
|
56
|
|
57 /* Verify the same as above but by writing into the first mmeber
|
|
58 of the first element of the array. */
|
|
59 memcpy (&a[0].a, src, n); /* { dg-warning "writing between 8 and 32 bytes into a region of size 4 overflows the destination" } */
|
|
60 escape (a, src);
|
|
61
|
|
62 n = range (12, 32);
|
|
63
|
|
64 struct B b[2];
|
|
65
|
|
66 memcpy (&b[0], src, n); /* { dg-warning "writing between 12 and 32 bytes into a region of size 8 overflows the destination" } */
|
|
67 escape (b);
|
|
68
|
|
69 /* The following idiom of clearing multiple members of a struct is
|
|
70 used in a few places in the Linux kernel. Verify that a warning
|
|
71 is issued for it when it writes past the end of the array object. */
|
|
72 memset (&b[0].a.b, 0, offsetfrom (struct B, b, a.b) + 1); /* { dg-warning "writing 8 bytes into a region of size 7" } */
|
|
73 escape (b);
|
|
74
|
|
75 memset (&b->a.b, 0, offsetfrom (struct B, b, a.b) + 1); /* { dg-warning "writing 8 bytes into a region of size 7" } */
|
|
76 escape (b);
|
|
77
|
|
78 memset (&b[0].c, 0, offsetfrom (struct B, b, c) + 1); /* { dg-warning "writing 7 bytes into a region of size 6" } */
|
|
79 escape (b);
|
|
80
|
|
81 memset (&b->c, 0, offsetfrom (struct B, b, c) + 1); /* { dg-warning "writing 7 bytes into a region of size 6" } */
|
|
82 escape (b);
|
|
83
|
|
84 memset (&b[0].d, 0, offsetfrom (struct B, b, d) + 1); /* { dg-warning "writing 6 bytes into a region of size 5" } */
|
|
85 escape (b);
|
|
86
|
|
87 memset (&b->d, 0, offsetfrom (struct B, b, d) + 1); /* { dg-warning "writing 6 bytes into a region of size 5" } */
|
|
88 escape (b);
|
|
89
|
|
90 /* Same as above but clearing just elements of the second element
|
|
91 of the array. */
|
|
92 memset (&b[1].a.b, 0, offsetfrom (struct B, b[1], a.b) + 1); /* { dg-warning "writing 4 bytes into a region of size 3" } */
|
|
93 escape (b);
|
|
94
|
|
95 memset (&b[1].c, 0, offsetfrom (struct B, b[1], c) + 1); /* { dg-warning "writing 3 bytes into a region of size 2" } */
|
|
96 escape (b);
|
|
97
|
|
98 memset (&b[1].d, 0, offsetfrom (struct B, b[1], d) + 1); /* { dg-warning "writing 2 bytes into a region of size 1" } */
|
|
99 escape (b);
|
|
100 }
|
|
101
|
|
102 /* Verify that writing past the end of a dynamically allocated array
|
|
103 of known size is diagnosed. */
|
|
104
|
|
105 void test_memop_warn_alloc (const void *src)
|
|
106 {
|
|
107 size_t n;
|
|
108
|
|
109 n = range (8, 32);
|
|
110
|
|
111 struct A *a = __builtin_malloc (sizeof *a * 2);
|
|
112
|
|
113 memcpy (a, src, n); /* { dg-warning "writing between 8 and 32 bytes into a region of size 4 overflows the destination" "memcpy into allocated" { xfail *-*-*} } */
|
|
114 escape (a, src);
|
|
115
|
|
116 /* At -Wrawmem-overflow=1 the destination is considered to be
|
|
117 the whole array and its size is therefore sizeof a. */
|
|
118 memcpy (&a[0], src, n); /* { dg-warning "writing between 8 and 32 bytes into a region of size 4 overflows the destination" "memcpy into allocated" { xfail *-*-*} } */
|
|
119 escape (a, src);
|
|
120
|
|
121 /* Verify the same as above but by writing into the first mmeber
|
|
122 of the first element of the array. */
|
|
123 memcpy (&a[0].a, src, n); /* { dg-warning "writing between 8 and 32 bytes into a region of size 4 overflows the destination" "memcpy into allocated" { xfail *-*-*} } */
|
|
124 escape (a, src);
|
|
125
|
|
126 n = range (12, 32);
|
|
127
|
|
128 struct B *b = __builtin_malloc (sizeof *b * 2);
|
|
129
|
|
130 memcpy (&b[0], src, n); /* { dg-warning "writing between 12 and 32 bytes into a region of size 8 overflows the destination" "memcpy into allocated" { xfail *-*-*} } */
|
|
131 escape (b);
|
|
132
|
|
133 /* The following idiom of clearing multiple members of a struct is
|
|
134 used in a few places in the Linux kernel. Verify that a warning
|
|
135 is issued for it when it writes past the end of the array object. */
|
|
136 memset (&b[0].a.b, 0, offsetfrom (struct B, b, a.b) + 1); /* { dg-warning "writing 8 bytes into a region of size 7" "memcpy into allocated" { xfail *-*-*} } */
|
|
137 escape (b);
|
|
138
|
|
139 memset (&b->a.b, 0, offsetfrom (struct B, b, a.b) + 1); /* { dg-warning "writing 8 bytes into a region of size 7" "memcpy into allocated" { xfail *-*-*} } */
|
|
140 escape (b);
|
|
141
|
|
142 memset (&b[0].c, 0, offsetfrom (struct B, b, c) + 1); /* { dg-warning "writing 7 bytes into a region of size 6" "memcpy into allocated" { xfail *-*-*} } */
|
|
143 escape (b);
|
|
144
|
|
145 memset (&b->c, 0, offsetfrom (struct B, b, c) + 1); /* { dg-warning "writing 7 bytes into a region of size 6" "memcpy into allocated" { xfail *-*-*} } */
|
|
146 escape (b);
|
|
147
|
|
148 memset (&b[0].d, 0, offsetfrom (struct B, b, d) + 1); /* { dg-warning "writing 6 bytes into a region of size 5" "memcpy into allocated" { xfail *-*-*} } */
|
|
149 escape (b);
|
|
150
|
|
151 memset (&b->d, 0, offsetfrom (struct B, b, d) + 1); /* { dg-warning "writing 6 bytes into a region of size 5" "memcpy into allocated" { xfail *-*-*} } */
|
|
152 escape (b);
|
|
153
|
|
154 /* Same as above but clearing just elements of the second element
|
|
155 of the array. */
|
|
156 memset (&b[1].a.b, 0, offsetfrom (struct B, b[1], a.b) + 1); /* { dg-warning "writing 4 bytes into a region of size 3" "memcpy into allocated" { xfail *-*-*} } */
|
|
157 escape (b);
|
|
158
|
|
159 memset (&b[1].c, 0, offsetfrom (struct B, b[1], c) + 1); /* { dg-warning "writing 3 bytes into a region of size 2" "memcpy into allocated" { xfail *-*-*} } */
|
|
160 escape (b);
|
|
161
|
|
162 memset (&b[1].d, 0, offsetfrom (struct B, b[1], d) + 1); /* { dg-warning "writing 2 bytes into a region of size 1" "memcpy into allocated" { xfail *-*-*} } */
|
|
163 escape (b);
|
|
164 }
|
|
165
|
|
166
|
|
167 void test_memop_nowarn (const void *src)
|
|
168 {
|
|
169 struct B b[2];
|
|
170
|
|
171 size_t n = range (sizeof b, 32);
|
|
172
|
|
173 /* Verify that clearing the whole array is not diagnosed regardless
|
|
174 of whether the expression pointing to its beginning is obtained
|
|
175 from the array itself or its first member(s). */
|
|
176 memcpy (b, src, n);
|
|
177 escape (b);
|
|
178
|
|
179 memcpy (&b[0], src, n);
|
|
180 escape (b);
|
|
181
|
|
182 memcpy (&b[0].a, src, n);
|
|
183 escape (b, src);
|
|
184
|
|
185 memcpy (&b[0].a.a, src, n);
|
|
186 escape (b, src);
|
|
187
|
|
188 /* Clearing multiple elements of an array of structs. */
|
|
189 memset (&b[0].a.b, 0, sizeof b - offsetof (struct B, a.b));
|
|
190 escape (b);
|
|
191
|
|
192 memset (&b->a.b, 0, sizeof b - offsetof (struct B, a.b));
|
|
193 escape (b);
|
|
194
|
|
195 memset (&b[0].c, 0, sizeof b - offsetof (struct B, c));
|
|
196 escape (b);
|
|
197
|
|
198 memset (&b->c, 0, sizeof b - offsetof (struct B, c));
|
|
199 escape (b);
|
|
200
|
|
201 memset (&b[0].d, 0, sizeof b - offsetof (struct B, d));
|
|
202 escape (b);
|
|
203
|
|
204 memset (&b->d, 0, sizeof b - offsetof (struct B, d));
|
|
205 escape (b);
|
|
206
|
|
207 /* Same as above but clearing just elements of the second element
|
|
208 of the array. */
|
|
209 memset (&b[1].a.b, 0, sizeof b[1] - offsetof (struct B, a.b));
|
|
210 escape (b);
|
|
211
|
|
212 memset (&b[1].c, 0, sizeof b[1] - offsetof (struct B, c));
|
|
213 escape (b);
|
|
214
|
|
215 memset (&b[1].d, 0, sizeof b[1] - offsetof (struct B, d));
|
|
216 escape (b);
|
|
217 }
|
|
218
|
|
219
|
|
220 /* The foollowing function could specify in its API that it takes
|
|
221 an array of exactly two elements, as shown below. Verify that
|
|
222 writing into both elements is not diagnosed. */
|
|
223 void test_memop_nowarn_arg (struct A[2], const void*);
|
|
224
|
|
225 void test_memop_nowarn_arg (struct A *a, const void *src)
|
|
226 {
|
|
227 memcpy (a, src, 2 * sizeof *a);
|
|
228 escape (a, src);
|
|
229
|
|
230 memcpy (a, src, range (2 * sizeof *a, 123));
|
|
231 escape (a, src);
|
|
232 }
|
|
233
|
|
234
|
|
235 struct C { char a[3], b; };
|
|
236 struct D { struct C c; char d, e; };
|
|
237
|
|
238 extern char* strncpy (char*, const char*, __SIZE_TYPE__);
|
|
239
|
|
240 void test_stringop_warn (void)
|
|
241 {
|
|
242 size_t n = range (2 * sizeof (struct D) + 1, 33);
|
|
243
|
|
244 struct C c[2];
|
|
245
|
|
246 /* Similarly, at -Wstringop-overflow=1 the destination is considered
|
|
247 to be the whole array and its size is therefore sizeof c. */
|
|
248 strncpy (c[0].a, "123", n); /* { dg-warning "writing between 13 and 33 bytes into a region of size 8 overflows the destination" } */
|
|
249
|
|
250 escape (c);
|
|
251 }
|
|
252
|
|
253
|
|
254 void test_stringop_nowarn (void)
|
|
255 {
|
|
256 struct D d[2];
|
|
257
|
|
258 strncpy (d[0].c.a, "123", range (sizeof d, 32));
|
|
259 escape (d);
|
|
260 }
|