Branch data Line data Source code
1 : : /* Bounds-checking of reads and writes to memory regions.
2 : : Copyright (C) 2019-2024 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 it
7 : : 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, but
12 : : WITHOUT ANY WARRANTY; without even the implied warranty of
13 : : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 : : 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 : : #define INCLUDE_VECTOR
22 : : #include "system.h"
23 : : #include "coretypes.h"
24 : : #include "make-unique.h"
25 : : #include "tree.h"
26 : : #include "function.h"
27 : : #include "basic-block.h"
28 : : #include "intl.h"
29 : : #include "gimple.h"
30 : : #include "gimple-iterator.h"
31 : : #include "diagnostic-core.h"
32 : : #include "diagnostic-diagram.h"
33 : : #include "diagnostic-format-sarif.h"
34 : : #include "analyzer/analyzer.h"
35 : : #include "analyzer/analyzer-logging.h"
36 : : #include "analyzer/region-model.h"
37 : : #include "analyzer/checker-event.h"
38 : : #include "analyzer/checker-path.h"
39 : : #include "analyzer/access-diagram.h"
40 : :
41 : : #if ENABLE_ANALYZER
42 : :
43 : : namespace ana {
44 : :
45 : : /* Abstract base class for all out-of-bounds warnings. */
46 : :
47 : : class out_of_bounds : public pending_diagnostic
48 : : {
49 : : public:
50 : : class oob_region_creation_event_capacity : public region_creation_event_capacity
51 : : {
52 : : public:
53 : 436 : oob_region_creation_event_capacity (tree byte_capacity,
54 : : const event_loc_info &loc_info,
55 : : out_of_bounds &oob)
56 : 436 : : region_creation_event_capacity (byte_capacity,
57 : : loc_info),
58 : 436 : m_oob (oob)
59 : : {
60 : : }
61 : 436 : void prepare_for_emission (checker_path *path,
62 : : pending_diagnostic *pd,
63 : : diagnostic_event_id_t emission_id) override
64 : : {
65 : 436 : region_creation_event_capacity::prepare_for_emission (path,
66 : : pd,
67 : : emission_id);
68 : 436 : m_oob.m_region_creation_event_id = emission_id;
69 : 436 : }
70 : : private:
71 : : out_of_bounds &m_oob;
72 : : };
73 : :
74 : 745 : out_of_bounds (const region_model &model,
75 : : const region *reg,
76 : : tree diag_arg,
77 : : const svalue *sval_hint)
78 : 436 : : m_model (model), m_reg (reg), m_diag_arg (diag_arg), m_sval_hint (sval_hint)
79 : : {}
80 : :
81 : 715 : bool subclass_equal_p (const pending_diagnostic &base_other) const override
82 : : {
83 : 715 : const out_of_bounds &other
84 : : (static_cast <const out_of_bounds &>(base_other));
85 : 715 : return (m_reg == other.m_reg
86 : 715 : && pending_diagnostic::same_tree_p (m_diag_arg, other.m_diag_arg));
87 : : }
88 : :
89 : 1288 : int get_controlling_option () const final override
90 : : {
91 : 1288 : return OPT_Wanalyzer_out_of_bounds;
92 : : }
93 : :
94 : 551 : void mark_interesting_stuff (interesting_t *interest) final override
95 : : {
96 : 551 : interest->add_region_creation (m_reg->get_base_region ());
97 : 551 : }
98 : :
99 : 203 : void add_region_creation_events (const region *,
100 : : tree byte_capacity,
101 : : const event_loc_info &loc_info,
102 : : checker_path &emission_path) override
103 : : {
104 : : /* The memory space is described in the diagnostic message itself,
105 : : so we don't need an event for that. */
106 : 203 : if (byte_capacity)
107 : 129 : emission_path.add_event
108 : 129 : (make_unique<oob_region_creation_event_capacity> (byte_capacity,
109 : : loc_info,
110 : : *this));
111 : 203 : }
112 : :
113 : 4 : void maybe_add_sarif_properties (sarif_object &result_obj)
114 : : const override
115 : : {
116 : 4 : sarif_property_bag &props = result_obj.get_or_create_properties ();
117 : : #define PROPERTY_PREFIX "gcc/analyzer/out_of_bounds/"
118 : 8 : props.set_string (PROPERTY_PREFIX "dir",
119 : 4 : get_dir () == DIR_READ ? "read" : "write");
120 : 4 : props.set (PROPERTY_PREFIX "model", m_model.to_json ());
121 : 4 : props.set (PROPERTY_PREFIX "region", m_reg->to_json ());
122 : 4 : props.set (PROPERTY_PREFIX "diag_arg", tree_to_json (m_diag_arg));
123 : 4 : if (m_sval_hint)
124 : 4 : props.set (PROPERTY_PREFIX "sval_hint", m_sval_hint->to_json ());
125 : 8 : props.set (PROPERTY_PREFIX "region_creation_event_id",
126 : 4 : diagnostic_event_id_to_json (m_region_creation_event_id));
127 : : #undef PROPERTY_PREFIX
128 : 4 : }
129 : :
130 : : virtual enum access_direction get_dir () const = 0;
131 : :
132 : : protected:
133 : 551 : enum memory_space get_memory_space () const
134 : : {
135 : 551 : return m_reg->get_memory_space ();
136 : : }
137 : :
138 : : void
139 : 551 : maybe_show_notes (diagnostic_emission_context &ctxt) const
140 : : {
141 : 551 : maybe_describe_array_bounds (ctxt.get_location ());
142 : 551 : maybe_show_diagram (ctxt.get_logger ());
143 : 551 : }
144 : :
145 : : /* Potentially add a note about valid ways to index this array, such
146 : : as (given "int arr[10];"):
147 : : note: valid subscripts for 'arr' are '[0]' to '[9]'
148 : : We print the '[' and ']' characters so as to express the valid
149 : : subscripts using C syntax, rather than just as byte ranges,
150 : : which hopefully is more clear to the user. */
151 : : void
152 : 551 : maybe_describe_array_bounds (location_t loc) const
153 : : {
154 : 551 : if (!m_diag_arg)
155 : : return;
156 : 394 : tree t = TREE_TYPE (m_diag_arg);
157 : 394 : if (!t)
158 : : return;
159 : 394 : if (TREE_CODE (t) != ARRAY_TYPE)
160 : : return;
161 : 351 : tree domain = TYPE_DOMAIN (t);
162 : 351 : if (!domain)
163 : : return;
164 : 350 : tree max_idx = TYPE_MAX_VALUE (domain);
165 : 350 : if (!max_idx)
166 : : return;
167 : 348 : tree min_idx = TYPE_MIN_VALUE (domain);
168 : 348 : inform (loc,
169 : : "valid subscripts for %qE are %<[%E]%> to %<[%E]%>",
170 : : m_diag_arg, min_idx, max_idx);
171 : : }
172 : :
173 : : void
174 : 551 : maybe_show_diagram (logger *logger) const
175 : : {
176 : 551 : access_operation op (m_model, get_dir (), *m_reg, m_sval_hint);
177 : :
178 : : /* Don't attempt to make a diagram if there's no valid way of
179 : : accessing the base region (e.g. a 0-element array). */
180 : 551 : if (op.get_valid_bits ().empty_p ())
181 : 8 : return;
182 : :
183 : 547 : if (const text_art::theme *theme = global_dc->get_diagram_theme ())
184 : : {
185 : 72 : text_art::style_manager sm;
186 : 72 : text_art::canvas canvas (make_access_diagram (op, sm, *theme, logger));
187 : 72 : if (canvas.get_size ().w == 0 && canvas.get_size ().h == 0)
188 : : {
189 : : /* In lieu of exceptions, return a zero-sized diagram if there's
190 : : a problem. Give up if that's happened. */
191 : 4 : return;
192 : : }
193 : 68 : diagnostic_diagram diagram
194 : : (canvas,
195 : : /* Alt text. */
196 : 68 : _("Diagram visualizing the predicted out-of-bounds access"));
197 : 68 : global_dc->emit_diagram (diagram);
198 : 72 : }
199 : : }
200 : :
201 : : text_art::canvas
202 : 72 : make_access_diagram (const access_operation &op,
203 : : text_art::style_manager &sm,
204 : : const text_art::theme &theme,
205 : : logger *logger) const
206 : : {
207 : 72 : access_diagram d (op, m_region_creation_event_id, sm, theme, logger);
208 : 72 : return d.to_canvas (sm);
209 : 72 : }
210 : :
211 : : region_model m_model;
212 : : const region *m_reg;
213 : : tree m_diag_arg;
214 : : const svalue *m_sval_hint;
215 : : diagnostic_event_id_t m_region_creation_event_id;
216 : : };
217 : :
218 : : /* Abstract base class for all out-of-bounds warnings where the
219 : : out-of-bounds range is concrete. */
220 : :
221 : : class concrete_out_of_bounds : public out_of_bounds
222 : : {
223 : : public:
224 : 600 : concrete_out_of_bounds (const region_model &model,
225 : : const region *reg, tree diag_arg,
226 : : bit_range out_of_bounds_bits,
227 : : const svalue *sval_hint)
228 : 600 : : out_of_bounds (model, reg, diag_arg, sval_hint),
229 : 1200 : m_out_of_bounds_bits (out_of_bounds_bits)
230 : : {}
231 : :
232 : 570 : bool subclass_equal_p (const pending_diagnostic &base_other) const override
233 : : {
234 : 570 : const concrete_out_of_bounds &other
235 : : (static_cast <const concrete_out_of_bounds &>(base_other));
236 : 570 : return (out_of_bounds::subclass_equal_p (other)
237 : 570 : && m_out_of_bounds_bits == other.m_out_of_bounds_bits);
238 : : }
239 : :
240 : 4 : void maybe_add_sarif_properties (sarif_object &result_obj)
241 : : const override
242 : : {
243 : 4 : out_of_bounds::maybe_add_sarif_properties (result_obj);
244 : 4 : sarif_property_bag &props = result_obj.get_or_create_properties ();
245 : : #define PROPERTY_PREFIX "gcc/analyzer/concrete_out_of_bounds/"
246 : 8 : props.set (PROPERTY_PREFIX "out_of_bounds_bits",
247 : 4 : m_out_of_bounds_bits.to_json ());
248 : 4 : byte_range out_of_bounds_bytes (0, 0);
249 : 4 : if (get_out_of_bounds_bytes (&out_of_bounds_bytes))
250 : 8 : props.set (PROPERTY_PREFIX "out_of_bounds_bytes",
251 : 4 : out_of_bounds_bytes.to_json ());
252 : : #undef PROPERTY_PREFIX
253 : 4 : }
254 : :
255 : 842 : bool get_out_of_bounds_bytes (byte_range *out) const
256 : : {
257 : 4 : return m_out_of_bounds_bits.as_byte_range (out);
258 : : }
259 : :
260 : : protected:
261 : : bit_range m_out_of_bounds_bits;
262 : : };
263 : :
264 : : /* Abstract subclass to complaing about concrete out-of-bounds
265 : : past the end of the buffer. */
266 : :
267 : : class concrete_past_the_end : public concrete_out_of_bounds
268 : : {
269 : : public:
270 : 436 : concrete_past_the_end (const region_model &model,
271 : : const region *reg, tree diag_arg, bit_range range,
272 : : tree bit_bound,
273 : : const svalue *sval_hint)
274 : 436 : : concrete_out_of_bounds (model, reg, diag_arg, range, sval_hint),
275 : 436 : m_bit_bound (bit_bound),
276 : 436 : m_byte_bound (NULL_TREE)
277 : : {
278 : 436 : if (m_bit_bound && TREE_CODE (m_bit_bound) == INTEGER_CST)
279 : 436 : m_byte_bound
280 : 436 : = wide_int_to_tree (size_type_node,
281 : 872 : wi::to_offset (m_bit_bound) >> LOG2_BITS_PER_UNIT);
282 : 436 : }
283 : :
284 : : bool
285 : 410 : subclass_equal_p (const pending_diagnostic &base_other) const final override
286 : : {
287 : 410 : const concrete_past_the_end &other
288 : : (static_cast <const concrete_past_the_end &>(base_other));
289 : 410 : return (concrete_out_of_bounds::subclass_equal_p (other)
290 : 820 : && pending_diagnostic::same_tree_p (m_bit_bound,
291 : 410 : other.m_bit_bound));
292 : : }
293 : :
294 : 307 : void add_region_creation_events (const region *,
295 : : tree,
296 : : const event_loc_info &loc_info,
297 : : checker_path &emission_path) final override
298 : : {
299 : 307 : if (m_byte_bound && TREE_CODE (m_byte_bound) == INTEGER_CST)
300 : 307 : emission_path.add_event
301 : 307 : (make_unique<oob_region_creation_event_capacity> (m_byte_bound,
302 : : loc_info,
303 : : *this));
304 : 307 : }
305 : :
306 : 4 : void maybe_add_sarif_properties (sarif_object &result_obj)
307 : : const final override
308 : : {
309 : 4 : concrete_out_of_bounds::maybe_add_sarif_properties (result_obj);
310 : 4 : sarif_property_bag &props = result_obj.get_or_create_properties ();
311 : : #define PROPERTY_PREFIX "gcc/analyzer/concrete_past_the_end/"
312 : 8 : props.set (PROPERTY_PREFIX "bit_bound",
313 : 4 : tree_to_json (m_bit_bound));
314 : 8 : props.set (PROPERTY_PREFIX "byte_bound",
315 : 4 : tree_to_json (m_byte_bound));
316 : : #undef PROPERTY_PREFIX
317 : 4 : }
318 : :
319 : : protected:
320 : : tree m_bit_bound;
321 : : tree m_byte_bound;
322 : : };
323 : :
324 : : /* Concrete subclass to complain about buffer overflows. */
325 : :
326 : : class concrete_buffer_overflow : public concrete_past_the_end
327 : : {
328 : : public:
329 : 182 : concrete_buffer_overflow (const region_model &model,
330 : : const region *reg, tree diag_arg,
331 : : bit_range range, tree bit_bound,
332 : : const svalue *sval_hint)
333 : 182 : : concrete_past_the_end (model, reg, diag_arg, range, bit_bound, sval_hint)
334 : : {}
335 : :
336 : 694 : const char *get_kind () const final override
337 : : {
338 : 694 : return "concrete_buffer_overflow";
339 : : }
340 : :
341 : 160 : bool emit (diagnostic_emission_context &ctxt) final override
342 : : {
343 : 160 : bool warned;
344 : 160 : switch (get_memory_space ())
345 : : {
346 : 58 : default:
347 : 58 : ctxt.add_cwe (787);
348 : 58 : warned = ctxt.warn ("buffer overflow");
349 : 58 : break;
350 : 73 : case MEMSPACE_STACK:
351 : 73 : ctxt.add_cwe (121);
352 : 73 : warned = ctxt.warn ("stack-based buffer overflow");
353 : 73 : break;
354 : 29 : case MEMSPACE_HEAP:
355 : 29 : ctxt.add_cwe (122);
356 : 29 : warned = ctxt.warn ("heap-based buffer overflow");
357 : 29 : break;
358 : : }
359 : :
360 : 160 : if (warned)
361 : : {
362 : 160 : if (wi::fits_uhwi_p (m_out_of_bounds_bits.m_size_in_bits))
363 : : {
364 : 160 : unsigned HOST_WIDE_INT num_bad_bits
365 : 160 : = m_out_of_bounds_bits.m_size_in_bits.to_uhwi ();
366 : 160 : if (num_bad_bits % BITS_PER_UNIT == 0)
367 : : {
368 : 160 : unsigned HOST_WIDE_INT num_bad_bytes
369 : : = num_bad_bits / BITS_PER_UNIT;
370 : 160 : if (m_diag_arg)
371 : 128 : inform_n (ctxt.get_location (),
372 : : num_bad_bytes,
373 : : "write of %wu byte to beyond the end of %qE",
374 : : "write of %wu bytes to beyond the end of %qE",
375 : : num_bad_bytes,
376 : : m_diag_arg);
377 : : else
378 : 32 : inform_n (ctxt.get_location (),
379 : : num_bad_bytes,
380 : : "write of %wu byte to beyond the end of the region",
381 : : "write of %wu bytes to beyond the end of the region",
382 : : num_bad_bytes);
383 : : }
384 : : else
385 : : {
386 : 0 : if (m_diag_arg)
387 : 0 : inform_n (ctxt.get_location (),
388 : : num_bad_bits,
389 : : "write of %wu bit to beyond the end of %qE",
390 : : "write of %wu bits to beyond the end of %qE",
391 : : num_bad_bits,
392 : : m_diag_arg);
393 : : else
394 : 0 : inform_n (ctxt.get_location (),
395 : : num_bad_bits,
396 : : "write of %wu bit to beyond the end of the region",
397 : : "write of %wu bits to beyond the end of the region",
398 : : num_bad_bits);
399 : : }
400 : : }
401 : 0 : else if (m_diag_arg)
402 : 0 : inform (ctxt.get_location (),
403 : : "write to beyond the end of %qE",
404 : : m_diag_arg);
405 : :
406 : 160 : maybe_show_notes (ctxt);
407 : : }
408 : :
409 : 160 : return warned;
410 : : }
411 : :
412 : : bool
413 : 320 : describe_final_event (pretty_printer &pp,
414 : : const evdesc::final_event &) final override
415 : : {
416 : 320 : if (m_byte_bound || !m_bit_bound)
417 : : {
418 : 320 : byte_range out_of_bounds_bytes (0, 0);
419 : 320 : if (get_out_of_bounds_bytes (&out_of_bounds_bytes))
420 : : {
421 : 320 : describe_final_event_as_bytes (pp, out_of_bounds_bytes);
422 : 320 : return true;
423 : : }
424 : : }
425 : 0 : describe_final_event_as_bits (pp);
426 : 0 : return true;
427 : : }
428 : :
429 : : void
430 : 320 : describe_final_event_as_bytes (pretty_printer &pp,
431 : : const byte_range &out_of_bounds_bytes)
432 : : {
433 : 320 : byte_size_t start = out_of_bounds_bytes.get_start_byte_offset ();
434 : 320 : byte_size_t end = out_of_bounds_bytes.get_last_byte_offset ();
435 : 320 : char start_buf[WIDE_INT_PRINT_BUFFER_SIZE];
436 : 320 : print_dec (start, start_buf, SIGNED);
437 : 320 : char end_buf[WIDE_INT_PRINT_BUFFER_SIZE];
438 : 320 : print_dec (end, end_buf, SIGNED);
439 : :
440 : 320 : if (start == end)
441 : : {
442 : 84 : if (m_diag_arg)
443 : 56 : pp_printf (&pp,
444 : : "out-of-bounds write at byte %s but %qE"
445 : : " ends at byte %E", start_buf, m_diag_arg,
446 : : m_byte_bound);
447 : : else
448 : 28 : pp_printf (&pp,
449 : : "out-of-bounds write at byte %s but region"
450 : : " ends at byte %E", start_buf,
451 : : m_byte_bound);
452 : : }
453 : : else
454 : : {
455 : 236 : if (m_diag_arg)
456 : 200 : pp_printf (&pp,
457 : : "out-of-bounds write from byte %s till"
458 : : " byte %s but %qE ends at byte %E",
459 : : start_buf, end_buf, m_diag_arg,
460 : : m_byte_bound);
461 : : else
462 : 36 : pp_printf (&pp,
463 : : "out-of-bounds write from byte %s till"
464 : : " byte %s but region ends at byte %E",
465 : : start_buf, end_buf, m_byte_bound);
466 : : }
467 : 320 : }
468 : :
469 : : void
470 : 0 : describe_final_event_as_bits (pretty_printer &pp)
471 : : {
472 : 0 : bit_size_t start = m_out_of_bounds_bits.get_start_bit_offset ();
473 : 0 : bit_size_t end = m_out_of_bounds_bits.get_last_bit_offset ();
474 : 0 : char start_buf[WIDE_INT_PRINT_BUFFER_SIZE];
475 : 0 : print_dec (start, start_buf, SIGNED);
476 : 0 : char end_buf[WIDE_INT_PRINT_BUFFER_SIZE];
477 : 0 : print_dec (end, end_buf, SIGNED);
478 : :
479 : 0 : if (start == end)
480 : : {
481 : 0 : if (m_diag_arg)
482 : 0 : pp_printf (&pp,
483 : : "out-of-bounds write at bit %s but %qE"
484 : : " ends at bit %E", start_buf, m_diag_arg,
485 : : m_bit_bound);
486 : : else
487 : 0 : pp_printf (&pp,
488 : : "out-of-bounds write at bit %s but region"
489 : : " ends at bit %E", start_buf,
490 : : m_bit_bound);
491 : : }
492 : : else
493 : : {
494 : 0 : if (m_diag_arg)
495 : 0 : pp_printf (&pp,
496 : : "out-of-bounds write from bit %s till"
497 : : " bit %s but %qE ends at bit %E",
498 : : start_buf, end_buf, m_diag_arg,
499 : : m_bit_bound);
500 : : else
501 : 0 : pp_printf (&pp,
502 : : "out-of-bounds write from bit %s till"
503 : : " bit %s but region ends at bit %E",
504 : : start_buf, end_buf, m_bit_bound);
505 : : }
506 : 0 : }
507 : :
508 : 164 : enum access_direction get_dir () const final override { return DIR_WRITE; }
509 : : };
510 : :
511 : : /* Concrete subclass to complain about buffer over-reads. */
512 : :
513 : : class concrete_buffer_over_read : public concrete_past_the_end
514 : : {
515 : : public:
516 : 254 : concrete_buffer_over_read (const region_model &model,
517 : : const region *reg, tree diag_arg,
518 : : bit_range range, tree bit_bound)
519 : 254 : : concrete_past_the_end (model, reg, diag_arg, range, bit_bound, NULL)
520 : : {}
521 : :
522 : 924 : const char *get_kind () const final override
523 : : {
524 : 924 : return "concrete_buffer_over_read";
525 : : }
526 : :
527 : 150 : bool emit (diagnostic_emission_context &ctxt) final override
528 : : {
529 : 150 : bool warned;
530 : 150 : ctxt.add_cwe (126);
531 : 150 : switch (get_memory_space ())
532 : : {
533 : 44 : default:
534 : 44 : warned = ctxt.warn ("buffer over-read");
535 : 44 : break;
536 : 98 : case MEMSPACE_STACK:
537 : 98 : warned = ctxt.warn ("stack-based buffer over-read");
538 : 98 : break;
539 : 8 : case MEMSPACE_HEAP:
540 : 8 : warned = ctxt.warn ("heap-based buffer over-read");
541 : 8 : break;
542 : : }
543 : :
544 : 150 : if (warned)
545 : : {
546 : 150 : if (wi::fits_uhwi_p (m_out_of_bounds_bits.m_size_in_bits))
547 : : {
548 : 150 : unsigned HOST_WIDE_INT num_bad_bits
549 : 150 : = m_out_of_bounds_bits.m_size_in_bits.to_uhwi ();
550 : 150 : if (num_bad_bits % BITS_PER_UNIT == 0)
551 : : {
552 : 150 : unsigned HOST_WIDE_INT num_bad_bytes
553 : : = num_bad_bits / BITS_PER_UNIT;
554 : 150 : if (m_diag_arg)
555 : 142 : inform_n (ctxt.get_location (),
556 : : num_bad_bytes,
557 : : "read of %wu byte from after the end of %qE",
558 : : "read of %wu bytes from after the end of %qE",
559 : : num_bad_bytes,
560 : : m_diag_arg);
561 : : else
562 : 8 : inform_n (ctxt.get_location (),
563 : : num_bad_bytes,
564 : : "read of %wu byte from after the end of the region",
565 : : "read of %wu bytes from after the end of the region",
566 : : num_bad_bytes);
567 : : }
568 : : else
569 : : {
570 : 0 : if (m_diag_arg)
571 : 0 : inform_n (ctxt.get_location (),
572 : : num_bad_bits,
573 : : "read of %wu bit from after the end of %qE",
574 : : "read of %wu bits from after the end of %qE",
575 : : num_bad_bits,
576 : : m_diag_arg);
577 : : else
578 : 0 : inform_n (ctxt.get_location (),
579 : : num_bad_bits,
580 : : "read of %wu bit from after the end of the region",
581 : : "read of %wu bits from after the end of the region",
582 : : num_bad_bits);
583 : : }
584 : : }
585 : 0 : else if (m_diag_arg)
586 : 0 : inform (ctxt.get_location (),
587 : : "read from after the end of %qE",
588 : : m_diag_arg);
589 : :
590 : 150 : maybe_show_notes (ctxt);
591 : : }
592 : :
593 : 150 : return warned;
594 : : }
595 : :
596 : : bool
597 : 300 : describe_final_event (pretty_printer &pp,
598 : : const evdesc::final_event &) final override
599 : : {
600 : 300 : if (m_byte_bound || !m_bit_bound)
601 : : {
602 : 300 : byte_range out_of_bounds_bytes (0, 0);
603 : 300 : if (get_out_of_bounds_bytes (&out_of_bounds_bytes))
604 : : {
605 : 300 : describe_final_event_as_bytes (pp, out_of_bounds_bytes);
606 : 300 : return true;
607 : : }
608 : : }
609 : 0 : describe_final_event_as_bits (pp);
610 : 0 : return true;
611 : : }
612 : :
613 : : void
614 : 300 : describe_final_event_as_bytes (pretty_printer &pp,
615 : : const byte_range &out_of_bounds_bytes)
616 : : {
617 : 300 : byte_size_t start = out_of_bounds_bytes.get_start_byte_offset ();
618 : 300 : byte_size_t end = out_of_bounds_bytes.get_last_byte_offset ();
619 : 300 : char start_buf[WIDE_INT_PRINT_BUFFER_SIZE];
620 : 300 : print_dec (start, start_buf, SIGNED);
621 : 300 : char end_buf[WIDE_INT_PRINT_BUFFER_SIZE];
622 : 300 : print_dec (end, end_buf, SIGNED);
623 : :
624 : 300 : if (start == end)
625 : : {
626 : 198 : if (m_diag_arg)
627 : 190 : pp_printf (&pp,
628 : : "out-of-bounds read at byte %s but %qE"
629 : : " ends at byte %E", start_buf, m_diag_arg,
630 : : m_byte_bound);
631 : : else
632 : 8 : pp_printf (&pp,
633 : : "out-of-bounds read at byte %s but region"
634 : : " ends at byte %E", start_buf,
635 : : m_byte_bound);
636 : : }
637 : : else
638 : : {
639 : 102 : if (m_diag_arg)
640 : 94 : pp_printf (&pp,
641 : : "out-of-bounds read from byte %s till"
642 : : " byte %s but %qE ends at byte %E",
643 : : start_buf, end_buf, m_diag_arg,
644 : : m_byte_bound);
645 : : else
646 : 8 : pp_printf (&pp,
647 : : "out-of-bounds read from byte %s till"
648 : : " byte %s but region ends at byte %E",
649 : : start_buf, end_buf, m_byte_bound);
650 : : }
651 : 300 : }
652 : :
653 : : void
654 : 0 : describe_final_event_as_bits (pretty_printer &pp)
655 : : {
656 : 0 : bit_size_t start = m_out_of_bounds_bits.get_start_bit_offset ();
657 : 0 : bit_size_t end = m_out_of_bounds_bits.get_last_bit_offset ();
658 : 0 : char start_buf[WIDE_INT_PRINT_BUFFER_SIZE];
659 : 0 : print_dec (start, start_buf, SIGNED);
660 : 0 : char end_buf[WIDE_INT_PRINT_BUFFER_SIZE];
661 : 0 : print_dec (end, end_buf, SIGNED);
662 : :
663 : 0 : if (start == end)
664 : : {
665 : 0 : if (m_diag_arg)
666 : 0 : pp_printf (&pp,
667 : : "out-of-bounds read at bit %s but %qE"
668 : : " ends at bit %E", start_buf, m_diag_arg,
669 : : m_bit_bound);
670 : : else
671 : 0 : pp_printf (&pp,
672 : : "out-of-bounds read at bit %s but region"
673 : : " ends at bit %E", start_buf,
674 : : m_bit_bound);
675 : : }
676 : : else
677 : : {
678 : 0 : if (m_diag_arg)
679 : 0 : pp_printf (&pp,
680 : : "out-of-bounds read from bit %s till"
681 : : " bit %s but %qE ends at bit %E",
682 : : start_buf, end_buf, m_diag_arg,
683 : : m_bit_bound);
684 : : else
685 : 0 : pp_printf (&pp,
686 : : "out-of-bounds read from bit %s till"
687 : : " bit %s but region ends at bit %E",
688 : : start_buf, end_buf, m_bit_bound);
689 : : }
690 : 0 : }
691 : :
692 : 150 : enum access_direction get_dir () const final override { return DIR_READ; }
693 : : };
694 : :
695 : : /* Concrete subclass to complain about buffer underwrites. */
696 : :
697 : : class concrete_buffer_underwrite : public concrete_out_of_bounds
698 : : {
699 : : public:
700 : 54 : concrete_buffer_underwrite (const region_model &model,
701 : : const region *reg, tree diag_arg,
702 : : bit_range range,
703 : : const svalue *sval_hint)
704 : 54 : : concrete_out_of_bounds (model, reg, diag_arg, range, sval_hint)
705 : : {}
706 : :
707 : 234 : const char *get_kind () const final override
708 : : {
709 : 234 : return "concrete_buffer_underwrite";
710 : : }
711 : :
712 : 54 : bool emit (diagnostic_emission_context &ctxt) final override
713 : : {
714 : 54 : bool warned;
715 : 54 : ctxt.add_cwe (124);
716 : 54 : switch (get_memory_space ())
717 : : {
718 : 36 : default:
719 : 36 : warned = ctxt.warn ("buffer underwrite");
720 : 36 : break;
721 : 14 : case MEMSPACE_STACK:
722 : 14 : warned = ctxt.warn ("stack-based buffer underwrite");
723 : 14 : break;
724 : 4 : case MEMSPACE_HEAP:
725 : 4 : warned = ctxt.warn ("heap-based buffer underwrite");
726 : 4 : break;
727 : : }
728 : 54 : if (warned)
729 : 54 : maybe_show_notes (ctxt);
730 : 54 : return warned;
731 : : }
732 : :
733 : : bool
734 : 108 : describe_final_event (pretty_printer &pp,
735 : : const evdesc::final_event &) final override
736 : : {
737 : 108 : byte_range out_of_bounds_bytes (0, 0);
738 : 108 : if (get_out_of_bounds_bytes (&out_of_bounds_bytes))
739 : 108 : describe_final_event_as_bytes (pp, out_of_bounds_bytes);
740 : : else
741 : 0 : describe_final_event_as_bits (pp);
742 : 108 : return true;
743 : : }
744 : :
745 : : void
746 : 108 : describe_final_event_as_bytes (pretty_printer &pp,
747 : : const byte_range &out_of_bounds_bytes)
748 : : {
749 : 108 : byte_size_t start = out_of_bounds_bytes.get_start_byte_offset ();
750 : 108 : byte_size_t end = out_of_bounds_bytes.get_last_byte_offset ();
751 : 108 : char start_buf[WIDE_INT_PRINT_BUFFER_SIZE];
752 : 108 : print_dec (start, start_buf, SIGNED);
753 : 108 : char end_buf[WIDE_INT_PRINT_BUFFER_SIZE];
754 : 108 : print_dec (end, end_buf, SIGNED);
755 : :
756 : 108 : if (start == end)
757 : : {
758 : 24 : if (m_diag_arg)
759 : 24 : pp_printf (&pp,
760 : : "out-of-bounds write at byte %s but %qE"
761 : : " starts at byte 0",
762 : : start_buf, m_diag_arg);
763 : : else
764 : 0 : pp_printf (&pp,
765 : : "out-of-bounds write at byte %s but region"
766 : : " starts at byte 0", start_buf);
767 : : }
768 : : else
769 : : {
770 : 84 : if (m_diag_arg)
771 : 74 : pp_printf (&pp,
772 : : "out-of-bounds write from byte %s till"
773 : : " byte %s but %qE starts at byte 0",
774 : : start_buf, end_buf, m_diag_arg);
775 : : else
776 : 10 : pp_printf (&pp,
777 : : "out-of-bounds write from byte %s till"
778 : : " byte %s but region starts at byte 0",
779 : 108 : start_buf, end_buf);;
780 : : }
781 : 108 : }
782 : :
783 : : void
784 : 0 : describe_final_event_as_bits (pretty_printer &pp)
785 : : {
786 : 0 : bit_size_t start = m_out_of_bounds_bits.get_start_bit_offset ();
787 : 0 : bit_size_t end = m_out_of_bounds_bits.get_last_bit_offset ();
788 : 0 : char start_buf[WIDE_INT_PRINT_BUFFER_SIZE];
789 : 0 : print_dec (start, start_buf, SIGNED);
790 : 0 : char end_buf[WIDE_INT_PRINT_BUFFER_SIZE];
791 : 0 : print_dec (end, end_buf, SIGNED);
792 : :
793 : 0 : if (start == end)
794 : : {
795 : 0 : if (m_diag_arg)
796 : 0 : pp_printf (&pp,
797 : : "out-of-bounds write at bit %s but %qE"
798 : : " starts at bit 0",
799 : : start_buf, m_diag_arg);
800 : : else
801 : 0 : pp_printf (&pp,
802 : : "out-of-bounds write at bit %s but region"
803 : : " starts at bit 0", start_buf);
804 : : }
805 : : else
806 : : {
807 : 0 : if (m_diag_arg)
808 : 0 : pp_printf (&pp,
809 : : "out-of-bounds write from bit %s till"
810 : : " bit %s but %qE starts at bit 0",
811 : : start_buf, end_buf, m_diag_arg);
812 : : else
813 : 0 : pp_printf (&pp,
814 : : "out-of-bounds write from bit %s till"
815 : : " bit %s but region starts at bit 0",
816 : 0 : start_buf, end_buf);;
817 : : }
818 : 0 : }
819 : :
820 : 54 : enum access_direction get_dir () const final override { return DIR_WRITE; }
821 : : };
822 : :
823 : : /* Concrete subclass to complain about buffer under-reads. */
824 : :
825 : : class concrete_buffer_under_read : public concrete_out_of_bounds
826 : : {
827 : : public:
828 : 110 : concrete_buffer_under_read (const region_model &model,
829 : : const region *reg, tree diag_arg,
830 : : bit_range range)
831 : 110 : : concrete_out_of_bounds (model, reg, diag_arg, range, NULL)
832 : : {}
833 : :
834 : 397 : const char *get_kind () const final override
835 : : {
836 : 397 : return "concrete_buffer_under_read";
837 : : }
838 : :
839 : 55 : bool emit (diagnostic_emission_context &ctxt) final override
840 : : {
841 : 55 : bool warned;
842 : 55 : ctxt.add_cwe (127);
843 : 55 : switch (get_memory_space ())
844 : : {
845 : 40 : default:
846 : 40 : warned = ctxt.warn ("buffer under-read");
847 : 40 : break;
848 : 11 : case MEMSPACE_STACK:
849 : 11 : warned = ctxt.warn ("stack-based buffer under-read");
850 : 11 : break;
851 : 4 : case MEMSPACE_HEAP:
852 : 4 : warned = ctxt.warn ("heap-based buffer under-read");
853 : 4 : break;
854 : : }
855 : 55 : if (warned)
856 : 55 : maybe_show_notes (ctxt);
857 : 55 : return warned;
858 : : }
859 : :
860 : : bool
861 : 110 : describe_final_event (pretty_printer &pp,
862 : : const evdesc::final_event &) final override
863 : : {
864 : 110 : byte_range out_of_bounds_bytes (0, 0);
865 : 110 : if (get_out_of_bounds_bytes (&out_of_bounds_bytes))
866 : 110 : describe_final_event_as_bytes (pp, out_of_bounds_bytes);
867 : : else
868 : 0 : describe_final_event_as_bits (pp);
869 : 110 : return true;
870 : : }
871 : :
872 : : void
873 : 110 : describe_final_event_as_bytes (pretty_printer &pp,
874 : : const byte_range &out_of_bounds_bytes)
875 : : {
876 : 110 : byte_size_t start = out_of_bounds_bytes.get_start_byte_offset ();
877 : 110 : byte_size_t end = out_of_bounds_bytes.get_last_byte_offset ();
878 : 110 : char start_buf[WIDE_INT_PRINT_BUFFER_SIZE];
879 : 110 : print_dec (start, start_buf, SIGNED);
880 : 110 : char end_buf[WIDE_INT_PRINT_BUFFER_SIZE];
881 : 110 : print_dec (end, end_buf, SIGNED);
882 : :
883 : 110 : if (start == end)
884 : : {
885 : 26 : if (m_diag_arg)
886 : 26 : pp_printf (&pp,
887 : : "out-of-bounds read at byte %s but %qE"
888 : : " starts at byte 0",
889 : : start_buf, m_diag_arg);
890 : : else
891 : 0 : pp_printf (&pp,
892 : : "out-of-bounds read at byte %s but region"
893 : : " starts at byte 0",
894 : : start_buf);
895 : : }
896 : : else
897 : : {
898 : 84 : if (m_diag_arg)
899 : 76 : pp_printf (&pp,
900 : : "out-of-bounds read from byte %s till"
901 : : " byte %s but %qE starts at byte 0",
902 : : start_buf, end_buf, m_diag_arg);
903 : : else
904 : 8 : pp_printf (&pp,
905 : : "out-of-bounds read from byte %s till"
906 : : " byte %s but region starts at byte 0",
907 : 110 : start_buf, end_buf);;
908 : : }
909 : 110 : }
910 : :
911 : : void
912 : 0 : describe_final_event_as_bits (pretty_printer &pp)
913 : : {
914 : 0 : bit_size_t start = m_out_of_bounds_bits.get_start_bit_offset ();
915 : 0 : bit_size_t end = m_out_of_bounds_bits.get_last_bit_offset ();
916 : 0 : char start_buf[WIDE_INT_PRINT_BUFFER_SIZE];
917 : 0 : print_dec (start, start_buf, SIGNED);
918 : 0 : char end_buf[WIDE_INT_PRINT_BUFFER_SIZE];
919 : 0 : print_dec (end, end_buf, SIGNED);
920 : :
921 : 0 : if (start == end)
922 : : {
923 : 0 : if (m_diag_arg)
924 : 0 : pp_printf (&pp,
925 : : "out-of-bounds read at bit %s but %qE"
926 : : " starts at bit 0",
927 : : start_buf, m_diag_arg);
928 : : else
929 : 0 : pp_printf (&pp,
930 : : "out-of-bounds read at bit %s but region"
931 : : " starts at bit 0", start_buf);
932 : : }
933 : : else
934 : : {
935 : 0 : if (m_diag_arg)
936 : 0 : pp_printf (&pp,
937 : : "out-of-bounds read from bit %s till"
938 : : " bit %s but %qE starts at bit 0",
939 : : start_buf, end_buf, m_diag_arg);
940 : : else
941 : 0 : pp_printf (&pp,
942 : : "out-of-bounds read from bit %s till"
943 : : " bit %s but region starts at bit 0",
944 : 0 : start_buf, end_buf);;
945 : : }
946 : 0 : }
947 : :
948 : 55 : enum access_direction get_dir () const final override { return DIR_READ; }
949 : : };
950 : :
951 : : /* Abstract class to complain about out-of-bounds read/writes where
952 : : the values are symbolic. */
953 : :
954 : : class symbolic_past_the_end : public out_of_bounds
955 : : {
956 : : public:
957 : 145 : symbolic_past_the_end (const region_model &model,
958 : : const region *reg, tree diag_arg, tree offset,
959 : : tree num_bytes, tree capacity,
960 : : const svalue *sval_hint)
961 : 145 : : out_of_bounds (model, reg, diag_arg, sval_hint),
962 : 145 : m_offset (offset),
963 : 145 : m_num_bytes (num_bytes),
964 : 290 : m_capacity (capacity)
965 : : {}
966 : :
967 : : bool
968 : 145 : subclass_equal_p (const pending_diagnostic &base_other) const final override
969 : : {
970 : 145 : const symbolic_past_the_end &other
971 : : (static_cast <const symbolic_past_the_end &>(base_other));
972 : 145 : return (out_of_bounds::subclass_equal_p (other)
973 : 145 : && pending_diagnostic::same_tree_p (m_offset, other.m_offset)
974 : 145 : && pending_diagnostic::same_tree_p (m_num_bytes, other.m_num_bytes)
975 : 290 : && pending_diagnostic::same_tree_p (m_capacity, other.m_capacity));
976 : : }
977 : :
978 : 0 : void maybe_add_sarif_properties (sarif_object &result_obj)
979 : : const final override
980 : : {
981 : 0 : out_of_bounds::maybe_add_sarif_properties (result_obj);
982 : 0 : sarif_property_bag &props = result_obj.get_or_create_properties ();
983 : : #define PROPERTY_PREFIX "gcc/analyzer/symbolic_past_the_end/"
984 : 0 : props.set (PROPERTY_PREFIX "offset", tree_to_json (m_offset));
985 : 0 : props.set (PROPERTY_PREFIX "num_bytes", tree_to_json (m_num_bytes));
986 : 0 : props.set (PROPERTY_PREFIX "capacity", tree_to_json (m_capacity));
987 : : #undef PROPERTY_PREFIX
988 : 0 : }
989 : :
990 : : protected:
991 : : tree m_offset;
992 : : tree m_num_bytes;
993 : : tree m_capacity;
994 : : };
995 : :
996 : : /* Concrete subclass to complain about overflows with symbolic values. */
997 : :
998 : : class symbolic_buffer_overflow : public symbolic_past_the_end
999 : : {
1000 : : public:
1001 : 112 : symbolic_buffer_overflow (const region_model &model,
1002 : : const region *reg, tree diag_arg, tree offset,
1003 : : tree num_bytes, tree capacity,
1004 : : const svalue *sval_hint)
1005 : : : symbolic_past_the_end (model, reg, diag_arg, offset, num_bytes, capacity,
1006 : 112 : sval_hint)
1007 : : {
1008 : : }
1009 : :
1010 : 510 : const char *get_kind () const final override
1011 : : {
1012 : 510 : return "symbolic_buffer_overflow";
1013 : : }
1014 : :
1015 : 111 : bool emit (diagnostic_emission_context &ctxt) final override
1016 : : {
1017 : 111 : bool warned;
1018 : 111 : switch (get_memory_space ())
1019 : : {
1020 : 36 : default:
1021 : 36 : ctxt.add_cwe (787);
1022 : 36 : warned = ctxt.warn ("buffer overflow");
1023 : 36 : break;
1024 : 30 : case MEMSPACE_STACK:
1025 : 30 : ctxt.add_cwe (121);
1026 : 30 : warned = ctxt.warn ("stack-based buffer overflow");
1027 : 30 : break;
1028 : 45 : case MEMSPACE_HEAP:
1029 : 45 : ctxt.add_cwe (122);
1030 : 45 : warned = ctxt.warn ("heap-based buffer overflow");
1031 : 45 : break;
1032 : : }
1033 : 111 : if (warned)
1034 : 111 : maybe_show_notes (ctxt);
1035 : 111 : return warned;
1036 : : }
1037 : :
1038 : : bool
1039 : 222 : describe_final_event (pretty_printer &pp,
1040 : : const evdesc::final_event &) final override
1041 : : {
1042 : 222 : if (m_offset)
1043 : : {
1044 : : /* Known offset. */
1045 : 188 : if (m_num_bytes)
1046 : : {
1047 : : /* Known offset, known size. */
1048 : 182 : if (TREE_CODE (m_num_bytes) == INTEGER_CST)
1049 : : {
1050 : : /* Known offset, known constant size. */
1051 : 164 : if (pending_diagnostic::same_tree_p (m_num_bytes,
1052 : : integer_one_node))
1053 : : {
1054 : : /* Singular m_num_bytes. */
1055 : 120 : if (m_diag_arg)
1056 : 32 : pp_printf (&pp,
1057 : : "write of %E byte at offset %qE exceeds %qE",
1058 : : m_num_bytes, m_offset, m_diag_arg);
1059 : : else
1060 : 88 : pp_printf (&pp,
1061 : : "write of %E byte at offset %qE exceeds"
1062 : : " the buffer",
1063 : : m_num_bytes, m_offset);
1064 : : }
1065 : : else
1066 : : {
1067 : : /* Plural m_num_bytes. */
1068 : 44 : if (m_diag_arg)
1069 : 8 : pp_printf (&pp,
1070 : : "write of %E bytes at offset %qE exceeds %qE",
1071 : : m_num_bytes, m_offset, m_diag_arg);
1072 : : else
1073 : 36 : pp_printf (&pp,
1074 : : "write of %E bytes at offset %qE exceeds"
1075 : : " the buffer",
1076 : : m_num_bytes, m_offset);
1077 : : }
1078 : : }
1079 : : else
1080 : : {
1081 : : /* Known offset, known symbolic size. */
1082 : 18 : if (m_diag_arg)
1083 : 0 : pp_printf (&pp,
1084 : : "write of %qE bytes at offset %qE exceeds %qE",
1085 : : m_num_bytes, m_offset, m_diag_arg);
1086 : : else
1087 : 18 : pp_printf (&pp,
1088 : : "write of %qE bytes at offset %qE exceeds"
1089 : : " the buffer",
1090 : : m_num_bytes, m_offset);
1091 : : }
1092 : : }
1093 : : else
1094 : : {
1095 : : /* Known offset, unknown size. */
1096 : 6 : if (m_diag_arg)
1097 : 0 : pp_printf (&pp,
1098 : : "write at offset %qE exceeds %qE",
1099 : : m_offset, m_diag_arg);
1100 : : else
1101 : 6 : pp_printf (&pp,
1102 : : "write at offset %qE exceeds the buffer",
1103 : : m_offset);
1104 : : }
1105 : : }
1106 : : else
1107 : : {
1108 : : /* Unknown offset. */
1109 : 34 : if (m_diag_arg)
1110 : 0 : pp_printf (&pp,
1111 : : "out-of-bounds write on %qE",
1112 : : m_diag_arg);
1113 : : else
1114 : 34 : pp_printf (&pp, "out-of-bounds write");
1115 : : }
1116 : 222 : return true;
1117 : : }
1118 : :
1119 : 111 : enum access_direction get_dir () const final override { return DIR_WRITE; }
1120 : : };
1121 : :
1122 : : /* Concrete subclass to complain about over-reads with symbolic values. */
1123 : :
1124 : : class symbolic_buffer_over_read : public symbolic_past_the_end
1125 : : {
1126 : : public:
1127 : 33 : symbolic_buffer_over_read (const region_model &model,
1128 : : const region *reg, tree diag_arg, tree offset,
1129 : : tree num_bytes, tree capacity)
1130 : : : symbolic_past_the_end (model, reg, diag_arg, offset, num_bytes, capacity,
1131 : 33 : NULL)
1132 : : {
1133 : : }
1134 : :
1135 : 183 : const char *get_kind () const final override
1136 : : {
1137 : 183 : return "symbolic_buffer_over_read";
1138 : : }
1139 : :
1140 : 21 : bool emit (diagnostic_emission_context &ctxt) final override
1141 : : {
1142 : 21 : ctxt.add_cwe (126);
1143 : 21 : bool warned;
1144 : 21 : switch (get_memory_space ())
1145 : : {
1146 : 0 : default:
1147 : 0 : ctxt.add_cwe (787);
1148 : 0 : warned = ctxt.warn ("buffer over-read");
1149 : 0 : break;
1150 : 21 : case MEMSPACE_STACK:
1151 : 21 : ctxt.add_cwe (121);
1152 : 21 : warned = ctxt.warn ("stack-based buffer over-read");
1153 : 21 : break;
1154 : 0 : case MEMSPACE_HEAP:
1155 : 0 : ctxt.add_cwe (122);
1156 : 0 : warned = ctxt.warn ("heap-based buffer over-read");
1157 : 0 : break;
1158 : : }
1159 : 21 : if (warned)
1160 : 21 : maybe_show_notes (ctxt);
1161 : 21 : return warned;
1162 : : }
1163 : :
1164 : : bool
1165 : 42 : describe_final_event (pretty_printer &pp,
1166 : : const evdesc::final_event &) final override
1167 : : {
1168 : 42 : if (m_offset)
1169 : : {
1170 : : /* Known offset. */
1171 : 42 : if (m_num_bytes)
1172 : : {
1173 : : /* Known offset, known size. */
1174 : 42 : if (TREE_CODE (m_num_bytes) == INTEGER_CST)
1175 : : {
1176 : : /* Known offset, known constant size. */
1177 : 24 : if (pending_diagnostic::same_tree_p (m_num_bytes,
1178 : : integer_one_node))
1179 : : {
1180 : : /* Singular m_num_bytes. */
1181 : 0 : if (m_diag_arg)
1182 : 0 : pp_printf (&pp,
1183 : : "read of %E byte at offset %qE exceeds %qE",
1184 : : m_num_bytes, m_offset, m_diag_arg);
1185 : : else
1186 : 0 : pp_printf (&pp,
1187 : : "read of %E byte at offset %qE exceeds"
1188 : : " the buffer",
1189 : : m_num_bytes, m_offset);
1190 : : }
1191 : : else
1192 : : {
1193 : : /* Plural m_num_bytes. */
1194 : 24 : if (m_diag_arg)
1195 : 8 : pp_printf (&pp,
1196 : : "read of %E bytes at offset %qE exceeds %qE",
1197 : : m_num_bytes, m_offset, m_diag_arg);
1198 : : else
1199 : 16 : pp_printf (&pp,
1200 : : "read of %E bytes at offset %qE exceeds"
1201 : : " the buffer",
1202 : : m_num_bytes, m_offset);
1203 : : }
1204 : : }
1205 : : else
1206 : : {
1207 : : /* Known offset, known symbolic size. */
1208 : 18 : if (m_diag_arg)
1209 : 0 : pp_printf (&pp,
1210 : : "read of %qE bytes at offset %qE exceeds %qE",
1211 : : m_num_bytes, m_offset, m_diag_arg);
1212 : : else
1213 : 18 : pp_printf (&pp,
1214 : : "read of %qE bytes at offset %qE exceeds"
1215 : : " the buffer",
1216 : : m_num_bytes, m_offset);
1217 : : }
1218 : : }
1219 : : else
1220 : : {
1221 : : /* Known offset, unknown size. */
1222 : 0 : if (m_diag_arg)
1223 : 0 : pp_printf (&pp,
1224 : : "read at offset %qE exceeds %qE",
1225 : : m_offset, m_diag_arg);
1226 : : else
1227 : 0 : pp_printf (&pp,
1228 : : "read at offset %qE exceeds the buffer",
1229 : : m_offset);
1230 : : }
1231 : : }
1232 : : else
1233 : : {
1234 : : /* Unknown offset. */
1235 : 0 : if (m_diag_arg)
1236 : 0 : pp_printf (&pp,
1237 : : "out-of-bounds read on %qE",
1238 : : m_diag_arg);
1239 : : else
1240 : 0 : pp_printf (&pp,
1241 : : "out-of-bounds read");
1242 : : }
1243 : 42 : return true;
1244 : : }
1245 : :
1246 : 21 : enum access_direction get_dir () const final override { return DIR_READ; }
1247 : : };
1248 : :
1249 : : const svalue *
1250 : 142003 : strip_types (const svalue *sval,
1251 : : region_model_manager &mgr)
1252 : : {
1253 : 154606 : switch (sval->get_kind ())
1254 : : {
1255 : 0 : default:
1256 : 0 : gcc_unreachable ();
1257 : 18 : case SK_REGION:
1258 : 18 : {
1259 : 18 : const region_svalue *region_sval = (const region_svalue *)sval;
1260 : 18 : return mgr.get_ptr_svalue (NULL_TREE, region_sval->get_pointee ());
1261 : : }
1262 : : case SK_CONSTANT:
1263 : : return sval;
1264 : 44988 : case SK_UNKNOWN:
1265 : 44988 : return mgr.get_or_create_unknown_svalue (NULL_TREE);
1266 : 8 : case SK_POISONED:
1267 : 8 : {
1268 : 8 : const poisoned_svalue *poisoned_sval = (const poisoned_svalue *)sval;
1269 : 8 : return mgr.get_or_create_poisoned_svalue
1270 : 8 : (poisoned_sval->get_poison_kind (),
1271 : 8 : NULL_TREE);
1272 : : }
1273 : : case SK_SETJMP:
1274 : : return sval;
1275 : : case SK_INITIAL:
1276 : : return sval;
1277 : 12695 : case SK_UNARYOP:
1278 : 12695 : {
1279 : 12695 : const unaryop_svalue *unaryop_sval = (const unaryop_svalue *)sval;
1280 : 12695 : const enum tree_code op = unaryop_sval->get_op ();
1281 : 12695 : if (op == VIEW_CONVERT_EXPR || op == NOP_EXPR)
1282 : 12603 : return strip_types (unaryop_sval->get_arg (), mgr);
1283 : 92 : return mgr.get_or_create_unaryop
1284 : 92 : (NULL_TREE,
1285 : : op,
1286 : 92 : strip_types (unaryop_sval->get_arg (), mgr));
1287 : : }
1288 : 21572 : case SK_BINOP:
1289 : 21572 : {
1290 : 21572 : const binop_svalue *binop_sval = (const binop_svalue *)sval;
1291 : 21572 : const enum tree_code op = binop_sval->get_op ();
1292 : 21572 : return mgr.get_or_create_binop
1293 : 21572 : (NULL_TREE,
1294 : : op,
1295 : : strip_types (binop_sval->get_arg0 (), mgr),
1296 : 21572 : strip_types (binop_sval->get_arg1 (), mgr));
1297 : : }
1298 : 114 : case SK_SUB:
1299 : 114 : {
1300 : 114 : const sub_svalue *sub_sval = (const sub_svalue *)sval;
1301 : 114 : return mgr.get_or_create_sub_svalue
1302 : 114 : (NULL_TREE,
1303 : : strip_types (sub_sval->get_parent (), mgr),
1304 : 114 : sub_sval->get_subregion ());
1305 : : }
1306 : 0 : case SK_REPEATED:
1307 : 0 : {
1308 : 0 : const repeated_svalue *repeated_sval = (const repeated_svalue *)sval;
1309 : 0 : return mgr.get_or_create_repeated_svalue
1310 : 0 : (NULL_TREE,
1311 : : strip_types (repeated_sval->get_outer_size (), mgr),
1312 : 0 : strip_types (repeated_sval->get_inner_svalue (), mgr));
1313 : : }
1314 : 4 : case SK_BITS_WITHIN:
1315 : 4 : {
1316 : 4 : const bits_within_svalue *bits_within_sval
1317 : : = (const bits_within_svalue *)sval;
1318 : 4 : return mgr.get_or_create_bits_within
1319 : 4 : (NULL_TREE,
1320 : : bits_within_sval->get_bits (),
1321 : 4 : strip_types (bits_within_sval->get_inner_svalue (), mgr));
1322 : : }
1323 : 0 : case SK_UNMERGEABLE:
1324 : 0 : {
1325 : 0 : const unmergeable_svalue *unmergeable_sval
1326 : : = (const unmergeable_svalue *)sval;
1327 : 0 : return mgr.get_or_create_unmergeable
1328 : 0 : (strip_types (unmergeable_sval->get_arg (), mgr));
1329 : : }
1330 : : case SK_PLACEHOLDER:
1331 : : return sval;
1332 : 1438 : case SK_WIDENING:
1333 : 1438 : {
1334 : 1438 : const widening_svalue *widening_sval = (const widening_svalue *)sval;
1335 : 1438 : return mgr.get_or_create_widening_svalue
1336 : 1438 : (NULL_TREE,
1337 : : widening_sval->get_point (),
1338 : : strip_types (widening_sval->get_base_svalue (), mgr),
1339 : 1438 : strip_types (widening_sval->get_iter_svalue (), mgr));
1340 : : }
1341 : 0 : case SK_COMPOUND:
1342 : 0 : {
1343 : 0 : const compound_svalue *compound_sval = (const compound_svalue *)sval;
1344 : 0 : binding_map typeless_map;
1345 : 0 : for (auto iter : compound_sval->get_map ())
1346 : : {
1347 : 0 : const binding_key *key = iter.first;
1348 : 0 : const svalue *bound_sval = iter.second;
1349 : 0 : typeless_map.put (key, strip_types (bound_sval, mgr));
1350 : : }
1351 : 0 : return mgr.get_or_create_compound_svalue (NULL_TREE, typeless_map);
1352 : 0 : }
1353 : : case SK_CONJURED:
1354 : : return sval;
1355 : 94 : case SK_ASM_OUTPUT:
1356 : 94 : {
1357 : 94 : const asm_output_svalue *asm_output_sval
1358 : : = (const asm_output_svalue *)sval;
1359 : 94 : auto_vec<const svalue *> typeless_inputs
1360 : 94 : (asm_output_sval->get_num_inputs ());
1361 : 282 : for (unsigned idx = 0; idx < asm_output_sval->get_num_inputs (); idx++)
1362 : 188 : typeless_inputs.quick_push
1363 : 188 : (strip_types (asm_output_sval->get_input (idx),
1364 : : mgr));
1365 : 94 : return mgr.get_or_create_asm_output_svalue
1366 : 94 : (NULL_TREE,
1367 : : asm_output_sval->get_asm_string (),
1368 : : asm_output_sval->get_output_idx (),
1369 : : asm_output_sval->get_num_outputs (),
1370 : : typeless_inputs);
1371 : 94 : }
1372 : 16 : case SK_CONST_FN_RESULT:
1373 : 16 : {
1374 : 16 : const const_fn_result_svalue *const_fn_result_sval
1375 : : = (const const_fn_result_svalue *)sval;
1376 : 16 : auto_vec<const svalue *> typeless_inputs
1377 : 16 : (const_fn_result_sval->get_num_inputs ());
1378 : 16 : for (unsigned idx = 0;
1379 : 32 : idx < const_fn_result_sval->get_num_inputs ();
1380 : : idx++)
1381 : 16 : typeless_inputs.quick_push
1382 : 16 : (strip_types (const_fn_result_sval->get_input (idx),
1383 : : mgr));
1384 : 16 : return mgr.get_or_create_const_fn_result_svalue
1385 : 16 : (NULL_TREE,
1386 : : const_fn_result_sval->get_fndecl (),
1387 : : typeless_inputs);
1388 : 16 : }
1389 : : }
1390 : : }
1391 : :
1392 : : /* Check whether an access is past the end of the BASE_REG.
1393 : : Return TRUE if the access was valid, FALSE otherwise. */
1394 : :
1395 : : bool
1396 : 47298 : region_model::check_symbolic_bounds (const region *base_reg,
1397 : : const svalue *sym_byte_offset,
1398 : : const svalue *num_bytes_sval,
1399 : : const svalue *capacity,
1400 : : enum access_direction dir,
1401 : : const svalue *sval_hint,
1402 : : region_model_context *ctxt) const
1403 : : {
1404 : 47298 : gcc_assert (ctxt);
1405 : :
1406 : 47298 : const svalue *next_byte
1407 : 47298 : = m_mgr->get_or_create_binop (NULL_TREE, PLUS_EXPR,
1408 : : sym_byte_offset, num_bytes_sval);
1409 : :
1410 : 47298 : next_byte = strip_types (next_byte, *m_mgr);
1411 : 47298 : capacity = strip_types (capacity, *m_mgr);
1412 : :
1413 : 47298 : if (eval_condition (next_byte, GT_EXPR, capacity).is_true ())
1414 : : {
1415 : 145 : tree diag_arg = get_representative_tree (base_reg);
1416 : 145 : tree offset_tree = get_representative_tree (sym_byte_offset);
1417 : 145 : tree num_bytes_tree = get_representative_tree (num_bytes_sval);
1418 : 145 : tree capacity_tree = get_representative_tree (capacity);
1419 : 145 : const region *offset_reg = m_mgr->get_offset_region (base_reg,
1420 : : NULL_TREE,
1421 : : sym_byte_offset);
1422 : 145 : const region *sized_offset_reg = m_mgr->get_sized_region (offset_reg,
1423 : : NULL_TREE,
1424 : 145 : num_bytes_sval);
1425 : 145 : switch (dir)
1426 : : {
1427 : 0 : default:
1428 : 0 : gcc_unreachable ();
1429 : 33 : break;
1430 : 33 : case DIR_READ:
1431 : 33 : gcc_assert (sval_hint == nullptr);
1432 : 33 : ctxt->warn (make_unique<symbolic_buffer_over_read> (*this,
1433 : : sized_offset_reg,
1434 : : diag_arg,
1435 : : offset_tree,
1436 : : num_bytes_tree,
1437 : : capacity_tree));
1438 : 33 : return false;
1439 : 112 : break;
1440 : 112 : case DIR_WRITE:
1441 : 112 : ctxt->warn (make_unique<symbolic_buffer_overflow> (*this,
1442 : : sized_offset_reg,
1443 : : diag_arg,
1444 : : offset_tree,
1445 : : num_bytes_tree,
1446 : : capacity_tree,
1447 : : sval_hint));
1448 : 112 : return false;
1449 : 145 : break;
1450 : : }
1451 : : }
1452 : : return true;
1453 : : }
1454 : :
1455 : : static tree
1456 : 1364072 : maybe_get_integer_cst_tree (const svalue *sval)
1457 : : {
1458 : 1364072 : tree cst_tree = sval->maybe_get_constant ();
1459 : 1364072 : if (cst_tree && TREE_CODE (cst_tree) == INTEGER_CST)
1460 : 1313685 : return cst_tree;
1461 : :
1462 : : return NULL_TREE;
1463 : : }
1464 : :
1465 : : /* May complain when the access on REG is out-of-bounds.
1466 : : Return TRUE if the access was valid, FALSE otherwise. */
1467 : :
1468 : : bool
1469 : 682046 : region_model::check_region_bounds (const region *reg,
1470 : : enum access_direction dir,
1471 : : const svalue *sval_hint,
1472 : : region_model_context *ctxt) const
1473 : : {
1474 : 682046 : gcc_assert (ctxt);
1475 : :
1476 : : /* Get the offset. */
1477 : 682046 : region_offset reg_offset = reg->get_offset (m_mgr);
1478 : 682046 : const region *base_reg = reg_offset.get_base_region ();
1479 : :
1480 : : /* Find out how many bits were accessed. */
1481 : 682046 : const svalue *num_bits_sval = reg->get_bit_size_sval (m_mgr);
1482 : 682046 : tree num_bits_tree = maybe_get_integer_cst_tree (num_bits_sval);
1483 : : /* Bail out if 0 bits are accessed. */
1484 : 682046 : if (num_bits_tree && zerop (num_bits_tree))
1485 : : return true;
1486 : :
1487 : : /* Get the capacity of the buffer (in bytes). */
1488 : 682026 : const svalue *byte_capacity = get_capacity (base_reg);
1489 : 682026 : tree cst_byte_capacity_tree = maybe_get_integer_cst_tree (byte_capacity);
1490 : :
1491 : : /* The constant offset from a pointer is represented internally as a sizetype
1492 : : but should be interpreted as a signed value here. The statement below
1493 : : converts the offset from bits to bytes and then to a signed integer with
1494 : : the same precision the sizetype has on the target system.
1495 : :
1496 : : For example, this is needed for out-of-bounds-3.c test1 to pass when
1497 : : compiled with a 64-bit gcc build targeting 32-bit systems. */
1498 : 682026 : bit_offset_t bit_offset;
1499 : 682026 : if (!reg_offset.symbolic_p ())
1500 : 672128 : bit_offset = wi::sext (reg_offset.get_bit_offset (),
1501 : 672128 : TYPE_PRECISION (size_type_node));
1502 : :
1503 : : /* If any of the base region, the offset, or the number of bytes accessed
1504 : : are symbolic, we have to reason about symbolic values. */
1505 : 682026 : if (base_reg->symbolic_p () || reg_offset.symbolic_p () || !num_bits_tree)
1506 : : {
1507 : 47298 : const svalue* byte_offset_sval;
1508 : 47298 : if (!reg_offset.symbolic_p ())
1509 : : {
1510 : 37400 : tree byte_offset_tree
1511 : 37400 : = wide_int_to_tree (integer_type_node,
1512 : 37400 : bit_offset >> LOG2_BITS_PER_UNIT);
1513 : 37400 : byte_offset_sval
1514 : 37400 : = m_mgr->get_or_create_constant_svalue (byte_offset_tree);
1515 : : }
1516 : : else
1517 : 9898 : byte_offset_sval = reg_offset.get_symbolic_byte_offset ();
1518 : 47298 : const svalue *num_bytes_sval = reg->get_byte_size_sval (m_mgr);
1519 : 47298 : return check_symbolic_bounds (base_reg, byte_offset_sval, num_bytes_sval,
1520 : 47298 : byte_capacity, dir, sval_hint, ctxt);
1521 : : }
1522 : :
1523 : : /* Otherwise continue to check with concrete values. */
1524 : 634728 : bit_range bits_outside (0, 0);
1525 : 634728 : bool oob_safe = true;
1526 : : /* NUM_BITS_TREE should always be interpreted as unsigned. */
1527 : 634728 : bit_offset_t num_bits_unsigned = wi::to_offset (num_bits_tree);
1528 : 634728 : bit_range read_bits (bit_offset, num_bits_unsigned);
1529 : : /* If read_bits has a subset < 0, we do have an underwrite. */
1530 : 634728 : if (read_bits.falls_short_of_p (0, &bits_outside))
1531 : : {
1532 : 164 : tree diag_arg = get_representative_tree (base_reg);
1533 : 164 : switch (dir)
1534 : : {
1535 : 0 : default:
1536 : 0 : gcc_unreachable ();
1537 : 110 : break;
1538 : 110 : case DIR_READ:
1539 : 110 : gcc_assert (sval_hint == nullptr);
1540 : 110 : ctxt->warn (make_unique<concrete_buffer_under_read> (*this, reg,
1541 : : diag_arg,
1542 : : bits_outside));
1543 : 110 : oob_safe = false;
1544 : 110 : break;
1545 : 54 : case DIR_WRITE:
1546 : 54 : ctxt->warn (make_unique<concrete_buffer_underwrite> (*this,
1547 : : reg, diag_arg,
1548 : : bits_outside,
1549 : : sval_hint));
1550 : 54 : oob_safe = false;
1551 : 54 : break;
1552 : : }
1553 : : }
1554 : :
1555 : : /* For accesses past the end, we do need a concrete capacity. No need to
1556 : : do a symbolic check here because the inequality check does not reason
1557 : : whether constants are greater than symbolic values. */
1558 : 634728 : if (!cst_byte_capacity_tree)
1559 : : return oob_safe;
1560 : :
1561 : 631753 : bit_range buffer (0, wi::to_offset (cst_byte_capacity_tree) * BITS_PER_UNIT);
1562 : : /* If READ_BITS exceeds BUFFER, we do have an overflow. */
1563 : 631753 : if (read_bits.exceeds_p (buffer, &bits_outside))
1564 : : {
1565 : 872 : tree bit_bound = wide_int_to_tree (size_type_node,
1566 : 436 : buffer.get_next_bit_offset ());
1567 : 436 : tree diag_arg = get_representative_tree (base_reg);
1568 : :
1569 : 436 : switch (dir)
1570 : : {
1571 : 0 : default:
1572 : 0 : gcc_unreachable ();
1573 : 254 : break;
1574 : 254 : case DIR_READ:
1575 : 254 : gcc_assert (sval_hint == nullptr);
1576 : 254 : ctxt->warn (make_unique<concrete_buffer_over_read> (*this,
1577 : : reg, diag_arg,
1578 : : bits_outside,
1579 : : bit_bound));
1580 : 254 : oob_safe = false;
1581 : 254 : break;
1582 : 182 : case DIR_WRITE:
1583 : 182 : ctxt->warn (make_unique<concrete_buffer_overflow> (*this,
1584 : : reg, diag_arg,
1585 : : bits_outside,
1586 : : bit_bound,
1587 : : sval_hint));
1588 : 182 : oob_safe = false;
1589 : 182 : break;
1590 : : }
1591 : : }
1592 : : return oob_safe;
1593 : : }
1594 : :
1595 : : } // namespace ana
1596 : :
1597 : : #endif /* #if ENABLE_ANALYZER */
|