]>
Commit | Line | Data |
---|---|---|
62efd1c4 | 1 | /* Array bounds checking. |
a945c346 | 2 | Copyright (C) 2005-2024 Free Software Foundation, Inc. |
62efd1c4 AH |
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" | |
a62b740d | 27 | #include "pointer-query.h" |
62efd1c4 AH |
28 | #include "gimple-array-bounds.h" |
29 | #include "gimple-iterator.h" | |
30 | #include "gimple-walk.h" | |
31 | #include "tree-dfa.h" | |
32 | #include "fold-const.h" | |
33 | #include "diagnostic-core.h" | |
34 | #include "intl.h" | |
35 | #include "tree-vrp.h" | |
36 | #include "alloc-pool.h" | |
37 | #include "vr-values.h" | |
38 | #include "domwalk.h" | |
39 | #include "tree-cfg.h" | |
3f9a497d | 40 | #include "attribs.h" |
a62b740d MS |
41 | |
42 | array_bounds_checker::array_bounds_checker (struct function *func, | |
43 | range_query *qry) | |
44 | : fun (func), m_ptr_qry (qry) | |
45 | { | |
46 | /* No-op. */ | |
47 | } | |
62efd1c4 | 48 | |
e6910b62 AH |
49 | void |
50 | array_bounds_checker::get_value_range (irange &r, const_tree op, gimple *stmt) | |
62efd1c4 | 51 | { |
e6910b62 AH |
52 | if (m_ptr_qry.rvals->range_of_expr (r, const_cast<tree> (op), stmt)) |
53 | return; | |
54 | r.set_varying (TREE_TYPE (op)); | |
62efd1c4 AH |
55 | } |
56 | ||
3f9a497d MS |
57 | /* Try to determine the DECL that REF refers to. Return the DECL or |
58 | the expression closest to it. Used in informational notes pointing | |
59 | to referenced objects or function parameters. */ | |
60 | ||
61 | static tree | |
62 | get_base_decl (tree ref) | |
63 | { | |
64 | tree base = get_base_address (ref); | |
65 | if (DECL_P (base)) | |
66 | return base; | |
67 | ||
68 | if (TREE_CODE (base) == MEM_REF) | |
69 | base = TREE_OPERAND (base, 0); | |
70 | ||
71 | if (TREE_CODE (base) != SSA_NAME) | |
72 | return base; | |
73 | ||
74 | do | |
75 | { | |
76 | gimple *def = SSA_NAME_DEF_STMT (base); | |
77 | if (gimple_assign_single_p (def)) | |
78 | { | |
79 | base = gimple_assign_rhs1 (def); | |
42639a36 | 80 | return base; |
3f9a497d MS |
81 | } |
82 | ||
83 | if (!gimple_nop_p (def)) | |
84 | return base; | |
85 | ||
86 | break; | |
87 | } while (true); | |
88 | ||
89 | tree var = SSA_NAME_VAR (base); | |
90 | if (TREE_CODE (var) != PARM_DECL) | |
91 | return base; | |
92 | ||
93 | return var; | |
94 | } | |
95 | ||
96 | /* Return the constant byte size of the object or type referenced by | |
97 | the MEM_REF ARG. On success, set *PREF to the DECL or expression | |
98 | ARG refers to. Otherwise return null. */ | |
99 | ||
100 | static tree | |
101 | get_ref_size (tree arg, tree *pref) | |
102 | { | |
103 | if (TREE_CODE (arg) != MEM_REF) | |
104 | return NULL_TREE; | |
105 | ||
106 | arg = TREE_OPERAND (arg, 0); | |
107 | tree type = TREE_TYPE (arg); | |
108 | if (!POINTER_TYPE_P (type)) | |
109 | return NULL_TREE; | |
110 | ||
111 | type = TREE_TYPE (type); | |
112 | if (TREE_CODE (type) != ARRAY_TYPE) | |
113 | return NULL_TREE; | |
114 | ||
115 | tree nbytes = TYPE_SIZE_UNIT (type); | |
116 | if (!nbytes || TREE_CODE (nbytes) != INTEGER_CST) | |
117 | return NULL_TREE; | |
118 | ||
119 | *pref = get_base_decl (arg); | |
120 | return nbytes; | |
121 | } | |
122 | ||
123 | /* Return true if REF is (likely) an ARRAY_REF to a trailing array member | |
ace0ae09 | 124 | of a struct. It refines array_ref_flexible_size_p by detecting a pointer |
3f9a497d MS |
125 | to an array and an array parameter declared using the [N] syntax (as |
126 | opposed to a pointer) and returning false. Set *PREF to the decl or | |
127 | expression REF refers to. */ | |
128 | ||
129 | static bool | |
130 | trailing_array (tree arg, tree *pref) | |
131 | { | |
132 | tree ref = arg; | |
133 | tree base = get_base_decl (arg); | |
134 | while (TREE_CODE (ref) == ARRAY_REF || TREE_CODE (ref) == MEM_REF) | |
135 | ref = TREE_OPERAND (ref, 0); | |
136 | ||
137 | if (TREE_CODE (ref) == COMPONENT_REF) | |
138 | { | |
139 | *pref = TREE_OPERAND (ref, 1); | |
140 | tree type = TREE_TYPE (*pref); | |
141 | if (TREE_CODE (type) == ARRAY_TYPE) | |
142 | { | |
143 | /* A multidimensional trailing array is not considered special | |
144 | no matter what its major bound is. */ | |
145 | type = TREE_TYPE (type); | |
146 | if (TREE_CODE (type) == ARRAY_TYPE) | |
147 | return false; | |
148 | } | |
149 | } | |
150 | else | |
151 | *pref = base; | |
152 | ||
153 | tree basetype = TREE_TYPE (base); | |
154 | if (TREE_CODE (base) == PARM_DECL | |
155 | && POINTER_TYPE_P (basetype)) | |
156 | { | |
157 | tree ptype = TREE_TYPE (basetype); | |
158 | if (TREE_CODE (ptype) == ARRAY_TYPE) | |
159 | return false; | |
160 | } | |
161 | ||
ace0ae09 | 162 | return array_ref_flexible_size_p (arg); |
3f9a497d MS |
163 | } |
164 | ||
710c9676 QZ |
165 | /* Acquire the upper bound and upper bound plus one for the array |
166 | reference REF and record them into UP_BOUND and UP_BOUND_P1. | |
167 | Set *DECL to the decl or expresssion REF refers to. */ | |
62efd1c4 | 168 | |
710c9676 QZ |
169 | static void |
170 | get_up_bounds_for_array_ref (tree ref, tree *decl, | |
171 | tree *up_bound, tree *up_bound_p1) | |
62efd1c4 | 172 | { |
710c9676 QZ |
173 | if (!(*up_bound) |
174 | || TREE_CODE (*up_bound) != INTEGER_CST | |
175 | || trailing_array (ref, decl)) | |
62efd1c4 AH |
176 | { |
177 | /* Accesses to trailing arrays via pointers may access storage | |
178 | beyond the types array bounds. For such arrays, or for flexible | |
179 | array members, as well as for other arrays of an unknown size, | |
180 | replace the upper bound with a more permissive one that assumes | |
181 | the size of the largest object is PTRDIFF_MAX. */ | |
182 | tree eltsize = array_ref_element_size (ref); | |
183 | ||
184 | if (TREE_CODE (eltsize) != INTEGER_CST | |
185 | || integer_zerop (eltsize)) | |
186 | { | |
710c9676 QZ |
187 | *up_bound = NULL_TREE; |
188 | *up_bound_p1 = NULL_TREE; | |
62efd1c4 AH |
189 | } |
190 | else | |
191 | { | |
192 | tree ptrdiff_max = TYPE_MAX_VALUE (ptrdiff_type_node); | |
193 | tree maxbound = ptrdiff_max; | |
194 | tree arg = TREE_OPERAND (ref, 0); | |
195 | ||
196 | const bool compref = TREE_CODE (arg) == COMPONENT_REF; | |
197 | if (compref) | |
198 | { | |
199 | /* Try to determine the size of the trailing array from | |
200 | its initializer (if it has one). */ | |
710c9676 | 201 | if (tree refsize = component_ref_size (arg)) |
62efd1c4 AH |
202 | if (TREE_CODE (refsize) == INTEGER_CST) |
203 | maxbound = refsize; | |
204 | } | |
205 | ||
206 | if (maxbound == ptrdiff_max) | |
207 | { | |
208 | /* Try to determine the size of the base object. Avoid | |
209 | COMPONENT_REF already tried above. Using its DECL_SIZE | |
210 | size wouldn't necessarily be correct if the reference is | |
211 | to its flexible array member initialized in a different | |
212 | translation unit. */ | |
213 | poly_int64 off; | |
214 | if (tree base = get_addr_base_and_unit_offset (arg, &off)) | |
215 | { | |
3f9a497d MS |
216 | if (TREE_CODE (base) == MEM_REF) |
217 | { | |
218 | /* Try to determine the size from a pointer to | |
219 | an array if BASE is one. */ | |
710c9676 | 220 | if (tree size = get_ref_size (base, decl)) |
3f9a497d MS |
221 | maxbound = size; |
222 | } | |
223 | else if (!compref && DECL_P (base)) | |
62efd1c4 AH |
224 | if (tree basesize = DECL_SIZE_UNIT (base)) |
225 | if (TREE_CODE (basesize) == INTEGER_CST) | |
226 | { | |
227 | maxbound = basesize; | |
710c9676 | 228 | *decl = base; |
62efd1c4 AH |
229 | } |
230 | ||
231 | if (known_gt (off, 0)) | |
232 | maxbound = wide_int_to_tree (sizetype, | |
233 | wi::sub (wi::to_wide (maxbound), | |
234 | off)); | |
235 | } | |
236 | } | |
237 | else | |
238 | maxbound = fold_convert (sizetype, maxbound); | |
239 | ||
710c9676 | 240 | *up_bound_p1 = int_const_binop (TRUNC_DIV_EXPR, maxbound, eltsize); |
62efd1c4 | 241 | |
710c9676 QZ |
242 | if (*up_bound_p1 != NULL_TREE) |
243 | *up_bound = int_const_binop (MINUS_EXPR, *up_bound_p1, | |
62efd1c4 AH |
244 | build_int_cst (ptrdiff_type_node, 1)); |
245 | else | |
710c9676 | 246 | *up_bound = NULL_TREE; |
62efd1c4 AH |
247 | } |
248 | } | |
249 | else | |
710c9676 QZ |
250 | *up_bound_p1 = int_const_binop (PLUS_EXPR, *up_bound, |
251 | build_int_cst (TREE_TYPE (*up_bound), 1)); | |
252 | return; | |
253 | } | |
62efd1c4 | 254 | |
710c9676 QZ |
255 | /* Given the LOW_SUB_ORG, LOW_SUB and UP_SUB, and the computed UP_BOUND |
256 | and UP_BOUND_P1, check whether the array reference REF is out of bound. | |
2a27ae32 QZ |
257 | When out of bounds, set OUT_OF_BOUND to true. |
258 | Issue warnings if FOR_ARRAY_BOUND is true. | |
259 | return TRUE if warnings are issued. */ | |
62efd1c4 | 260 | |
710c9676 QZ |
261 | static bool |
262 | check_out_of_bounds_and_warn (location_t location, tree ref, | |
263 | tree low_sub_org, tree low_sub, tree up_sub, | |
264 | tree up_bound, tree up_bound_p1, | |
265 | const value_range *vr, | |
2a27ae32 QZ |
266 | bool ignore_off_by_one, bool for_array_bound, |
267 | bool *out_of_bound) | |
710c9676 | 268 | { |
5bdc5155 | 269 | tree min, max; |
710c9676 | 270 | tree low_bound = array_ref_low_bound (ref); |
62efd1c4 AH |
271 | tree artype = TREE_TYPE (TREE_OPERAND (ref, 0)); |
272 | ||
273 | bool warned = false; | |
2a27ae32 | 274 | *out_of_bound = false; |
62efd1c4 AH |
275 | |
276 | /* Empty array. */ | |
277 | if (up_bound && tree_int_cst_equal (low_bound, up_bound_p1)) | |
2a27ae32 QZ |
278 | { |
279 | *out_of_bound = true; | |
280 | if (for_array_bound) | |
281 | warned = warning_at (location, OPT_Warray_bounds_, | |
282 | "array subscript %E is outside array" | |
283 | " bounds of %qT", low_sub_org, artype); | |
284 | } | |
62efd1c4 AH |
285 | |
286 | if (warned) | |
287 | ; /* Do nothing. */ | |
5bdc5155 | 288 | else if (get_legacy_range (*vr, min, max) == VR_ANTI_RANGE) |
62efd1c4 AH |
289 | { |
290 | if (up_bound | |
291 | && TREE_CODE (up_sub) == INTEGER_CST | |
292 | && (ignore_off_by_one | |
293 | ? tree_int_cst_lt (up_bound, up_sub) | |
294 | : tree_int_cst_le (up_bound, up_sub)) | |
295 | && TREE_CODE (low_sub) == INTEGER_CST | |
296 | && tree_int_cst_le (low_sub, low_bound)) | |
2a27ae32 QZ |
297 | { |
298 | *out_of_bound = true; | |
299 | if (for_array_bound) | |
300 | warned = warning_at (location, OPT_Warray_bounds_, | |
301 | "array subscript [%E, %E] is outside " | |
302 | "array bounds of %qT", | |
303 | low_sub, up_sub, artype); | |
304 | } | |
62efd1c4 AH |
305 | } |
306 | else if (up_bound | |
307 | && TREE_CODE (up_sub) == INTEGER_CST | |
308 | && (ignore_off_by_one | |
309 | ? !tree_int_cst_le (up_sub, up_bound_p1) | |
310 | : !tree_int_cst_le (up_sub, up_bound))) | |
2a27ae32 QZ |
311 | { |
312 | *out_of_bound = true; | |
313 | if (for_array_bound) | |
314 | warned = warning_at (location, OPT_Warray_bounds_, | |
315 | "array subscript %E is above array bounds of %qT", | |
316 | up_sub, artype); | |
317 | } | |
62efd1c4 AH |
318 | else if (TREE_CODE (low_sub) == INTEGER_CST |
319 | && tree_int_cst_lt (low_sub, low_bound)) | |
2a27ae32 QZ |
320 | { |
321 | *out_of_bound = true; | |
322 | if (for_array_bound) | |
323 | warned = warning_at (location, OPT_Warray_bounds_, | |
324 | "array subscript %E is below array bounds of %qT", | |
325 | low_sub, artype); | |
326 | } | |
710c9676 QZ |
327 | return warned; |
328 | } | |
329 | ||
330 | /* Checks one ARRAY_REF in REF, located at LOCUS. Ignores flexible | |
331 | arrays and "struct" hacks. If VRP can determine that the array | |
332 | subscript is a constant, check if it is outside valid range. If | |
333 | the array subscript is a RANGE, warn if it is non-overlapping with | |
334 | valid range. IGNORE_OFF_BY_ONE is true if the ARRAY_REF is inside | |
335 | a ADDR_EXPR. Return true if a warning has been issued or if | |
336 | no-warning is set. */ | |
337 | ||
338 | bool | |
339 | array_bounds_checker::check_array_ref (location_t location, tree ref, | |
340 | gimple *stmt, bool ignore_off_by_one) | |
341 | { | |
342 | if (warning_suppressed_p (ref, OPT_Warray_bounds_)) | |
343 | /* Return true to have the caller prevent warnings for enclosing | |
344 | refs. */ | |
345 | return true; | |
346 | ||
347 | /* Upper bound and Upper bound plus one for -Warray-bounds. */ | |
348 | tree up_bound = array_ref_up_bound (ref); | |
349 | tree up_bound_p1 = NULL_TREE; | |
350 | ||
351 | /* Referenced decl if one can be determined. */ | |
352 | tree decl = NULL_TREE; | |
353 | ||
354 | /* Set to the type of the special array member for a COMPONENT_REF. */ | |
355 | special_array_member sam{ }; | |
a3e8727b | 356 | tree afield_decl = NULL_TREE; |
710c9676 | 357 | tree arg = TREE_OPERAND (ref, 0); |
710c9676 | 358 | |
a3e8727b | 359 | if (TREE_CODE (arg) == COMPONENT_REF) |
2a27ae32 QZ |
360 | { |
361 | /* Try to determine special array member type for this COMPONENT_REF. */ | |
362 | sam = component_ref_sam_type (arg); | |
a3e8727b | 363 | afield_decl = TREE_OPERAND (arg, 1); |
2a27ae32 | 364 | } |
710c9676 QZ |
365 | |
366 | get_up_bounds_for_array_ref (ref, &decl, &up_bound, &up_bound_p1); | |
367 | ||
368 | bool warned = false; | |
2a27ae32 | 369 | bool out_of_bound = false; |
710c9676 QZ |
370 | |
371 | tree artype = TREE_TYPE (TREE_OPERAND (ref, 0)); | |
372 | tree low_sub_org = TREE_OPERAND (ref, 1); | |
373 | tree up_sub = low_sub_org; | |
374 | tree low_sub = low_sub_org; | |
375 | ||
e6910b62 | 376 | value_range vr; |
710c9676 QZ |
377 | if (TREE_CODE (low_sub_org) == SSA_NAME) |
378 | { | |
e6910b62 AH |
379 | get_value_range (vr, low_sub_org, stmt); |
380 | if (!vr.undefined_p () && !vr.varying_p ()) | |
710c9676 | 381 | { |
5bdc5155 AH |
382 | tree min, max; |
383 | value_range_kind kind = get_legacy_range (vr, min, max); | |
384 | low_sub = kind == VR_RANGE ? max : min; | |
385 | up_sub = kind == VR_RANGE ? min : max; | |
710c9676 QZ |
386 | } |
387 | } | |
388 | ||
389 | warned = check_out_of_bounds_and_warn (location, ref, | |
390 | low_sub_org, low_sub, up_sub, | |
e6910b62 | 391 | up_bound, up_bound_p1, &vr, |
2a27ae32 QZ |
392 | ignore_off_by_one, warn_array_bounds, |
393 | &out_of_bound); | |
710c9676 | 394 | |
62efd1c4 | 395 | |
de05c19d | 396 | if (!warned && sam == special_array_member::int_0) |
62efd1c4 AH |
397 | warned = warning_at (location, OPT_Wzero_length_bounds, |
398 | (TREE_CODE (low_sub) == INTEGER_CST | |
399 | ? G_("array subscript %E is outside the bounds " | |
400 | "of an interior zero-length array %qT") | |
401 | : G_("array subscript %qE is outside the bounds " | |
402 | "of an interior zero-length array %qT")), | |
403 | low_sub, artype); | |
404 | ||
c6232ba2 | 405 | if (warned && dump_file && (dump_flags & TDF_DETAILS)) |
62efd1c4 | 406 | { |
c6232ba2 AP |
407 | fprintf (dump_file, "Array bound warning for "); |
408 | dump_generic_expr (MSG_NOTE, TDF_SLIM, ref); | |
409 | fprintf (dump_file, "\n"); | |
410 | } | |
411 | ||
412 | /* Issue warnings for -Wstrict-flex-arrays according to the level of | |
413 | flag_strict_flex_arrays. */ | |
414 | if (out_of_bound && warn_strict_flex_arrays | |
415 | && (sam == special_array_member::trail_0 | |
416 | || sam == special_array_member::trail_1 | |
417 | || sam == special_array_member::trail_n) | |
418 | && DECL_NOT_FLEXARRAY (afield_decl)) | |
419 | { | |
420 | bool warned1 | |
421 | = warning_at (location, OPT_Wstrict_flex_arrays, | |
422 | "trailing array %qT should not be used as " | |
423 | "a flexible array member", | |
424 | artype); | |
425 | ||
426 | if (warned1 && dump_file && (dump_flags & TDF_DETAILS)) | |
62efd1c4 | 427 | { |
c6232ba2 | 428 | fprintf (dump_file, "Trailing non flexible-like array bound warning for "); |
62efd1c4 AH |
429 | dump_generic_expr (MSG_NOTE, TDF_SLIM, ref); |
430 | fprintf (dump_file, "\n"); | |
431 | } | |
c6232ba2 AP |
432 | warned |= warned1; |
433 | } | |
62efd1c4 | 434 | |
c6232ba2 AP |
435 | if (warned) |
436 | { | |
3f9a497d MS |
437 | /* Avoid more warnings when checking more significant subscripts |
438 | of the same expression. */ | |
439 | ref = TREE_OPERAND (ref, 0); | |
7c01d029 | 440 | suppress_warning (ref, OPT_Warray_bounds_); |
2a27ae32 | 441 | suppress_warning (ref, OPT_Wstrict_flex_arrays); |
3f9a497d MS |
442 | |
443 | if (decl) | |
444 | ref = decl; | |
62efd1c4 AH |
445 | |
446 | tree rec = NULL_TREE; | |
447 | if (TREE_CODE (ref) == COMPONENT_REF) | |
448 | { | |
449 | /* For a reference to a member of a struct object also mention | |
450 | the object if it's known. It may be defined in a different | |
451 | function than the out-of-bounds access. */ | |
452 | rec = TREE_OPERAND (ref, 0); | |
453 | if (!VAR_P (rec)) | |
454 | rec = NULL_TREE; | |
455 | ref = TREE_OPERAND (ref, 1); | |
456 | } | |
457 | ||
458 | if (DECL_P (ref)) | |
459 | inform (DECL_SOURCE_LOCATION (ref), "while referencing %qD", ref); | |
460 | if (rec && DECL_P (rec)) | |
461 | inform (DECL_SOURCE_LOCATION (rec), "defined here %qD", rec); | |
62efd1c4 AH |
462 | } |
463 | ||
464 | return warned; | |
465 | } | |
466 | ||
467 | /* Checks one MEM_REF in REF, located at LOCATION, for out-of-bounds | |
468 | references to string constants. If VRP can determine that the array | |
469 | subscript is a constant, check if it is outside valid range. | |
470 | If the array subscript is a RANGE, warn if it is non-overlapping | |
471 | with valid range. | |
472 | IGNORE_OFF_BY_ONE is true if the MEM_REF is inside an ADDR_EXPR | |
473 | (used to allow one-past-the-end indices for code that takes | |
474 | the address of the just-past-the-end element of an array). | |
475 | Returns true if a warning has been issued. */ | |
476 | ||
477 | bool | |
478 | array_bounds_checker::check_mem_ref (location_t location, tree ref, | |
479 | bool ignore_off_by_one) | |
480 | { | |
7c01d029 | 481 | if (warning_suppressed_p (ref, OPT_Warray_bounds_)) |
62efd1c4 AH |
482 | return false; |
483 | ||
3f9a497d MS |
484 | /* The statement used to allocate the array or null. */ |
485 | gimple *alloc_stmt = NULL; | |
486 | /* For an allocation statement, the low bound of the size range. */ | |
487 | offset_int minbound = 0; | |
a1108556 MS |
488 | /* The type and size of the access. */ |
489 | tree axstype = TREE_TYPE (ref); | |
490 | offset_int axssize = 0; | |
b9cbf8c9 MS |
491 | if (tree access_size = TYPE_SIZE_UNIT (axstype)) |
492 | if (TREE_CODE (access_size) == INTEGER_CST) | |
493 | axssize = wi::to_offset (access_size); | |
62efd1c4 | 494 | |
a1108556 | 495 | access_ref aref; |
a62b740d | 496 | if (!m_ptr_qry.get_ref (ref, m_stmt, &aref, 0)) |
a1108556 | 497 | return false; |
62efd1c4 | 498 | |
a1108556 MS |
499 | if (aref.offset_in_range (axssize)) |
500 | return false; | |
3f9a497d | 501 | |
a1108556 | 502 | if (TREE_CODE (aref.ref) == SSA_NAME) |
62efd1c4 | 503 | { |
a1108556 MS |
504 | gimple *def = SSA_NAME_DEF_STMT (aref.ref); |
505 | if (is_gimple_call (def)) | |
3f9a497d | 506 | { |
a1108556 MS |
507 | /* Save the allocation call and the low bound on the size. */ |
508 | alloc_stmt = def; | |
509 | minbound = aref.sizrng[0]; | |
3f9a497d | 510 | } |
62efd1c4 | 511 | } |
a1108556 MS |
512 | |
513 | /* The range of the byte offset into the reference. Adjusted below. */ | |
514 | offset_int offrange[2] = { aref.offrng[0], aref.offrng[1] }; | |
515 | ||
516 | /* The type of the referenced object. */ | |
517 | tree reftype = TREE_TYPE (aref.ref); | |
518 | /* The size of the referenced array element. */ | |
519 | offset_int eltsize = 1; | |
a1108556 MS |
520 | if (POINTER_TYPE_P (reftype)) |
521 | reftype = TREE_TYPE (reftype); | |
a1108556 | 522 | |
b9cbf8c9 MS |
523 | if (TREE_CODE (reftype) == FUNCTION_TYPE) |
524 | /* Restore the original (pointer) type and avoid trying to create | |
525 | an array of functions (done below). */ | |
526 | reftype = TREE_TYPE (aref.ref); | |
527 | else | |
528 | { | |
529 | /* The byte size of the array has already been determined above | |
530 | based on a pointer ARG. Set ELTSIZE to the size of the type | |
531 | it points to and REFTYPE to the array with the size, rounded | |
532 | down as necessary. */ | |
533 | if (TREE_CODE (reftype) == ARRAY_TYPE) | |
534 | reftype = TREE_TYPE (reftype); | |
535 | if (tree refsize = TYPE_SIZE_UNIT (reftype)) | |
536 | if (TREE_CODE (refsize) == INTEGER_CST) | |
537 | eltsize = wi::to_offset (refsize); | |
538 | ||
539 | const offset_int nelts = aref.sizrng[1] / eltsize; | |
540 | reftype = build_printable_array_type (reftype, nelts.to_uhwi ()); | |
541 | } | |
62efd1c4 AH |
542 | |
543 | /* Compute the more permissive upper bound when IGNORE_OFF_BY_ONE | |
544 | is set (when taking the address of the one-past-last element | |
545 | of an array) but always use the stricter bound in diagnostics. */ | |
a1108556 | 546 | offset_int ubound = aref.sizrng[1]; |
62efd1c4 | 547 | if (ignore_off_by_one) |
3f9a497d | 548 | ubound += eltsize; |
62efd1c4 | 549 | |
3f9a497d | 550 | /* Set if the lower bound of the subscript is out of bounds. */ |
a1108556 | 551 | const bool lboob = (aref.sizrng[1] == 0 |
3f9a497d | 552 | || offrange[0] >= ubound |
a1108556 | 553 | || offrange[1] < 0); |
3f9a497d MS |
554 | /* Set if only the upper bound of the subscript is out of bounds. |
555 | This can happen when using a bigger type to index into an array | |
556 | of a smaller type, as is common with unsigned char. */ | |
3f9a497d MS |
557 | const bool uboob = !lboob && offrange[0] + axssize > ubound; |
558 | if (lboob || uboob) | |
62efd1c4 AH |
559 | { |
560 | /* Treat a reference to a non-array object as one to an array | |
561 | of a single element. */ | |
562 | if (TREE_CODE (reftype) != ARRAY_TYPE) | |
f72e3d8c | 563 | reftype = build_printable_array_type (reftype, 1); |
62efd1c4 AH |
564 | |
565 | /* Extract the element type out of MEM_REF and use its size | |
566 | to compute the index to print in the diagnostic; arrays | |
567 | in MEM_REF don't mean anything. A type with no size like | |
568 | void is as good as having a size of 1. */ | |
a1108556 | 569 | tree type = strip_array_types (TREE_TYPE (ref)); |
62efd1c4 AH |
570 | if (tree size = TYPE_SIZE_UNIT (type)) |
571 | { | |
572 | offrange[0] = offrange[0] / wi::to_offset (size); | |
573 | offrange[1] = offrange[1] / wi::to_offset (size); | |
574 | } | |
3f9a497d | 575 | } |
62efd1c4 | 576 | |
a1108556 | 577 | bool warned = false; |
3f9a497d MS |
578 | if (lboob) |
579 | { | |
62efd1c4 | 580 | if (offrange[0] == offrange[1]) |
7c01d029 | 581 | warned = warning_at (location, OPT_Warray_bounds_, |
62efd1c4 AH |
582 | "array subscript %wi is outside array bounds " |
583 | "of %qT", | |
584 | offrange[0].to_shwi (), reftype); | |
585 | else | |
7c01d029 | 586 | warned = warning_at (location, OPT_Warray_bounds_, |
62efd1c4 AH |
587 | "array subscript [%wi, %wi] is outside " |
588 | "array bounds of %qT", | |
589 | offrange[0].to_shwi (), | |
590 | offrange[1].to_shwi (), reftype); | |
3f9a497d MS |
591 | } |
592 | else if (uboob && !ignore_off_by_one) | |
593 | { | |
594 | tree backtype = reftype; | |
595 | if (alloc_stmt) | |
596 | /* If the memory was dynamically allocated refer to it as if | |
597 | it were an untyped array of bytes. */ | |
598 | backtype = build_array_type_nelts (unsigned_char_type_node, | |
a1108556 | 599 | aref.sizrng[1].to_uhwi ()); |
3f9a497d | 600 | |
7c01d029 | 601 | warned = warning_at (location, OPT_Warray_bounds_, |
3f9a497d MS |
602 | "array subscript %<%T[%wi]%> is partly " |
603 | "outside array bounds of %qT", | |
604 | axstype, offrange[0].to_shwi (), backtype); | |
605 | } | |
606 | ||
607 | if (warned) | |
608 | { | |
a1108556 MS |
609 | /* TODO: Determine the access from the statement and use it. */ |
610 | aref.inform_access (access_none); | |
7c01d029 | 611 | suppress_warning (ref, OPT_Warray_bounds_); |
3f9a497d | 612 | return true; |
62efd1c4 AH |
613 | } |
614 | ||
615 | if (warn_array_bounds < 2) | |
616 | return false; | |
617 | ||
618 | /* At level 2 check also intermediate offsets. */ | |
619 | int i = 0; | |
a1108556 | 620 | if (aref.offmax[i] < -aref.sizrng[1] || aref.offmax[i = 1] > ubound) |
62efd1c4 | 621 | { |
bb04f9f2 | 622 | HOST_WIDE_INT tmpidx = (aref.offmax[i] / eltsize).to_shwi (); |
62efd1c4 | 623 | |
7c01d029 | 624 | if (warning_at (location, OPT_Warray_bounds_, |
62efd1c4 AH |
625 | "intermediate array offset %wi is outside array bounds " |
626 | "of %qT", tmpidx, reftype)) | |
627 | { | |
7c01d029 | 628 | suppress_warning (ref, OPT_Warray_bounds_); |
62efd1c4 AH |
629 | return true; |
630 | } | |
631 | } | |
632 | ||
633 | return false; | |
634 | } | |
635 | ||
636 | /* Searches if the expr T, located at LOCATION computes | |
637 | address of an ARRAY_REF, and call check_array_ref on it. */ | |
638 | ||
639 | void | |
dd44445f AH |
640 | array_bounds_checker::check_addr_expr (location_t location, tree t, |
641 | gimple *stmt) | |
62efd1c4 | 642 | { |
07bd5544 MS |
643 | /* For the most significant subscript only, accept taking the address |
644 | of the just-past-the-end element. */ | |
645 | bool ignore_off_by_one = true; | |
646 | ||
62efd1c4 AH |
647 | /* Check each ARRAY_REF and MEM_REF in the reference chain. */ |
648 | do | |
649 | { | |
650 | bool warned = false; | |
651 | if (TREE_CODE (t) == ARRAY_REF) | |
07bd5544 | 652 | { |
dd44445f | 653 | warned = check_array_ref (location, t, stmt, ignore_off_by_one); |
07bd5544 MS |
654 | ignore_off_by_one = false; |
655 | } | |
62efd1c4 | 656 | else if (TREE_CODE (t) == MEM_REF) |
07bd5544 | 657 | warned = check_mem_ref (location, t, ignore_off_by_one); |
62efd1c4 AH |
658 | |
659 | if (warned) | |
7c01d029 | 660 | suppress_warning (t, OPT_Warray_bounds_); |
62efd1c4 AH |
661 | |
662 | t = TREE_OPERAND (t, 0); | |
663 | } | |
664 | while (handled_component_p (t) || TREE_CODE (t) == MEM_REF); | |
665 | ||
666 | if (TREE_CODE (t) != MEM_REF | |
667 | || TREE_CODE (TREE_OPERAND (t, 0)) != ADDR_EXPR | |
7c01d029 | 668 | || warning_suppressed_p (t, OPT_Warray_bounds_)) |
62efd1c4 AH |
669 | return; |
670 | ||
671 | tree tem = TREE_OPERAND (TREE_OPERAND (t, 0), 0); | |
672 | tree low_bound, up_bound, el_sz; | |
673 | if (TREE_CODE (TREE_TYPE (tem)) != ARRAY_TYPE | |
674 | || TREE_CODE (TREE_TYPE (TREE_TYPE (tem))) == ARRAY_TYPE | |
675 | || !TYPE_DOMAIN (TREE_TYPE (tem))) | |
676 | return; | |
677 | ||
678 | low_bound = TYPE_MIN_VALUE (TYPE_DOMAIN (TREE_TYPE (tem))); | |
679 | up_bound = TYPE_MAX_VALUE (TYPE_DOMAIN (TREE_TYPE (tem))); | |
680 | el_sz = TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (tem))); | |
681 | if (!low_bound | |
682 | || TREE_CODE (low_bound) != INTEGER_CST | |
683 | || !up_bound | |
684 | || TREE_CODE (up_bound) != INTEGER_CST | |
685 | || !el_sz | |
686 | || TREE_CODE (el_sz) != INTEGER_CST) | |
687 | return; | |
688 | ||
689 | offset_int idx; | |
690 | if (!mem_ref_offset (t).is_constant (&idx)) | |
691 | return; | |
692 | ||
693 | bool warned = false; | |
694 | idx = wi::sdiv_trunc (idx, wi::to_offset (el_sz)); | |
695 | if (idx < 0) | |
696 | { | |
697 | if (dump_file && (dump_flags & TDF_DETAILS)) | |
698 | { | |
699 | fprintf (dump_file, "Array bound warning for "); | |
700 | dump_generic_expr (MSG_NOTE, TDF_SLIM, t); | |
701 | fprintf (dump_file, "\n"); | |
702 | } | |
7c01d029 | 703 | warned = warning_at (location, OPT_Warray_bounds_, |
62efd1c4 AH |
704 | "array subscript %wi is below " |
705 | "array bounds of %qT", | |
706 | idx.to_shwi (), TREE_TYPE (tem)); | |
707 | } | |
708 | else if (idx > (wi::to_offset (up_bound) | |
709 | - wi::to_offset (low_bound) + 1)) | |
710 | { | |
711 | if (dump_file && (dump_flags & TDF_DETAILS)) | |
712 | { | |
713 | fprintf (dump_file, "Array bound warning for "); | |
714 | dump_generic_expr (MSG_NOTE, TDF_SLIM, t); | |
715 | fprintf (dump_file, "\n"); | |
716 | } | |
7c01d029 | 717 | warned = warning_at (location, OPT_Warray_bounds_, |
62efd1c4 AH |
718 | "array subscript %wu is above " |
719 | "array bounds of %qT", | |
720 | idx.to_uhwi (), TREE_TYPE (tem)); | |
721 | } | |
722 | ||
723 | if (warned) | |
724 | { | |
725 | if (DECL_P (t)) | |
726 | inform (DECL_SOURCE_LOCATION (t), "while referencing %qD", t); | |
727 | ||
7c01d029 | 728 | suppress_warning (t, OPT_Warray_bounds_); |
62efd1c4 AH |
729 | } |
730 | } | |
731 | ||
f3daa6c0 MS |
732 | /* Return true if T is a reference to a member of a base class that's within |
733 | the bounds of the enclosing complete object. The function "hacks" around | |
734 | problems discussed in pr98266 and pr97595. */ | |
735 | ||
736 | static bool | |
9a27acc3 | 737 | inbounds_memaccess_p (tree t, gimple *stmt) |
f3daa6c0 MS |
738 | { |
739 | if (TREE_CODE (t) != COMPONENT_REF) | |
740 | return false; | |
741 | ||
742 | tree mref = TREE_OPERAND (t, 0); | |
743 | if (TREE_CODE (mref) != MEM_REF) | |
744 | return false; | |
745 | ||
746 | /* Consider the access if its type is a derived class. */ | |
747 | tree mreftype = TREE_TYPE (mref); | |
748 | if (!RECORD_OR_UNION_TYPE_P (mreftype) | |
749 | || !TYPE_BINFO (mreftype)) | |
750 | return false; | |
751 | ||
752 | /* Compute the size of the referenced object (it could be dynamically | |
753 | allocated). */ | |
754 | access_ref aref; // unused | |
755 | tree refop = TREE_OPERAND (mref, 0); | |
9a27acc3 | 756 | tree refsize = compute_objsize (refop, stmt, 1, &aref); |
f3daa6c0 MS |
757 | if (!refsize || TREE_CODE (refsize) != INTEGER_CST) |
758 | return false; | |
759 | ||
760 | /* Compute the byte offset of the member within its enclosing class. */ | |
761 | tree fld = TREE_OPERAND (t, 1); | |
762 | tree fldpos = byte_position (fld); | |
763 | if (TREE_CODE (fldpos) != INTEGER_CST) | |
764 | return false; | |
765 | ||
766 | /* Compute the byte offset of the member with the outermost complete | |
767 | object by adding its offset computed above to the MEM_REF offset. */ | |
768 | tree refoff = TREE_OPERAND (mref, 1); | |
769 | tree fldoff = int_const_binop (PLUS_EXPR, fldpos, refoff); | |
30b10dac MS |
770 | /* Return false if the member offset is greater or equal to the size |
771 | of the complete object. */ | |
772 | if (!tree_int_cst_lt (fldoff, refsize)) | |
773 | return false; | |
774 | ||
775 | tree fldsiz = DECL_SIZE_UNIT (fld); | |
776 | if (!fldsiz || TREE_CODE (fldsiz) != INTEGER_CST) | |
777 | return false; | |
f3daa6c0 | 778 | |
30b10dac MS |
779 | /* Return true if the offset just past the end of the member is less |
780 | than or equal to the size of the complete object. */ | |
781 | tree fldend = int_const_binop (PLUS_EXPR, fldoff, fldsiz); | |
782 | return tree_int_cst_le (fldend, refsize); | |
f3daa6c0 MS |
783 | } |
784 | ||
62efd1c4 AH |
785 | /* Callback for walk_tree to check a tree for out of bounds array |
786 | accesses. The array_bounds_checker class is passed in DATA. */ | |
787 | ||
788 | tree | |
789 | array_bounds_checker::check_array_bounds (tree *tp, int *walk_subtree, | |
790 | void *data) | |
791 | { | |
792 | tree t = *tp; | |
793 | struct walk_stmt_info *wi = (struct walk_stmt_info *) data; | |
9a27acc3 | 794 | |
62efd1c4 AH |
795 | location_t location; |
796 | ||
797 | if (EXPR_HAS_LOCATION (t)) | |
798 | location = EXPR_LOCATION (t); | |
799 | else | |
800 | location = gimple_location (wi->stmt); | |
801 | ||
3339220b | 802 | *walk_subtree = true; |
62efd1c4 AH |
803 | |
804 | bool warned = false; | |
805 | array_bounds_checker *checker = (array_bounds_checker *) wi->info; | |
9a27acc3 MS |
806 | gcc_assert (checker->m_stmt == wi->stmt); |
807 | ||
62efd1c4 | 808 | if (TREE_CODE (t) == ARRAY_REF) |
dd44445f | 809 | warned = checker->check_array_ref (location, t, wi->stmt, |
62efd1c4 AH |
810 | false/*ignore_off_by_one*/); |
811 | else if (TREE_CODE (t) == MEM_REF) | |
812 | warned = checker->check_mem_ref (location, t, | |
813 | false /*ignore_off_by_one*/); | |
814 | else if (TREE_CODE (t) == ADDR_EXPR) | |
815 | { | |
dd44445f | 816 | checker->check_addr_expr (location, t, wi->stmt); |
f3daa6c0 | 817 | *walk_subtree = false; |
62efd1c4 | 818 | } |
9a27acc3 | 819 | else if (inbounds_memaccess_p (t, wi->stmt)) |
f3daa6c0 MS |
820 | /* Hack: Skip MEM_REF checks in accesses to a member of a base class |
821 | at an offset that's within the bounds of the enclosing object. | |
822 | See pr98266 and pr97595. */ | |
823 | *walk_subtree = false; | |
824 | ||
e9e2bad7 MS |
825 | /* Propagate the no-warning bit to the outer statement to avoid also |
826 | issuing -Wstringop-overflow/-overread for the out-of-bounds accesses. */ | |
62efd1c4 | 827 | if (warned) |
7c01d029 | 828 | suppress_warning (wi->stmt, OPT_Warray_bounds_); |
62efd1c4 AH |
829 | |
830 | return NULL_TREE; | |
831 | } | |
832 | ||
833 | /* A dom_walker subclass for use by check_all_array_refs, to walk over | |
834 | all statements of all reachable BBs and call check_array_bounds on | |
835 | them. */ | |
836 | ||
837 | class check_array_bounds_dom_walker : public dom_walker | |
838 | { | |
839 | public: | |
840 | check_array_bounds_dom_walker (array_bounds_checker *checker) | |
841 | : dom_walker (CDI_DOMINATORS, | |
842 | /* Discover non-executable edges, preserving EDGE_EXECUTABLE | |
843 | flags, so that we can merge in information on | |
844 | non-executable edges from vrp_folder . */ | |
845 | REACHABLE_BLOCKS_PRESERVING_FLAGS), | |
846 | checker (checker) { } | |
847 | ~check_array_bounds_dom_walker () {} | |
848 | ||
ff171cb1 | 849 | edge before_dom_children (basic_block) final override; |
62efd1c4 AH |
850 | |
851 | private: | |
852 | array_bounds_checker *checker; | |
853 | }; | |
854 | ||
855 | /* Implementation of dom_walker::before_dom_children. | |
856 | ||
857 | Walk over all statements of BB and call check_array_bounds on them, | |
858 | and determine if there's a unique successor edge. */ | |
859 | ||
860 | edge | |
861 | check_array_bounds_dom_walker::before_dom_children (basic_block bb) | |
862 | { | |
863 | gimple_stmt_iterator si; | |
864 | for (si = gsi_start_bb (bb); !gsi_end_p (si); gsi_next (&si)) | |
865 | { | |
866 | gimple *stmt = gsi_stmt (si); | |
62efd1c4 AH |
867 | if (!gimple_has_location (stmt) |
868 | || is_gimple_debug (stmt)) | |
869 | continue; | |
870 | ||
9a27acc3 | 871 | struct walk_stmt_info wi{ }; |
62efd1c4 | 872 | wi.info = checker; |
9a27acc3 | 873 | checker->m_stmt = stmt; |
62efd1c4 AH |
874 | |
875 | walk_gimple_op (stmt, array_bounds_checker::check_array_bounds, &wi); | |
876 | } | |
877 | ||
878 | /* Determine if there's a unique successor edge, and if so, return | |
879 | that back to dom_walker, ensuring that we don't visit blocks that | |
880 | became unreachable during the VRP propagation | |
881 | (PR tree-optimization/83312). */ | |
882 | return find_taken_edge (bb, NULL_TREE); | |
883 | } | |
884 | ||
885 | void | |
886 | array_bounds_checker::check () | |
887 | { | |
888 | check_array_bounds_dom_walker w (this); | |
889 | w.walk (ENTRY_BLOCK_PTR_FOR_FN (fun)); | |
890 | } |