1 /* Handling for the known behavior of various specific functions.
2 Copyright (C) 2020-2023 Free Software Foundation, Inc.
3 Contributed by David Malcolm <dmalcolm@redhat.com>.
5 This file is part of GCC.
7 GCC is free software; you can redistribute it and/or modify it
8 under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3, or (at your option)
12 GCC is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with GCC; see the file COPYING3. If not see
19 <http://www.gnu.org/licenses/>. */
22 #define INCLUDE_MEMORY
24 #include "coretypes.h"
27 #include "basic-block.h"
29 #include "diagnostic-core.h"
30 #include "diagnostic-metadata.h"
31 #include "analyzer/analyzer.h"
32 #include "analyzer/analyzer-logging.h"
33 #include "diagnostic.h"
34 #include "analyzer/region-model.h"
35 #include "analyzer/call-details.h"
36 #include "analyzer/call-info.h"
37 #include "make-unique.h"
43 /* class pure_known_function_with_default_return : public known_function. */
46 pure_known_function_with_default_return::
47 impl_call_pre (const call_details
&cd
) const
49 cd
.set_any_lhs_with_defaults ();
52 /* Implementations of specific functions. */
54 /* Handler for "alloca". */
56 class kf_alloca
: public builtin_known_function
59 bool matches_call_types_p (const call_details
&cd
) const final override
61 return cd
.num_args () == 1;
63 enum built_in_function
builtin_code () const final override
65 return BUILT_IN_ALLOCA
;
67 void impl_call_pre (const call_details
&cd
) const final override
;
71 kf_alloca::impl_call_pre (const call_details
&cd
) const
73 const svalue
*size_sval
= cd
.get_arg_svalue (0);
75 region_model
*model
= cd
.get_model ();
76 region_model_manager
*mgr
= cd
.get_manager ();
79 = model
->create_region_for_alloca (size_sval
, cd
.get_ctxt ());
80 const svalue
*ptr_sval
81 = mgr
->get_ptr_svalue (cd
.get_lhs_type (), new_reg
);
82 cd
.maybe_set_lhs (ptr_sval
);
86 void __atomic_exchange (type *ptr, type *val, type *ret, int memorder). */
88 class kf_atomic_exchange
: public internal_known_function
91 /* This is effectively:
95 void impl_call_pre (const call_details
&cd
) const final override
97 const svalue
*ptr_ptr_sval
= cd
.get_arg_svalue (0);
98 tree ptr_ptr_tree
= cd
.get_arg_tree (0);
99 const svalue
*val_ptr_sval
= cd
.get_arg_svalue (1);
100 tree val_ptr_tree
= cd
.get_arg_tree (1);
101 const svalue
*ret_ptr_sval
= cd
.get_arg_svalue (2);
102 tree ret_ptr_tree
= cd
.get_arg_tree (2);
103 /* Ignore the memorder param. */
105 region_model
*model
= cd
.get_model ();
106 region_model_context
*ctxt
= cd
.get_ctxt ();
108 const region
*val_region
109 = model
->deref_rvalue (val_ptr_sval
, val_ptr_tree
, ctxt
);
110 const svalue
*star_val_sval
= model
->get_store_value (val_region
, ctxt
);
111 const region
*ptr_region
112 = model
->deref_rvalue (ptr_ptr_sval
, ptr_ptr_tree
, ctxt
);
113 const svalue
*star_ptr_sval
= model
->get_store_value (ptr_region
, ctxt
);
114 const region
*ret_region
115 = model
->deref_rvalue (ret_ptr_sval
, ret_ptr_tree
, ctxt
);
116 model
->set_value (ptr_region
, star_val_sval
, ctxt
);
117 model
->set_value (ret_region
, star_ptr_sval
, ctxt
);
122 __atomic_exchange_n (type *ptr, type val, int memorder). */
124 class kf_atomic_exchange_n
: public internal_known_function
127 /* This is effectively:
132 void impl_call_pre (const call_details
&cd
) const final override
134 const svalue
*ptr_sval
= cd
.get_arg_svalue (0);
135 tree ptr_tree
= cd
.get_arg_tree (0);
136 const svalue
*set_sval
= cd
.get_arg_svalue (1);
137 /* Ignore the memorder param. */
139 region_model
*model
= cd
.get_model ();
140 region_model_context
*ctxt
= cd
.get_ctxt ();
142 const region
*dst_region
= model
->deref_rvalue (ptr_sval
, ptr_tree
, ctxt
);
143 const svalue
*ret_sval
= model
->get_store_value (dst_region
, ctxt
);
144 model
->set_value (dst_region
, set_sval
, ctxt
);
145 cd
.maybe_set_lhs (ret_sval
);
150 type __atomic_fetch_add (type *ptr, type val, int memorder);
151 type __atomic_fetch_sub (type *ptr, type val, int memorder);
152 type __atomic_fetch_and (type *ptr, type val, int memorder);
153 type __atomic_fetch_xor (type *ptr, type val, int memorder);
154 type __atomic_fetch_or (type *ptr, type val, int memorder);
157 class kf_atomic_fetch_op
: public internal_known_function
160 kf_atomic_fetch_op (enum tree_code op
): m_op (op
) {}
162 /* This is effectively:
167 void impl_call_pre (const call_details
&cd
) const final override
169 const svalue
*ptr_sval
= cd
.get_arg_svalue (0);
170 tree ptr_tree
= cd
.get_arg_tree (0);
171 const svalue
*val_sval
= cd
.get_arg_svalue (1);
172 /* Ignore the memorder param. */
174 region_model
*model
= cd
.get_model ();
175 region_model_manager
*mgr
= cd
.get_manager ();
176 region_model_context
*ctxt
= cd
.get_ctxt ();
178 const region
*star_ptr_region
179 = model
->deref_rvalue (ptr_sval
, ptr_tree
, ctxt
);
180 const svalue
*old_sval
= model
->get_store_value (star_ptr_region
, ctxt
);
181 const svalue
*new_sval
= mgr
->get_or_create_binop (old_sval
->get_type (),
184 model
->set_value (star_ptr_region
, new_sval
, ctxt
);
185 cd
.maybe_set_lhs (old_sval
);
193 type __atomic_add_fetch (type *ptr, type val, int memorder);
194 type __atomic_sub_fetch (type *ptr, type val, int memorder);
195 type __atomic_and_fetch (type *ptr, type val, int memorder);
196 type __atomic_xor_fetch (type *ptr, type val, int memorder);
197 type __atomic_or_fetch (type *ptr, type val, int memorder);
200 class kf_atomic_op_fetch
: public internal_known_function
203 kf_atomic_op_fetch (enum tree_code op
): m_op (op
) {}
205 /* This is effectively:
209 void impl_call_pre (const call_details
&cd
) const final override
211 const svalue
*ptr_sval
= cd
.get_arg_svalue (0);
212 tree ptr_tree
= cd
.get_arg_tree (0);
213 const svalue
*val_sval
= cd
.get_arg_svalue (1);
214 /* Ignore the memorder param. */
216 region_model
*model
= cd
.get_model ();
217 region_model_manager
*mgr
= cd
.get_manager ();
218 region_model_context
*ctxt
= cd
.get_ctxt ();
220 const region
*star_ptr_region
221 = model
->deref_rvalue (ptr_sval
, ptr_tree
, ctxt
);
222 const svalue
*old_sval
= model
->get_store_value (star_ptr_region
, ctxt
);
223 const svalue
*new_sval
= mgr
->get_or_create_binop (old_sval
->get_type (),
226 model
->set_value (star_ptr_region
, new_sval
, ctxt
);
227 cd
.maybe_set_lhs (new_sval
);
235 void __atomic_load (type *ptr, type *ret, int memorder). */
237 class kf_atomic_load
: public internal_known_function
240 /* This is effectively:
243 void impl_call_pre (const call_details
&cd
) const final override
245 const svalue
*ptr_ptr_sval
= cd
.get_arg_svalue (0);
246 tree ptr_ptr_tree
= cd
.get_arg_tree (0);
247 const svalue
*ret_ptr_sval
= cd
.get_arg_svalue (1);
248 tree ret_ptr_tree
= cd
.get_arg_tree (1);
249 /* Ignore the memorder param. */
251 region_model
*model
= cd
.get_model ();
252 region_model_context
*ctxt
= cd
.get_ctxt ();
254 const region
*ptr_region
255 = model
->deref_rvalue (ptr_ptr_sval
, ptr_ptr_tree
, ctxt
);
256 const svalue
*star_ptr_sval
= model
->get_store_value (ptr_region
, ctxt
);
257 const region
*ret_region
258 = model
->deref_rvalue (ret_ptr_sval
, ret_ptr_tree
, ctxt
);
259 model
->set_value (ret_region
, star_ptr_sval
, ctxt
);
264 type __atomic_load_n (type *ptr, int memorder) */
266 class kf_atomic_load_n
: public internal_known_function
269 /* This is effectively:
273 void impl_call_pre (const call_details
&cd
) const final override
275 const svalue
*ptr_ptr_sval
= cd
.get_arg_svalue (0);
276 tree ptr_ptr_tree
= cd
.get_arg_tree (0);
277 /* Ignore the memorder param. */
279 region_model
*model
= cd
.get_model ();
280 region_model_context
*ctxt
= cd
.get_ctxt ();
282 const region
*ptr_region
283 = model
->deref_rvalue (ptr_ptr_sval
, ptr_ptr_tree
, ctxt
);
284 const svalue
*star_ptr_sval
= model
->get_store_value (ptr_region
, ctxt
);
285 cd
.maybe_set_lhs (star_ptr_sval
);
290 void __atomic_store_n (type *ptr, type val, int memorder) */
292 class kf_atomic_store_n
: public internal_known_function
295 /* This is effectively:
298 void impl_call_pre (const call_details
&cd
) const final override
300 const svalue
*ptr_sval
= cd
.get_arg_svalue (0);
301 tree ptr_tree
= cd
.get_arg_tree (0);
302 const svalue
*new_sval
= cd
.get_arg_svalue (1);
303 /* Ignore the memorder param. */
305 region_model
*model
= cd
.get_model ();
306 region_model_context
*ctxt
= cd
.get_ctxt ();
308 const region
*star_ptr_region
309 = model
->deref_rvalue (ptr_sval
, ptr_tree
, ctxt
);
310 model
->set_value (star_ptr_region
, new_sval
, ctxt
);
314 /* Handler for "__builtin_expect" etc. */
316 class kf_expect
: public internal_known_function
319 void impl_call_pre (const call_details
&cd
) const final override
321 /* __builtin_expect's return value is its initial argument. */
322 const svalue
*sval
= cd
.get_arg_svalue (0);
323 cd
.maybe_set_lhs (sval
);
327 /* Handler for "calloc". */
329 class kf_calloc
: public builtin_known_function
332 bool matches_call_types_p (const call_details
&cd
) const final override
334 return (cd
.num_args () == 2
335 && cd
.arg_is_size_p (0)
336 && cd
.arg_is_size_p (1));
338 enum built_in_function
builtin_code () const final override
340 return BUILT_IN_CALLOC
;
343 void impl_call_pre (const call_details
&cd
) const final override
;
347 kf_calloc::impl_call_pre (const call_details
&cd
) const
349 region_model
*model
= cd
.get_model ();
350 region_model_manager
*mgr
= cd
.get_manager ();
351 const svalue
*nmemb_sval
= cd
.get_arg_svalue (0);
352 const svalue
*size_sval
= cd
.get_arg_svalue (1);
353 /* TODO: check for overflow here? */
354 const svalue
*prod_sval
355 = mgr
->get_or_create_binop (size_type_node
, MULT_EXPR
,
356 nmemb_sval
, size_sval
);
357 const region
*new_reg
358 = model
->get_or_create_region_for_heap_alloc (prod_sval
, cd
.get_ctxt ());
359 const region
*sized_reg
360 = mgr
->get_sized_region (new_reg
, NULL_TREE
, prod_sval
);
361 model
->zero_fill_region (sized_reg
, cd
.get_ctxt ());
362 if (cd
.get_lhs_type ())
364 const svalue
*ptr_sval
365 = mgr
->get_ptr_svalue (cd
.get_lhs_type (), new_reg
);
366 cd
.maybe_set_lhs (ptr_sval
);
370 /* Handler for glibc's "__errno_location". */
372 class kf_errno_location
: public known_function
375 bool matches_call_types_p (const call_details
&cd
) const final override
377 return cd
.num_args () == 0;
380 void impl_call_pre (const call_details
&cd
) const final override
382 if (cd
.get_lhs_region ())
384 region_model_manager
*mgr
= cd
.get_manager ();
385 const region
*errno_reg
= mgr
->get_errno_region ();
386 const svalue
*errno_ptr
= mgr
->get_ptr_svalue (cd
.get_lhs_type (),
388 cd
.maybe_set_lhs (errno_ptr
);
393 /* Handler for "error" and "error_at_line" from GNU's non-standard <error.h>.
394 MIN_ARGS identifies the minimum number of expected arguments
395 to be consistent with such a call (3 and 5 respectively). */
397 class kf_error
: public known_function
400 kf_error (unsigned min_args
) : m_min_args (min_args
) {}
402 bool matches_call_types_p (const call_details
&cd
) const final override
404 return (cd
.num_args () >= m_min_args
405 && cd
.get_arg_type (0) == integer_type_node
);
408 void impl_call_pre (const call_details
&cd
) const final override
;
415 kf_error::impl_call_pre (const call_details
&cd
) const
417 /* The process exits if status != 0, so it only continues
418 for the case where status == 0.
419 Add that constraint, or terminate this analysis path. */
420 tree status
= cd
.get_arg_tree (0);
421 region_model_context
*ctxt
= cd
.get_ctxt ();
422 region_model
*model
= cd
.get_model ();
423 if (!model
->add_constraint (status
, EQ_EXPR
, integer_zero_node
, ctxt
))
425 ctxt
->terminate_path ();
427 /* Check "format" arg. */
428 const int fmt_arg_idx
= (m_min_args
== 3) ? 2 : 4;
429 model
->check_for_null_terminated_string_arg (cd
, fmt_arg_idx
);
432 /* Handler for fopen.
433 FILE *fopen (const char *filename, const char *mode);
434 See e.g. https://en.cppreference.com/w/c/io/fopen
435 https://www.man7.org/linux/man-pages/man3/fopen.3.html
436 https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/fopen-wfopen?view=msvc-170 */
438 class kf_fopen
: public known_function
441 bool matches_call_types_p (const call_details
&cd
) const final override
443 return (cd
.num_args () == 2
444 && cd
.arg_is_pointer_p (0)
445 && cd
.arg_is_pointer_p (1));
448 void impl_call_pre (const call_details
&cd
) const final override
450 cd
.check_for_null_terminated_string_arg (0);
451 cd
.check_for_null_terminated_string_arg (1);
452 cd
.set_any_lhs_with_defaults ();
454 /* fopen's mode param is effectively a mini-DSL, but there are various
455 non-standard extensions, so we don't bother to check it. */
459 /* Handler for "free", after sm-handling.
461 If the ptr points to an underlying heap region, delete the region,
462 poisoning pointers to it and regions within it.
464 We delay this until after sm-state has been updated so that the
465 sm-handling can transition all of the various casts of the pointer
466 to a "freed" state *before* we delete the related region here.
468 This has to be done here so that the sm-handling can use the fact
469 that they point to the same region to establish that they are equal
470 (in region_model::eval_condition), and thus transition
471 all pointers to the region to the "freed" state together, regardless
474 class kf_free
: public builtin_known_function
477 bool matches_call_types_p (const call_details
&cd
) const final override
479 return (cd
.num_args () == 1 && cd
.arg_is_pointer_p (0));
481 enum built_in_function
builtin_code () const final override
483 return BUILT_IN_FREE
;
485 void impl_call_post (const call_details
&cd
) const final override
;
489 kf_free::impl_call_post (const call_details
&cd
) const
491 const svalue
*ptr_sval
= cd
.get_arg_svalue (0);
492 if (const region
*freed_reg
= ptr_sval
->maybe_get_region ())
494 /* If the ptr points to an underlying heap region, delete it,
495 poisoning pointers. */
496 region_model
*model
= cd
.get_model ();
497 model
->unbind_region_and_descendents (freed_reg
, POISON_KIND_FREED
);
498 model
->unset_dynamic_extents (freed_reg
);
502 /* Handle the on_call_pre part of "malloc". */
504 class kf_malloc
: public builtin_known_function
507 bool matches_call_types_p (const call_details
&cd
) const final override
509 return (cd
.num_args () == 1
510 && cd
.arg_is_size_p (0));
512 enum built_in_function
builtin_code () const final override
514 return BUILT_IN_MALLOC
;
516 void impl_call_pre (const call_details
&cd
) const final override
;
520 kf_malloc::impl_call_pre (const call_details
&cd
) const
522 region_model
*model
= cd
.get_model ();
523 region_model_manager
*mgr
= cd
.get_manager ();
524 const svalue
*size_sval
= cd
.get_arg_svalue (0);
525 const region
*new_reg
526 = model
->get_or_create_region_for_heap_alloc (size_sval
, cd
.get_ctxt ());
527 if (cd
.get_lhs_type ())
529 const svalue
*ptr_sval
530 = mgr
->get_ptr_svalue (cd
.get_lhs_type (), new_reg
);
531 cd
.maybe_set_lhs (ptr_sval
);
535 /* Handler for "memcpy" and "__builtin_memcpy",
536 "memmove", and "__builtin_memmove". */
538 class kf_memcpy_memmove
: public builtin_known_function
541 enum kf_memcpy_memmove_variant
548 kf_memcpy_memmove (enum kf_memcpy_memmove_variant variant
)
549 : m_variant (variant
) {};
550 bool matches_call_types_p (const call_details
&cd
) const final override
552 return (cd
.num_args () == 3
553 && cd
.arg_is_pointer_p (0)
554 && cd
.arg_is_pointer_p (1)
555 && cd
.arg_is_size_p (2));
557 enum built_in_function
builtin_code () const final override
562 return BUILT_IN_MEMCPY
;
564 return BUILT_IN_MEMCPY_CHK
;
566 return BUILT_IN_MEMMOVE
;
568 return BUILT_IN_MEMMOVE_CHK
;
573 void impl_call_pre (const call_details
&cd
) const final override
;
575 const enum kf_memcpy_memmove_variant m_variant
;
579 kf_memcpy_memmove::impl_call_pre (const call_details
&cd
) const
581 const svalue
*dest_ptr_sval
= cd
.get_arg_svalue (0);
582 const svalue
*src_ptr_sval
= cd
.get_arg_svalue (1);
583 const svalue
*num_bytes_sval
= cd
.get_arg_svalue (2);
585 region_model
*model
= cd
.get_model ();
587 const region
*dest_reg
588 = model
->deref_rvalue (dest_ptr_sval
, cd
.get_arg_tree (0), cd
.get_ctxt ());
589 const region
*src_reg
590 = model
->deref_rvalue (src_ptr_sval
, cd
.get_arg_tree (1), cd
.get_ctxt ());
592 cd
.maybe_set_lhs (dest_ptr_sval
);
593 /* Check for overlap. */
598 cd
.complain_about_overlap (0, 1, num_bytes_sval
);
603 /* It's OK for memmove's arguments to overlap. */
609 model
->copy_bytes (dest_reg
,
610 src_reg
, cd
.get_arg_tree (1),
615 /* Handler for "memset" and "__builtin_memset". */
617 class kf_memset
: public builtin_known_function
620 kf_memset (bool chk_variant
) : m_chk_variant (chk_variant
) {}
621 bool matches_call_types_p (const call_details
&cd
) const final override
623 return (cd
.num_args () == 3 && cd
.arg_is_pointer_p (0));
625 enum built_in_function
builtin_code () const final override
627 return m_chk_variant
? BUILT_IN_MEMSET_CHK
: BUILT_IN_MEMSET
;
629 void impl_call_pre (const call_details
&cd
) const final override
;
631 const bool m_chk_variant
;
635 kf_memset::impl_call_pre (const call_details
&cd
) const
637 const svalue
*dest_sval
= cd
.get_arg_svalue (0);
638 const svalue
*fill_value_sval
= cd
.get_arg_svalue (1);
639 const svalue
*num_bytes_sval
= cd
.get_arg_svalue (2);
641 region_model
*model
= cd
.get_model ();
642 region_model_manager
*mgr
= cd
.get_manager ();
644 const region
*dest_reg
645 = model
->deref_rvalue (dest_sval
, cd
.get_arg_tree (0), cd
.get_ctxt ());
647 const svalue
*fill_value_u8
648 = mgr
->get_or_create_cast (unsigned_char_type_node
, fill_value_sval
);
650 const region
*sized_dest_reg
= mgr
->get_sized_region (dest_reg
,
653 model
->fill_region (sized_dest_reg
, fill_value_u8
, cd
.get_ctxt ());
655 cd
.maybe_set_lhs (dest_sval
);
658 /* A subclass of pending_diagnostic for complaining about 'putenv'
659 called on an auto var. */
661 class putenv_of_auto_var
662 : public pending_diagnostic_subclass
<putenv_of_auto_var
>
665 putenv_of_auto_var (tree fndecl
, const region
*reg
)
666 : m_fndecl (fndecl
), m_reg (reg
),
667 m_var_decl (reg
->get_base_region ()->maybe_get_decl ())
671 const char *get_kind () const final override
673 return "putenv_of_auto_var";
676 bool operator== (const putenv_of_auto_var
&other
) const
678 return (m_fndecl
== other
.m_fndecl
679 && m_reg
== other
.m_reg
680 && same_tree_p (m_var_decl
, other
.m_var_decl
));
683 int get_controlling_option () const final override
685 return OPT_Wanalyzer_putenv_of_auto_var
;
688 bool emit (rich_location
*rich_loc
, logger
*) final override
690 auto_diagnostic_group d
;
691 diagnostic_metadata m
;
693 /* SEI CERT C Coding Standard: "POS34-C. Do not call putenv() with a
694 pointer to an automatic variable as the argument". */
695 diagnostic_metadata::precanned_rule
696 rule ("POS34-C", "https://wiki.sei.cmu.edu/confluence/x/6NYxBQ");
701 warned
= warning_meta (rich_loc
, m
, get_controlling_option (),
702 "%qE on a pointer to automatic variable %qE",
703 m_fndecl
, m_var_decl
);
705 warned
= warning_meta (rich_loc
, m
, get_controlling_option (),
706 "%qE on a pointer to an on-stack buffer",
711 inform (DECL_SOURCE_LOCATION (m_var_decl
),
712 "%qE declared on stack here", m_var_decl
);
713 inform (rich_loc
->get_loc (), "perhaps use %qs rather than %qE",
720 label_text
describe_final_event (const evdesc::final_event
&ev
) final override
723 return ev
.formatted_print ("%qE on a pointer to automatic variable %qE",
724 m_fndecl
, m_var_decl
);
726 return ev
.formatted_print ("%qE on a pointer to an on-stack buffer",
730 void mark_interesting_stuff (interesting_t
*interest
) final override
733 interest
->add_region_creation (m_reg
->get_base_region ());
737 tree m_fndecl
; // non-NULL
738 const region
*m_reg
; // non-NULL
739 tree m_var_decl
; // could be NULL
742 /* Handler for calls to "putenv".
744 In theory we could try to model the state of the environment variables
745 for the process; for now we merely complain about putenv of regions
748 class kf_putenv
: public known_function
751 bool matches_call_types_p (const call_details
&cd
) const final override
753 return (cd
.num_args () == 1 && cd
.arg_is_pointer_p (0));
756 void impl_call_pre (const call_details
&cd
) const final override
758 tree fndecl
= cd
.get_fndecl_for_call ();
760 region_model_context
*ctxt
= cd
.get_ctxt ();
761 region_model
*model
= cd
.get_model ();
762 model
->check_for_null_terminated_string_arg (cd
, 0);
763 const svalue
*ptr_sval
= cd
.get_arg_svalue (0);
765 = model
->deref_rvalue (ptr_sval
, cd
.get_arg_tree (0), ctxt
);
766 model
->get_store ()->mark_as_escaped (reg
);
767 enum memory_space mem_space
= reg
->get_memory_space ();
772 case MEMSPACE_UNKNOWN
:
774 case MEMSPACE_GLOBALS
:
776 case MEMSPACE_READONLY_DATA
:
780 ctxt
->warn (make_unique
<putenv_of_auto_var
> (fndecl
, reg
));
783 cd
.set_any_lhs_with_defaults ();
787 /* Handler for "realloc":
789 void *realloc(void *ptr, size_t size);
791 realloc(3) is awkward, since it has various different outcomes
792 that are best modelled as separate exploded nodes/edges.
794 We first check for sm-state, in
795 malloc_state_machine::on_realloc_call, so that we
796 can complain about issues such as realloc of a non-heap
797 pointer, and terminate the path for such cases (and issue
798 the complaints at the call's exploded node).
800 Assuming that these checks pass, we split the path here into
801 three special cases (and terminate the "standard" path):
802 (A) failure, returning NULL
803 (B) success, growing the buffer in-place without moving it
804 (C) success, allocating a new buffer, copying the content
805 of the old buffer to it, and freeing the old buffer.
807 Each of these has a custom_edge_info subclass, which updates
808 the region_model and sm-state of the destination state. */
810 class kf_realloc
: public builtin_known_function
813 bool matches_call_types_p (const call_details
&cd
) const final override
815 return (cd
.num_args () == 2
816 && cd
.arg_is_pointer_p (0)
817 && cd
.arg_is_size_p (1));
820 enum built_in_function
builtin_code () const final override
822 return BUILT_IN_REALLOC
;
825 void impl_call_post (const call_details
&cd
) const final override
;
829 kf_realloc::impl_call_post (const call_details
&cd
) const
831 /* Three custom subclasses of custom_edge_info, for handling the various
832 outcomes of "realloc". */
834 /* Concrete custom_edge_info: a realloc call that fails, returning NULL. */
835 class failure
: public failed_call_info
838 failure (const call_details
&cd
)
839 : failed_call_info (cd
)
843 bool update_model (region_model
*model
,
844 const exploded_edge
*,
845 region_model_context
*ctxt
) const final override
847 /* Return NULL; everything else is unchanged. */
848 const call_details
cd (get_call_details (model
, ctxt
));
849 region_model_manager
*mgr
= cd
.get_manager ();
850 if (cd
.get_lhs_type ())
853 = mgr
->get_or_create_int_cst (cd
.get_lhs_type (), 0);
854 model
->set_value (cd
.get_lhs_region (),
862 /* Concrete custom_edge_info: a realloc call that succeeds, growing
863 the existing buffer without moving it. */
864 class success_no_move
: public call_info
867 success_no_move (const call_details
&cd
)
872 label_text
get_desc (bool can_colorize
) const final override
874 return make_label_text (can_colorize
,
875 "when %qE succeeds, without moving buffer",
879 bool update_model (region_model
*model
,
880 const exploded_edge
*,
881 region_model_context
*ctxt
) const final override
883 /* Update size of buffer and return the ptr unchanged. */
884 const call_details
cd (get_call_details (model
, ctxt
));
885 region_model_manager
*mgr
= cd
.get_manager ();
886 const svalue
*ptr_sval
= cd
.get_arg_svalue (0);
887 const svalue
*size_sval
= cd
.get_arg_svalue (1);
889 /* We can only grow in place with a non-NULL pointer. */
891 const svalue
*null_ptr
892 = mgr
->get_or_create_int_cst (ptr_sval
->get_type (), 0);
893 if (!model
->add_constraint (ptr_sval
, NE_EXPR
, null_ptr
,
898 if (const region
*buffer_reg
= model
->deref_rvalue (ptr_sval
, NULL_TREE
,
900 if (compat_types_p (size_sval
->get_type (), size_type_node
))
901 model
->set_dynamic_extents (buffer_reg
, size_sval
, ctxt
);
902 if (cd
.get_lhs_region ())
904 model
->set_value (cd
.get_lhs_region (), ptr_sval
, cd
.get_ctxt ());
906 = mgr
->get_or_create_int_cst (cd
.get_lhs_type (), 0);
907 return model
->add_constraint (ptr_sval
, NE_EXPR
, zero
, ctxt
);
914 /* Concrete custom_edge_info: a realloc call that succeeds, freeing
915 the existing buffer and moving the content to a freshly allocated
917 class success_with_move
: public call_info
920 success_with_move (const call_details
&cd
)
925 label_text
get_desc (bool can_colorize
) const final override
927 return make_label_text (can_colorize
,
928 "when %qE succeeds, moving buffer",
931 bool update_model (region_model
*model
,
932 const exploded_edge
*,
933 region_model_context
*ctxt
) const final override
935 const call_details
cd (get_call_details (model
, ctxt
));
936 region_model_manager
*mgr
= cd
.get_manager ();
937 const svalue
*old_ptr_sval
= cd
.get_arg_svalue (0);
938 const svalue
*new_size_sval
= cd
.get_arg_svalue (1);
940 /* Create the new region. */
941 const region
*new_reg
942 = model
->get_or_create_region_for_heap_alloc (new_size_sval
, ctxt
);
943 const svalue
*new_ptr_sval
944 = mgr
->get_ptr_svalue (cd
.get_lhs_type (), new_reg
);
945 if (!model
->add_constraint (new_ptr_sval
, NE_EXPR
, old_ptr_sval
,
949 if (cd
.get_lhs_type ())
950 cd
.maybe_set_lhs (new_ptr_sval
);
952 if (const region
*freed_reg
= model
->deref_rvalue (old_ptr_sval
,
956 const svalue
*old_size_sval
= model
->get_dynamic_extents (freed_reg
);
959 const svalue
*copied_size_sval
960 = get_copied_size (model
, old_size_sval
, new_size_sval
);
961 const region
*copied_old_reg
962 = mgr
->get_sized_region (freed_reg
, NULL
, copied_size_sval
);
963 const svalue
*buffer_content_sval
964 = model
->get_store_value (copied_old_reg
, cd
.get_ctxt ());
965 const region
*copied_new_reg
966 = mgr
->get_sized_region (new_reg
, NULL
, copied_size_sval
);
967 model
->set_value (copied_new_reg
, buffer_content_sval
,
972 /* We don't know how big the old region was;
973 mark the new region as having been touched to avoid uninit
975 model
->mark_region_as_unknown (new_reg
, cd
.get_uncertainty ());
978 /* Free the old region, so that pointers to the old buffer become
981 /* If the ptr points to an underlying heap region, delete it,
982 poisoning pointers. */
983 model
->unbind_region_and_descendents (freed_reg
, POISON_KIND_FREED
);
984 model
->unset_dynamic_extents (freed_reg
);
987 /* Update the sm-state: mark the old_ptr_sval as "freed",
988 and the new_ptr_sval as "nonnull". */
989 model
->on_realloc_with_move (cd
, old_ptr_sval
, new_ptr_sval
);
991 if (cd
.get_lhs_type ())
994 = mgr
->get_or_create_int_cst (cd
.get_lhs_type (), 0);
995 return model
->add_constraint (new_ptr_sval
, NE_EXPR
, zero
,
1003 /* Return the lesser of OLD_SIZE_SVAL and NEW_SIZE_SVAL.
1004 If unknown, OLD_SIZE_SVAL is returned. */
1005 const svalue
*get_copied_size (region_model
*model
,
1006 const svalue
*old_size_sval
,
1007 const svalue
*new_size_sval
) const
1010 = model
->eval_condition (old_size_sval
, GT_EXPR
, new_size_sval
);
1011 switch (res
.get_value ())
1013 case tristate::TS_TRUE
:
1014 return new_size_sval
;
1015 case tristate::TS_FALSE
:
1016 case tristate::TS_UNKNOWN
:
1017 return old_size_sval
;
1024 /* Body of kf_realloc::impl_call_post. */
1028 cd
.get_ctxt ()->bifurcate (make_unique
<failure
> (cd
));
1029 cd
.get_ctxt ()->bifurcate (make_unique
<success_no_move
> (cd
));
1030 cd
.get_ctxt ()->bifurcate (make_unique
<success_with_move
> (cd
));
1031 cd
.get_ctxt ()->terminate_path ();
1035 /* Handler for "strchr" and "__builtin_strchr". */
1037 class kf_strchr
: public builtin_known_function
1040 bool matches_call_types_p (const call_details
&cd
) const final override
1042 return (cd
.num_args () == 2 && cd
.arg_is_pointer_p (0));
1044 void impl_call_pre (const call_details
&cd
) const final override
1046 cd
.check_for_null_terminated_string_arg (0);
1049 enum built_in_function
builtin_code () const final override
1051 return BUILT_IN_STRCHR
;
1053 void impl_call_post (const call_details
&cd
) const final override
;
1057 kf_strchr::impl_call_post (const call_details
&cd
) const
1059 class strchr_call_info
: public call_info
1062 strchr_call_info (const call_details
&cd
, bool found
)
1063 : call_info (cd
), m_found (found
)
1067 label_text
get_desc (bool can_colorize
) const final override
1070 return make_label_text (can_colorize
,
1071 "when %qE returns non-NULL",
1074 return make_label_text (can_colorize
,
1075 "when %qE returns NULL",
1079 bool update_model (region_model
*model
,
1080 const exploded_edge
*,
1081 region_model_context
*ctxt
) const final override
1083 const call_details
cd (get_call_details (model
, ctxt
));
1084 if (tree lhs_type
= cd
.get_lhs_type ())
1086 region_model_manager
*mgr
= model
->get_manager ();
1087 const svalue
*result
;
1090 const svalue
*str_sval
= cd
.get_arg_svalue (0);
1091 const region
*str_reg
1092 = model
->deref_rvalue (str_sval
, cd
.get_arg_tree (0),
1094 /* We want str_sval + OFFSET for some unknown OFFSET.
1095 Use a conjured_svalue to represent the offset,
1096 using the str_reg as the id of the conjured_svalue. */
1097 const svalue
*offset
1098 = mgr
->get_or_create_conjured_svalue (size_type_node
,
1099 cd
.get_call_stmt (),
1101 conjured_purge (model
,
1103 result
= mgr
->get_or_create_binop (lhs_type
, POINTER_PLUS_EXPR
,
1107 result
= mgr
->get_or_create_int_cst (lhs_type
, 0);
1108 cd
.maybe_set_lhs (result
);
1116 /* Body of kf_strchr::impl_call_post. */
1119 cd
.get_ctxt ()->bifurcate (make_unique
<strchr_call_info
> (cd
, false));
1120 cd
.get_ctxt ()->bifurcate (make_unique
<strchr_call_info
> (cd
, true));
1121 cd
.get_ctxt ()->terminate_path ();
1125 /* Handler for "sprintf".
1126 int sprintf(char *str, const char *format, ...);
1129 class kf_sprintf
: public builtin_known_function
1132 bool matches_call_types_p (const call_details
&cd
) const final override
1134 return (cd
.num_args () >= 2
1135 && cd
.arg_is_pointer_p (0)
1136 && cd
.arg_is_pointer_p (1));
1139 enum built_in_function
builtin_code () const final override
1141 return BUILT_IN_SPRINTF
;
1144 void impl_call_pre (const call_details
&cd
) const final override
1146 /* For now, merely assume that the destination buffer gets set to a
1148 region_model
*model
= cd
.get_model ();
1149 region_model_context
*ctxt
= cd
.get_ctxt ();
1150 const svalue
*dst_ptr
= cd
.get_arg_svalue (0);
1151 const region
*dst_reg
1152 = model
->deref_rvalue (dst_ptr
, cd
.get_arg_tree (0), ctxt
);
1153 const svalue
*content
= cd
.get_or_create_conjured_svalue (dst_reg
);
1154 model
->set_value (dst_reg
, content
, ctxt
);
1155 cd
.set_any_lhs_with_defaults ();
1159 /* Handler for "__builtin_stack_restore". */
1161 class kf_stack_restore
: public pure_known_function_with_default_return
1164 bool matches_call_types_p (const call_details
&) const final override
1169 /* Currently a no-op. */
1172 /* Handler for "__builtin_stack_save". */
1174 class kf_stack_save
: public pure_known_function_with_default_return
1177 bool matches_call_types_p (const call_details
&) const final override
1182 /* Currently a no-op. */
1185 /* Handler for "strcat" and "__builtin_strcat_chk". */
1187 class kf_strcat
: public builtin_known_function
1190 kf_strcat (unsigned int num_args
, bool chk_variant
)
1191 : m_num_args (num_args
),
1192 m_chk_variant (chk_variant
) {}
1193 bool matches_call_types_p (const call_details
&cd
) const final override
1195 return (cd
.num_args () == m_num_args
1196 && cd
.arg_is_pointer_p (0)
1197 && cd
.arg_is_pointer_p (1));
1200 enum built_in_function
builtin_code () const final override
1202 return m_chk_variant
? BUILT_IN_STRCAT_CHK
: BUILT_IN_STRCAT
;
1205 void impl_call_pre (const call_details
&cd
) const final override
1207 region_model
*model
= cd
.get_model ();
1208 region_model_manager
*mgr
= cd
.get_manager ();
1210 const svalue
*dest_sval
= cd
.get_arg_svalue (0);
1211 const region
*dest_reg
= model
->deref_rvalue (dest_sval
, cd
.get_arg_tree (0),
1214 const svalue
*dst_strlen_sval
1215 = cd
.check_for_null_terminated_string_arg (0, false, nullptr);
1216 if (!dst_strlen_sval
)
1219 cd
.get_ctxt ()->terminate_path ();
1223 const svalue
*bytes_to_copy
;
1224 const svalue
*num_src_bytes_read_sval
1225 = cd
.check_for_null_terminated_string_arg (1, true, &bytes_to_copy
);
1226 if (!num_src_bytes_read_sval
)
1229 cd
.get_ctxt ()->terminate_path ();
1233 cd
.maybe_set_lhs (dest_sval
);
1234 cd
.complain_about_overlap (0, 1, num_src_bytes_read_sval
);
1236 const region
*offset_reg
1237 = mgr
->get_offset_region (dest_reg
, NULL_TREE
, dst_strlen_sval
);
1238 model
->write_bytes (offset_reg
,
1239 num_src_bytes_read_sval
,
1245 unsigned int m_num_args
;
1246 const bool m_chk_variant
;
1249 /* Handler for "strcpy" and "__builtin_strcpy_chk". */
1251 class kf_strcpy
: public builtin_known_function
1254 kf_strcpy (unsigned int num_args
, bool chk_variant
)
1255 : m_num_args (num_args
),
1256 m_chk_variant (chk_variant
) {}
1257 bool matches_call_types_p (const call_details
&cd
) const final override
1259 return (cd
.num_args () == m_num_args
1260 && cd
.arg_is_pointer_p (0)
1261 && cd
.arg_is_pointer_p (1));
1263 enum built_in_function
builtin_code () const final override
1265 return m_chk_variant
? BUILT_IN_STRCPY_CHK
: BUILT_IN_STRCPY
;
1267 void impl_call_pre (const call_details
&cd
) const final override
;
1270 unsigned int m_num_args
;
1271 const bool m_chk_variant
;
1275 kf_strcpy::impl_call_pre (const call_details
&cd
) const
1277 region_model
*model
= cd
.get_model ();
1278 region_model_context
*ctxt
= cd
.get_ctxt ();
1280 const svalue
*dest_sval
= cd
.get_arg_svalue (0);
1281 const region
*dest_reg
= model
->deref_rvalue (dest_sval
, cd
.get_arg_tree (0),
1283 /* strcpy returns the initial param. */
1284 cd
.maybe_set_lhs (dest_sval
);
1286 const svalue
*bytes_to_copy
;
1287 if (const svalue
*num_bytes_read_sval
1288 = cd
.check_for_null_terminated_string_arg (1, true, &bytes_to_copy
))
1290 cd
.complain_about_overlap (0, 1, num_bytes_read_sval
);
1291 model
->write_bytes (dest_reg
, num_bytes_read_sval
, bytes_to_copy
, ctxt
);
1296 cd
.get_ctxt ()->terminate_path ();
1300 /* Handler for "strdup" and "__builtin_strdup". */
1302 class kf_strdup
: public builtin_known_function
1305 bool matches_call_types_p (const call_details
&cd
) const final override
1307 return (cd
.num_args () == 1 && cd
.arg_is_pointer_p (0));
1309 enum built_in_function
builtin_code () const final override
1311 return BUILT_IN_STRDUP
;
1313 void impl_call_pre (const call_details
&cd
) const final override
1315 region_model
*model
= cd
.get_model ();
1316 region_model_context
*ctxt
= cd
.get_ctxt ();
1317 region_model_manager
*mgr
= cd
.get_manager ();
1318 const svalue
*bytes_to_copy
;
1319 if (const svalue
*num_bytes_read_sval
1320 = cd
.check_for_null_terminated_string_arg (0, true, &bytes_to_copy
))
1322 const region
*new_reg
1323 = model
->get_or_create_region_for_heap_alloc (num_bytes_read_sval
,
1325 model
->write_bytes (new_reg
, num_bytes_read_sval
, bytes_to_copy
, ctxt
);
1326 if (cd
.get_lhs_type ())
1328 const svalue
*ptr_sval
1329 = mgr
->get_ptr_svalue (cd
.get_lhs_type (), new_reg
);
1330 cd
.maybe_set_lhs (ptr_sval
);
1336 ctxt
->terminate_path ();
1341 /* Handler for "strlen" and for "__analyzer_get_strlen". */
1343 class kf_strlen
: public builtin_known_function
1346 bool matches_call_types_p (const call_details
&cd
) const final override
1348 return (cd
.num_args () == 1 && cd
.arg_is_pointer_p (0));
1350 enum built_in_function
builtin_code () const final override
1352 return BUILT_IN_STRLEN
;
1355 void impl_call_pre (const call_details
&cd
) const final override
1357 if (const svalue
*strlen_sval
1358 = cd
.check_for_null_terminated_string_arg (0, false, nullptr))
1359 if (strlen_sval
->get_kind () != SK_UNKNOWN
)
1361 cd
.maybe_set_lhs (strlen_sval
);
1365 /* Use a conjured svalue. */
1366 cd
.set_any_lhs_with_defaults ();
1370 /* Factory function, so that kf-analyzer.cc can use this class. */
1372 std::unique_ptr
<known_function
>
1375 return make_unique
<kf_strlen
> ();
1378 /* Handler for "strncpy" and "__builtin_strncpy".
1379 See e.g. https://en.cppreference.com/w/c/string/byte/strncpy
1381 extern char *strncpy (char *dst, const char *src, size_t count);
1383 Handle this by splitting into two outcomes:
1384 (a) truncated read from "src" of "count" bytes,
1385 writing "count" bytes to "dst"
1386 (b) read from "src" of up to (and including) the null terminator,
1387 where the number of bytes read < "count" bytes,
1388 writing those bytes to "dst", and zero-filling the rest,
1391 class kf_strncpy
: public builtin_known_function
1394 bool matches_call_types_p (const call_details
&cd
) const final override
1396 return (cd
.num_args () == 3
1397 && cd
.arg_is_pointer_p (0)
1398 && cd
.arg_is_pointer_p (1)
1399 && cd
.arg_is_integral_p (2));
1401 enum built_in_function
builtin_code () const final override
1403 return BUILT_IN_STRNCPY
;
1405 void impl_call_post (const call_details
&cd
) const final override
;
1409 kf_strncpy::impl_call_post (const call_details
&cd
) const
1411 class strncpy_call_info
: public call_info
1414 strncpy_call_info (const call_details
&cd
,
1415 const svalue
*num_bytes_with_terminator_sval
,
1416 bool truncated_read
)
1418 m_num_bytes_with_terminator_sval (num_bytes_with_terminator_sval
),
1419 m_truncated_read (truncated_read
)
1423 label_text
get_desc (bool can_colorize
) const final override
1425 if (m_truncated_read
)
1426 return make_label_text (can_colorize
,
1427 "when %qE truncates the source string",
1430 return make_label_text (can_colorize
,
1431 "when %qE copies the full source string",
1435 bool update_model (region_model
*model
,
1436 const exploded_edge
*,
1437 region_model_context
*ctxt
) const final override
1439 const call_details
cd (get_call_details (model
, ctxt
));
1441 const svalue
*dest_sval
= cd
.get_arg_svalue (0);
1442 const region
*dest_reg
1443 = model
->deref_rvalue (dest_sval
, cd
.get_arg_tree (0), ctxt
);
1445 const svalue
*src_sval
= cd
.get_arg_svalue (1);
1446 const region
*src_reg
1447 = model
->deref_rvalue (src_sval
, cd
.get_arg_tree (1), ctxt
);
1449 const svalue
*count_sval
= cd
.get_arg_svalue (2);
1451 /* strncpy returns the initial param. */
1452 cd
.maybe_set_lhs (dest_sval
);
1454 const svalue
*num_bytes_read_sval
;
1455 if (m_truncated_read
)
1457 /* Truncated read. */
1458 num_bytes_read_sval
= count_sval
;
1460 if (m_num_bytes_with_terminator_sval
)
1462 /* The terminator is after the limit. */
1463 if (!model
->add_constraint (m_num_bytes_with_terminator_sval
,
1471 /* We don't know where the terminator is, or if there is one.
1472 In theory we know that the first COUNT bytes are non-zero,
1473 but we don't have a way to record that constraint. */
1478 /* Full read of the src string before reaching the limit,
1479 so there must be a terminator and it must be at or before
1481 if (m_num_bytes_with_terminator_sval
)
1483 if (!model
->add_constraint (m_num_bytes_with_terminator_sval
,
1488 num_bytes_read_sval
= m_num_bytes_with_terminator_sval
;
1490 /* First, zero-fill the dest buffer.
1491 We don't need to do this for the truncation case, as
1492 this fully populates the dest buffer. */
1493 const region
*sized_dest_reg
1494 = model
->get_manager ()->get_sized_region (dest_reg
,
1497 model
->zero_fill_region (sized_dest_reg
, ctxt
);
1501 /* Don't analyze this case; the other case will
1502 assume a "truncated" read up to the limit. */
1507 gcc_assert (num_bytes_read_sval
);
1509 const svalue
*bytes_to_copy
1510 = model
->read_bytes (src_reg
,
1511 cd
.get_arg_tree (1),
1512 num_bytes_read_sval
,
1514 cd
.complain_about_overlap (0, 1, num_bytes_read_sval
);
1515 model
->write_bytes (dest_reg
,
1516 num_bytes_read_sval
,
1523 /* (strlen + 1) of the source string if it has a terminator,
1524 or NULL for the case where UB would happen before
1525 finding any terminator. */
1526 const svalue
*m_num_bytes_with_terminator_sval
;
1528 /* true: if this is the outcome where the limit was reached before
1530 false: if the null terminator was reached before the limit. */
1531 bool m_truncated_read
;
1534 /* Body of kf_strncpy::impl_call_post. */
1537 /* First, scan for a null terminator as if there were no limit,
1538 with a null ctxt so no errors are reported. */
1539 const region_model
*model
= cd
.get_model ();
1540 const svalue
*ptr_arg_sval
= cd
.get_arg_svalue (1);
1541 const region
*buf_reg
1542 = model
->deref_rvalue (ptr_arg_sval
, cd
.get_arg_tree (1), nullptr);
1543 const svalue
*num_bytes_with_terminator_sval
1544 = model
->scan_for_null_terminator (buf_reg
,
1545 cd
.get_arg_tree (1),
1548 cd
.get_ctxt ()->bifurcate
1549 (make_unique
<strncpy_call_info
> (cd
, num_bytes_with_terminator_sval
,
1551 cd
.get_ctxt ()->bifurcate
1552 (make_unique
<strncpy_call_info
> (cd
, num_bytes_with_terminator_sval
,
1554 cd
.get_ctxt ()->terminate_path ();
1558 /* Handler for "strndup" and "__builtin_strndup". */
1560 class kf_strndup
: public builtin_known_function
1563 bool matches_call_types_p (const call_details
&cd
) const final override
1565 return (cd
.num_args () == 2 && cd
.arg_is_pointer_p (0));
1567 enum built_in_function
builtin_code () const final override
1569 return BUILT_IN_STRNDUP
;
1571 void impl_call_pre (const call_details
&cd
) const final override
1573 region_model
*model
= cd
.get_model ();
1574 region_model_manager
*mgr
= cd
.get_manager ();
1575 /* Ideally we'd get the size here, and simulate copying the bytes. */
1576 const region
*new_reg
1577 = model
->get_or_create_region_for_heap_alloc (NULL
, cd
.get_ctxt ());
1578 model
->mark_region_as_unknown (new_reg
, NULL
);
1579 if (cd
.get_lhs_type ())
1581 const svalue
*ptr_sval
1582 = mgr
->get_ptr_svalue (cd
.get_lhs_type (), new_reg
);
1583 cd
.maybe_set_lhs (ptr_sval
);
1588 /* Handler for "strstr" and "__builtin_strstr".
1589 extern char *strstr (const char* str, const char* substr);
1590 See e.g. https://en.cppreference.com/w/c/string/byte/strstr */
1592 class kf_strstr
: public builtin_known_function
1595 bool matches_call_types_p (const call_details
&cd
) const final override
1597 return (cd
.num_args () == 2
1598 && cd
.arg_is_pointer_p (0)
1599 && cd
.arg_is_pointer_p (1));
1601 enum built_in_function
builtin_code () const final override
1603 return BUILT_IN_STRSTR
;
1605 void impl_call_pre (const call_details
&cd
) const final override
1607 cd
.check_for_null_terminated_string_arg (0);
1608 cd
.check_for_null_terminated_string_arg (1);
1610 void impl_call_post (const call_details
&cd
) const final override
;
1614 kf_strstr::impl_call_post (const call_details
&cd
) const
1616 class strstr_call_info
: public call_info
1619 strstr_call_info (const call_details
&cd
, bool found
)
1620 : call_info (cd
), m_found (found
)
1624 label_text
get_desc (bool can_colorize
) const final override
1627 return make_label_text (can_colorize
,
1628 "when %qE returns non-NULL",
1631 return make_label_text (can_colorize
,
1632 "when %qE returns NULL",
1636 bool update_model (region_model
*model
,
1637 const exploded_edge
*,
1638 region_model_context
*ctxt
) const final override
1640 const call_details
cd (get_call_details (model
, ctxt
));
1641 if (tree lhs_type
= cd
.get_lhs_type ())
1643 region_model_manager
*mgr
= model
->get_manager ();
1644 const svalue
*result
;
1647 const svalue
*str_sval
= cd
.get_arg_svalue (0);
1648 const region
*str_reg
1649 = model
->deref_rvalue (str_sval
, cd
.get_arg_tree (0),
1651 /* We want str_sval + OFFSET for some unknown OFFSET.
1652 Use a conjured_svalue to represent the offset,
1653 using the str_reg as the id of the conjured_svalue. */
1654 const svalue
*offset
1655 = mgr
->get_or_create_conjured_svalue (size_type_node
,
1656 cd
.get_call_stmt (),
1658 conjured_purge (model
,
1660 result
= mgr
->get_or_create_binop (lhs_type
, POINTER_PLUS_EXPR
,
1664 result
= mgr
->get_or_create_int_cst (lhs_type
, 0);
1665 cd
.maybe_set_lhs (result
);
1673 /* Body of kf_strstr::impl_call_post. */
1676 cd
.get_ctxt ()->bifurcate (make_unique
<strstr_call_info
> (cd
, false));
1677 cd
.get_ctxt ()->bifurcate (make_unique
<strstr_call_info
> (cd
, true));
1678 cd
.get_ctxt ()->terminate_path ();
1682 class kf_ubsan_bounds
: public internal_known_function
1687 /* Handle calls to functions referenced by
1688 __attribute__((malloc(FOO))). */
1691 region_model::impl_deallocation_call (const call_details
&cd
)
1694 kf
.impl_call_post (cd
);
1698 register_atomic_builtins (known_function_manager
&kfm
)
1700 kfm
.add (BUILT_IN_ATOMIC_EXCHANGE
, make_unique
<kf_atomic_exchange
> ());
1701 kfm
.add (BUILT_IN_ATOMIC_EXCHANGE_N
, make_unique
<kf_atomic_exchange_n
> ());
1702 kfm
.add (BUILT_IN_ATOMIC_EXCHANGE_1
, make_unique
<kf_atomic_exchange_n
> ());
1703 kfm
.add (BUILT_IN_ATOMIC_EXCHANGE_2
, make_unique
<kf_atomic_exchange_n
> ());
1704 kfm
.add (BUILT_IN_ATOMIC_EXCHANGE_4
, make_unique
<kf_atomic_exchange_n
> ());
1705 kfm
.add (BUILT_IN_ATOMIC_EXCHANGE_8
, make_unique
<kf_atomic_exchange_n
> ());
1706 kfm
.add (BUILT_IN_ATOMIC_EXCHANGE_16
, make_unique
<kf_atomic_exchange_n
> ());
1707 kfm
.add (BUILT_IN_ATOMIC_LOAD
, make_unique
<kf_atomic_load
> ());
1708 kfm
.add (BUILT_IN_ATOMIC_LOAD_N
, make_unique
<kf_atomic_load_n
> ());
1709 kfm
.add (BUILT_IN_ATOMIC_LOAD_1
, make_unique
<kf_atomic_load_n
> ());
1710 kfm
.add (BUILT_IN_ATOMIC_LOAD_2
, make_unique
<kf_atomic_load_n
> ());
1711 kfm
.add (BUILT_IN_ATOMIC_LOAD_4
, make_unique
<kf_atomic_load_n
> ());
1712 kfm
.add (BUILT_IN_ATOMIC_LOAD_8
, make_unique
<kf_atomic_load_n
> ());
1713 kfm
.add (BUILT_IN_ATOMIC_LOAD_16
, make_unique
<kf_atomic_load_n
> ());
1714 kfm
.add (BUILT_IN_ATOMIC_STORE_N
, make_unique
<kf_atomic_store_n
> ());
1715 kfm
.add (BUILT_IN_ATOMIC_STORE_1
, make_unique
<kf_atomic_store_n
> ());
1716 kfm
.add (BUILT_IN_ATOMIC_STORE_2
, make_unique
<kf_atomic_store_n
> ());
1717 kfm
.add (BUILT_IN_ATOMIC_STORE_4
, make_unique
<kf_atomic_store_n
> ());
1718 kfm
.add (BUILT_IN_ATOMIC_STORE_8
, make_unique
<kf_atomic_store_n
> ());
1719 kfm
.add (BUILT_IN_ATOMIC_STORE_16
, make_unique
<kf_atomic_store_n
> ());
1720 kfm
.add (BUILT_IN_ATOMIC_ADD_FETCH_1
,
1721 make_unique
<kf_atomic_op_fetch
> (PLUS_EXPR
));
1722 kfm
.add (BUILT_IN_ATOMIC_ADD_FETCH_2
,
1723 make_unique
<kf_atomic_op_fetch
> (PLUS_EXPR
));
1724 kfm
.add (BUILT_IN_ATOMIC_ADD_FETCH_4
,
1725 make_unique
<kf_atomic_op_fetch
> (PLUS_EXPR
));
1726 kfm
.add (BUILT_IN_ATOMIC_ADD_FETCH_8
,
1727 make_unique
<kf_atomic_op_fetch
> (PLUS_EXPR
));
1728 kfm
.add (BUILT_IN_ATOMIC_ADD_FETCH_16
,
1729 make_unique
<kf_atomic_op_fetch
> (PLUS_EXPR
));
1730 kfm
.add (BUILT_IN_ATOMIC_SUB_FETCH_1
,
1731 make_unique
<kf_atomic_op_fetch
> (MINUS_EXPR
));
1732 kfm
.add (BUILT_IN_ATOMIC_SUB_FETCH_2
,
1733 make_unique
<kf_atomic_op_fetch
> (MINUS_EXPR
));
1734 kfm
.add (BUILT_IN_ATOMIC_SUB_FETCH_4
,
1735 make_unique
<kf_atomic_op_fetch
> (MINUS_EXPR
));
1736 kfm
.add (BUILT_IN_ATOMIC_SUB_FETCH_8
,
1737 make_unique
<kf_atomic_op_fetch
> (MINUS_EXPR
));
1738 kfm
.add (BUILT_IN_ATOMIC_SUB_FETCH_16
,
1739 make_unique
<kf_atomic_op_fetch
> (MINUS_EXPR
));
1740 kfm
.add (BUILT_IN_ATOMIC_AND_FETCH_1
,
1741 make_unique
<kf_atomic_op_fetch
> (BIT_AND_EXPR
));
1742 kfm
.add (BUILT_IN_ATOMIC_AND_FETCH_2
,
1743 make_unique
<kf_atomic_op_fetch
> (BIT_AND_EXPR
));
1744 kfm
.add (BUILT_IN_ATOMIC_AND_FETCH_4
,
1745 make_unique
<kf_atomic_op_fetch
> (BIT_AND_EXPR
));
1746 kfm
.add (BUILT_IN_ATOMIC_AND_FETCH_8
,
1747 make_unique
<kf_atomic_op_fetch
> (BIT_AND_EXPR
));
1748 kfm
.add (BUILT_IN_ATOMIC_AND_FETCH_16
,
1749 make_unique
<kf_atomic_op_fetch
> (BIT_AND_EXPR
));
1750 kfm
.add (BUILT_IN_ATOMIC_XOR_FETCH_1
,
1751 make_unique
<kf_atomic_op_fetch
> (BIT_XOR_EXPR
));
1752 kfm
.add (BUILT_IN_ATOMIC_XOR_FETCH_2
,
1753 make_unique
<kf_atomic_op_fetch
> (BIT_XOR_EXPR
));
1754 kfm
.add (BUILT_IN_ATOMIC_XOR_FETCH_4
,
1755 make_unique
<kf_atomic_op_fetch
> (BIT_XOR_EXPR
));
1756 kfm
.add (BUILT_IN_ATOMIC_XOR_FETCH_8
,
1757 make_unique
<kf_atomic_op_fetch
> (BIT_XOR_EXPR
));
1758 kfm
.add (BUILT_IN_ATOMIC_XOR_FETCH_16
,
1759 make_unique
<kf_atomic_op_fetch
> (BIT_XOR_EXPR
));
1760 kfm
.add (BUILT_IN_ATOMIC_OR_FETCH_1
,
1761 make_unique
<kf_atomic_op_fetch
> (BIT_IOR_EXPR
));
1762 kfm
.add (BUILT_IN_ATOMIC_OR_FETCH_2
,
1763 make_unique
<kf_atomic_op_fetch
> (BIT_IOR_EXPR
));
1764 kfm
.add (BUILT_IN_ATOMIC_OR_FETCH_4
,
1765 make_unique
<kf_atomic_op_fetch
> (BIT_IOR_EXPR
));
1766 kfm
.add (BUILT_IN_ATOMIC_OR_FETCH_8
,
1767 make_unique
<kf_atomic_op_fetch
> (BIT_IOR_EXPR
));
1768 kfm
.add (BUILT_IN_ATOMIC_OR_FETCH_16
,
1769 make_unique
<kf_atomic_op_fetch
> (BIT_IOR_EXPR
));
1770 kfm
.add (BUILT_IN_ATOMIC_FETCH_ADD_1
,
1771 make_unique
<kf_atomic_fetch_op
> (PLUS_EXPR
));
1772 kfm
.add (BUILT_IN_ATOMIC_FETCH_ADD_2
,
1773 make_unique
<kf_atomic_fetch_op
> (PLUS_EXPR
));
1774 kfm
.add (BUILT_IN_ATOMIC_FETCH_ADD_4
,
1775 make_unique
<kf_atomic_fetch_op
> (PLUS_EXPR
));
1776 kfm
.add (BUILT_IN_ATOMIC_FETCH_ADD_8
,
1777 make_unique
<kf_atomic_fetch_op
> (PLUS_EXPR
));
1778 kfm
.add (BUILT_IN_ATOMIC_FETCH_ADD_16
,
1779 make_unique
<kf_atomic_fetch_op
> (PLUS_EXPR
));
1780 kfm
.add (BUILT_IN_ATOMIC_FETCH_SUB_1
,
1781 make_unique
<kf_atomic_fetch_op
> (MINUS_EXPR
));
1782 kfm
.add (BUILT_IN_ATOMIC_FETCH_SUB_2
,
1783 make_unique
<kf_atomic_fetch_op
> (MINUS_EXPR
));
1784 kfm
.add (BUILT_IN_ATOMIC_FETCH_SUB_4
,
1785 make_unique
<kf_atomic_fetch_op
> (MINUS_EXPR
));
1786 kfm
.add (BUILT_IN_ATOMIC_FETCH_SUB_8
,
1787 make_unique
<kf_atomic_fetch_op
> (MINUS_EXPR
));
1788 kfm
.add (BUILT_IN_ATOMIC_FETCH_SUB_16
,
1789 make_unique
<kf_atomic_fetch_op
> (MINUS_EXPR
));
1790 kfm
.add (BUILT_IN_ATOMIC_FETCH_AND_1
,
1791 make_unique
<kf_atomic_fetch_op
> (BIT_AND_EXPR
));
1792 kfm
.add (BUILT_IN_ATOMIC_FETCH_AND_2
,
1793 make_unique
<kf_atomic_fetch_op
> (BIT_AND_EXPR
));
1794 kfm
.add (BUILT_IN_ATOMIC_FETCH_AND_4
,
1795 make_unique
<kf_atomic_fetch_op
> (BIT_AND_EXPR
));
1796 kfm
.add (BUILT_IN_ATOMIC_FETCH_AND_8
,
1797 make_unique
<kf_atomic_fetch_op
> (BIT_AND_EXPR
));
1798 kfm
.add (BUILT_IN_ATOMIC_FETCH_AND_16
,
1799 make_unique
<kf_atomic_fetch_op
> (BIT_AND_EXPR
));
1800 kfm
.add (BUILT_IN_ATOMIC_FETCH_XOR_1
,
1801 make_unique
<kf_atomic_fetch_op
> (BIT_XOR_EXPR
));
1802 kfm
.add (BUILT_IN_ATOMIC_FETCH_XOR_2
,
1803 make_unique
<kf_atomic_fetch_op
> (BIT_XOR_EXPR
));
1804 kfm
.add (BUILT_IN_ATOMIC_FETCH_XOR_4
,
1805 make_unique
<kf_atomic_fetch_op
> (BIT_XOR_EXPR
));
1806 kfm
.add (BUILT_IN_ATOMIC_FETCH_XOR_8
,
1807 make_unique
<kf_atomic_fetch_op
> (BIT_XOR_EXPR
));
1808 kfm
.add (BUILT_IN_ATOMIC_FETCH_XOR_16
,
1809 make_unique
<kf_atomic_fetch_op
> (BIT_XOR_EXPR
));
1810 kfm
.add (BUILT_IN_ATOMIC_FETCH_OR_1
,
1811 make_unique
<kf_atomic_fetch_op
> (BIT_IOR_EXPR
));
1812 kfm
.add (BUILT_IN_ATOMIC_FETCH_OR_2
,
1813 make_unique
<kf_atomic_fetch_op
> (BIT_IOR_EXPR
));
1814 kfm
.add (BUILT_IN_ATOMIC_FETCH_OR_4
,
1815 make_unique
<kf_atomic_fetch_op
> (BIT_IOR_EXPR
));
1816 kfm
.add (BUILT_IN_ATOMIC_FETCH_OR_8
,
1817 make_unique
<kf_atomic_fetch_op
> (BIT_IOR_EXPR
));
1818 kfm
.add (BUILT_IN_ATOMIC_FETCH_OR_16
,
1819 make_unique
<kf_atomic_fetch_op
> (BIT_IOR_EXPR
));
1822 /* Populate KFM with instances of known functions supported by the core of the
1823 analyzer (as opposed to plugins). */
1826 register_known_functions (known_function_manager
&kfm
)
1828 /* Debugging/test support functions, all with a "__analyzer_" prefix. */
1829 register_known_analyzer_functions (kfm
);
1831 /* Internal fns the analyzer has known_functions for. */
1833 kfm
.add (IFN_BUILTIN_EXPECT
, make_unique
<kf_expect
> ());
1834 kfm
.add (IFN_UBSAN_BOUNDS
, make_unique
<kf_ubsan_bounds
> ());
1837 /* GCC built-ins that do not correspond to a function
1838 in the standard library. */
1840 kfm
.add (BUILT_IN_EXPECT
, make_unique
<kf_expect
> ());
1841 kfm
.add (BUILT_IN_EXPECT_WITH_PROBABILITY
, make_unique
<kf_expect
> ());
1842 kfm
.add (BUILT_IN_ALLOCA_WITH_ALIGN
, make_unique
<kf_alloca
> ());
1843 kfm
.add (BUILT_IN_STACK_RESTORE
, make_unique
<kf_stack_restore
> ());
1844 kfm
.add (BUILT_IN_STACK_SAVE
, make_unique
<kf_stack_save
> ());
1846 register_atomic_builtins (kfm
);
1847 register_varargs_builtins (kfm
);
1850 /* Known builtins and C standard library functions
1851 the analyzer has known functions for. */
1853 kfm
.add ("alloca", make_unique
<kf_alloca
> ());
1854 kfm
.add ("__builtin_alloca", make_unique
<kf_alloca
> ());
1855 kfm
.add ("calloc", make_unique
<kf_calloc
> ());
1856 kfm
.add ("__builtin_calloc", make_unique
<kf_calloc
> ());
1857 kfm
.add ("free", make_unique
<kf_free
> ());
1858 kfm
.add ("__builtin_free", make_unique
<kf_free
> ());
1859 kfm
.add ("malloc", make_unique
<kf_malloc
> ());
1860 kfm
.add ("__builtin_malloc", make_unique
<kf_malloc
> ());
1862 make_unique
<kf_memcpy_memmove
> (kf_memcpy_memmove::KF_MEMCPY
));
1863 kfm
.add ("__builtin_memcpy",
1864 make_unique
<kf_memcpy_memmove
> (kf_memcpy_memmove::KF_MEMCPY
));
1865 kfm
.add ("__memcpy_chk", make_unique
<kf_memcpy_memmove
>
1866 (kf_memcpy_memmove::KF_MEMCPY_CHK
));
1867 kfm
.add ("__builtin___memcpy_chk", make_unique
<kf_memcpy_memmove
>
1868 (kf_memcpy_memmove::KF_MEMCPY_CHK
));
1870 make_unique
<kf_memcpy_memmove
> (kf_memcpy_memmove::KF_MEMMOVE
));
1871 kfm
.add ("__builtin_memmove",
1872 make_unique
<kf_memcpy_memmove
> (kf_memcpy_memmove::KF_MEMMOVE
));
1873 kfm
.add ("__memmove_chk", make_unique
<kf_memcpy_memmove
>
1874 (kf_memcpy_memmove::KF_MEMMOVE_CHK
));
1875 kfm
.add ("__builtin___memmove_chk", make_unique
<kf_memcpy_memmove
>
1876 (kf_memcpy_memmove::KF_MEMMOVE_CHK
));
1877 kfm
.add ("memset", make_unique
<kf_memset
> (false));
1878 kfm
.add ("__builtin_memset", make_unique
<kf_memset
> (false));
1879 kfm
.add ("__memset_chk", make_unique
<kf_memset
> (true));
1880 kfm
.add ("__builtin___memset_chk", make_unique
<kf_memset
> (true));
1881 kfm
.add ("realloc", make_unique
<kf_realloc
> ());
1882 kfm
.add ("__builtin_realloc", make_unique
<kf_realloc
> ());
1883 kfm
.add ("sprintf", make_unique
<kf_sprintf
> ());
1884 kfm
.add ("__builtin_sprintf", make_unique
<kf_sprintf
> ());
1885 kfm
.add ("strchr", make_unique
<kf_strchr
> ());
1886 kfm
.add ("__builtin_strchr", make_unique
<kf_strchr
> ());
1887 kfm
.add ("strcpy", make_unique
<kf_strcpy
> (2, false));
1888 kfm
.add ("__builtin_strcpy", make_unique
<kf_strcpy
> (2, false));
1889 kfm
.add ("__strcpy_chk", make_unique
<kf_strcpy
> (3, true));
1890 kfm
.add ("__builtin___strcpy_chk", make_unique
<kf_strcpy
> (3, true));
1891 kfm
.add ("strcat", make_unique
<kf_strcat
> (2, false));
1892 kfm
.add ("__builtin_strcat", make_unique
<kf_strcat
> (2, false));
1893 kfm
.add ("__strcat_chk", make_unique
<kf_strcat
> (3, true));
1894 kfm
.add ("__builtin___strcat_chk", make_unique
<kf_strcat
> (3, true));
1895 kfm
.add ("strdup", make_unique
<kf_strdup
> ());
1896 kfm
.add ("__builtin_strdup", make_unique
<kf_strdup
> ());
1897 kfm
.add ("strncpy", make_unique
<kf_strncpy
> ());
1898 kfm
.add ("__builtin_strncpy", make_unique
<kf_strncpy
> ());
1899 kfm
.add ("strndup", make_unique
<kf_strndup
> ());
1900 kfm
.add ("__builtin_strndup", make_unique
<kf_strndup
> ());
1901 kfm
.add ("strlen", make_unique
<kf_strlen
> ());
1902 kfm
.add ("__builtin_strlen", make_unique
<kf_strlen
> ());
1903 kfm
.add ("strstr", make_unique
<kf_strstr
> ());
1904 kfm
.add ("__builtin_strstr", make_unique
<kf_strstr
> ());
1906 register_atomic_builtins (kfm
);
1907 register_varargs_builtins (kfm
);
1910 /* Known POSIX functions, and some non-standard extensions. */
1912 kfm
.add ("fopen", make_unique
<kf_fopen
> ());
1913 kfm
.add ("putenv", make_unique
<kf_putenv
> ());
1915 register_known_fd_functions (kfm
);
1916 register_known_file_functions (kfm
);
1919 /* glibc functions. */
1921 kfm
.add ("__errno_location", make_unique
<kf_errno_location
> ());
1922 kfm
.add ("error", make_unique
<kf_error
> (3));
1923 kfm
.add ("error_at_line", make_unique
<kf_error
> (5));
1926 /* Other implementations of C standard library. */
1928 /* According to PR 107807 comment #2, Solaris implements "errno"
1930 extern int *___errno(void) __attribute__((__const__));
1931 #define errno (*(___errno()))
1932 and macOS like this:
1933 extern int * __error(void);
1934 #define errno (*__error())
1935 and similarly __errno for newlib.
1936 Add these as synonyms for "__errno_location". */
1937 kfm
.add ("___errno", make_unique
<kf_errno_location
> ());
1938 kfm
.add ("__error", make_unique
<kf_errno_location
> ());
1939 kfm
.add ("__errno", make_unique
<kf_errno_location
> ());
1942 /* Language-specific support functions. */
1943 register_known_functions_lang_cp (kfm
);
1948 #endif /* #if ENABLE_ANALYZER */