1 | /* UndefinedBehaviorSanitizer, undefined behavior detector. |
2 | Copyright (C) 2013-2023 Free Software Foundation, Inc. |
3 | Contributed by Marek Polacek <polacek@redhat.com> |
4 | |
5 | This file is part of GCC. |
6 | |
7 | GCC is free software; you can redistribute it and/or modify it under |
8 | the terms of the GNU General Public License as published by the Free |
9 | Software Foundation; either version 3, or (at your option) any later |
10 | version. |
11 | |
12 | GCC is distributed in the hope that it will be useful, but WITHOUT ANY |
13 | WARRANTY; without even the implied warranty of MERCHANTABILITY or |
14 | FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
15 | for more details. |
16 | |
17 | You should have received a copy of the GNU General Public License |
18 | along with GCC; see the file COPYING3. If not see |
19 | <http://www.gnu.org/licenses/>. */ |
20 | |
21 | #include "config.h" |
22 | #include "system.h" |
23 | #include "coretypes.h" |
24 | #include "tm.h" |
25 | #include "c-family/c-common.h" |
26 | #include "ubsan.h" |
27 | #include "c-family/c-ubsan.h" |
28 | #include "stor-layout.h" |
29 | #include "builtins.h" |
30 | #include "gimplify.h" |
31 | #include "stringpool.h" |
32 | #include "attribs.h" |
33 | #include "asan.h" |
34 | #include "langhooks.h" |
35 | |
36 | /* Instrument division by zero and INT_MIN / -1. If not instrumenting, |
37 | return NULL_TREE. */ |
38 | |
39 | tree |
40 | ubsan_instrument_division (location_t loc, tree op0, tree op1) |
41 | { |
42 | tree t, tt, x = NULL_TREE; |
43 | tree type = TREE_TYPE (op0); |
44 | enum sanitize_code flag = SANITIZE_DIVIDE; |
45 | |
46 | /* At this point both operands should have the same type, |
47 | because they are already converted to RESULT_TYPE. |
48 | Use TYPE_MAIN_VARIANT since typedefs can confuse us. */ |
49 | tree top0 = TYPE_MAIN_VARIANT (type); |
50 | tree top1 = TYPE_MAIN_VARIANT (TREE_TYPE (op1)); |
51 | gcc_checking_assert (lang_hooks.types_compatible_p (top0, top1)); |
52 | |
53 | op0 = unshare_expr (op0); |
54 | op1 = unshare_expr (op1); |
55 | |
56 | if (INTEGRAL_TYPE_P (type) |
57 | && sanitize_flags_p (flag: SANITIZE_DIVIDE)) |
58 | t = fold_build2 (EQ_EXPR, boolean_type_node, |
59 | op1, build_int_cst (type, 0)); |
60 | else if (SCALAR_FLOAT_TYPE_P (type) |
61 | && sanitize_flags_p (flag: SANITIZE_FLOAT_DIVIDE)) |
62 | { |
63 | t = fold_build2 (EQ_EXPR, boolean_type_node, |
64 | op1, build_real (type, dconst0)); |
65 | flag = SANITIZE_FLOAT_DIVIDE; |
66 | } |
67 | else |
68 | t = NULL_TREE; |
69 | |
70 | /* We check INT_MIN / -1 only for signed types. */ |
71 | if (INTEGRAL_TYPE_P (type) |
72 | && sanitize_flags_p (flag: SANITIZE_SI_OVERFLOW) |
73 | && !TYPE_UNSIGNED (type)) |
74 | { |
75 | tt = fold_build2 (EQ_EXPR, boolean_type_node, unshare_expr (op1), |
76 | build_int_cst (type, -1)); |
77 | x = fold_build2 (EQ_EXPR, boolean_type_node, op0, |
78 | TYPE_MIN_VALUE (type)); |
79 | x = fold_build2 (TRUTH_AND_EXPR, boolean_type_node, x, tt); |
80 | if (t == NULL_TREE || integer_zerop (t)) |
81 | { |
82 | t = x; |
83 | x = NULL_TREE; |
84 | flag = SANITIZE_SI_OVERFLOW; |
85 | } |
86 | else if ((((flag_sanitize_trap & SANITIZE_DIVIDE) == 0) |
87 | == ((flag_sanitize_trap & SANITIZE_SI_OVERFLOW) == 0)) |
88 | && (((flag_sanitize_recover & SANITIZE_DIVIDE) == 0) |
89 | == ((flag_sanitize_recover & SANITIZE_SI_OVERFLOW) == 0))) |
90 | { |
91 | t = fold_build2 (TRUTH_OR_EXPR, boolean_type_node, t, x); |
92 | x = NULL_TREE; |
93 | } |
94 | else if (integer_zerop (x)) |
95 | x = NULL_TREE; |
96 | } |
97 | else if (t == NULL_TREE) |
98 | return NULL_TREE; |
99 | |
100 | /* If the condition was folded to 0, no need to instrument |
101 | this expression. */ |
102 | if (integer_zerop (t)) |
103 | return NULL_TREE; |
104 | |
105 | /* In case we have a SAVE_EXPR in a conditional context, we need to |
106 | make sure it gets evaluated before the condition. */ |
107 | t = fold_build2 (COMPOUND_EXPR, TREE_TYPE (t), unshare_expr (op0), t); |
108 | t = fold_build2 (COMPOUND_EXPR, TREE_TYPE (t), unshare_expr (op1), t); |
109 | if ((flag_sanitize_trap & flag) && x == NULL_TREE) |
110 | tt = build_call_expr_loc (loc, builtin_decl_explicit (fncode: BUILT_IN_TRAP), 0); |
111 | else |
112 | { |
113 | tree data = ubsan_create_data ("__ubsan_overflow_data" , 1, &loc, |
114 | ubsan_type_descriptor (type), NULL_TREE, |
115 | NULL_TREE); |
116 | data = build_fold_addr_expr_loc (loc, data); |
117 | if (flag_sanitize_trap & flag) |
118 | tt = build_call_expr_loc (loc, builtin_decl_explicit (fncode: BUILT_IN_TRAP), |
119 | 0); |
120 | else |
121 | { |
122 | enum built_in_function bcode |
123 | = (flag_sanitize_recover & flag) |
124 | ? BUILT_IN_UBSAN_HANDLE_DIVREM_OVERFLOW |
125 | : BUILT_IN_UBSAN_HANDLE_DIVREM_OVERFLOW_ABORT; |
126 | tt = builtin_decl_explicit (fncode: bcode); |
127 | op0 = unshare_expr (op0); |
128 | op1 = unshare_expr (op1); |
129 | tt = build_call_expr_loc (loc, tt, 3, data, ubsan_encode_value (op0), |
130 | ubsan_encode_value (op1)); |
131 | } |
132 | if (x) |
133 | { |
134 | tree xt; |
135 | if (flag_sanitize_trap & SANITIZE_SI_OVERFLOW) |
136 | xt = build_call_expr_loc (loc, |
137 | builtin_decl_explicit (fncode: BUILT_IN_TRAP), |
138 | 0); |
139 | else |
140 | { |
141 | enum built_in_function bcode |
142 | = (flag_sanitize_recover & SANITIZE_SI_OVERFLOW) |
143 | ? BUILT_IN_UBSAN_HANDLE_DIVREM_OVERFLOW |
144 | : BUILT_IN_UBSAN_HANDLE_DIVREM_OVERFLOW_ABORT; |
145 | xt = builtin_decl_explicit (fncode: bcode); |
146 | op0 = unshare_expr (op0); |
147 | op1 = unshare_expr (op1); |
148 | xt = build_call_expr_loc (loc, xt, 3, data, |
149 | ubsan_encode_value (op0), |
150 | ubsan_encode_value (op1)); |
151 | } |
152 | x = fold_build3 (COND_EXPR, void_type_node, x, xt, void_node); |
153 | } |
154 | } |
155 | t = fold_build3 (COND_EXPR, void_type_node, t, tt, x ? x : void_node); |
156 | |
157 | return t; |
158 | } |
159 | |
160 | /* Instrument left and right shifts. */ |
161 | |
162 | tree |
163 | ubsan_instrument_shift (location_t loc, enum tree_code code, |
164 | tree op0, tree op1) |
165 | { |
166 | tree t, tt = NULL_TREE; |
167 | tree type0 = TREE_TYPE (op0); |
168 | tree type1 = TREE_TYPE (op1); |
169 | if (!INTEGRAL_TYPE_P (type0)) |
170 | return NULL_TREE; |
171 | |
172 | tree op1_utype = unsigned_type_for (type1); |
173 | HOST_WIDE_INT op0_prec = TYPE_PRECISION (type0); |
174 | tree uprecm1 = build_int_cst (op1_utype, op0_prec - 1); |
175 | |
176 | op0 = unshare_expr (op0); |
177 | op1 = unshare_expr (op1); |
178 | |
179 | t = fold_convert_loc (loc, op1_utype, op1); |
180 | t = fold_build2 (GT_EXPR, boolean_type_node, t, uprecm1); |
181 | |
182 | /* If this is not a signed operation, don't perform overflow checks. |
183 | Also punt on bit-fields. */ |
184 | if (TYPE_OVERFLOW_WRAPS (type0) |
185 | || maybe_ne (a: GET_MODE_BITSIZE (TYPE_MODE (type0)), |
186 | TYPE_PRECISION (type0)) |
187 | || !sanitize_flags_p (flag: SANITIZE_SHIFT_BASE) |
188 | /* In C++20 and later, shifts are well defined except when |
189 | the second operand is not within bounds. */ |
190 | || cxx_dialect >= cxx20) |
191 | ; |
192 | |
193 | /* For signed x << y, in C99 and later, the following: |
194 | (unsigned) x >> (uprecm1 - y) |
195 | if non-zero, is undefined. */ |
196 | else if (code == LSHIFT_EXPR && flag_isoc99 && cxx_dialect < cxx11) |
197 | { |
198 | tree x = fold_build2 (MINUS_EXPR, op1_utype, uprecm1, |
199 | fold_convert (op1_utype, unshare_expr (op1))); |
200 | tt = fold_convert_loc (loc, unsigned_type_for (type0), op0); |
201 | tt = fold_build2 (RSHIFT_EXPR, TREE_TYPE (tt), tt, x); |
202 | tt = fold_build2 (NE_EXPR, boolean_type_node, tt, |
203 | build_int_cst (TREE_TYPE (tt), 0)); |
204 | } |
205 | |
206 | /* For signed x << y, in C++11 to C++17, the following: |
207 | x < 0 || ((unsigned) x >> (uprecm1 - y)) |
208 | if > 1, is undefined. */ |
209 | else if (code == LSHIFT_EXPR && cxx_dialect >= cxx11) |
210 | { |
211 | tree x = fold_build2 (MINUS_EXPR, op1_utype, uprecm1, |
212 | fold_convert (op1_utype, unshare_expr (op1))); |
213 | tt = fold_convert_loc (loc, unsigned_type_for (type0), |
214 | unshare_expr (op0)); |
215 | tt = fold_build2 (RSHIFT_EXPR, TREE_TYPE (tt), tt, x); |
216 | tt = fold_build2 (GT_EXPR, boolean_type_node, tt, |
217 | build_int_cst (TREE_TYPE (tt), 1)); |
218 | x = fold_build2 (LT_EXPR, boolean_type_node, unshare_expr (op0), |
219 | build_int_cst (type0, 0)); |
220 | tt = fold_build2 (TRUTH_OR_EXPR, boolean_type_node, x, tt); |
221 | } |
222 | |
223 | /* If the condition was folded to 0, no need to instrument |
224 | this expression. */ |
225 | if (integer_zerop (t) && (tt == NULL_TREE || integer_zerop (tt))) |
226 | return NULL_TREE; |
227 | |
228 | /* In case we have a SAVE_EXPR in a conditional context, we need to |
229 | make sure it gets evaluated before the condition. */ |
230 | t = fold_build2 (COMPOUND_EXPR, TREE_TYPE (t), unshare_expr (op0), t); |
231 | t = fold_build2 (COMPOUND_EXPR, TREE_TYPE (t), unshare_expr (op1), t); |
232 | |
233 | enum sanitize_code recover_kind = SANITIZE_SHIFT_EXPONENT; |
234 | tree else_t = void_node; |
235 | if (tt) |
236 | { |
237 | if (!sanitize_flags_p (flag: SANITIZE_SHIFT_EXPONENT)) |
238 | { |
239 | t = fold_build1 (TRUTH_NOT_EXPR, boolean_type_node, t); |
240 | t = fold_build2 (TRUTH_AND_EXPR, boolean_type_node, t, tt); |
241 | recover_kind = SANITIZE_SHIFT_BASE; |
242 | } |
243 | else |
244 | { |
245 | if (((!(flag_sanitize_trap & SANITIZE_SHIFT_EXPONENT)) |
246 | == (!(flag_sanitize_trap & SANITIZE_SHIFT_BASE))) |
247 | && ((!(flag_sanitize_recover & SANITIZE_SHIFT_EXPONENT)) |
248 | == (!(flag_sanitize_recover & SANITIZE_SHIFT_BASE)))) |
249 | t = fold_build2 (TRUTH_OR_EXPR, boolean_type_node, t, tt); |
250 | else |
251 | else_t = tt; |
252 | } |
253 | } |
254 | |
255 | if ((flag_sanitize_trap & recover_kind) && else_t == void_node) |
256 | tt = build_call_expr_loc (loc, builtin_decl_explicit (fncode: BUILT_IN_TRAP), 0); |
257 | else |
258 | { |
259 | tree utd0 = ubsan_type_descriptor (type0, UBSAN_PRINT_FORCE_INT); |
260 | tree data = ubsan_create_data ("__ubsan_shift_data" , 1, &loc, utd0, |
261 | ubsan_type_descriptor (type1), NULL_TREE, |
262 | NULL_TREE); |
263 | data = build_fold_addr_expr_loc (loc, data); |
264 | |
265 | if (flag_sanitize_trap & recover_kind) |
266 | tt = build_call_expr_loc (loc, builtin_decl_explicit (fncode: BUILT_IN_TRAP), 0); |
267 | else |
268 | { |
269 | enum built_in_function bcode |
270 | = (flag_sanitize_recover & recover_kind) |
271 | ? BUILT_IN_UBSAN_HANDLE_SHIFT_OUT_OF_BOUNDS |
272 | : BUILT_IN_UBSAN_HANDLE_SHIFT_OUT_OF_BOUNDS_ABORT; |
273 | tt = builtin_decl_explicit (fncode: bcode); |
274 | op0 = unshare_expr (op0); |
275 | op1 = unshare_expr (op1); |
276 | tt = build_call_expr_loc (loc, tt, 3, data, ubsan_encode_value (op0), |
277 | ubsan_encode_value (op1)); |
278 | } |
279 | if (else_t != void_node) |
280 | { |
281 | tree else_tt; |
282 | if (flag_sanitize_trap & SANITIZE_SHIFT_BASE) |
283 | else_tt |
284 | = build_call_expr_loc (loc, |
285 | builtin_decl_explicit (fncode: BUILT_IN_TRAP), 0); |
286 | else |
287 | { |
288 | enum built_in_function bcode |
289 | = (flag_sanitize_recover & SANITIZE_SHIFT_BASE) |
290 | ? BUILT_IN_UBSAN_HANDLE_SHIFT_OUT_OF_BOUNDS |
291 | : BUILT_IN_UBSAN_HANDLE_SHIFT_OUT_OF_BOUNDS_ABORT; |
292 | else_tt = builtin_decl_explicit (fncode: bcode); |
293 | op0 = unshare_expr (op0); |
294 | op1 = unshare_expr (op1); |
295 | else_tt = build_call_expr_loc (loc, else_tt, 3, data, |
296 | ubsan_encode_value (op0), |
297 | ubsan_encode_value (op1)); |
298 | } |
299 | else_t = fold_build3 (COND_EXPR, void_type_node, else_t, |
300 | else_tt, void_node); |
301 | } |
302 | } |
303 | t = fold_build3 (COND_EXPR, void_type_node, t, tt, else_t); |
304 | |
305 | return t; |
306 | } |
307 | |
308 | /* Instrument variable length array bound. */ |
309 | |
310 | tree |
311 | ubsan_instrument_vla (location_t loc, tree size) |
312 | { |
313 | tree type = TREE_TYPE (size); |
314 | tree t, tt; |
315 | |
316 | t = fold_build2 (LE_EXPR, boolean_type_node, size, build_int_cst (type, 0)); |
317 | if (flag_sanitize_trap & SANITIZE_VLA) |
318 | tt = build_call_expr_loc (loc, builtin_decl_explicit (fncode: BUILT_IN_TRAP), 0); |
319 | else |
320 | { |
321 | tree data = ubsan_create_data ("__ubsan_vla_data" , 1, &loc, |
322 | ubsan_type_descriptor (type), NULL_TREE, |
323 | NULL_TREE); |
324 | data = build_fold_addr_expr_loc (loc, data); |
325 | enum built_in_function bcode |
326 | = (flag_sanitize_recover & SANITIZE_VLA) |
327 | ? BUILT_IN_UBSAN_HANDLE_VLA_BOUND_NOT_POSITIVE |
328 | : BUILT_IN_UBSAN_HANDLE_VLA_BOUND_NOT_POSITIVE_ABORT; |
329 | tt = builtin_decl_explicit (fncode: bcode); |
330 | tt = build_call_expr_loc (loc, tt, 2, data, ubsan_encode_value (size)); |
331 | } |
332 | t = fold_build3 (COND_EXPR, void_type_node, t, tt, void_node); |
333 | |
334 | return t; |
335 | } |
336 | |
337 | /* Instrument missing return in C++ functions returning non-void. */ |
338 | |
339 | tree |
340 | ubsan_instrument_return (location_t loc) |
341 | { |
342 | if (flag_sanitize_trap & SANITIZE_RETURN) |
343 | /* pass_warn_function_return checks for BUILTINS_LOCATION. */ |
344 | return build_call_expr_loc (BUILTINS_LOCATION, |
345 | builtin_decl_explicit (fncode: BUILT_IN_TRAP), 0); |
346 | |
347 | tree data = ubsan_create_data ("__ubsan_missing_return_data" , 1, &loc, |
348 | NULL_TREE, NULL_TREE); |
349 | tree t = builtin_decl_explicit (fncode: BUILT_IN_UBSAN_HANDLE_MISSING_RETURN); |
350 | return build_call_expr_loc (loc, t, 1, build_fold_addr_expr_loc (loc, data)); |
351 | } |
352 | |
353 | /* Instrument array bounds for ARRAY_REFs. We create special builtin, |
354 | that gets expanded in the sanopt pass, and make an array dimension |
355 | of it. ARRAY is the array, *INDEX is an index to the array. |
356 | Return NULL_TREE if no instrumentation is emitted. |
357 | IGNORE_OFF_BY_ONE is true if the ARRAY_REF is inside an ADDR_EXPR. */ |
358 | |
359 | tree |
360 | ubsan_instrument_bounds (location_t loc, tree array, tree *index, |
361 | bool ignore_off_by_one) |
362 | { |
363 | tree type = TREE_TYPE (array); |
364 | tree domain = TYPE_DOMAIN (type); |
365 | |
366 | if (domain == NULL_TREE) |
367 | return NULL_TREE; |
368 | |
369 | tree bound = TYPE_MAX_VALUE (domain); |
370 | if (!bound) |
371 | { |
372 | /* Handle C [0] arrays, which have TYPE_MAX_VALUE NULL, like |
373 | C++ [0] arrays which have TYPE_MIN_VALUE 0 TYPE_MAX_VALUE -1. */ |
374 | if (!c_dialect_cxx () |
375 | && COMPLETE_TYPE_P (type) |
376 | && integer_zerop (TYPE_SIZE (type))) |
377 | bound = build_int_cst (TREE_TYPE (TYPE_MIN_VALUE (domain)), -1); |
378 | else |
379 | return NULL_TREE; |
380 | } |
381 | |
382 | bound = fold_build2 (PLUS_EXPR, TREE_TYPE (bound), bound, |
383 | build_int_cst (TREE_TYPE (bound), |
384 | 1 + ignore_off_by_one)); |
385 | |
386 | /* Detect flexible array members and suchlike, unless |
387 | -fsanitize=bounds-strict. */ |
388 | tree base = get_base_address (t: array); |
389 | if (!sanitize_flags_p (flag: SANITIZE_BOUNDS_STRICT) |
390 | && TREE_CODE (array) == COMPONENT_REF |
391 | && base && (INDIRECT_REF_P (base) || TREE_CODE (base) == MEM_REF)) |
392 | { |
393 | tree next = NULL_TREE; |
394 | tree cref = array; |
395 | |
396 | /* Walk all structs/unions. */ |
397 | while (TREE_CODE (cref) == COMPONENT_REF) |
398 | { |
399 | if (TREE_CODE (TREE_TYPE (TREE_OPERAND (cref, 0))) == RECORD_TYPE) |
400 | for (next = DECL_CHAIN (TREE_OPERAND (cref, 1)); |
401 | next && TREE_CODE (next) != FIELD_DECL; |
402 | next = DECL_CHAIN (next)) |
403 | ; |
404 | if (next) |
405 | /* Not a last element. Instrument it. */ |
406 | break; |
407 | if (TREE_CODE (TREE_TYPE (TREE_OPERAND (cref, 1))) == ARRAY_TYPE |
408 | && !c_dialect_cxx ()) |
409 | { |
410 | unsigned l |
411 | = c_strict_flex_array_level_of (TREE_OPERAND (cref, 1)); |
412 | tree type2 = TREE_TYPE (TREE_OPERAND (cref, 1)); |
413 | if (TYPE_DOMAIN (type2) != NULL_TREE) |
414 | { |
415 | tree max = TYPE_MAX_VALUE (TYPE_DOMAIN (type2)); |
416 | if (max == NULL_TREE) |
417 | { |
418 | /* C [0] */ |
419 | if (COMPLETE_TYPE_P (type2) |
420 | && integer_zerop (TYPE_SIZE (type2)) |
421 | && l == 3) |
422 | next = TREE_OPERAND (cref, 1); |
423 | } |
424 | else if (TREE_CODE (max) == INTEGER_CST) |
425 | { |
426 | if (c_dialect_cxx () |
427 | && integer_all_onesp (max)) |
428 | { |
429 | /* C++ [0] */ |
430 | if (l == 3) |
431 | next = TREE_OPERAND (cref, 1); |
432 | } |
433 | else if (integer_zerop (max)) |
434 | { |
435 | /* C/C++ [1] */ |
436 | if (l >= 2) |
437 | next = TREE_OPERAND (cref, 1); |
438 | } |
439 | else if (l >= 1) |
440 | next = TREE_OPERAND (cref, 1); |
441 | } |
442 | } |
443 | if (next) |
444 | break; |
445 | } |
446 | /* Ok, this is the last field of the structure/union. But the |
447 | aggregate containing the field must be the last field too, |
448 | recursively. */ |
449 | cref = TREE_OPERAND (cref, 0); |
450 | } |
451 | if (!next) |
452 | /* Don't instrument this flexible array member-like array in non-strict |
453 | -fsanitize=bounds mode. */ |
454 | return NULL_TREE; |
455 | } |
456 | |
457 | /* Don't emit instrumentation in the most common cases. */ |
458 | tree idx = NULL_TREE; |
459 | if (TREE_CODE (*index) == INTEGER_CST) |
460 | idx = *index; |
461 | else if (TREE_CODE (*index) == BIT_AND_EXPR |
462 | && TREE_CODE (TREE_OPERAND (*index, 1)) == INTEGER_CST) |
463 | idx = TREE_OPERAND (*index, 1); |
464 | if (idx |
465 | && TREE_CODE (bound) == INTEGER_CST |
466 | && tree_int_cst_sgn (idx) >= 0 |
467 | && tree_int_cst_lt (t1: idx, t2: bound)) |
468 | return NULL_TREE; |
469 | |
470 | *index = save_expr (*index); |
471 | /* Create a "(T *) 0" tree node to describe the array type. */ |
472 | tree zero_with_type = build_int_cst (build_pointer_type (type), 0); |
473 | return build_call_expr_internal_loc (loc, IFN_UBSAN_BOUNDS, |
474 | void_type_node, 3, zero_with_type, |
475 | *index, bound); |
476 | } |
477 | |
478 | /* Return true iff T is an array that was instrumented by SANITIZE_BOUNDS. */ |
479 | |
480 | bool |
481 | ubsan_array_ref_instrumented_p (const_tree t) |
482 | { |
483 | if (TREE_CODE (t) != ARRAY_REF) |
484 | return false; |
485 | |
486 | tree op1 = TREE_OPERAND (t, 1); |
487 | return TREE_CODE (op1) == COMPOUND_EXPR |
488 | && TREE_CODE (TREE_OPERAND (op1, 0)) == CALL_EXPR |
489 | && CALL_EXPR_FN (TREE_OPERAND (op1, 0)) == NULL_TREE |
490 | && CALL_EXPR_IFN (TREE_OPERAND (op1, 0)) == IFN_UBSAN_BOUNDS; |
491 | } |
492 | |
493 | /* Instrument an ARRAY_REF, if it hasn't already been instrumented. |
494 | IGNORE_OFF_BY_ONE is true if the ARRAY_REF is inside a ADDR_EXPR. */ |
495 | |
496 | void |
497 | ubsan_maybe_instrument_array_ref (tree *expr_p, bool ignore_off_by_one) |
498 | { |
499 | if (!ubsan_array_ref_instrumented_p (t: *expr_p) |
500 | && sanitize_flags_p (flag: SANITIZE_BOUNDS | SANITIZE_BOUNDS_STRICT) |
501 | && current_function_decl != NULL_TREE) |
502 | { |
503 | tree op0 = TREE_OPERAND (*expr_p, 0); |
504 | tree op1 = TREE_OPERAND (*expr_p, 1); |
505 | tree e = ubsan_instrument_bounds (EXPR_LOCATION (*expr_p), array: op0, index: &op1, |
506 | ignore_off_by_one); |
507 | if (e != NULL_TREE) |
508 | TREE_OPERAND (*expr_p, 1) = build2 (COMPOUND_EXPR, TREE_TYPE (op1), |
509 | e, op1); |
510 | } |
511 | } |
512 | |
513 | static tree |
514 | ubsan_maybe_instrument_reference_or_call (location_t loc, tree op, tree ptype, |
515 | enum ubsan_null_ckind ckind) |
516 | { |
517 | if (!sanitize_flags_p (flag: SANITIZE_ALIGNMENT | SANITIZE_NULL) |
518 | || current_function_decl == NULL_TREE) |
519 | return NULL_TREE; |
520 | |
521 | tree type = TREE_TYPE (ptype); |
522 | tree orig_op = op; |
523 | bool instrument = false; |
524 | unsigned int mina = 0; |
525 | |
526 | if (sanitize_flags_p (flag: SANITIZE_ALIGNMENT)) |
527 | { |
528 | mina = min_align_of_type (type); |
529 | if (mina <= 1) |
530 | mina = 0; |
531 | } |
532 | while ((TREE_CODE (op) == NOP_EXPR |
533 | || TREE_CODE (op) == NON_LVALUE_EXPR) |
534 | && TREE_CODE (TREE_TYPE (op)) == POINTER_TYPE) |
535 | op = TREE_OPERAND (op, 0); |
536 | if (TREE_CODE (op) == NOP_EXPR |
537 | && TREE_CODE (TREE_TYPE (op)) == REFERENCE_TYPE) |
538 | { |
539 | if (mina && mina > min_align_of_type (TREE_TYPE (TREE_TYPE (op)))) |
540 | instrument = true; |
541 | } |
542 | else |
543 | { |
544 | if (sanitize_flags_p (flag: SANITIZE_NULL) && TREE_CODE (op) == ADDR_EXPR) |
545 | { |
546 | bool strict_overflow_p = false; |
547 | /* tree_single_nonzero_warnv_p will not return true for non-weak |
548 | non-automatic decls with -fno-delete-null-pointer-checks, |
549 | which is disabled during -fsanitize=null. We don't want to |
550 | instrument those, just weak vars though. */ |
551 | int save_flag_delete_null_pointer_checks |
552 | = flag_delete_null_pointer_checks; |
553 | flag_delete_null_pointer_checks = 1; |
554 | if (!tree_single_nonzero_warnv_p (op, &strict_overflow_p) |
555 | || strict_overflow_p) |
556 | instrument = true; |
557 | flag_delete_null_pointer_checks |
558 | = save_flag_delete_null_pointer_checks; |
559 | } |
560 | else if (sanitize_flags_p (flag: SANITIZE_NULL)) |
561 | instrument = true; |
562 | if (mina && mina > 1) |
563 | { |
564 | if (!POINTER_TYPE_P (TREE_TYPE (op)) |
565 | || mina > get_pointer_alignment (op) / BITS_PER_UNIT) |
566 | instrument = true; |
567 | } |
568 | } |
569 | if (!instrument) |
570 | return NULL_TREE; |
571 | op = save_expr (orig_op); |
572 | gcc_assert (POINTER_TYPE_P (ptype)); |
573 | if (TREE_CODE (ptype) == REFERENCE_TYPE) |
574 | ptype = build_pointer_type (TREE_TYPE (ptype)); |
575 | tree kind = build_int_cst (ptype, ckind); |
576 | tree align = build_int_cst (pointer_sized_int_node, mina); |
577 | tree call |
578 | = build_call_expr_internal_loc (loc, IFN_UBSAN_NULL, void_type_node, |
579 | 3, op, kind, align); |
580 | TREE_SIDE_EFFECTS (call) = 1; |
581 | return fold_build2 (COMPOUND_EXPR, TREE_TYPE (op), call, op); |
582 | } |
583 | |
584 | /* Instrument a NOP_EXPR to REFERENCE_TYPE or INTEGER_CST with REFERENCE_TYPE |
585 | type if needed. */ |
586 | |
587 | void |
588 | ubsan_maybe_instrument_reference (tree *stmt_p) |
589 | { |
590 | tree stmt = *stmt_p; |
591 | tree op = stmt; |
592 | if (TREE_CODE (stmt) == NOP_EXPR) |
593 | op = TREE_OPERAND (stmt, 0); |
594 | op = ubsan_maybe_instrument_reference_or_call (EXPR_LOCATION (stmt), op, |
595 | TREE_TYPE (stmt), |
596 | ckind: UBSAN_REF_BINDING); |
597 | if (op) |
598 | { |
599 | if (TREE_CODE (stmt) == NOP_EXPR) |
600 | TREE_OPERAND (stmt, 0) = op; |
601 | else |
602 | *stmt_p = op; |
603 | } |
604 | } |
605 | |
606 | /* Instrument a CALL_EXPR to a method if needed. */ |
607 | |
608 | void |
609 | ubsan_maybe_instrument_member_call (tree stmt, bool is_ctor) |
610 | { |
611 | if (call_expr_nargs (stmt) == 0) |
612 | return; |
613 | tree op = CALL_EXPR_ARG (stmt, 0); |
614 | if (op == error_mark_node |
615 | || !POINTER_TYPE_P (TREE_TYPE (op))) |
616 | return; |
617 | op = ubsan_maybe_instrument_reference_or_call (EXPR_LOCATION (stmt), op, |
618 | TREE_TYPE (op), |
619 | ckind: is_ctor ? UBSAN_CTOR_CALL |
620 | : UBSAN_MEMBER_CALL); |
621 | if (op) |
622 | CALL_EXPR_ARG (stmt, 0) = op; |
623 | } |
624 | |