hppa: Fix pr110279-1.c on hppa
[official-gcc.git] / gcc / analyzer / bounds-checking.cc
blob551d9796f7994abbe1072cdc35f3da7efb34c7ba
1 /* Bounds-checking of reads and writes to memory regions.
2 Copyright (C) 2019-2023 Free Software Foundation, Inc.
4 This file is part of GCC.
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.
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.
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/>. */
20 #include "config.h"
21 #define INCLUDE_MEMORY
22 #define INCLUDE_VECTOR
23 #include "system.h"
24 #include "coretypes.h"
25 #include "make-unique.h"
26 #include "tree.h"
27 #include "function.h"
28 #include "basic-block.h"
29 #include "intl.h"
30 #include "gimple.h"
31 #include "gimple-iterator.h"
32 #include "diagnostic-core.h"
33 #include "diagnostic-diagram.h"
34 #include "diagnostic-format-sarif.h"
35 #include "analyzer/analyzer.h"
36 #include "analyzer/analyzer-logging.h"
37 #include "analyzer/region-model.h"
38 #include "analyzer/checker-event.h"
39 #include "analyzer/checker-path.h"
40 #include "analyzer/access-diagram.h"
42 #if ENABLE_ANALYZER
44 namespace ana {
46 /* Abstract base class for all out-of-bounds warnings. */
48 class out_of_bounds : public pending_diagnostic
50 public:
51 class oob_region_creation_event_capacity : public region_creation_event_capacity
53 public:
54 oob_region_creation_event_capacity (tree byte_capacity,
55 const event_loc_info &loc_info,
56 out_of_bounds &oob)
57 : region_creation_event_capacity (byte_capacity,
58 loc_info),
59 m_oob (oob)
62 void prepare_for_emission (checker_path *path,
63 pending_diagnostic *pd,
64 diagnostic_event_id_t emission_id) override
66 region_creation_event_capacity::prepare_for_emission (path,
67 pd,
68 emission_id);
69 m_oob.m_region_creation_event_id = emission_id;
71 private:
72 out_of_bounds &m_oob;
75 out_of_bounds (const region_model &model,
76 const region *reg,
77 tree diag_arg,
78 const svalue *sval_hint)
79 : m_model (model), m_reg (reg), m_diag_arg (diag_arg), m_sval_hint (sval_hint)
82 bool subclass_equal_p (const pending_diagnostic &base_other) const override
84 const out_of_bounds &other
85 (static_cast <const out_of_bounds &>(base_other));
86 return (m_reg == other.m_reg
87 && pending_diagnostic::same_tree_p (m_diag_arg, other.m_diag_arg));
90 int get_controlling_option () const final override
92 return OPT_Wanalyzer_out_of_bounds;
95 void mark_interesting_stuff (interesting_t *interest) final override
97 interest->add_region_creation (m_reg->get_base_region ());
100 void add_region_creation_events (const region *,
101 tree byte_capacity,
102 const event_loc_info &loc_info,
103 checker_path &emission_path) override
105 /* The memory space is described in the diagnostic message itself,
106 so we don't need an event for that. */
107 if (byte_capacity)
108 emission_path.add_event
109 (make_unique<oob_region_creation_event_capacity> (byte_capacity,
110 loc_info,
111 *this));
114 void maybe_add_sarif_properties (sarif_object &result_obj)
115 const override
117 sarif_property_bag &props = result_obj.get_or_create_properties ();
118 #define PROPERTY_PREFIX "gcc/analyzer/out_of_bounds/"
119 props.set_string (PROPERTY_PREFIX "dir",
120 get_dir () == DIR_READ ? "read" : "write");
121 props.set (PROPERTY_PREFIX "model", m_model.to_json ());
122 props.set (PROPERTY_PREFIX "region", m_reg->to_json ());
123 props.set (PROPERTY_PREFIX "diag_arg", tree_to_json (m_diag_arg));
124 if (m_sval_hint)
125 props.set (PROPERTY_PREFIX "sval_hint", m_sval_hint->to_json ());
126 props.set (PROPERTY_PREFIX "region_creation_event_id",
127 diagnostic_event_id_to_json (m_region_creation_event_id));
128 #undef PROPERTY_PREFIX
131 virtual enum access_direction get_dir () const = 0;
133 protected:
134 enum memory_space get_memory_space () const
136 return m_reg->get_memory_space ();
139 void
140 maybe_show_notes (diagnostic_emission_context &ctxt) const
142 maybe_describe_array_bounds (ctxt.get_location ());
143 maybe_show_diagram (ctxt.get_logger ());
146 /* Potentially add a note about valid ways to index this array, such
147 as (given "int arr[10];"):
148 note: valid subscripts for 'arr' are '[0]' to '[9]'
149 We print the '[' and ']' characters so as to express the valid
150 subscripts using C syntax, rather than just as byte ranges,
151 which hopefully is more clear to the user. */
152 void
153 maybe_describe_array_bounds (location_t loc) const
155 if (!m_diag_arg)
156 return;
157 tree t = TREE_TYPE (m_diag_arg);
158 if (!t)
159 return;
160 if (TREE_CODE (t) != ARRAY_TYPE)
161 return;
162 tree domain = TYPE_DOMAIN (t);
163 if (!domain)
164 return;
165 tree max_idx = TYPE_MAX_VALUE (domain);
166 if (!max_idx)
167 return;
168 tree min_idx = TYPE_MIN_VALUE (domain);
169 inform (loc,
170 "valid subscripts for %qE are %<[%E]%> to %<[%E]%>",
171 m_diag_arg, min_idx, max_idx);
174 void
175 maybe_show_diagram (logger *logger) const
177 access_operation op (m_model, get_dir (), *m_reg, m_sval_hint);
179 /* Don't attempt to make a diagram if there's no valid way of
180 accessing the base region (e.g. a 0-element array). */
181 if (op.get_valid_bits ().empty_p ())
182 return;
184 if (const text_art::theme *theme = global_dc->get_diagram_theme ())
186 text_art::style_manager sm;
187 text_art::canvas canvas (make_access_diagram (op, sm, *theme, logger));
188 if (canvas.get_size ().w == 0 && canvas.get_size ().h == 0)
190 /* In lieu of exceptions, return a zero-sized diagram if there's
191 a problem. Give up if that's happened. */
192 return;
194 diagnostic_diagram diagram
195 (canvas,
196 /* Alt text. */
197 _("Diagram visualizing the predicted out-of-bounds access"));
198 global_dc->emit_diagram (diagram);
202 text_art::canvas
203 make_access_diagram (const access_operation &op,
204 text_art::style_manager &sm,
205 const text_art::theme &theme,
206 logger *logger) const
208 access_diagram d (op, m_region_creation_event_id, sm, theme, logger);
209 return d.to_canvas (sm);
212 region_model m_model;
213 const region *m_reg;
214 tree m_diag_arg;
215 const svalue *m_sval_hint;
216 diagnostic_event_id_t m_region_creation_event_id;
219 /* Abstract base class for all out-of-bounds warnings where the
220 out-of-bounds range is concrete. */
222 class concrete_out_of_bounds : public out_of_bounds
224 public:
225 concrete_out_of_bounds (const region_model &model,
226 const region *reg, tree diag_arg,
227 bit_range out_of_bounds_bits,
228 const svalue *sval_hint)
229 : out_of_bounds (model, reg, diag_arg, sval_hint),
230 m_out_of_bounds_bits (out_of_bounds_bits)
233 bool subclass_equal_p (const pending_diagnostic &base_other) const override
235 const concrete_out_of_bounds &other
236 (static_cast <const concrete_out_of_bounds &>(base_other));
237 return (out_of_bounds::subclass_equal_p (other)
238 && m_out_of_bounds_bits == other.m_out_of_bounds_bits);
241 void maybe_add_sarif_properties (sarif_object &result_obj)
242 const override
244 out_of_bounds::maybe_add_sarif_properties (result_obj);
245 sarif_property_bag &props = result_obj.get_or_create_properties ();
246 #define PROPERTY_PREFIX "gcc/analyzer/concrete_out_of_bounds/"
247 props.set (PROPERTY_PREFIX "out_of_bounds_bits",
248 m_out_of_bounds_bits.to_json ());
249 byte_range out_of_bounds_bytes (0, 0);
250 if (get_out_of_bounds_bytes (&out_of_bounds_bytes))
251 props.set (PROPERTY_PREFIX "out_of_bounds_bytes",
252 out_of_bounds_bytes.to_json ());
253 #undef PROPERTY_PREFIX
256 bool get_out_of_bounds_bytes (byte_range *out) const
258 return m_out_of_bounds_bits.as_byte_range (out);
261 protected:
262 bit_range m_out_of_bounds_bits;
265 /* Abstract subclass to complaing about concrete out-of-bounds
266 past the end of the buffer. */
268 class concrete_past_the_end : public concrete_out_of_bounds
270 public:
271 concrete_past_the_end (const region_model &model,
272 const region *reg, tree diag_arg, bit_range range,
273 tree bit_bound,
274 const svalue *sval_hint)
275 : concrete_out_of_bounds (model, reg, diag_arg, range, sval_hint),
276 m_bit_bound (bit_bound),
277 m_byte_bound (NULL_TREE)
279 if (m_bit_bound && TREE_CODE (m_bit_bound) == INTEGER_CST)
280 m_byte_bound
281 = wide_int_to_tree (size_type_node,
282 wi::to_offset (m_bit_bound) >> LOG2_BITS_PER_UNIT);
285 bool
286 subclass_equal_p (const pending_diagnostic &base_other) const final override
288 const concrete_past_the_end &other
289 (static_cast <const concrete_past_the_end &>(base_other));
290 return (concrete_out_of_bounds::subclass_equal_p (other)
291 && pending_diagnostic::same_tree_p (m_bit_bound,
292 other.m_bit_bound));
295 void add_region_creation_events (const region *,
296 tree,
297 const event_loc_info &loc_info,
298 checker_path &emission_path) final override
300 if (m_byte_bound && TREE_CODE (m_byte_bound) == INTEGER_CST)
301 emission_path.add_event
302 (make_unique<oob_region_creation_event_capacity> (m_byte_bound,
303 loc_info,
304 *this));
307 void maybe_add_sarif_properties (sarif_object &result_obj)
308 const final override
310 concrete_out_of_bounds::maybe_add_sarif_properties (result_obj);
311 sarif_property_bag &props = result_obj.get_or_create_properties ();
312 #define PROPERTY_PREFIX "gcc/analyzer/concrete_past_the_end/"
313 props.set (PROPERTY_PREFIX "bit_bound",
314 tree_to_json (m_bit_bound));
315 props.set (PROPERTY_PREFIX "byte_bound",
316 tree_to_json (m_byte_bound));
317 #undef PROPERTY_PREFIX
320 protected:
321 tree m_bit_bound;
322 tree m_byte_bound;
325 /* Concrete subclass to complain about buffer overflows. */
327 class concrete_buffer_overflow : public concrete_past_the_end
329 public:
330 concrete_buffer_overflow (const region_model &model,
331 const region *reg, tree diag_arg,
332 bit_range range, tree bit_bound,
333 const svalue *sval_hint)
334 : concrete_past_the_end (model, reg, diag_arg, range, bit_bound, sval_hint)
337 const char *get_kind () const final override
339 return "concrete_buffer_overflow";
342 bool emit (diagnostic_emission_context &ctxt) final override
344 bool warned;
345 switch (get_memory_space ())
347 default:
348 ctxt.add_cwe (787);
349 warned = ctxt.warn ("buffer overflow");
350 break;
351 case MEMSPACE_STACK:
352 ctxt.add_cwe (121);
353 warned = ctxt.warn ("stack-based buffer overflow");
354 break;
355 case MEMSPACE_HEAP:
356 ctxt.add_cwe (122);
357 warned = ctxt.warn ("heap-based buffer overflow");
358 break;
361 if (warned)
363 if (wi::fits_uhwi_p (m_out_of_bounds_bits.m_size_in_bits))
365 unsigned HOST_WIDE_INT num_bad_bits
366 = m_out_of_bounds_bits.m_size_in_bits.to_uhwi ();
367 if (num_bad_bits % BITS_PER_UNIT == 0)
369 unsigned HOST_WIDE_INT num_bad_bytes
370 = num_bad_bits / BITS_PER_UNIT;
371 if (m_diag_arg)
372 inform_n (ctxt.get_location (),
373 num_bad_bytes,
374 "write of %wu byte to beyond the end of %qE",
375 "write of %wu bytes to beyond the end of %qE",
376 num_bad_bytes,
377 m_diag_arg);
378 else
379 inform_n (ctxt.get_location (),
380 num_bad_bytes,
381 "write of %wu byte to beyond the end of the region",
382 "write of %wu bytes to beyond the end of the region",
383 num_bad_bytes);
385 else
387 if (m_diag_arg)
388 inform_n (ctxt.get_location (),
389 num_bad_bits,
390 "write of %wu bit to beyond the end of %qE",
391 "write of %wu bits to beyond the end of %qE",
392 num_bad_bits,
393 m_diag_arg);
394 else
395 inform_n (ctxt.get_location (),
396 num_bad_bits,
397 "write of %wu bit to beyond the end of the region",
398 "write of %wu bits to beyond the end of the region",
399 num_bad_bits);
402 else if (m_diag_arg)
403 inform (ctxt.get_location (),
404 "write to beyond the end of %qE",
405 m_diag_arg);
407 maybe_show_notes (ctxt);
410 return warned;
413 label_text describe_final_event (const evdesc::final_event &ev)
414 final override
416 if (m_byte_bound || !m_bit_bound)
418 byte_range out_of_bounds_bytes (0, 0);
419 if (get_out_of_bounds_bytes (&out_of_bounds_bytes))
420 return describe_final_event_as_bytes (ev, out_of_bounds_bytes);
422 return describe_final_event_as_bits (ev);
425 label_text
426 describe_final_event_as_bytes (const evdesc::final_event &ev,
427 const byte_range &out_of_bounds_bytes)
429 byte_size_t start = out_of_bounds_bytes.get_start_byte_offset ();
430 byte_size_t end = out_of_bounds_bytes.get_last_byte_offset ();
431 char start_buf[WIDE_INT_PRINT_BUFFER_SIZE];
432 print_dec (start, start_buf, SIGNED);
433 char end_buf[WIDE_INT_PRINT_BUFFER_SIZE];
434 print_dec (end, end_buf, SIGNED);
436 if (start == end)
438 if (m_diag_arg)
439 return ev.formatted_print ("out-of-bounds write at byte %s but %qE"
440 " ends at byte %E", start_buf, m_diag_arg,
441 m_byte_bound);
442 return ev.formatted_print ("out-of-bounds write at byte %s but region"
443 " ends at byte %E", start_buf,
444 m_byte_bound);
446 else
448 if (m_diag_arg)
449 return ev.formatted_print ("out-of-bounds write from byte %s till"
450 " byte %s but %qE ends at byte %E",
451 start_buf, end_buf, m_diag_arg,
452 m_byte_bound);
453 return ev.formatted_print ("out-of-bounds write from byte %s till"
454 " byte %s but region ends at byte %E",
455 start_buf, end_buf, m_byte_bound);
459 label_text describe_final_event_as_bits (const evdesc::final_event &ev)
461 bit_size_t start = m_out_of_bounds_bits.get_start_bit_offset ();
462 bit_size_t end = m_out_of_bounds_bits.get_last_bit_offset ();
463 char start_buf[WIDE_INT_PRINT_BUFFER_SIZE];
464 print_dec (start, start_buf, SIGNED);
465 char end_buf[WIDE_INT_PRINT_BUFFER_SIZE];
466 print_dec (end, end_buf, SIGNED);
468 if (start == end)
470 if (m_diag_arg)
471 return ev.formatted_print ("out-of-bounds write at bit %s but %qE"
472 " ends at bit %E", start_buf, m_diag_arg,
473 m_bit_bound);
474 return ev.formatted_print ("out-of-bounds write at bit %s but region"
475 " ends at bit %E", start_buf,
476 m_bit_bound);
478 else
480 if (m_diag_arg)
481 return ev.formatted_print ("out-of-bounds write from bit %s till"
482 " bit %s but %qE ends at bit %E",
483 start_buf, end_buf, m_diag_arg,
484 m_bit_bound);
485 return ev.formatted_print ("out-of-bounds write from bit %s till"
486 " bit %s but region ends at bit %E",
487 start_buf, end_buf, m_bit_bound);
491 enum access_direction get_dir () const final override { return DIR_WRITE; }
494 /* Concrete subclass to complain about buffer over-reads. */
496 class concrete_buffer_over_read : public concrete_past_the_end
498 public:
499 concrete_buffer_over_read (const region_model &model,
500 const region *reg, tree diag_arg,
501 bit_range range, tree bit_bound)
502 : concrete_past_the_end (model, reg, diag_arg, range, bit_bound, NULL)
505 const char *get_kind () const final override
507 return "concrete_buffer_over_read";
510 bool emit (diagnostic_emission_context &ctxt) final override
512 bool warned;
513 ctxt.add_cwe (126);
514 switch (get_memory_space ())
516 default:
517 warned = ctxt.warn ("buffer over-read");
518 break;
519 case MEMSPACE_STACK:
520 warned = ctxt.warn ("stack-based buffer over-read");
521 break;
522 case MEMSPACE_HEAP:
523 warned = ctxt.warn ("heap-based buffer over-read");
524 break;
527 if (warned)
529 if (wi::fits_uhwi_p (m_out_of_bounds_bits.m_size_in_bits))
531 unsigned HOST_WIDE_INT num_bad_bits
532 = m_out_of_bounds_bits.m_size_in_bits.to_uhwi ();
533 if (num_bad_bits % BITS_PER_UNIT == 0)
535 unsigned HOST_WIDE_INT num_bad_bytes
536 = num_bad_bits / BITS_PER_UNIT;
537 if (m_diag_arg)
538 inform_n (ctxt.get_location (),
539 num_bad_bytes,
540 "read of %wu byte from after the end of %qE",
541 "read of %wu bytes from after the end of %qE",
542 num_bad_bytes,
543 m_diag_arg);
544 else
545 inform_n (ctxt.get_location (),
546 num_bad_bytes,
547 "read of %wu byte from after the end of the region",
548 "read of %wu bytes from after the end of the region",
549 num_bad_bytes);
551 else
553 if (m_diag_arg)
554 inform_n (ctxt.get_location (),
555 num_bad_bits,
556 "read of %wu bit from after the end of %qE",
557 "read of %wu bits from after the end of %qE",
558 num_bad_bits,
559 m_diag_arg);
560 else
561 inform_n (ctxt.get_location (),
562 num_bad_bits,
563 "read of %wu bit from after the end of the region",
564 "read of %wu bits from after the end of the region",
565 num_bad_bits);
568 else if (m_diag_arg)
569 inform (ctxt.get_location (),
570 "read from after the end of %qE",
571 m_diag_arg);
573 maybe_show_notes (ctxt);
576 return warned;
579 label_text describe_final_event (const evdesc::final_event &ev)
580 final override
582 if (m_byte_bound || !m_bit_bound)
584 byte_range out_of_bounds_bytes (0, 0);
585 if (get_out_of_bounds_bytes (&out_of_bounds_bytes))
586 return describe_final_event_as_bytes (ev, out_of_bounds_bytes);
588 return describe_final_event_as_bits (ev);
591 label_text
592 describe_final_event_as_bytes (const evdesc::final_event &ev,
593 const byte_range &out_of_bounds_bytes)
595 byte_size_t start = out_of_bounds_bytes.get_start_byte_offset ();
596 byte_size_t end = out_of_bounds_bytes.get_last_byte_offset ();
597 char start_buf[WIDE_INT_PRINT_BUFFER_SIZE];
598 print_dec (start, start_buf, SIGNED);
599 char end_buf[WIDE_INT_PRINT_BUFFER_SIZE];
600 print_dec (end, end_buf, SIGNED);
602 if (start == end)
604 if (m_diag_arg)
605 return ev.formatted_print ("out-of-bounds read at byte %s but %qE"
606 " ends at byte %E", start_buf, m_diag_arg,
607 m_byte_bound);
608 return ev.formatted_print ("out-of-bounds read at byte %s but region"
609 " ends at byte %E", start_buf,
610 m_byte_bound);
612 else
614 if (m_diag_arg)
615 return ev.formatted_print ("out-of-bounds read from byte %s till"
616 " byte %s but %qE ends at byte %E",
617 start_buf, end_buf, m_diag_arg,
618 m_byte_bound);
619 return ev.formatted_print ("out-of-bounds read from byte %s till"
620 " byte %s but region ends at byte %E",
621 start_buf, end_buf, m_byte_bound);
625 label_text describe_final_event_as_bits (const evdesc::final_event &ev)
627 bit_size_t start = m_out_of_bounds_bits.get_start_bit_offset ();
628 bit_size_t end = m_out_of_bounds_bits.get_last_bit_offset ();
629 char start_buf[WIDE_INT_PRINT_BUFFER_SIZE];
630 print_dec (start, start_buf, SIGNED);
631 char end_buf[WIDE_INT_PRINT_BUFFER_SIZE];
632 print_dec (end, end_buf, SIGNED);
634 if (start == end)
636 if (m_diag_arg)
637 return ev.formatted_print ("out-of-bounds read at bit %s but %qE"
638 " ends at bit %E", start_buf, m_diag_arg,
639 m_bit_bound);
640 return ev.formatted_print ("out-of-bounds read at bit %s but region"
641 " ends at bit %E", start_buf,
642 m_bit_bound);
644 else
646 if (m_diag_arg)
647 return ev.formatted_print ("out-of-bounds read from bit %s till"
648 " bit %s but %qE ends at bit %E",
649 start_buf, end_buf, m_diag_arg,
650 m_bit_bound);
651 return ev.formatted_print ("out-of-bounds read from bit %s till"
652 " bit %s but region ends at bit %E",
653 start_buf, end_buf, m_bit_bound);
657 enum access_direction get_dir () const final override { return DIR_READ; }
660 /* Concrete subclass to complain about buffer underwrites. */
662 class concrete_buffer_underwrite : public concrete_out_of_bounds
664 public:
665 concrete_buffer_underwrite (const region_model &model,
666 const region *reg, tree diag_arg,
667 bit_range range,
668 const svalue *sval_hint)
669 : concrete_out_of_bounds (model, reg, diag_arg, range, sval_hint)
672 const char *get_kind () const final override
674 return "concrete_buffer_underwrite";
677 bool emit (diagnostic_emission_context &ctxt) final override
679 bool warned;
680 ctxt.add_cwe (124);
681 switch (get_memory_space ())
683 default:
684 warned = ctxt.warn ("buffer underwrite");
685 break;
686 case MEMSPACE_STACK:
687 warned = ctxt.warn ("stack-based buffer underwrite");
688 break;
689 case MEMSPACE_HEAP:
690 warned = ctxt.warn ("heap-based buffer underwrite");
691 break;
693 if (warned)
694 maybe_show_notes (ctxt);
695 return warned;
698 label_text describe_final_event (const evdesc::final_event &ev)
699 final override
701 byte_range out_of_bounds_bytes (0, 0);
702 if (get_out_of_bounds_bytes (&out_of_bounds_bytes))
703 return describe_final_event_as_bytes (ev, out_of_bounds_bytes);
704 return describe_final_event_as_bits (ev);
707 label_text
708 describe_final_event_as_bytes (const evdesc::final_event &ev,
709 const byte_range &out_of_bounds_bytes)
711 byte_size_t start = out_of_bounds_bytes.get_start_byte_offset ();
712 byte_size_t end = out_of_bounds_bytes.get_last_byte_offset ();
713 char start_buf[WIDE_INT_PRINT_BUFFER_SIZE];
714 print_dec (start, start_buf, SIGNED);
715 char end_buf[WIDE_INT_PRINT_BUFFER_SIZE];
716 print_dec (end, end_buf, SIGNED);
718 if (start == end)
720 if (m_diag_arg)
721 return ev.formatted_print ("out-of-bounds write at byte %s but %qE"
722 " starts at byte 0",
723 start_buf, m_diag_arg);
724 return ev.formatted_print ("out-of-bounds write at byte %s but region"
725 " starts at byte 0", start_buf);
727 else
729 if (m_diag_arg)
730 return ev.formatted_print ("out-of-bounds write from byte %s till"
731 " byte %s but %qE starts at byte 0",
732 start_buf, end_buf, m_diag_arg);
733 return ev.formatted_print ("out-of-bounds write from byte %s till"
734 " byte %s but region starts at byte 0",
735 start_buf, end_buf);;
739 label_text
740 describe_final_event_as_bits (const evdesc::final_event &ev)
742 bit_size_t start = m_out_of_bounds_bits.get_start_bit_offset ();
743 bit_size_t end = m_out_of_bounds_bits.get_last_bit_offset ();
744 char start_buf[WIDE_INT_PRINT_BUFFER_SIZE];
745 print_dec (start, start_buf, SIGNED);
746 char end_buf[WIDE_INT_PRINT_BUFFER_SIZE];
747 print_dec (end, end_buf, SIGNED);
749 if (start == end)
751 if (m_diag_arg)
752 return ev.formatted_print ("out-of-bounds write at bit %s but %qE"
753 " starts at bit 0",
754 start_buf, m_diag_arg);
755 return ev.formatted_print ("out-of-bounds write at bit %s but region"
756 " starts at bit 0", start_buf);
758 else
760 if (m_diag_arg)
761 return ev.formatted_print ("out-of-bounds write from bit %s till"
762 " bit %s but %qE starts at bit 0",
763 start_buf, end_buf, m_diag_arg);
764 return ev.formatted_print ("out-of-bounds write from bit %s till"
765 " bit %s but region starts at bit 0",
766 start_buf, end_buf);;
770 enum access_direction get_dir () const final override { return DIR_WRITE; }
773 /* Concrete subclass to complain about buffer under-reads. */
775 class concrete_buffer_under_read : public concrete_out_of_bounds
777 public:
778 concrete_buffer_under_read (const region_model &model,
779 const region *reg, tree diag_arg,
780 bit_range range)
781 : concrete_out_of_bounds (model, reg, diag_arg, range, NULL)
784 const char *get_kind () const final override
786 return "concrete_buffer_under_read";
789 bool emit (diagnostic_emission_context &ctxt) final override
791 bool warned;
792 ctxt.add_cwe (127);
793 switch (get_memory_space ())
795 default:
796 warned = ctxt.warn ("buffer under-read");
797 break;
798 case MEMSPACE_STACK:
799 warned = ctxt.warn ("stack-based buffer under-read");
800 break;
801 case MEMSPACE_HEAP:
802 warned = ctxt.warn ("heap-based buffer under-read");
803 break;
805 if (warned)
806 maybe_show_notes (ctxt);
807 return warned;
810 label_text describe_final_event (const evdesc::final_event &ev)
811 final override
813 byte_range out_of_bounds_bytes (0, 0);
814 if (get_out_of_bounds_bytes (&out_of_bounds_bytes))
815 return describe_final_event_as_bytes (ev, out_of_bounds_bytes);
816 return describe_final_event_as_bits (ev);
819 label_text
820 describe_final_event_as_bytes (const evdesc::final_event &ev,
821 const byte_range &out_of_bounds_bytes)
823 byte_size_t start = out_of_bounds_bytes.get_start_byte_offset ();
824 byte_size_t end = out_of_bounds_bytes.get_last_byte_offset ();
825 char start_buf[WIDE_INT_PRINT_BUFFER_SIZE];
826 print_dec (start, start_buf, SIGNED);
827 char end_buf[WIDE_INT_PRINT_BUFFER_SIZE];
828 print_dec (end, end_buf, SIGNED);
830 if (start == end)
832 if (m_diag_arg)
833 return ev.formatted_print ("out-of-bounds read at byte %s but %qE"
834 " starts at byte 0", start_buf,
835 m_diag_arg);
836 return ev.formatted_print ("out-of-bounds read at byte %s but region"
837 " starts at byte 0", start_buf);
839 else
841 if (m_diag_arg)
842 return ev.formatted_print ("out-of-bounds read from byte %s till"
843 " byte %s but %qE starts at byte 0",
844 start_buf, end_buf, m_diag_arg);
845 return ev.formatted_print ("out-of-bounds read from byte %s till"
846 " byte %s but region starts at byte 0",
847 start_buf, end_buf);;
851 label_text describe_final_event_as_bits (const evdesc::final_event &ev)
853 bit_size_t start = m_out_of_bounds_bits.get_start_bit_offset ();
854 bit_size_t end = m_out_of_bounds_bits.get_last_bit_offset ();
855 char start_buf[WIDE_INT_PRINT_BUFFER_SIZE];
856 print_dec (start, start_buf, SIGNED);
857 char end_buf[WIDE_INT_PRINT_BUFFER_SIZE];
858 print_dec (end, end_buf, SIGNED);
860 if (start == end)
862 if (m_diag_arg)
863 return ev.formatted_print ("out-of-bounds read at bit %s but %qE"
864 " starts at bit 0", start_buf,
865 m_diag_arg);
866 return ev.formatted_print ("out-of-bounds read at bit %s but region"
867 " starts at bit 0", start_buf);
869 else
871 if (m_diag_arg)
872 return ev.formatted_print ("out-of-bounds read from bit %s till"
873 " bit %s but %qE starts at bit 0",
874 start_buf, end_buf, m_diag_arg);
875 return ev.formatted_print ("out-of-bounds read from bit %s till"
876 " bit %s but region starts at bit 0",
877 start_buf, end_buf);;
881 enum access_direction get_dir () const final override { return DIR_READ; }
884 /* Abstract class to complain about out-of-bounds read/writes where
885 the values are symbolic. */
887 class symbolic_past_the_end : public out_of_bounds
889 public:
890 symbolic_past_the_end (const region_model &model,
891 const region *reg, tree diag_arg, tree offset,
892 tree num_bytes, tree capacity,
893 const svalue *sval_hint)
894 : out_of_bounds (model, reg, diag_arg, sval_hint),
895 m_offset (offset),
896 m_num_bytes (num_bytes),
897 m_capacity (capacity)
900 bool
901 subclass_equal_p (const pending_diagnostic &base_other) const final override
903 const symbolic_past_the_end &other
904 (static_cast <const symbolic_past_the_end &>(base_other));
905 return (out_of_bounds::subclass_equal_p (other)
906 && pending_diagnostic::same_tree_p (m_offset, other.m_offset)
907 && pending_diagnostic::same_tree_p (m_num_bytes, other.m_num_bytes)
908 && pending_diagnostic::same_tree_p (m_capacity, other.m_capacity));
911 void maybe_add_sarif_properties (sarif_object &result_obj)
912 const final override
914 out_of_bounds::maybe_add_sarif_properties (result_obj);
915 sarif_property_bag &props = result_obj.get_or_create_properties ();
916 #define PROPERTY_PREFIX "gcc/analyzer/symbolic_past_the_end/"
917 props.set (PROPERTY_PREFIX "offset", tree_to_json (m_offset));
918 props.set (PROPERTY_PREFIX "num_bytes", tree_to_json (m_num_bytes));
919 props.set (PROPERTY_PREFIX "capacity", tree_to_json (m_capacity));
920 #undef PROPERTY_PREFIX
923 protected:
924 tree m_offset;
925 tree m_num_bytes;
926 tree m_capacity;
929 /* Concrete subclass to complain about overflows with symbolic values. */
931 class symbolic_buffer_overflow : public symbolic_past_the_end
933 public:
934 symbolic_buffer_overflow (const region_model &model,
935 const region *reg, tree diag_arg, tree offset,
936 tree num_bytes, tree capacity,
937 const svalue *sval_hint)
938 : symbolic_past_the_end (model, reg, diag_arg, offset, num_bytes, capacity,
939 sval_hint)
943 const char *get_kind () const final override
945 return "symbolic_buffer_overflow";
948 bool emit (diagnostic_emission_context &ctxt) final override
950 bool warned;
951 switch (get_memory_space ())
953 default:
954 ctxt.add_cwe (787);
955 warned = ctxt.warn ("buffer overflow");
956 break;
957 case MEMSPACE_STACK:
958 ctxt.add_cwe (121);
959 warned = ctxt.warn ("stack-based buffer overflow");
960 break;
961 case MEMSPACE_HEAP:
962 ctxt.add_cwe (122);
963 warned = ctxt.warn ("heap-based buffer overflow");
964 break;
966 if (warned)
967 maybe_show_notes (ctxt);
968 return warned;
971 label_text
972 describe_final_event (const evdesc::final_event &ev) final override
974 if (m_offset)
976 /* Known offset. */
977 if (m_num_bytes)
979 /* Known offset, known size. */
980 if (TREE_CODE (m_num_bytes) == INTEGER_CST)
982 /* Known offset, known constant size. */
983 if (pending_diagnostic::same_tree_p (m_num_bytes,
984 integer_one_node))
986 /* Singular m_num_bytes. */
987 if (m_diag_arg)
988 return ev.formatted_print
989 ("write of %E byte at offset %qE exceeds %qE",
990 m_num_bytes, m_offset, m_diag_arg);
991 else
992 return ev.formatted_print
993 ("write of %E byte at offset %qE exceeds the buffer",
994 m_num_bytes, m_offset);
996 else
998 /* Plural m_num_bytes. */
999 if (m_diag_arg)
1000 return ev.formatted_print
1001 ("write of %E bytes at offset %qE exceeds %qE",
1002 m_num_bytes, m_offset, m_diag_arg);
1003 else
1004 return ev.formatted_print
1005 ("write of %E bytes at offset %qE exceeds the buffer",
1006 m_num_bytes, m_offset);
1009 else
1011 /* Known offset, known symbolic size. */
1012 if (m_diag_arg)
1013 return ev.formatted_print
1014 ("write of %qE bytes at offset %qE exceeds %qE",
1015 m_num_bytes, m_offset, m_diag_arg);
1016 else
1017 return ev.formatted_print
1018 ("write of %qE bytes at offset %qE exceeds the buffer",
1019 m_num_bytes, m_offset);
1022 else
1024 /* Known offset, unknown size. */
1025 if (m_diag_arg)
1026 return ev.formatted_print ("write at offset %qE exceeds %qE",
1027 m_offset, m_diag_arg);
1028 else
1029 return ev.formatted_print ("write at offset %qE exceeds the"
1030 " buffer", m_offset);
1033 /* Unknown offset. */
1034 if (m_diag_arg)
1035 return ev.formatted_print ("out-of-bounds write on %qE",
1036 m_diag_arg);
1037 return ev.formatted_print ("out-of-bounds write");
1040 enum access_direction get_dir () const final override { return DIR_WRITE; }
1043 /* Concrete subclass to complain about over-reads with symbolic values. */
1045 class symbolic_buffer_over_read : public symbolic_past_the_end
1047 public:
1048 symbolic_buffer_over_read (const region_model &model,
1049 const region *reg, tree diag_arg, tree offset,
1050 tree num_bytes, tree capacity)
1051 : symbolic_past_the_end (model, reg, diag_arg, offset, num_bytes, capacity,
1052 NULL)
1056 const char *get_kind () const final override
1058 return "symbolic_buffer_over_read";
1061 bool emit (diagnostic_emission_context &ctxt) final override
1063 ctxt.add_cwe (126);
1064 bool warned;
1065 switch (get_memory_space ())
1067 default:
1068 ctxt.add_cwe (787);
1069 warned = ctxt.warn ("buffer over-read");
1070 break;
1071 case MEMSPACE_STACK:
1072 ctxt.add_cwe (121);
1073 warned = ctxt.warn ("stack-based buffer over-read");
1074 break;
1075 case MEMSPACE_HEAP:
1076 ctxt.add_cwe (122);
1077 warned = ctxt.warn ("heap-based buffer over-read");
1078 break;
1080 if (warned)
1081 maybe_show_notes (ctxt);
1082 return warned;
1085 label_text
1086 describe_final_event (const evdesc::final_event &ev) final override
1088 if (m_offset)
1090 /* Known offset. */
1091 if (m_num_bytes)
1093 /* Known offset, known size. */
1094 if (TREE_CODE (m_num_bytes) == INTEGER_CST)
1096 /* Known offset, known constant size. */
1097 if (pending_diagnostic::same_tree_p (m_num_bytes,
1098 integer_one_node))
1100 /* Singular m_num_bytes. */
1101 if (m_diag_arg)
1102 return ev.formatted_print
1103 ("read of %E byte at offset %qE exceeds %qE",
1104 m_num_bytes, m_offset, m_diag_arg);
1105 else
1106 return ev.formatted_print
1107 ("read of %E byte at offset %qE exceeds the buffer",
1108 m_num_bytes, m_offset);
1110 else
1112 /* Plural m_num_bytes. */
1113 if (m_diag_arg)
1114 return ev.formatted_print
1115 ("read of %E bytes at offset %qE exceeds %qE",
1116 m_num_bytes, m_offset, m_diag_arg);
1117 else
1118 return ev.formatted_print
1119 ("read of %E bytes at offset %qE exceeds the buffer",
1120 m_num_bytes, m_offset);
1123 else
1125 /* Known offset, known symbolic size. */
1126 if (m_diag_arg)
1127 return ev.formatted_print
1128 ("read of %qE bytes at offset %qE exceeds %qE",
1129 m_num_bytes, m_offset, m_diag_arg);
1130 else
1131 return ev.formatted_print
1132 ("read of %qE bytes at offset %qE exceeds the buffer",
1133 m_num_bytes, m_offset);
1136 else
1138 /* Known offset, unknown size. */
1139 if (m_diag_arg)
1140 return ev.formatted_print ("read at offset %qE exceeds %qE",
1141 m_offset, m_diag_arg);
1142 else
1143 return ev.formatted_print ("read at offset %qE exceeds the"
1144 " buffer", m_offset);
1147 /* Unknown offset. */
1148 if (m_diag_arg)
1149 return ev.formatted_print ("out-of-bounds read on %qE",
1150 m_diag_arg);
1151 return ev.formatted_print ("out-of-bounds read");
1154 enum access_direction get_dir () const final override { return DIR_READ; }
1157 /* Check whether an access is past the end of the BASE_REG.
1158 Return TRUE if the access was valid, FALSE otherwise. */
1160 bool
1161 region_model::check_symbolic_bounds (const region *base_reg,
1162 const svalue *sym_byte_offset,
1163 const svalue *num_bytes_sval,
1164 const svalue *capacity,
1165 enum access_direction dir,
1166 const svalue *sval_hint,
1167 region_model_context *ctxt) const
1169 gcc_assert (ctxt);
1171 const svalue *next_byte
1172 = m_mgr->get_or_create_binop (num_bytes_sval->get_type (), PLUS_EXPR,
1173 sym_byte_offset, num_bytes_sval);
1175 if (eval_condition (next_byte, GT_EXPR, capacity).is_true ())
1177 tree diag_arg = get_representative_tree (base_reg);
1178 tree offset_tree = get_representative_tree (sym_byte_offset);
1179 tree num_bytes_tree = get_representative_tree (num_bytes_sval);
1180 tree capacity_tree = get_representative_tree (capacity);
1181 const region *offset_reg = m_mgr->get_offset_region (base_reg,
1182 NULL_TREE,
1183 sym_byte_offset);
1184 const region *sized_offset_reg = m_mgr->get_sized_region (offset_reg,
1185 NULL_TREE,
1186 num_bytes_sval);
1187 switch (dir)
1189 default:
1190 gcc_unreachable ();
1191 break;
1192 case DIR_READ:
1193 gcc_assert (sval_hint == nullptr);
1194 ctxt->warn (make_unique<symbolic_buffer_over_read> (*this,
1195 sized_offset_reg,
1196 diag_arg,
1197 offset_tree,
1198 num_bytes_tree,
1199 capacity_tree));
1200 return false;
1201 break;
1202 case DIR_WRITE:
1203 ctxt->warn (make_unique<symbolic_buffer_overflow> (*this,
1204 sized_offset_reg,
1205 diag_arg,
1206 offset_tree,
1207 num_bytes_tree,
1208 capacity_tree,
1209 sval_hint));
1210 return false;
1211 break;
1214 return true;
1217 static tree
1218 maybe_get_integer_cst_tree (const svalue *sval)
1220 tree cst_tree = sval->maybe_get_constant ();
1221 if (cst_tree && TREE_CODE (cst_tree) == INTEGER_CST)
1222 return cst_tree;
1224 return NULL_TREE;
1227 /* May complain when the access on REG is out-of-bounds.
1228 Return TRUE if the access was valid, FALSE otherwise. */
1230 bool
1231 region_model::check_region_bounds (const region *reg,
1232 enum access_direction dir,
1233 const svalue *sval_hint,
1234 region_model_context *ctxt) const
1236 gcc_assert (ctxt);
1238 /* Get the offset. */
1239 region_offset reg_offset = reg->get_offset (m_mgr);
1240 const region *base_reg = reg_offset.get_base_region ();
1242 /* Find out how many bits were accessed. */
1243 const svalue *num_bits_sval = reg->get_bit_size_sval (m_mgr);
1244 tree num_bits_tree = maybe_get_integer_cst_tree (num_bits_sval);
1245 /* Bail out if 0 bits are accessed. */
1246 if (num_bits_tree && zerop (num_bits_tree))
1247 return true;
1249 /* Get the capacity of the buffer (in bytes). */
1250 const svalue *byte_capacity = get_capacity (base_reg);
1251 tree cst_byte_capacity_tree = maybe_get_integer_cst_tree (byte_capacity);
1253 /* The constant offset from a pointer is represented internally as a sizetype
1254 but should be interpreted as a signed value here. The statement below
1255 converts the offset from bits to bytes and then to a signed integer with
1256 the same precision the sizetype has on the target system.
1258 For example, this is needed for out-of-bounds-3.c test1 to pass when
1259 compiled with a 64-bit gcc build targeting 32-bit systems. */
1260 bit_offset_t bit_offset;
1261 if (!reg_offset.symbolic_p ())
1262 bit_offset = wi::sext (reg_offset.get_bit_offset (),
1263 TYPE_PRECISION (size_type_node));
1265 /* If any of the base region, the offset, or the number of bytes accessed
1266 are symbolic, we have to reason about symbolic values. */
1267 if (base_reg->symbolic_p () || reg_offset.symbolic_p () || !num_bits_tree)
1269 const svalue* byte_offset_sval;
1270 if (!reg_offset.symbolic_p ())
1272 tree byte_offset_tree
1273 = wide_int_to_tree (integer_type_node,
1274 bit_offset >> LOG2_BITS_PER_UNIT);
1275 byte_offset_sval
1276 = m_mgr->get_or_create_constant_svalue (byte_offset_tree);
1278 else
1279 byte_offset_sval = reg_offset.get_symbolic_byte_offset ();
1280 const svalue *num_bytes_sval = reg->get_byte_size_sval (m_mgr);
1281 return check_symbolic_bounds (base_reg, byte_offset_sval, num_bytes_sval,
1282 byte_capacity, dir, sval_hint, ctxt);
1285 /* Otherwise continue to check with concrete values. */
1286 bit_range bits_outside (0, 0);
1287 bool oob_safe = true;
1288 /* NUM_BITS_TREE should always be interpreted as unsigned. */
1289 bit_offset_t num_bits_unsigned = wi::to_offset (num_bits_tree);
1290 bit_range read_bits (bit_offset, num_bits_unsigned);
1291 /* If read_bits has a subset < 0, we do have an underwrite. */
1292 if (read_bits.falls_short_of_p (0, &bits_outside))
1294 tree diag_arg = get_representative_tree (base_reg);
1295 switch (dir)
1297 default:
1298 gcc_unreachable ();
1299 break;
1300 case DIR_READ:
1301 gcc_assert (sval_hint == nullptr);
1302 ctxt->warn (make_unique<concrete_buffer_under_read> (*this, reg,
1303 diag_arg,
1304 bits_outside));
1305 oob_safe = false;
1306 break;
1307 case DIR_WRITE:
1308 ctxt->warn (make_unique<concrete_buffer_underwrite> (*this,
1309 reg, diag_arg,
1310 bits_outside,
1311 sval_hint));
1312 oob_safe = false;
1313 break;
1317 /* For accesses past the end, we do need a concrete capacity. No need to
1318 do a symbolic check here because the inequality check does not reason
1319 whether constants are greater than symbolic values. */
1320 if (!cst_byte_capacity_tree)
1321 return oob_safe;
1323 bit_range buffer (0, wi::to_offset (cst_byte_capacity_tree) * BITS_PER_UNIT);
1324 /* If READ_BITS exceeds BUFFER, we do have an overflow. */
1325 if (read_bits.exceeds_p (buffer, &bits_outside))
1327 tree bit_bound = wide_int_to_tree (size_type_node,
1328 buffer.get_next_bit_offset ());
1329 tree diag_arg = get_representative_tree (base_reg);
1331 switch (dir)
1333 default:
1334 gcc_unreachable ();
1335 break;
1336 case DIR_READ:
1337 gcc_assert (sval_hint == nullptr);
1338 ctxt->warn (make_unique<concrete_buffer_over_read> (*this,
1339 reg, diag_arg,
1340 bits_outside,
1341 bit_bound));
1342 oob_safe = false;
1343 break;
1344 case DIR_WRITE:
1345 ctxt->warn (make_unique<concrete_buffer_overflow> (*this,
1346 reg, diag_arg,
1347 bits_outside,
1348 bit_bound,
1349 sval_hint));
1350 oob_safe = false;
1351 break;
1354 return oob_safe;
1357 } // namespace ana
1359 #endif /* #if ENABLE_ANALYZER */