Branch data Line data Source code
1 : : /* Bounds-checking of reads and writes to memory regions.
2 : : Copyright (C) 2019-2025 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 : 440 : oob_region_creation_event_capacity (tree byte_capacity,
45 : : const event_loc_info &loc_info,
46 : : out_of_bounds &oob)
47 : 440 : : region_creation_event_capacity (byte_capacity,
48 : : loc_info),
49 : 440 : m_oob (oob)
50 : : {
51 : : }
52 : 440 : void prepare_for_emission (checker_path *path,
53 : : pending_diagnostic *pd,
54 : : diagnostics::paths::event_id_t emission_id) override
55 : : {
56 : 440 : region_creation_event_capacity::prepare_for_emission (path,
57 : : pd,
58 : : emission_id);
59 : 440 : m_oob.m_region_creation_event_id = emission_id;
60 : 440 : }
61 : : private:
62 : : out_of_bounds &m_oob;
63 : : };
64 : :
65 : 784 : out_of_bounds (const region_model &model,
66 : : const region *reg,
67 : : tree diag_arg,
68 : : const svalue *sval_hint)
69 : 467 : : m_model (model), m_reg (reg), m_diag_arg (diag_arg), m_sval_hint (sval_hint)
70 : : {}
71 : :
72 : 754 : bool subclass_equal_p (const pending_diagnostic &base_other) const override
73 : : {
74 : 754 : const out_of_bounds &other
75 : : (static_cast <const out_of_bounds &>(base_other));
76 : 754 : return (m_reg == other.m_reg
77 : 754 : && pending_diagnostic::same_tree_p (m_diag_arg, other.m_diag_arg));
78 : : }
79 : :
80 : 1331 : int get_controlling_option () const final override
81 : : {
82 : 1331 : return OPT_Wanalyzer_out_of_bounds;
83 : : }
84 : :
85 : 555 : void mark_interesting_stuff (interesting_t *interest) final override
86 : : {
87 : 555 : interest->add_region_creation (m_reg->get_base_region ());
88 : 555 : }
89 : :
90 : 207 : 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 : 207 : if (byte_capacity)
98 : 133 : emission_path.add_event
99 : 133 : (std::make_unique<oob_region_creation_event_capacity> (byte_capacity,
100 : : loc_info,
101 : : *this));
102 : 207 : }
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 : 555 : enum memory_space get_memory_space () const
126 : : {
127 : 555 : return m_reg->get_memory_space ();
128 : : }
129 : :
130 : : void
131 : 555 : maybe_show_notes (diagnostic_emission_context &ctxt) const
132 : : {
133 : 555 : maybe_describe_array_bounds (ctxt.get_location ());
134 : 555 : maybe_show_diagram (ctxt.get_logger ());
135 : 555 : }
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 : 555 : maybe_describe_array_bounds (location_t loc) const
145 : : {
146 : 555 : if (!m_diag_arg)
147 : : return;
148 : 398 : tree t = TREE_TYPE (m_diag_arg);
149 : 398 : if (!t)
150 : : return;
151 : 398 : if (TREE_CODE (t) != ARRAY_TYPE)
152 : : return;
153 : 355 : tree domain = TYPE_DOMAIN (t);
154 : 355 : if (!domain)
155 : : return;
156 : 354 : tree max_idx = TYPE_MAX_VALUE (domain);
157 : 354 : if (!max_idx)
158 : : return;
159 : 352 : tree min_idx = TYPE_MIN_VALUE (domain);
160 : 352 : inform (loc,
161 : : "valid subscripts for %qE are %<[%E]%> to %<[%E]%>",
162 : : m_diag_arg, min_idx, max_idx);
163 : : }
164 : :
165 : : void
166 : 555 : maybe_show_diagram (logger *logger) const
167 : : {
168 : 555 : 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 : 555 : if (op.get_valid_bits ().empty_p ())
173 : 8 : return;
174 : :
175 : 551 : 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 : 639 : 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 : 639 : : out_of_bounds (model, reg, diag_arg, sval_hint),
221 : 1278 : m_out_of_bounds_bits (out_of_bounds_bits)
222 : : {}
223 : :
224 : 609 : bool subclass_equal_p (const pending_diagnostic &base_other) const override
225 : : {
226 : 609 : const concrete_out_of_bounds &other
227 : : (static_cast <const concrete_out_of_bounds &>(base_other));
228 : 609 : return (out_of_bounds::subclass_equal_p (other)
229 : 609 : && 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 : 850 : 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 : 467 : 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 : 467 : : concrete_out_of_bounds (model, reg, diag_arg, range, sval_hint),
267 : 467 : m_bit_bound (bit_bound),
268 : 467 : m_byte_bound (NULL_TREE)
269 : : {
270 : 467 : if (m_bit_bound && TREE_CODE (m_bit_bound) == INTEGER_CST)
271 : 467 : m_byte_bound
272 : 934 : = wide_int_to_tree (size_type_node,
273 : 467 : wi::to_offset (m_bit_bound) >> LOG2_BITS_PER_UNIT);
274 : 467 : }
275 : :
276 : : bool
277 : 441 : subclass_equal_p (const pending_diagnostic &base_other) const final override
278 : : {
279 : 441 : const concrete_past_the_end &other
280 : : (static_cast <const concrete_past_the_end &>(base_other));
281 : 441 : return (concrete_out_of_bounds::subclass_equal_p (other)
282 : 882 : && pending_diagnostic::same_tree_p (m_bit_bound,
283 : 441 : 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 : 1530 : const char *get_kind () const final override
329 : : {
330 : 1530 : 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 : 289 : concrete_buffer_over_read (const region_model &model,
509 : : const region *reg, tree diag_arg,
510 : : bit_range range, tree bit_bound)
511 : 289 : : concrete_past_the_end (model, reg, diag_arg, range, bit_bound, nullptr)
512 : : {}
513 : :
514 : 1763 : const char *get_kind () const final override
515 : : {
516 : 1763 : 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 : 206 : if (m_diag_arg)
619 : 198 : 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 : 102 : if (m_diag_arg)
632 : 94 : 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 : 844 : const char *get_kind () const final override
700 : : {
701 : 844 : 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 : 118 : concrete_buffer_under_read (const region_model &model,
821 : : const region *reg, tree diag_arg,
822 : : bit_range range)
823 : 118 : : concrete_out_of_bounds (model, reg, diag_arg, range, nullptr)
824 : : {}
825 : :
826 : 1129 : const char *get_kind () const final override
827 : : {
828 : 1129 : return "concrete_buffer_under_read";
829 : : }
830 : :
831 : 59 : bool emit (diagnostic_emission_context &ctxt) final override
832 : : {
833 : 59 : bool warned;
834 : 59 : ctxt.add_cwe (127);
835 : 59 : switch (get_memory_space ())
836 : : {
837 : 40 : default:
838 : 40 : warned = ctxt.warn ("buffer under-read");
839 : 40 : break;
840 : 15 : case MEMSPACE_STACK:
841 : 15 : warned = ctxt.warn ("stack-based buffer under-read");
842 : 15 : break;
843 : 4 : case MEMSPACE_HEAP:
844 : 4 : warned = ctxt.warn ("heap-based buffer under-read");
845 : 4 : break;
846 : : }
847 : 59 : if (warned)
848 : 59 : maybe_show_notes (ctxt);
849 : 59 : return warned;
850 : : }
851 : :
852 : : bool
853 : 118 : describe_final_event (pretty_printer &pp,
854 : : const evdesc::final_event &) final override
855 : : {
856 : 118 : byte_range out_of_bounds_bytes (0, 0);
857 : 118 : if (get_out_of_bounds_bytes (&out_of_bounds_bytes))
858 : 118 : describe_final_event_as_bytes (pp, out_of_bounds_bytes);
859 : : else
860 : 0 : describe_final_event_as_bits (pp);
861 : 118 : return true;
862 : : }
863 : :
864 : : void
865 : 118 : describe_final_event_as_bytes (pretty_printer &pp,
866 : : const byte_range &out_of_bounds_bytes)
867 : : {
868 : 118 : byte_size_t start = out_of_bounds_bytes.get_start_byte_offset ();
869 : 118 : byte_size_t end = out_of_bounds_bytes.get_last_byte_offset ();
870 : 118 : char start_buf[WIDE_INT_PRINT_BUFFER_SIZE];
871 : 118 : print_dec (start, start_buf, SIGNED);
872 : 118 : char end_buf[WIDE_INT_PRINT_BUFFER_SIZE];
873 : 118 : print_dec (end, end_buf, SIGNED);
874 : :
875 : 118 : 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 : 84 : if (m_diag_arg)
891 : 76 : 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 : 118 : start_buf, end_buf);;
900 : : }
901 : 118 : }
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 : 59 : 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 : 145 : 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 : 145 : : out_of_bounds (model, reg, diag_arg, sval_hint),
954 : 145 : m_offset (offset),
955 : 145 : m_num_bytes (num_bytes),
956 : 290 : 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 : 1875 : const char *get_kind () const final override
1004 : : {
1005 : 1875 : 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 : 33 : 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 : 33 : nullptr)
1125 : : {
1126 : : }
1127 : :
1128 : 348 : const char *get_kind () const final override
1129 : : {
1130 : 348 : 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 : 152847 : strip_types (const svalue *sval,
1244 : : region_model_manager &mgr)
1245 : : {
1246 : 165572 : 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 : 55437 : case SK_UNKNOWN:
1258 : 55437 : 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 : 12817 : case SK_UNARYOP:
1271 : 12817 : {
1272 : 12817 : const unaryop_svalue *unaryop_sval = (const unaryop_svalue *)sval;
1273 : 12817 : const enum tree_code op = unaryop_sval->get_op ();
1274 : 12817 : if (op == VIEW_CONVERT_EXPR || op == NOP_EXPR)
1275 : 12725 : return strip_types (unaryop_sval->get_arg (), mgr);
1276 : 92 : return mgr.get_or_create_unaryop
1277 : 92 : (NULL_TREE,
1278 : : op,
1279 : 92 : strip_types (unaryop_sval->get_arg (), mgr));
1280 : : }
1281 : 21713 : case SK_BINOP:
1282 : 21713 : {
1283 : 21713 : const binop_svalue *binop_sval = (const binop_svalue *)sval;
1284 : 21713 : const enum tree_code op = binop_sval->get_op ();
1285 : 21713 : return mgr.get_or_create_binop
1286 : 21713 : (NULL_TREE,
1287 : : op,
1288 : : strip_types (binop_sval->get_arg0 (), mgr),
1289 : 21713 : strip_types (binop_sval->get_arg1 (), mgr));
1290 : : }
1291 : 150 : case SK_SUB:
1292 : 150 : {
1293 : 150 : const sub_svalue *sub_sval = (const sub_svalue *)sval;
1294 : 150 : return mgr.get_or_create_sub_svalue
1295 : 150 : (NULL_TREE,
1296 : : strip_types (sub_sval->get_parent (), mgr),
1297 : 150 : sub_sval->get_subregion ());
1298 : : }
1299 : 0 : case SK_REPEATED:
1300 : 0 : {
1301 : 0 : const repeated_svalue *repeated_sval = (const repeated_svalue *)sval;
1302 : 0 : return mgr.get_or_create_repeated_svalue
1303 : 0 : (NULL_TREE,
1304 : : strip_types (repeated_sval->get_outer_size (), mgr),
1305 : 0 : 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 : 1478 : case SK_WIDENING:
1326 : 1478 : {
1327 : 1478 : const widening_svalue *widening_sval = (const widening_svalue *)sval;
1328 : 1478 : return mgr.get_or_create_widening_svalue
1329 : 1478 : (NULL_TREE,
1330 : : widening_sval->get_point (),
1331 : : strip_types (widening_sval->get_base_svalue (), mgr),
1332 : 1478 : 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;
1338 : 0 : for (auto iter : compound_sval->get_map ())
1339 : : {
1340 : 0 : const binding_key *key = iter.first;
1341 : 0 : const svalue *bound_sval = iter.second;
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 : 94 : case SK_ASM_OUTPUT:
1349 : 94 : {
1350 : 94 : const asm_output_svalue *asm_output_sval
1351 : : = (const asm_output_svalue *)sval;
1352 : 94 : auto_vec<const svalue *> typeless_inputs
1353 : 94 : (asm_output_sval->get_num_inputs ());
1354 : 282 : for (unsigned idx = 0; idx < asm_output_sval->get_num_inputs (); idx++)
1355 : 188 : typeless_inputs.quick_push
1356 : 188 : (strip_types (asm_output_sval->get_input (idx),
1357 : : mgr));
1358 : 94 : return mgr.get_or_create_asm_output_svalue
1359 : 94 : (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 : 94 : }
1365 : 16 : case SK_CONST_FN_RESULT:
1366 : 16 : {
1367 : 16 : const const_fn_result_svalue *const_fn_result_sval
1368 : : = (const const_fn_result_svalue *)sval;
1369 : 16 : auto_vec<const svalue *> typeless_inputs
1370 : 16 : (const_fn_result_sval->get_num_inputs ());
1371 : 16 : for (unsigned idx = 0;
1372 : 32 : idx < const_fn_result_sval->get_num_inputs ();
1373 : : idx++)
1374 : 16 : typeless_inputs.quick_push
1375 : 16 : (strip_types (const_fn_result_sval->get_input (idx),
1376 : : mgr));
1377 : 16 : return mgr.get_or_create_const_fn_result_svalue
1378 : 16 : (NULL_TREE,
1379 : : const_fn_result_sval->get_fndecl (),
1380 : : typeless_inputs);
1381 : 16 : }
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 : 52569 : 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 : 52569 : gcc_assert (ctxt);
1398 : :
1399 : 52569 : const svalue *next_byte
1400 : 52569 : = m_mgr->get_or_create_binop (NULL_TREE, PLUS_EXPR,
1401 : : sym_byte_offset, num_bytes_sval);
1402 : :
1403 : 52569 : next_byte = strip_types (next_byte, *m_mgr);
1404 : 52569 : capacity = strip_types (capacity, *m_mgr);
1405 : :
1406 : 52569 : if (eval_condition (next_byte, GT_EXPR, capacity).is_true ())
1407 : : {
1408 : 145 : tree diag_arg = get_representative_tree (base_reg);
1409 : 145 : tree offset_tree = get_representative_tree (sym_byte_offset);
1410 : 145 : tree num_bytes_tree = get_representative_tree (num_bytes_sval);
1411 : 145 : tree capacity_tree = get_representative_tree (capacity);
1412 : 145 : const region *offset_reg = m_mgr->get_offset_region (base_reg,
1413 : : NULL_TREE,
1414 : : sym_byte_offset);
1415 : 145 : const region *sized_offset_reg = m_mgr->get_sized_region (offset_reg,
1416 : : NULL_TREE,
1417 : 145 : num_bytes_sval);
1418 : 145 : switch (dir)
1419 : : {
1420 : 0 : default:
1421 : 0 : gcc_unreachable ();
1422 : 33 : break;
1423 : 33 : case access_direction::read:
1424 : 33 : gcc_assert (sval_hint == nullptr);
1425 : 33 : ctxt->warn
1426 : 33 : (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 : 33 : 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 : 145 : break;
1445 : : }
1446 : : }
1447 : : return true;
1448 : : }
1449 : :
1450 : : static tree
1451 : 1394860 : maybe_get_integer_cst_tree (const svalue *sval)
1452 : : {
1453 : 1394860 : tree cst_tree = sval->maybe_get_constant ();
1454 : 1394860 : if (cst_tree && TREE_CODE (cst_tree) == INTEGER_CST)
1455 : 1333539 : 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 : 697440 : 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 : 697440 : gcc_assert (ctxt);
1470 : :
1471 : : /* Get the offset. */
1472 : 697440 : region_offset reg_offset = reg->get_offset (m_mgr);
1473 : 697440 : const region *base_reg = reg_offset.get_base_region ();
1474 : :
1475 : : /* Find out how many bits were accessed. */
1476 : 697440 : const svalue *num_bits_sval = reg->get_bit_size_sval (m_mgr);
1477 : 697440 : tree num_bits_tree = maybe_get_integer_cst_tree (num_bits_sval);
1478 : : /* Bail out if 0 bits are accessed. */
1479 : 697440 : if (num_bits_tree && zerop (num_bits_tree))
1480 : : return true;
1481 : :
1482 : : /* Get the capacity of the buffer (in bytes). */
1483 : 697420 : const svalue *byte_capacity = get_capacity (base_reg);
1484 : 697420 : 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 : 697420 : bit_offset_t bit_offset;
1494 : 697420 : if (!reg_offset.symbolic_p ())
1495 : 687503 : bit_offset = wi::sext (reg_offset.get_bit_offset (),
1496 : 687503 : 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 : 697420 : if (base_reg->symbolic_p () || reg_offset.symbolic_p () || !num_bits_tree)
1501 : : {
1502 : 52569 : const svalue* byte_offset_sval;
1503 : 52569 : if (!reg_offset.symbolic_p ())
1504 : : {
1505 : 42652 : tree byte_offset_tree
1506 : 85304 : = wide_int_to_tree (integer_type_node,
1507 : 42652 : bit_offset >> LOG2_BITS_PER_UNIT);
1508 : 42652 : byte_offset_sval
1509 : 42652 : = m_mgr->get_or_create_constant_svalue (byte_offset_tree);
1510 : : }
1511 : : else
1512 : 9917 : byte_offset_sval = reg_offset.get_symbolic_byte_offset ();
1513 : 52569 : const svalue *num_bytes_sval = reg->get_byte_size_sval (m_mgr);
1514 : 52569 : return check_symbolic_bounds (base_reg, byte_offset_sval, num_bytes_sval,
1515 : 52569 : byte_capacity, dir, sval_hint, ctxt);
1516 : : }
1517 : :
1518 : : /* Otherwise continue to check with concrete values. */
1519 : 644851 : bit_range bits_outside (0, 0);
1520 : 644851 : bool oob_safe = true;
1521 : : /* NUM_BITS_TREE should always be interpreted as unsigned. */
1522 : 644851 : bit_offset_t num_bits_unsigned = wi::to_offset (num_bits_tree);
1523 : 644851 : bit_range read_bits (bit_offset, num_bits_unsigned);
1524 : : /* If read_bits has a subset < 0, we do have an underwrite. */
1525 : 644851 : if (read_bits.falls_short_of_p (0, &bits_outside))
1526 : : {
1527 : 172 : tree diag_arg = get_representative_tree (base_reg);
1528 : 172 : switch (dir)
1529 : : {
1530 : 0 : default:
1531 : 0 : gcc_unreachable ();
1532 : 118 : break;
1533 : 118 : case access_direction::read:
1534 : 118 : gcc_assert (sval_hint == nullptr);
1535 : 118 : ctxt->warn
1536 : 118 : (std::make_unique<concrete_buffer_under_read> (*this, reg,
1537 : : diag_arg,
1538 : : bits_outside));
1539 : 118 : oob_safe = false;
1540 : 118 : 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 : 644851 : if (!cst_byte_capacity_tree)
1556 : : return oob_safe;
1557 : :
1558 : 641509 : 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 : 641509 : if (read_bits.exceeds_p (buffer, &bits_outside))
1561 : : {
1562 : 467 : tree bit_bound = wide_int_to_tree (size_type_node,
1563 : 467 : buffer.get_next_bit_offset ());
1564 : 467 : tree diag_arg = get_representative_tree (base_reg);
1565 : :
1566 : 467 : switch (dir)
1567 : : {
1568 : 0 : default:
1569 : 0 : gcc_unreachable ();
1570 : 289 : break;
1571 : 289 : case access_direction::read:
1572 : 289 : gcc_assert (sval_hint == nullptr);
1573 : 289 : ctxt->warn
1574 : 289 : (std::make_unique<concrete_buffer_over_read> (*this,
1575 : : reg, diag_arg,
1576 : : bits_outside,
1577 : : bit_bound));
1578 : 289 : oob_safe = false;
1579 : 289 : 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 */
|