]>
Commit | Line | Data |
---|---|---|
62efd1c4 AH |
1 | /* Array bounds checking. |
2 | Copyright (C) 2005-2020 Free Software Foundation, Inc. | |
3 | ||
4 | This file is part of GCC. | |
5 | ||
6 | GCC is free software; you can redistribute it and/or modify | |
7 | it under the terms of the GNU General Public License as published by | |
8 | the Free Software Foundation; either version 3, or (at your option) | |
9 | any later version. | |
10 | ||
11 | GCC is distributed in the hope that it will be useful, | |
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | GNU General Public License for more details. | |
15 | ||
16 | You should have received a copy of the GNU General Public License | |
17 | along with GCC; see the file COPYING3. If not see | |
18 | <http://www.gnu.org/licenses/>. */ | |
19 | ||
20 | #include "config.h" | |
21 | #include "system.h" | |
22 | #include "coretypes.h" | |
23 | #include "backend.h" | |
24 | #include "tree.h" | |
25 | #include "gimple.h" | |
26 | #include "ssa.h" | |
27 | #include "gimple-array-bounds.h" | |
28 | #include "gimple-iterator.h" | |
29 | #include "gimple-walk.h" | |
30 | #include "tree-dfa.h" | |
31 | #include "fold-const.h" | |
32 | #include "diagnostic-core.h" | |
33 | #include "intl.h" | |
34 | #include "tree-vrp.h" | |
35 | #include "alloc-pool.h" | |
36 | #include "vr-values.h" | |
37 | #include "domwalk.h" | |
38 | #include "tree-cfg.h" | |
39 | ||
40 | // This purposely returns a value_range, not a value_range_equiv, to | |
41 | // break the dependency on equivalences for this pass. | |
42 | ||
43 | const value_range * | |
44 | array_bounds_checker::get_value_range (const_tree op) | |
45 | { | |
46 | return ranges->get_value_range (op); | |
47 | } | |
48 | ||
49 | /* Checks one ARRAY_REF in REF, located at LOCUS. Ignores flexible | |
50 | arrays and "struct" hacks. If VRP can determine that the array | |
51 | subscript is a constant, check if it is outside valid range. If | |
52 | the array subscript is a RANGE, warn if it is non-overlapping with | |
53 | valid range. IGNORE_OFF_BY_ONE is true if the ARRAY_REF is inside | |
54 | a ADDR_EXPR. Returns true if a warning has been issued. */ | |
55 | ||
56 | bool | |
57 | array_bounds_checker::check_array_ref (location_t location, tree ref, | |
58 | bool ignore_off_by_one) | |
59 | { | |
60 | if (TREE_NO_WARNING (ref)) | |
61 | return false; | |
62 | ||
63 | tree low_sub = TREE_OPERAND (ref, 1); | |
64 | tree up_sub = low_sub; | |
65 | tree up_bound = array_ref_up_bound (ref); | |
66 | ||
67 | /* Referenced decl if one can be determined. */ | |
68 | tree decl = NULL_TREE; | |
69 | ||
70 | /* Set for accesses to interior zero-length arrays. */ | |
71 | bool interior_zero_len = false; | |
72 | ||
73 | tree up_bound_p1; | |
74 | ||
75 | if (!up_bound | |
76 | || TREE_CODE (up_bound) != INTEGER_CST | |
77 | || (warn_array_bounds < 2 | |
78 | && array_at_struct_end_p (ref))) | |
79 | { | |
80 | /* Accesses to trailing arrays via pointers may access storage | |
81 | beyond the types array bounds. For such arrays, or for flexible | |
82 | array members, as well as for other arrays of an unknown size, | |
83 | replace the upper bound with a more permissive one that assumes | |
84 | the size of the largest object is PTRDIFF_MAX. */ | |
85 | tree eltsize = array_ref_element_size (ref); | |
86 | ||
87 | if (TREE_CODE (eltsize) != INTEGER_CST | |
88 | || integer_zerop (eltsize)) | |
89 | { | |
90 | up_bound = NULL_TREE; | |
91 | up_bound_p1 = NULL_TREE; | |
92 | } | |
93 | else | |
94 | { | |
95 | tree ptrdiff_max = TYPE_MAX_VALUE (ptrdiff_type_node); | |
96 | tree maxbound = ptrdiff_max; | |
97 | tree arg = TREE_OPERAND (ref, 0); | |
98 | ||
99 | const bool compref = TREE_CODE (arg) == COMPONENT_REF; | |
100 | if (compref) | |
101 | { | |
102 | /* Try to determine the size of the trailing array from | |
103 | its initializer (if it has one). */ | |
104 | if (tree refsize = component_ref_size (arg, &interior_zero_len)) | |
105 | if (TREE_CODE (refsize) == INTEGER_CST) | |
106 | maxbound = refsize; | |
107 | } | |
108 | ||
109 | if (maxbound == ptrdiff_max) | |
110 | { | |
111 | /* Try to determine the size of the base object. Avoid | |
112 | COMPONENT_REF already tried above. Using its DECL_SIZE | |
113 | size wouldn't necessarily be correct if the reference is | |
114 | to its flexible array member initialized in a different | |
115 | translation unit. */ | |
116 | poly_int64 off; | |
117 | if (tree base = get_addr_base_and_unit_offset (arg, &off)) | |
118 | { | |
119 | if (!compref && DECL_P (base)) | |
120 | if (tree basesize = DECL_SIZE_UNIT (base)) | |
121 | if (TREE_CODE (basesize) == INTEGER_CST) | |
122 | { | |
123 | maxbound = basesize; | |
124 | decl = base; | |
125 | } | |
126 | ||
127 | if (known_gt (off, 0)) | |
128 | maxbound = wide_int_to_tree (sizetype, | |
129 | wi::sub (wi::to_wide (maxbound), | |
130 | off)); | |
131 | } | |
132 | } | |
133 | else | |
134 | maxbound = fold_convert (sizetype, maxbound); | |
135 | ||
136 | up_bound_p1 = int_const_binop (TRUNC_DIV_EXPR, maxbound, eltsize); | |
137 | ||
138 | if (up_bound_p1 != NULL_TREE) | |
139 | up_bound = int_const_binop (MINUS_EXPR, up_bound_p1, | |
140 | build_int_cst (ptrdiff_type_node, 1)); | |
141 | else | |
142 | up_bound = NULL_TREE; | |
143 | } | |
144 | } | |
145 | else | |
146 | up_bound_p1 = int_const_binop (PLUS_EXPR, up_bound, | |
147 | build_int_cst (TREE_TYPE (up_bound), 1)); | |
148 | ||
149 | tree low_bound = array_ref_low_bound (ref); | |
150 | ||
151 | tree artype = TREE_TYPE (TREE_OPERAND (ref, 0)); | |
152 | ||
153 | bool warned = false; | |
154 | ||
155 | /* Empty array. */ | |
156 | if (up_bound && tree_int_cst_equal (low_bound, up_bound_p1)) | |
157 | warned = warning_at (location, OPT_Warray_bounds, | |
158 | "array subscript %E is outside array bounds of %qT", | |
159 | low_sub, artype); | |
160 | ||
161 | const value_range *vr = NULL; | |
162 | if (TREE_CODE (low_sub) == SSA_NAME) | |
163 | { | |
164 | vr = get_value_range (low_sub); | |
165 | if (!vr->undefined_p () && !vr->varying_p ()) | |
166 | { | |
167 | low_sub = vr->kind () == VR_RANGE ? vr->max () : vr->min (); | |
168 | up_sub = vr->kind () == VR_RANGE ? vr->min () : vr->max (); | |
169 | } | |
170 | } | |
171 | ||
172 | if (warned) | |
173 | ; /* Do nothing. */ | |
174 | else if (vr && vr->kind () == VR_ANTI_RANGE) | |
175 | { | |
176 | if (up_bound | |
177 | && TREE_CODE (up_sub) == INTEGER_CST | |
178 | && (ignore_off_by_one | |
179 | ? tree_int_cst_lt (up_bound, up_sub) | |
180 | : tree_int_cst_le (up_bound, up_sub)) | |
181 | && TREE_CODE (low_sub) == INTEGER_CST | |
182 | && tree_int_cst_le (low_sub, low_bound)) | |
183 | warned = warning_at (location, OPT_Warray_bounds, | |
184 | "array subscript [%E, %E] is outside " | |
185 | "array bounds of %qT", | |
186 | low_sub, up_sub, artype); | |
187 | } | |
188 | else if (up_bound | |
189 | && TREE_CODE (up_sub) == INTEGER_CST | |
190 | && (ignore_off_by_one | |
191 | ? !tree_int_cst_le (up_sub, up_bound_p1) | |
192 | : !tree_int_cst_le (up_sub, up_bound))) | |
193 | warned = warning_at (location, OPT_Warray_bounds, | |
194 | "array subscript %E is above array bounds of %qT", | |
195 | up_sub, artype); | |
196 | else if (TREE_CODE (low_sub) == INTEGER_CST | |
197 | && tree_int_cst_lt (low_sub, low_bound)) | |
198 | warned = warning_at (location, OPT_Warray_bounds, | |
199 | "array subscript %E is below array bounds of %qT", | |
200 | low_sub, artype); | |
201 | ||
202 | if (!warned && interior_zero_len) | |
203 | warned = warning_at (location, OPT_Wzero_length_bounds, | |
204 | (TREE_CODE (low_sub) == INTEGER_CST | |
205 | ? G_("array subscript %E is outside the bounds " | |
206 | "of an interior zero-length array %qT") | |
207 | : G_("array subscript %qE is outside the bounds " | |
208 | "of an interior zero-length array %qT")), | |
209 | low_sub, artype); | |
210 | ||
211 | if (warned) | |
212 | { | |
213 | if (dump_file && (dump_flags & TDF_DETAILS)) | |
214 | { | |
215 | fprintf (dump_file, "Array bound warning for "); | |
216 | dump_generic_expr (MSG_NOTE, TDF_SLIM, ref); | |
217 | fprintf (dump_file, "\n"); | |
218 | } | |
219 | ||
220 | ref = decl ? decl : TREE_OPERAND (ref, 0); | |
221 | ||
222 | tree rec = NULL_TREE; | |
223 | if (TREE_CODE (ref) == COMPONENT_REF) | |
224 | { | |
225 | /* For a reference to a member of a struct object also mention | |
226 | the object if it's known. It may be defined in a different | |
227 | function than the out-of-bounds access. */ | |
228 | rec = TREE_OPERAND (ref, 0); | |
229 | if (!VAR_P (rec)) | |
230 | rec = NULL_TREE; | |
231 | ref = TREE_OPERAND (ref, 1); | |
232 | } | |
233 | ||
234 | if (DECL_P (ref)) | |
235 | inform (DECL_SOURCE_LOCATION (ref), "while referencing %qD", ref); | |
236 | if (rec && DECL_P (rec)) | |
237 | inform (DECL_SOURCE_LOCATION (rec), "defined here %qD", rec); | |
238 | ||
239 | TREE_NO_WARNING (ref) = 1; | |
240 | } | |
241 | ||
242 | return warned; | |
243 | } | |
244 | ||
245 | /* Checks one MEM_REF in REF, located at LOCATION, for out-of-bounds | |
246 | references to string constants. If VRP can determine that the array | |
247 | subscript is a constant, check if it is outside valid range. | |
248 | If the array subscript is a RANGE, warn if it is non-overlapping | |
249 | with valid range. | |
250 | IGNORE_OFF_BY_ONE is true if the MEM_REF is inside an ADDR_EXPR | |
251 | (used to allow one-past-the-end indices for code that takes | |
252 | the address of the just-past-the-end element of an array). | |
253 | Returns true if a warning has been issued. */ | |
254 | ||
255 | bool | |
256 | array_bounds_checker::check_mem_ref (location_t location, tree ref, | |
257 | bool ignore_off_by_one) | |
258 | { | |
259 | if (TREE_NO_WARNING (ref)) | |
260 | return false; | |
261 | ||
262 | tree arg = TREE_OPERAND (ref, 0); | |
263 | /* The constant and variable offset of the reference. */ | |
264 | tree cstoff = TREE_OPERAND (ref, 1); | |
265 | tree varoff = NULL_TREE; | |
266 | ||
267 | const offset_int maxobjsize = tree_to_shwi (max_object_size ()); | |
268 | ||
269 | /* The array or string constant bounds in bytes. Initially set | |
270 | to [-MAXOBJSIZE - 1, MAXOBJSIZE] until a tighter bound is | |
271 | determined. */ | |
272 | offset_int arrbounds[2] = { -maxobjsize - 1, maxobjsize }; | |
273 | ||
274 | /* The minimum and maximum intermediate offset. For a reference | |
275 | to be valid, not only does the final offset/subscript must be | |
276 | in bounds but all intermediate offsets should be as well. | |
277 | GCC may be able to deal gracefully with such out-of-bounds | |
278 | offsets so the checking is only enbaled at -Warray-bounds=2 | |
279 | where it may help detect bugs in uses of the intermediate | |
280 | offsets that could otherwise not be detectable. */ | |
281 | offset_int ioff = wi::to_offset (fold_convert (ptrdiff_type_node, cstoff)); | |
282 | offset_int extrema[2] = { 0, wi::abs (ioff) }; | |
283 | ||
284 | /* The range of the byte offset into the reference. */ | |
285 | offset_int offrange[2] = { 0, 0 }; | |
286 | ||
287 | const value_range *vr = NULL; | |
288 | ||
289 | /* Determine the offsets and increment OFFRANGE for the bounds of each. | |
290 | The loop computes the range of the final offset for expressions such | |
291 | as (A + i0 + ... + iN)[CSTOFF] where i0 through iN are SSA_NAMEs in | |
292 | some range. */ | |
293 | const unsigned limit = param_ssa_name_def_chain_limit; | |
294 | for (unsigned n = 0; TREE_CODE (arg) == SSA_NAME && n < limit; ++n) | |
295 | { | |
296 | gimple *def = SSA_NAME_DEF_STMT (arg); | |
297 | if (!is_gimple_assign (def)) | |
298 | break; | |
299 | ||
300 | tree_code code = gimple_assign_rhs_code (def); | |
301 | if (code == POINTER_PLUS_EXPR) | |
302 | { | |
303 | arg = gimple_assign_rhs1 (def); | |
304 | varoff = gimple_assign_rhs2 (def); | |
305 | } | |
306 | else if (code == ASSERT_EXPR) | |
307 | { | |
308 | arg = TREE_OPERAND (gimple_assign_rhs1 (def), 0); | |
309 | continue; | |
310 | } | |
311 | else | |
312 | return false; | |
313 | ||
314 | /* VAROFF should always be a SSA_NAME here (and not even | |
315 | INTEGER_CST) but there's no point in taking chances. */ | |
316 | if (TREE_CODE (varoff) != SSA_NAME) | |
317 | break; | |
318 | ||
319 | vr = get_value_range (varoff); | |
320 | if (!vr || vr->undefined_p () || vr->varying_p ()) | |
321 | break; | |
322 | ||
323 | if (!vr->constant_p ()) | |
324 | break; | |
325 | ||
326 | if (vr->kind () == VR_RANGE) | |
327 | { | |
328 | offset_int min | |
329 | = wi::to_offset (fold_convert (ptrdiff_type_node, vr->min ())); | |
330 | offset_int max | |
331 | = wi::to_offset (fold_convert (ptrdiff_type_node, vr->max ())); | |
332 | if (min < max) | |
333 | { | |
334 | offrange[0] += min; | |
335 | offrange[1] += max; | |
336 | } | |
337 | else | |
338 | { | |
339 | /* When MIN >= MAX, the offset is effectively in a union | |
340 | of two ranges: [-MAXOBJSIZE -1, MAX] and [MIN, MAXOBJSIZE]. | |
341 | Since there is no way to represent such a range across | |
342 | additions, conservatively add [-MAXOBJSIZE -1, MAXOBJSIZE] | |
343 | to OFFRANGE. */ | |
344 | offrange[0] += arrbounds[0]; | |
345 | offrange[1] += arrbounds[1]; | |
346 | } | |
347 | } | |
348 | else | |
349 | { | |
350 | /* For an anti-range, analogously to the above, conservatively | |
351 | add [-MAXOBJSIZE -1, MAXOBJSIZE] to OFFRANGE. */ | |
352 | offrange[0] += arrbounds[0]; | |
353 | offrange[1] += arrbounds[1]; | |
354 | } | |
355 | ||
356 | /* Keep track of the minimum and maximum offset. */ | |
357 | if (offrange[1] < 0 && offrange[1] < extrema[0]) | |
358 | extrema[0] = offrange[1]; | |
359 | if (offrange[0] > 0 && offrange[0] > extrema[1]) | |
360 | extrema[1] = offrange[0]; | |
361 | ||
362 | if (offrange[0] < arrbounds[0]) | |
363 | offrange[0] = arrbounds[0]; | |
364 | ||
365 | if (offrange[1] > arrbounds[1]) | |
366 | offrange[1] = arrbounds[1]; | |
367 | } | |
368 | ||
369 | if (TREE_CODE (arg) == ADDR_EXPR) | |
370 | { | |
371 | arg = TREE_OPERAND (arg, 0); | |
372 | if (TREE_CODE (arg) != STRING_CST | |
373 | && TREE_CODE (arg) != PARM_DECL | |
374 | && TREE_CODE (arg) != VAR_DECL) | |
375 | return false; | |
376 | } | |
377 | else | |
378 | return false; | |
379 | ||
380 | /* The type of the object being referred to. It can be an array, | |
381 | string literal, or a non-array type when the MEM_REF represents | |
382 | a reference/subscript via a pointer to an object that is not | |
383 | an element of an array. Incomplete types are excluded as well | |
384 | because their size is not known. */ | |
385 | tree reftype = TREE_TYPE (arg); | |
386 | if (POINTER_TYPE_P (reftype) | |
387 | || !COMPLETE_TYPE_P (reftype) | |
388 | || TREE_CODE (TYPE_SIZE_UNIT (reftype)) != INTEGER_CST) | |
389 | return false; | |
390 | ||
391 | /* Except in declared objects, references to trailing array members | |
392 | of structs and union objects are excluded because MEM_REF doesn't | |
393 | make it possible to identify the member where the reference | |
394 | originated. */ | |
395 | if (RECORD_OR_UNION_TYPE_P (reftype) | |
396 | && (!VAR_P (arg) | |
397 | || (DECL_EXTERNAL (arg) && array_at_struct_end_p (ref)))) | |
398 | return false; | |
399 | ||
400 | arrbounds[0] = 0; | |
401 | ||
402 | offset_int eltsize; | |
403 | if (TREE_CODE (reftype) == ARRAY_TYPE) | |
404 | { | |
405 | eltsize = wi::to_offset (TYPE_SIZE_UNIT (TREE_TYPE (reftype))); | |
406 | if (tree dom = TYPE_DOMAIN (reftype)) | |
407 | { | |
408 | tree bnds[] = { TYPE_MIN_VALUE (dom), TYPE_MAX_VALUE (dom) }; | |
409 | if (TREE_CODE (arg) == COMPONENT_REF) | |
410 | { | |
411 | offset_int size = maxobjsize; | |
412 | if (tree fldsize = component_ref_size (arg)) | |
413 | size = wi::to_offset (fldsize); | |
414 | arrbounds[1] = wi::lrshift (size, wi::floor_log2 (eltsize)); | |
415 | } | |
416 | else if (array_at_struct_end_p (arg) || !bnds[0] || !bnds[1]) | |
417 | arrbounds[1] = wi::lrshift (maxobjsize, wi::floor_log2 (eltsize)); | |
418 | else | |
419 | arrbounds[1] = (wi::to_offset (bnds[1]) - wi::to_offset (bnds[0]) | |
420 | + 1) * eltsize; | |
421 | } | |
422 | else | |
423 | arrbounds[1] = wi::lrshift (maxobjsize, wi::floor_log2 (eltsize)); | |
424 | ||
425 | /* Determine a tighter bound of the non-array element type. */ | |
426 | tree eltype = TREE_TYPE (reftype); | |
427 | while (TREE_CODE (eltype) == ARRAY_TYPE) | |
428 | eltype = TREE_TYPE (eltype); | |
429 | eltsize = wi::to_offset (TYPE_SIZE_UNIT (eltype)); | |
430 | } | |
431 | else | |
432 | { | |
433 | eltsize = 1; | |
434 | tree size = TYPE_SIZE_UNIT (reftype); | |
435 | if (VAR_P (arg)) | |
436 | if (tree initsize = DECL_SIZE_UNIT (arg)) | |
437 | if (tree_int_cst_lt (size, initsize)) | |
438 | size = initsize; | |
439 | ||
440 | arrbounds[1] = wi::to_offset (size); | |
441 | } | |
442 | ||
443 | offrange[0] += ioff; | |
444 | offrange[1] += ioff; | |
445 | ||
446 | /* Compute the more permissive upper bound when IGNORE_OFF_BY_ONE | |
447 | is set (when taking the address of the one-past-last element | |
448 | of an array) but always use the stricter bound in diagnostics. */ | |
449 | offset_int ubound = arrbounds[1]; | |
450 | if (ignore_off_by_one) | |
451 | ubound += 1; | |
452 | ||
453 | if (arrbounds[0] == arrbounds[1] | |
454 | || offrange[0] >= ubound | |
455 | || offrange[1] < arrbounds[0]) | |
456 | { | |
457 | /* Treat a reference to a non-array object as one to an array | |
458 | of a single element. */ | |
459 | if (TREE_CODE (reftype) != ARRAY_TYPE) | |
460 | reftype = build_array_type_nelts (reftype, 1); | |
461 | ||
462 | /* Extract the element type out of MEM_REF and use its size | |
463 | to compute the index to print in the diagnostic; arrays | |
464 | in MEM_REF don't mean anything. A type with no size like | |
465 | void is as good as having a size of 1. */ | |
466 | tree type = TREE_TYPE (ref); | |
467 | while (TREE_CODE (type) == ARRAY_TYPE) | |
468 | type = TREE_TYPE (type); | |
469 | if (tree size = TYPE_SIZE_UNIT (type)) | |
470 | { | |
471 | offrange[0] = offrange[0] / wi::to_offset (size); | |
472 | offrange[1] = offrange[1] / wi::to_offset (size); | |
473 | } | |
474 | ||
475 | bool warned; | |
476 | if (offrange[0] == offrange[1]) | |
477 | warned = warning_at (location, OPT_Warray_bounds, | |
478 | "array subscript %wi is outside array bounds " | |
479 | "of %qT", | |
480 | offrange[0].to_shwi (), reftype); | |
481 | else | |
482 | warned = warning_at (location, OPT_Warray_bounds, | |
483 | "array subscript [%wi, %wi] is outside " | |
484 | "array bounds of %qT", | |
485 | offrange[0].to_shwi (), | |
486 | offrange[1].to_shwi (), reftype); | |
487 | if (warned && DECL_P (arg)) | |
488 | inform (DECL_SOURCE_LOCATION (arg), "while referencing %qD", arg); | |
489 | ||
490 | if (warned) | |
491 | TREE_NO_WARNING (ref) = 1; | |
492 | return warned; | |
493 | } | |
494 | ||
495 | if (warn_array_bounds < 2) | |
496 | return false; | |
497 | ||
498 | /* At level 2 check also intermediate offsets. */ | |
499 | int i = 0; | |
500 | if (extrema[i] < -arrbounds[1] || extrema[i = 1] > ubound) | |
501 | { | |
502 | HOST_WIDE_INT tmpidx = extrema[i].to_shwi () / eltsize.to_shwi (); | |
503 | ||
504 | if (warning_at (location, OPT_Warray_bounds, | |
505 | "intermediate array offset %wi is outside array bounds " | |
506 | "of %qT", tmpidx, reftype)) | |
507 | { | |
508 | TREE_NO_WARNING (ref) = 1; | |
509 | return true; | |
510 | } | |
511 | } | |
512 | ||
513 | return false; | |
514 | } | |
515 | ||
516 | /* Searches if the expr T, located at LOCATION computes | |
517 | address of an ARRAY_REF, and call check_array_ref on it. */ | |
518 | ||
519 | void | |
520 | array_bounds_checker::check_addr_expr (location_t location, tree t) | |
521 | { | |
522 | /* Check each ARRAY_REF and MEM_REF in the reference chain. */ | |
523 | do | |
524 | { | |
525 | bool warned = false; | |
526 | if (TREE_CODE (t) == ARRAY_REF) | |
527 | warned = check_array_ref (location, t, true /*ignore_off_by_one*/); | |
528 | else if (TREE_CODE (t) == MEM_REF) | |
529 | warned = check_mem_ref (location, t, true /*ignore_off_by_one*/); | |
530 | ||
531 | if (warned) | |
532 | TREE_NO_WARNING (t) = true; | |
533 | ||
534 | t = TREE_OPERAND (t, 0); | |
535 | } | |
536 | while (handled_component_p (t) || TREE_CODE (t) == MEM_REF); | |
537 | ||
538 | if (TREE_CODE (t) != MEM_REF | |
539 | || TREE_CODE (TREE_OPERAND (t, 0)) != ADDR_EXPR | |
540 | || TREE_NO_WARNING (t)) | |
541 | return; | |
542 | ||
543 | tree tem = TREE_OPERAND (TREE_OPERAND (t, 0), 0); | |
544 | tree low_bound, up_bound, el_sz; | |
545 | if (TREE_CODE (TREE_TYPE (tem)) != ARRAY_TYPE | |
546 | || TREE_CODE (TREE_TYPE (TREE_TYPE (tem))) == ARRAY_TYPE | |
547 | || !TYPE_DOMAIN (TREE_TYPE (tem))) | |
548 | return; | |
549 | ||
550 | low_bound = TYPE_MIN_VALUE (TYPE_DOMAIN (TREE_TYPE (tem))); | |
551 | up_bound = TYPE_MAX_VALUE (TYPE_DOMAIN (TREE_TYPE (tem))); | |
552 | el_sz = TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (tem))); | |
553 | if (!low_bound | |
554 | || TREE_CODE (low_bound) != INTEGER_CST | |
555 | || !up_bound | |
556 | || TREE_CODE (up_bound) != INTEGER_CST | |
557 | || !el_sz | |
558 | || TREE_CODE (el_sz) != INTEGER_CST) | |
559 | return; | |
560 | ||
561 | offset_int idx; | |
562 | if (!mem_ref_offset (t).is_constant (&idx)) | |
563 | return; | |
564 | ||
565 | bool warned = false; | |
566 | idx = wi::sdiv_trunc (idx, wi::to_offset (el_sz)); | |
567 | if (idx < 0) | |
568 | { | |
569 | if (dump_file && (dump_flags & TDF_DETAILS)) | |
570 | { | |
571 | fprintf (dump_file, "Array bound warning for "); | |
572 | dump_generic_expr (MSG_NOTE, TDF_SLIM, t); | |
573 | fprintf (dump_file, "\n"); | |
574 | } | |
575 | warned = warning_at (location, OPT_Warray_bounds, | |
576 | "array subscript %wi is below " | |
577 | "array bounds of %qT", | |
578 | idx.to_shwi (), TREE_TYPE (tem)); | |
579 | } | |
580 | else if (idx > (wi::to_offset (up_bound) | |
581 | - wi::to_offset (low_bound) + 1)) | |
582 | { | |
583 | if (dump_file && (dump_flags & TDF_DETAILS)) | |
584 | { | |
585 | fprintf (dump_file, "Array bound warning for "); | |
586 | dump_generic_expr (MSG_NOTE, TDF_SLIM, t); | |
587 | fprintf (dump_file, "\n"); | |
588 | } | |
589 | warned = warning_at (location, OPT_Warray_bounds, | |
590 | "array subscript %wu is above " | |
591 | "array bounds of %qT", | |
592 | idx.to_uhwi (), TREE_TYPE (tem)); | |
593 | } | |
594 | ||
595 | if (warned) | |
596 | { | |
597 | if (DECL_P (t)) | |
598 | inform (DECL_SOURCE_LOCATION (t), "while referencing %qD", t); | |
599 | ||
600 | TREE_NO_WARNING (t) = 1; | |
601 | } | |
602 | } | |
603 | ||
604 | /* Callback for walk_tree to check a tree for out of bounds array | |
605 | accesses. The array_bounds_checker class is passed in DATA. */ | |
606 | ||
607 | tree | |
608 | array_bounds_checker::check_array_bounds (tree *tp, int *walk_subtree, | |
609 | void *data) | |
610 | { | |
611 | tree t = *tp; | |
612 | struct walk_stmt_info *wi = (struct walk_stmt_info *) data; | |
613 | location_t location; | |
614 | ||
615 | if (EXPR_HAS_LOCATION (t)) | |
616 | location = EXPR_LOCATION (t); | |
617 | else | |
618 | location = gimple_location (wi->stmt); | |
619 | ||
620 | *walk_subtree = TRUE; | |
621 | ||
622 | bool warned = false; | |
623 | array_bounds_checker *checker = (array_bounds_checker *) wi->info; | |
624 | if (TREE_CODE (t) == ARRAY_REF) | |
625 | warned = checker->check_array_ref (location, t, | |
626 | false/*ignore_off_by_one*/); | |
627 | else if (TREE_CODE (t) == MEM_REF) | |
628 | warned = checker->check_mem_ref (location, t, | |
629 | false /*ignore_off_by_one*/); | |
630 | else if (TREE_CODE (t) == ADDR_EXPR) | |
631 | { | |
632 | checker->check_addr_expr (location, t); | |
633 | *walk_subtree = FALSE; | |
634 | } | |
635 | /* Propagate the no-warning bit to the outer expression. */ | |
636 | if (warned) | |
637 | TREE_NO_WARNING (t) = true; | |
638 | ||
639 | return NULL_TREE; | |
640 | } | |
641 | ||
642 | /* A dom_walker subclass for use by check_all_array_refs, to walk over | |
643 | all statements of all reachable BBs and call check_array_bounds on | |
644 | them. */ | |
645 | ||
646 | class check_array_bounds_dom_walker : public dom_walker | |
647 | { | |
648 | public: | |
649 | check_array_bounds_dom_walker (array_bounds_checker *checker) | |
650 | : dom_walker (CDI_DOMINATORS, | |
651 | /* Discover non-executable edges, preserving EDGE_EXECUTABLE | |
652 | flags, so that we can merge in information on | |
653 | non-executable edges from vrp_folder . */ | |
654 | REACHABLE_BLOCKS_PRESERVING_FLAGS), | |
655 | checker (checker) { } | |
656 | ~check_array_bounds_dom_walker () {} | |
657 | ||
658 | edge before_dom_children (basic_block) FINAL OVERRIDE; | |
659 | ||
660 | private: | |
661 | array_bounds_checker *checker; | |
662 | }; | |
663 | ||
664 | /* Implementation of dom_walker::before_dom_children. | |
665 | ||
666 | Walk over all statements of BB and call check_array_bounds on them, | |
667 | and determine if there's a unique successor edge. */ | |
668 | ||
669 | edge | |
670 | check_array_bounds_dom_walker::before_dom_children (basic_block bb) | |
671 | { | |
672 | gimple_stmt_iterator si; | |
673 | for (si = gsi_start_bb (bb); !gsi_end_p (si); gsi_next (&si)) | |
674 | { | |
675 | gimple *stmt = gsi_stmt (si); | |
676 | struct walk_stmt_info wi; | |
677 | if (!gimple_has_location (stmt) | |
678 | || is_gimple_debug (stmt)) | |
679 | continue; | |
680 | ||
681 | memset (&wi, 0, sizeof (wi)); | |
682 | ||
683 | wi.info = checker; | |
684 | ||
685 | walk_gimple_op (stmt, array_bounds_checker::check_array_bounds, &wi); | |
686 | } | |
687 | ||
688 | /* Determine if there's a unique successor edge, and if so, return | |
689 | that back to dom_walker, ensuring that we don't visit blocks that | |
690 | became unreachable during the VRP propagation | |
691 | (PR tree-optimization/83312). */ | |
692 | return find_taken_edge (bb, NULL_TREE); | |
693 | } | |
694 | ||
695 | void | |
696 | array_bounds_checker::check () | |
697 | { | |
698 | check_array_bounds_dom_walker w (this); | |
699 | w.walk (ENTRY_BLOCK_PTR_FOR_FN (fun)); | |
700 | } |