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