1 /* Bounds-checking of reads and writes to memory regions.
2 Copyright (C) 2019-2024 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)
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/>. */
21 #define INCLUDE_MEMORY
22 #define INCLUDE_VECTOR
24 #include "coretypes.h"
25 #include "make-unique.h"
28 #include "basic-block.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"
46 /* Abstract base class for all out-of-bounds warnings. */
48 class out_of_bounds
: public pending_diagnostic
51 class oob_region_creation_event_capacity
: public region_creation_event_capacity
54 oob_region_creation_event_capacity (tree byte_capacity
,
55 const event_loc_info
&loc_info
,
57 : region_creation_event_capacity (byte_capacity
,
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
,
69 m_oob
.m_region_creation_event_id
= emission_id
;
75 out_of_bounds (const region_model
&model
,
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
*,
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. */
108 emission_path
.add_event
109 (make_unique
<oob_region_creation_event_capacity
> (byte_capacity
,
114 void maybe_add_sarif_properties (sarif_object
&result_obj
)
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
));
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;
134 enum memory_space
get_memory_space () const
136 return m_reg
->get_memory_space ();
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. */
153 maybe_describe_array_bounds (location_t loc
) const
157 tree t
= TREE_TYPE (m_diag_arg
);
160 if (TREE_CODE (t
) != ARRAY_TYPE
)
162 tree domain
= TYPE_DOMAIN (t
);
165 tree max_idx
= TYPE_MAX_VALUE (domain
);
168 tree min_idx
= TYPE_MIN_VALUE (domain
);
170 "valid subscripts for %qE are %<[%E]%> to %<[%E]%>",
171 m_diag_arg
, min_idx
, max_idx
);
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 ())
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. */
194 diagnostic_diagram diagram
197 _("Diagram visualizing the predicted out-of-bounds access"));
198 global_dc
->emit_diagram (diagram
);
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
;
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
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
)
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
);
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
271 concrete_past_the_end (const region_model
&model
,
272 const region
*reg
, tree diag_arg
, bit_range range
,
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
)
281 = wide_int_to_tree (size_type_node
,
282 wi::to_offset (m_bit_bound
) >> LOG2_BITS_PER_UNIT
);
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
,
295 void add_region_creation_events (const region
*,
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
,
307 void maybe_add_sarif_properties (sarif_object
&result_obj
)
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
325 /* Concrete subclass to complain about buffer overflows. */
327 class concrete_buffer_overflow
: public concrete_past_the_end
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
345 switch (get_memory_space ())
349 warned
= ctxt
.warn ("buffer overflow");
353 warned
= ctxt
.warn ("stack-based buffer overflow");
357 warned
= ctxt
.warn ("heap-based buffer overflow");
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
;
372 inform_n (ctxt
.get_location (),
374 "write of %wu byte to beyond the end of %qE",
375 "write of %wu bytes to beyond the end of %qE",
379 inform_n (ctxt
.get_location (),
381 "write of %wu byte to beyond the end of the region",
382 "write of %wu bytes to beyond the end of the region",
388 inform_n (ctxt
.get_location (),
390 "write of %wu bit to beyond the end of %qE",
391 "write of %wu bits to beyond the end of %qE",
395 inform_n (ctxt
.get_location (),
397 "write of %wu bit to beyond the end of the region",
398 "write of %wu bits to beyond the end of the region",
403 inform (ctxt
.get_location (),
404 "write to beyond the end of %qE",
407 maybe_show_notes (ctxt
);
413 label_text
describe_final_event (const evdesc::final_event
&ev
)
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
);
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
);
439 return ev
.formatted_print ("out-of-bounds write at byte %s but %qE"
440 " ends at byte %E", start_buf
, m_diag_arg
,
442 return ev
.formatted_print ("out-of-bounds write at byte %s but region"
443 " ends at byte %E", start_buf
,
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
,
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
);
471 return ev
.formatted_print ("out-of-bounds write at bit %s but %qE"
472 " ends at bit %E", start_buf
, m_diag_arg
,
474 return ev
.formatted_print ("out-of-bounds write at bit %s but region"
475 " ends at bit %E", start_buf
,
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
,
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
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
514 switch (get_memory_space ())
517 warned
= ctxt
.warn ("buffer over-read");
520 warned
= ctxt
.warn ("stack-based buffer over-read");
523 warned
= ctxt
.warn ("heap-based buffer over-read");
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
;
538 inform_n (ctxt
.get_location (),
540 "read of %wu byte from after the end of %qE",
541 "read of %wu bytes from after the end of %qE",
545 inform_n (ctxt
.get_location (),
547 "read of %wu byte from after the end of the region",
548 "read of %wu bytes from after the end of the region",
554 inform_n (ctxt
.get_location (),
556 "read of %wu bit from after the end of %qE",
557 "read of %wu bits from after the end of %qE",
561 inform_n (ctxt
.get_location (),
563 "read of %wu bit from after the end of the region",
564 "read of %wu bits from after the end of the region",
569 inform (ctxt
.get_location (),
570 "read from after the end of %qE",
573 maybe_show_notes (ctxt
);
579 label_text
describe_final_event (const evdesc::final_event
&ev
)
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
);
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
);
605 return ev
.formatted_print ("out-of-bounds read at byte %s but %qE"
606 " ends at byte %E", start_buf
, m_diag_arg
,
608 return ev
.formatted_print ("out-of-bounds read at byte %s but region"
609 " ends at byte %E", start_buf
,
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
,
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
);
637 return ev
.formatted_print ("out-of-bounds read at bit %s but %qE"
638 " ends at bit %E", start_buf
, m_diag_arg
,
640 return ev
.formatted_print ("out-of-bounds read at bit %s but region"
641 " ends at bit %E", start_buf
,
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
,
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
665 concrete_buffer_underwrite (const region_model
&model
,
666 const region
*reg
, tree diag_arg
,
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
681 switch (get_memory_space ())
684 warned
= ctxt
.warn ("buffer underwrite");
687 warned
= ctxt
.warn ("stack-based buffer underwrite");
690 warned
= ctxt
.warn ("heap-based buffer underwrite");
694 maybe_show_notes (ctxt
);
698 label_text
describe_final_event (const evdesc::final_event
&ev
)
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
);
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
);
721 return ev
.formatted_print ("out-of-bounds write at byte %s but %qE"
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
);
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
);;
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
);
752 return ev
.formatted_print ("out-of-bounds write at bit %s but %qE"
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
);
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
778 concrete_buffer_under_read (const region_model
&model
,
779 const region
*reg
, tree diag_arg
,
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
793 switch (get_memory_space ())
796 warned
= ctxt
.warn ("buffer under-read");
799 warned
= ctxt
.warn ("stack-based buffer under-read");
802 warned
= ctxt
.warn ("heap-based buffer under-read");
806 maybe_show_notes (ctxt
);
810 label_text
describe_final_event (const evdesc::final_event
&ev
)
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
);
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
);
833 return ev
.formatted_print ("out-of-bounds read at byte %s but %qE"
834 " starts at byte 0", start_buf
,
836 return ev
.formatted_print ("out-of-bounds read at byte %s but region"
837 " starts at byte 0", start_buf
);
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
);
863 return ev
.formatted_print ("out-of-bounds read at bit %s but %qE"
864 " starts at bit 0", start_buf
,
866 return ev
.formatted_print ("out-of-bounds read at bit %s but region"
867 " starts at bit 0", start_buf
);
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
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
),
896 m_num_bytes (num_bytes
),
897 m_capacity (capacity
)
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
)
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
929 /* Concrete subclass to complain about overflows with symbolic values. */
931 class symbolic_buffer_overflow
: public symbolic_past_the_end
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
,
943 const char *get_kind () const final override
945 return "symbolic_buffer_overflow";
948 bool emit (diagnostic_emission_context
&ctxt
) final override
951 switch (get_memory_space ())
955 warned
= ctxt
.warn ("buffer overflow");
959 warned
= ctxt
.warn ("stack-based buffer overflow");
963 warned
= ctxt
.warn ("heap-based buffer overflow");
967 maybe_show_notes (ctxt
);
972 describe_final_event (const evdesc::final_event
&ev
) final override
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
,
986 /* Singular m_num_bytes. */
988 return ev
.formatted_print
989 ("write of %E byte at offset %qE exceeds %qE",
990 m_num_bytes
, m_offset
, m_diag_arg
);
992 return ev
.formatted_print
993 ("write of %E byte at offset %qE exceeds the buffer",
994 m_num_bytes
, m_offset
);
998 /* Plural m_num_bytes. */
1000 return ev
.formatted_print
1001 ("write of %E bytes at offset %qE exceeds %qE",
1002 m_num_bytes
, m_offset
, m_diag_arg
);
1004 return ev
.formatted_print
1005 ("write of %E bytes at offset %qE exceeds the buffer",
1006 m_num_bytes
, m_offset
);
1011 /* Known offset, known symbolic size. */
1013 return ev
.formatted_print
1014 ("write of %qE bytes at offset %qE exceeds %qE",
1015 m_num_bytes
, m_offset
, m_diag_arg
);
1017 return ev
.formatted_print
1018 ("write of %qE bytes at offset %qE exceeds the buffer",
1019 m_num_bytes
, m_offset
);
1024 /* Known offset, unknown size. */
1026 return ev
.formatted_print ("write at offset %qE exceeds %qE",
1027 m_offset
, m_diag_arg
);
1029 return ev
.formatted_print ("write at offset %qE exceeds the"
1030 " buffer", m_offset
);
1033 /* Unknown offset. */
1035 return ev
.formatted_print ("out-of-bounds write on %qE",
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
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
,
1056 const char *get_kind () const final override
1058 return "symbolic_buffer_over_read";
1061 bool emit (diagnostic_emission_context
&ctxt
) final override
1065 switch (get_memory_space ())
1069 warned
= ctxt
.warn ("buffer over-read");
1071 case MEMSPACE_STACK
:
1073 warned
= ctxt
.warn ("stack-based buffer over-read");
1077 warned
= ctxt
.warn ("heap-based buffer over-read");
1081 maybe_show_notes (ctxt
);
1086 describe_final_event (const evdesc::final_event
&ev
) final override
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
,
1100 /* Singular m_num_bytes. */
1102 return ev
.formatted_print
1103 ("read of %E byte at offset %qE exceeds %qE",
1104 m_num_bytes
, m_offset
, m_diag_arg
);
1106 return ev
.formatted_print
1107 ("read of %E byte at offset %qE exceeds the buffer",
1108 m_num_bytes
, m_offset
);
1112 /* Plural m_num_bytes. */
1114 return ev
.formatted_print
1115 ("read of %E bytes at offset %qE exceeds %qE",
1116 m_num_bytes
, m_offset
, m_diag_arg
);
1118 return ev
.formatted_print
1119 ("read of %E bytes at offset %qE exceeds the buffer",
1120 m_num_bytes
, m_offset
);
1125 /* Known offset, known symbolic size. */
1127 return ev
.formatted_print
1128 ("read of %qE bytes at offset %qE exceeds %qE",
1129 m_num_bytes
, m_offset
, m_diag_arg
);
1131 return ev
.formatted_print
1132 ("read of %qE bytes at offset %qE exceeds the buffer",
1133 m_num_bytes
, m_offset
);
1138 /* Known offset, unknown size. */
1140 return ev
.formatted_print ("read at offset %qE exceeds %qE",
1141 m_offset
, m_diag_arg
);
1143 return ev
.formatted_print ("read at offset %qE exceeds the"
1144 " buffer", m_offset
);
1147 /* Unknown offset. */
1149 return ev
.formatted_print ("out-of-bounds read on %qE",
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. */
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
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
,
1184 const region
*sized_offset_reg
= m_mgr
->get_sized_region (offset_reg
,
1193 gcc_assert (sval_hint
== nullptr);
1194 ctxt
->warn (make_unique
<symbolic_buffer_over_read
> (*this,
1203 ctxt
->warn (make_unique
<symbolic_buffer_overflow
> (*this,
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
)
1227 /* May complain when the access on REG is out-of-bounds.
1228 Return TRUE if the access was valid, FALSE otherwise. */
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
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
))
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
);
1276 = m_mgr
->get_or_create_constant_svalue (byte_offset_tree
);
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
);
1301 gcc_assert (sval_hint
== nullptr);
1302 ctxt
->warn (make_unique
<concrete_buffer_under_read
> (*this, reg
,
1308 ctxt
->warn (make_unique
<concrete_buffer_underwrite
> (*this,
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
)
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
);
1337 gcc_assert (sval_hint
== nullptr);
1338 ctxt
->warn (make_unique
<concrete_buffer_over_read
> (*this,
1345 ctxt
->warn (make_unique
<concrete_buffer_overflow
> (*this,
1359 #endif /* #if ENABLE_ANALYZER */