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