1/* Handling for the known behavior of various specific functions.
2 Copyright (C) 2020-2024 Free Software Foundation, Inc.
3 Contributed by David Malcolm <dmalcolm@redhat.com>.
4
5This file is part of GCC.
6
7GCC is free software; you can redistribute it and/or modify it
8under the terms of the GNU General Public License as published by
9the Free Software Foundation; either version 3, or (at your option)
10any later version.
11
12GCC is distributed in the hope that it will be useful, but
13WITHOUT ANY WARRANTY; without even the implied warranty of
14MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15General Public License for more details.
16
17You should have received a copy of the GNU General Public License
18along with GCC; see the file COPYING3. If not see
19<http://www.gnu.org/licenses/>. */
20
21#include "config.h"
22#define INCLUDE_MEMORY
23#include "system.h"
24#include "coretypes.h"
25#include "tree.h"
26#include "function.h"
27#include "basic-block.h"
28#include "gimple.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"
38
39#if ENABLE_ANALYZER
40
41namespace ana {
42
43/* Abstract subclass for describing undefined behavior of an API. */
44
45class undefined_function_behavior
46 : public pending_diagnostic_subclass<undefined_function_behavior>
47{
48public:
49 undefined_function_behavior (const call_details &cd)
50 : m_call_stmt (cd.get_call_stmt ()),
51 m_callee_fndecl (cd.get_fndecl_for_call ())
52 {
53 gcc_assert (m_call_stmt);
54 gcc_assert (m_callee_fndecl);
55 }
56
57 const char *get_kind () const final override
58 {
59 return "undefined_behavior";
60 }
61
62 bool operator== (const undefined_function_behavior &other) const
63 {
64 return (m_call_stmt == other.m_call_stmt
65 && m_callee_fndecl == other.m_callee_fndecl);
66 }
67
68 bool terminate_path_p () const final override { return true; }
69
70 tree get_callee_fndecl () const { return m_callee_fndecl; }
71
72private:
73 const gimple *m_call_stmt;
74 tree m_callee_fndecl;
75};
76
77/* class pure_known_function_with_default_return : public known_function. */
78
79void
80pure_known_function_with_default_return::
81impl_call_pre (const call_details &cd) const
82{
83 cd.set_any_lhs_with_defaults ();
84}
85
86/* Implementations of specific functions. */
87
88/* Handler for "alloca". */
89
90class kf_alloca : public builtin_known_function
91{
92public:
93 bool matches_call_types_p (const call_details &cd) const final override
94 {
95 return cd.num_args () == 1;
96 }
97 enum built_in_function builtin_code () const final override
98 {
99 return BUILT_IN_ALLOCA;
100 }
101 void impl_call_pre (const call_details &cd) const final override;
102};
103
104void
105kf_alloca::impl_call_pre (const call_details &cd) const
106{
107 const svalue *size_sval = cd.get_arg_svalue (idx: 0);
108
109 region_model *model = cd.get_model ();
110 region_model_manager *mgr = cd.get_manager ();
111
112 const region *new_reg
113 = model->create_region_for_alloca (size_in_bytes: size_sval, ctxt: cd.get_ctxt ());
114 const svalue *ptr_sval
115 = mgr->get_ptr_svalue (ptr_type: cd.get_lhs_type (), pointee: new_reg);
116 cd.maybe_set_lhs (result: ptr_sval);
117}
118
119/* Handler for __atomic_exchange.
120 Although the user-facing documentation specifies it as having this
121 signature:
122 void __atomic_exchange (type *ptr, type *val, type *ret, int memorder)
123
124 by the time the C/C++ frontends have acted on it, any calls that
125 can't be mapped to a _N variation end up with this signature:
126
127 void
128 __atomic_exchange (size_t sz, void *ptr, void *val, void *ret,
129 int memorder)
130
131 as seen in the gimple seen by the analyzer, and as specified
132 in sync-builtins.def. */
133
134class kf_atomic_exchange : public internal_known_function
135{
136public:
137 /* This is effectively:
138 tmpA = *PTR;
139 tmpB = *VAL;
140 *PTR = tmpB;
141 *RET = tmpA;
142 */
143 void impl_call_pre (const call_details &cd) const final override
144 {
145 const svalue *num_bytes_sval = cd.get_arg_svalue (idx: 0);
146 const svalue *ptr_sval = cd.get_arg_svalue (idx: 1);
147 tree ptr_tree = cd.get_arg_tree (idx: 1);
148 const svalue *val_sval = cd.get_arg_svalue (idx: 2);
149 tree val_tree = cd.get_arg_tree (idx: 2);
150 const svalue *ret_sval = cd.get_arg_svalue (idx: 3);
151 tree ret_tree = cd.get_arg_tree (idx: 3);
152 /* Ignore the memorder param. */
153
154 region_model *model = cd.get_model ();
155 region_model_context *ctxt = cd.get_ctxt ();
156
157 const region *ptr_reg = model->deref_rvalue (ptr_sval, ptr_tree, ctxt);
158 const region *val_reg = model->deref_rvalue (ptr_sval: val_sval, ptr_tree: val_tree, ctxt);
159 const region *ret_reg = model->deref_rvalue (ptr_sval: ret_sval, ptr_tree: ret_tree, ctxt);
160
161 const svalue *tmp_a_sval
162 = model->read_bytes (src_reg: ptr_reg, src_ptr_expr: ptr_tree, num_bytes_sval, ctxt);
163 const svalue *tmp_b_sval
164 = model->read_bytes (src_reg: val_reg, src_ptr_expr: val_tree, num_bytes_sval, ctxt);
165 model->write_bytes (dest_reg: ptr_reg, num_bytes_sval, sval: tmp_b_sval, ctxt);
166 model->write_bytes (dest_reg: ret_reg, num_bytes_sval, sval: tmp_a_sval, ctxt);
167 }
168};
169
170/* Handler for:
171 __atomic_exchange_n (type *ptr, type val, int memorder). */
172
173class kf_atomic_exchange_n : public internal_known_function
174{
175public:
176 /* This is effectively:
177 RET = *PTR;
178 *PTR = VAL;
179 return RET;
180 */
181 void impl_call_pre (const call_details &cd) const final override
182 {
183 const svalue *ptr_sval = cd.get_arg_svalue (idx: 0);
184 tree ptr_tree = cd.get_arg_tree (idx: 0);
185 const svalue *set_sval = cd.get_arg_svalue (idx: 1);
186 /* Ignore the memorder param. */
187
188 region_model *model = cd.get_model ();
189 region_model_context *ctxt = cd.get_ctxt ();
190
191 const region *dst_region = model->deref_rvalue (ptr_sval, ptr_tree, ctxt);
192 const svalue *ret_sval = model->get_store_value (reg: dst_region, ctxt);
193 model->set_value (lhs_reg: dst_region, rhs_sval: set_sval, ctxt);
194 cd.maybe_set_lhs (result: ret_sval);
195 }
196};
197
198/* Handler for:
199 type __atomic_fetch_add (type *ptr, type val, int memorder);
200 type __atomic_fetch_sub (type *ptr, type val, int memorder);
201 type __atomic_fetch_and (type *ptr, type val, int memorder);
202 type __atomic_fetch_xor (type *ptr, type val, int memorder);
203 type __atomic_fetch_or (type *ptr, type val, int memorder);
204*/
205
206class kf_atomic_fetch_op : public internal_known_function
207{
208public:
209 kf_atomic_fetch_op (enum tree_code op): m_op (op) {}
210
211 /* This is effectively:
212 RET = *PTR;
213 *PTR = RET OP VAL;
214 return RET;
215 */
216 void impl_call_pre (const call_details &cd) const final override
217 {
218 const svalue *ptr_sval = cd.get_arg_svalue (idx: 0);
219 tree ptr_tree = cd.get_arg_tree (idx: 0);
220 const svalue *val_sval = cd.get_arg_svalue (idx: 1);
221 /* Ignore the memorder param. */
222
223 region_model *model = cd.get_model ();
224 region_model_manager *mgr = cd.get_manager ();
225 region_model_context *ctxt = cd.get_ctxt ();
226
227 const region *star_ptr_region
228 = model->deref_rvalue (ptr_sval, ptr_tree, ctxt);
229 const svalue *old_sval = model->get_store_value (reg: star_ptr_region, ctxt);
230 const svalue *new_sval = mgr->get_or_create_binop (type: old_sval->get_type (),
231 op: m_op,
232 arg0: old_sval, arg1: val_sval);
233 model->set_value (lhs_reg: star_ptr_region, rhs_sval: new_sval, ctxt);
234 cd.maybe_set_lhs (result: old_sval);
235 }
236
237private:
238 enum tree_code m_op;
239};
240
241/* Handler for:
242 type __atomic_add_fetch (type *ptr, type val, int memorder);
243 type __atomic_sub_fetch (type *ptr, type val, int memorder);
244 type __atomic_and_fetch (type *ptr, type val, int memorder);
245 type __atomic_xor_fetch (type *ptr, type val, int memorder);
246 type __atomic_or_fetch (type *ptr, type val, int memorder);
247*/
248
249class kf_atomic_op_fetch : public internal_known_function
250{
251public:
252 kf_atomic_op_fetch (enum tree_code op): m_op (op) {}
253
254 /* This is effectively:
255 *PTR = RET OP VAL;
256 return *PTR;
257 */
258 void impl_call_pre (const call_details &cd) const final override
259 {
260 const svalue *ptr_sval = cd.get_arg_svalue (idx: 0);
261 tree ptr_tree = cd.get_arg_tree (idx: 0);
262 const svalue *val_sval = cd.get_arg_svalue (idx: 1);
263 /* Ignore the memorder param. */
264
265 region_model *model = cd.get_model ();
266 region_model_manager *mgr = cd.get_manager ();
267 region_model_context *ctxt = cd.get_ctxt ();
268
269 const region *star_ptr_region
270 = model->deref_rvalue (ptr_sval, ptr_tree, ctxt);
271 const svalue *old_sval = model->get_store_value (reg: star_ptr_region, ctxt);
272 const svalue *new_sval = mgr->get_or_create_binop (type: old_sval->get_type (),
273 op: m_op,
274 arg0: old_sval, arg1: val_sval);
275 model->set_value (lhs_reg: star_ptr_region, rhs_sval: new_sval, ctxt);
276 cd.maybe_set_lhs (result: new_sval);
277 }
278
279private:
280 enum tree_code m_op;
281};
282
283/* Handler for __atomic_load.
284 Although the user-facing documentation specifies it as having this
285 signature:
286
287 void __atomic_load (type *ptr, type *ret, int memorder)
288
289 by the time the C/C++ frontends have acted on it, any calls that
290 can't be mapped to a _N variation end up with this signature:
291
292 void __atomic_load (size_t sz, const void *src, void *dst, int memorder);
293
294 as seen in the gimple seen by the analyzer, and as specified
295 in sync-builtins.def. */
296
297class kf_atomic_load : public internal_known_function
298{
299public:
300 /* This is effectively:
301 memmove (dst, src, sz);
302 */
303 void impl_call_pre (const call_details &cd) const final override
304 {
305 const svalue *num_bytes_sval = cd.get_arg_svalue (idx: 0);
306 const svalue *src_sval = cd.get_arg_svalue (idx: 1);
307 tree src_tree = cd.get_arg_tree (idx: 1);
308 const svalue *dst_sval = cd.get_arg_svalue (idx: 2);
309 tree dst_tree = cd.get_arg_tree (idx: 2);
310 /* Ignore the memorder param. */
311
312 region_model *model = cd.get_model ();
313 region_model_context *ctxt = cd.get_ctxt ();
314
315 const region *dst_reg = model->deref_rvalue (ptr_sval: dst_sval, ptr_tree: dst_tree, ctxt);
316 const region *src_reg = model->deref_rvalue (ptr_sval: src_sval, ptr_tree: src_tree, ctxt);
317
318 const svalue *data_sval
319 = model->read_bytes (src_reg, src_ptr_expr: src_tree, num_bytes_sval, ctxt);
320 model->write_bytes (dest_reg: dst_reg, num_bytes_sval, sval: data_sval, ctxt);
321 }
322};
323
324/* Handler for __atomic_store.
325 Although the user-facing documentation specifies it as having this
326 signature:
327
328 void __atomic_store (type *ptr, type *val, int memorder)
329
330 by the time the C/C++ frontends have acted on it, any calls that
331 can't be mapped to a _N variation end up with this signature:
332
333 void __atomic_store (size_t sz, type *dst, type *src, int memorder)
334
335 as seen in the gimple seen by the analyzer, and as specified
336 in sync-builtins.def. */
337
338class kf_atomic_store : public internal_known_function
339{
340public:
341 /* This is effectively:
342 memmove (dst, src, sz);
343 */
344 void impl_call_pre (const call_details &cd) const final override
345 {
346 const svalue *num_bytes_sval = cd.get_arg_svalue (idx: 0);
347 const svalue *dst_sval = cd.get_arg_svalue (idx: 1);
348 tree dst_tree = cd.get_arg_tree (idx: 1);
349 const svalue *src_sval = cd.get_arg_svalue (idx: 2);
350 tree src_tree = cd.get_arg_tree (idx: 2);
351 /* Ignore the memorder param. */
352
353 region_model *model = cd.get_model ();
354 region_model_context *ctxt = cd.get_ctxt ();
355
356 const region *dst_reg = model->deref_rvalue (ptr_sval: dst_sval, ptr_tree: dst_tree, ctxt);
357 const region *src_reg = model->deref_rvalue (ptr_sval: src_sval, ptr_tree: src_tree, ctxt);
358
359 const svalue *data_sval
360 = model->read_bytes (src_reg, src_ptr_expr: src_tree, num_bytes_sval, ctxt);
361 model->write_bytes (dest_reg: dst_reg, num_bytes_sval, sval: data_sval, ctxt);
362 }
363};
364
365/* Handler for:
366 type __atomic_load_n (type *ptr, int memorder) */
367
368class kf_atomic_load_n : public internal_known_function
369{
370public:
371 /* This is effectively:
372 RET = *PTR;
373 return RET;
374 */
375 void impl_call_pre (const call_details &cd) const final override
376 {
377 const svalue *ptr_ptr_sval = cd.get_arg_svalue (idx: 0);
378 tree ptr_ptr_tree = cd.get_arg_tree (idx: 0);
379 /* Ignore the memorder param. */
380
381 region_model *model = cd.get_model ();
382 region_model_context *ctxt = cd.get_ctxt ();
383
384 const region *ptr_region
385 = model->deref_rvalue (ptr_sval: ptr_ptr_sval, ptr_tree: ptr_ptr_tree, ctxt);
386 const svalue *star_ptr_sval = model->get_store_value (reg: ptr_region, ctxt);
387 cd.maybe_set_lhs (result: star_ptr_sval);
388 }
389};
390
391/* Handler for:
392 void __atomic_store_n (type *ptr, type val, int memorder) */
393
394class kf_atomic_store_n : public internal_known_function
395{
396public:
397 /* This is effectively:
398 *PTR = VAL;
399 */
400 void impl_call_pre (const call_details &cd) const final override
401 {
402 const svalue *ptr_sval = cd.get_arg_svalue (idx: 0);
403 tree ptr_tree = cd.get_arg_tree (idx: 0);
404 const svalue *new_sval = cd.get_arg_svalue (idx: 1);
405 /* Ignore the memorder param. */
406
407 region_model *model = cd.get_model ();
408 region_model_context *ctxt = cd.get_ctxt ();
409
410 const region *star_ptr_region
411 = model->deref_rvalue (ptr_sval, ptr_tree, ctxt);
412 model->set_value (lhs_reg: star_ptr_region, rhs_sval: new_sval, ctxt);
413 }
414};
415
416/* Handler for "__builtin_expect" etc. */
417
418class kf_expect : public internal_known_function
419{
420public:
421 void impl_call_pre (const call_details &cd) const final override
422 {
423 /* __builtin_expect's return value is its initial argument. */
424 const svalue *sval = cd.get_arg_svalue (idx: 0);
425 cd.maybe_set_lhs (result: sval);
426 }
427};
428
429/* Handler for "calloc". */
430
431class kf_calloc : public builtin_known_function
432{
433public:
434 bool matches_call_types_p (const call_details &cd) const final override
435 {
436 return (cd.num_args () == 2
437 && cd.arg_is_size_p (idx: 0)
438 && cd.arg_is_size_p (idx: 1));
439 }
440 enum built_in_function builtin_code () const final override
441 {
442 return BUILT_IN_CALLOC;
443 }
444
445 void impl_call_pre (const call_details &cd) const final override;
446};
447
448void
449kf_calloc::impl_call_pre (const call_details &cd) const
450{
451 region_model *model = cd.get_model ();
452 region_model_manager *mgr = cd.get_manager ();
453 const svalue *nmemb_sval = cd.get_arg_svalue (idx: 0);
454 const svalue *size_sval = cd.get_arg_svalue (idx: 1);
455 /* TODO: check for overflow here? */
456 const svalue *prod_sval
457 = mgr->get_or_create_binop (size_type_node, op: MULT_EXPR,
458 arg0: nmemb_sval, arg1: size_sval);
459 const region *new_reg
460 = model->get_or_create_region_for_heap_alloc (size_in_bytes: prod_sval, ctxt: cd.get_ctxt ());
461 const region *sized_reg
462 = mgr->get_sized_region (parent: new_reg, NULL_TREE, byte_size_sval: prod_sval);
463 model->zero_fill_region (reg: sized_reg, ctxt: cd.get_ctxt ());
464 if (cd.get_lhs_type ())
465 {
466 const svalue *ptr_sval
467 = mgr->get_ptr_svalue (ptr_type: cd.get_lhs_type (), pointee: new_reg);
468 cd.maybe_set_lhs (result: ptr_sval);
469 }
470}
471
472/* Handler for glibc's "__errno_location". */
473
474class kf_errno_location : public known_function
475{
476public:
477 bool matches_call_types_p (const call_details &cd) const final override
478 {
479 return cd.num_args () == 0;
480 }
481
482 void impl_call_pre (const call_details &cd) const final override
483 {
484 if (cd.get_lhs_region ())
485 {
486 region_model_manager *mgr = cd.get_manager ();
487 const region *errno_reg = mgr->get_errno_region ();
488 const svalue *errno_ptr = mgr->get_ptr_svalue (ptr_type: cd.get_lhs_type (),
489 pointee: errno_reg);
490 cd.maybe_set_lhs (result: errno_ptr);
491 }
492 }
493};
494
495/* Handler for "error" and "error_at_line" from GNU's non-standard <error.h>.
496 MIN_ARGS identifies the minimum number of expected arguments
497 to be consistent with such a call (3 and 5 respectively). */
498
499class kf_error : public known_function
500{
501public:
502 kf_error (unsigned min_args) : m_min_args (min_args) {}
503
504 bool matches_call_types_p (const call_details &cd) const final override
505 {
506 return (cd.num_args () >= m_min_args
507 && cd.get_arg_type (idx: 0) == integer_type_node);
508 }
509
510 void impl_call_pre (const call_details &cd) const final override;
511
512private:
513 unsigned m_min_args;
514};
515
516void
517kf_error::impl_call_pre (const call_details &cd) const
518{
519 /* The process exits if status != 0, so it only continues
520 for the case where status == 0.
521 Add that constraint, or terminate this analysis path. */
522 tree status = cd.get_arg_tree (idx: 0);
523 region_model_context *ctxt = cd.get_ctxt ();
524 region_model *model = cd.get_model ();
525 if (!model->add_constraint (lhs: status, op: EQ_EXPR, integer_zero_node, ctxt))
526 if (ctxt)
527 ctxt->terminate_path ();
528
529 /* Check "format" arg. */
530 const int fmt_arg_idx = (m_min_args == 3) ? 2 : 4;
531 model->check_for_null_terminated_string_arg (cd, idx: fmt_arg_idx);
532}
533
534/* Handler for fopen.
535 FILE *fopen (const char *filename, const char *mode);
536 See e.g. https://en.cppreference.com/w/c/io/fopen
537 https://www.man7.org/linux/man-pages/man3/fopen.3.html
538 https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/fopen-wfopen?view=msvc-170 */
539
540class kf_fopen : public known_function
541{
542public:
543 bool matches_call_types_p (const call_details &cd) const final override
544 {
545 return (cd.num_args () == 2
546 && cd.arg_is_pointer_p (idx: 0)
547 && cd.arg_is_pointer_p (idx: 1));
548 }
549
550 void impl_call_pre (const call_details &cd) const final override
551 {
552 cd.check_for_null_terminated_string_arg (arg_idx: 0);
553 cd.check_for_null_terminated_string_arg (arg_idx: 1);
554 cd.set_any_lhs_with_defaults ();
555
556 /* fopen's mode param is effectively a mini-DSL, but there are various
557 non-standard extensions, so we don't bother to check it. */
558 }
559};
560
561/* Handler for "free", after sm-handling.
562
563 If the ptr points to an underlying heap region, delete the region,
564 poisoning pointers to it and regions within it.
565
566 We delay this until after sm-state has been updated so that the
567 sm-handling can transition all of the various casts of the pointer
568 to a "freed" state *before* we delete the related region here.
569
570 This has to be done here so that the sm-handling can use the fact
571 that they point to the same region to establish that they are equal
572 (in region_model::eval_condition), and thus transition
573 all pointers to the region to the "freed" state together, regardless
574 of casts. */
575
576class kf_free : public builtin_known_function
577{
578public:
579 bool matches_call_types_p (const call_details &cd) const final override
580 {
581 return (cd.num_args () == 1 && cd.arg_is_pointer_p (idx: 0));
582 }
583 enum built_in_function builtin_code () const final override
584 {
585 return BUILT_IN_FREE;
586 }
587 void impl_call_post (const call_details &cd) const final override;
588};
589
590void
591kf_free::impl_call_post (const call_details &cd) const
592{
593 const svalue *ptr_sval = cd.get_arg_svalue (idx: 0);
594 if (const region *freed_reg = ptr_sval->maybe_get_region ())
595 {
596 /* If the ptr points to an underlying heap region, delete it,
597 poisoning pointers. */
598 region_model *model = cd.get_model ();
599 model->unbind_region_and_descendents (reg: freed_reg, pkind: POISON_KIND_FREED);
600 model->unset_dynamic_extents (reg: freed_reg);
601 }
602}
603
604/* Handle the on_call_pre part of "malloc". */
605
606class kf_malloc : public builtin_known_function
607{
608public:
609 bool matches_call_types_p (const call_details &cd) const final override
610 {
611 return (cd.num_args () == 1
612 && cd.arg_is_size_p (idx: 0));
613 }
614 enum built_in_function builtin_code () const final override
615 {
616 return BUILT_IN_MALLOC;
617 }
618 void impl_call_pre (const call_details &cd) const final override;
619};
620
621void
622kf_malloc::impl_call_pre (const call_details &cd) const
623{
624 region_model *model = cd.get_model ();
625 region_model_manager *mgr = cd.get_manager ();
626 const svalue *size_sval = cd.get_arg_svalue (idx: 0);
627 const region *new_reg
628 = model->get_or_create_region_for_heap_alloc (size_in_bytes: size_sval, ctxt: cd.get_ctxt ());
629 if (cd.get_lhs_type ())
630 {
631 const svalue *ptr_sval
632 = mgr->get_ptr_svalue (ptr_type: cd.get_lhs_type (), pointee: new_reg);
633 cd.maybe_set_lhs (result: ptr_sval);
634 }
635}
636
637/* Handler for "memcpy" and "__builtin_memcpy",
638 "memmove", and "__builtin_memmove". */
639
640class kf_memcpy_memmove : public builtin_known_function
641{
642public:
643 enum kf_memcpy_memmove_variant
644 {
645 KF_MEMCPY,
646 KF_MEMCPY_CHK,
647 KF_MEMMOVE,
648 KF_MEMMOVE_CHK,
649 };
650 kf_memcpy_memmove (enum kf_memcpy_memmove_variant variant)
651 : m_variant (variant) {};
652 bool matches_call_types_p (const call_details &cd) const final override
653 {
654 return (cd.num_args () == 3
655 && cd.arg_is_pointer_p (idx: 0)
656 && cd.arg_is_pointer_p (idx: 1)
657 && cd.arg_is_size_p (idx: 2));
658 }
659 enum built_in_function builtin_code () const final override
660 {
661 switch (m_variant)
662 {
663 case KF_MEMCPY:
664 return BUILT_IN_MEMCPY;
665 case KF_MEMCPY_CHK:
666 return BUILT_IN_MEMCPY_CHK;
667 case KF_MEMMOVE:
668 return BUILT_IN_MEMMOVE;
669 case KF_MEMMOVE_CHK:
670 return BUILT_IN_MEMMOVE_CHK;
671 default:
672 gcc_unreachable ();
673 }
674 }
675 void impl_call_pre (const call_details &cd) const final override;
676private:
677 const enum kf_memcpy_memmove_variant m_variant;
678};
679
680void
681kf_memcpy_memmove::impl_call_pre (const call_details &cd) const
682{
683 const svalue *dest_ptr_sval = cd.get_arg_svalue (idx: 0);
684 const svalue *src_ptr_sval = cd.get_arg_svalue (idx: 1);
685 const svalue *num_bytes_sval = cd.get_arg_svalue (idx: 2);
686
687 region_model *model = cd.get_model ();
688
689 const region *dest_reg
690 = model->deref_rvalue (ptr_sval: dest_ptr_sval, ptr_tree: cd.get_arg_tree (idx: 0), ctxt: cd.get_ctxt ());
691 const region *src_reg
692 = model->deref_rvalue (ptr_sval: src_ptr_sval, ptr_tree: cd.get_arg_tree (idx: 1), ctxt: cd.get_ctxt ());
693
694 cd.maybe_set_lhs (result: dest_ptr_sval);
695 /* Check for overlap. */
696 switch (m_variant)
697 {
698 case KF_MEMCPY:
699 case KF_MEMCPY_CHK:
700 cd.complain_about_overlap (arg_idx_a: 0, arg_idx_b: 1, num_bytes_read_sval: num_bytes_sval);
701 break;
702
703 case KF_MEMMOVE:
704 case KF_MEMMOVE_CHK:
705 /* It's OK for memmove's arguments to overlap. */
706 break;
707
708 default:
709 gcc_unreachable ();
710 }
711 model->copy_bytes (dest_reg,
712 src_reg, src_ptr_expr: cd.get_arg_tree (idx: 1),
713 num_bytes_sval,
714 ctxt: cd.get_ctxt ());
715}
716
717/* Handler for "memset" and "__builtin_memset". */
718
719class kf_memset : public builtin_known_function
720{
721public:
722 kf_memset (bool chk_variant) : m_chk_variant (chk_variant) {}
723 bool matches_call_types_p (const call_details &cd) const final override
724 {
725 return (cd.num_args () == 3 && cd.arg_is_pointer_p (idx: 0));
726 }
727 enum built_in_function builtin_code () const final override
728 {
729 return m_chk_variant ? BUILT_IN_MEMSET_CHK : BUILT_IN_MEMSET;
730 }
731 void impl_call_pre (const call_details &cd) const final override;
732private:
733 const bool m_chk_variant;
734};
735
736void
737kf_memset::impl_call_pre (const call_details &cd) const
738{
739 const svalue *dest_sval = cd.get_arg_svalue (idx: 0);
740 const svalue *fill_value_sval = cd.get_arg_svalue (idx: 1);
741 const svalue *num_bytes_sval = cd.get_arg_svalue (idx: 2);
742
743 region_model *model = cd.get_model ();
744 region_model_manager *mgr = cd.get_manager ();
745
746 const region *dest_reg
747 = model->deref_rvalue (ptr_sval: dest_sval, ptr_tree: cd.get_arg_tree (idx: 0), ctxt: cd.get_ctxt ());
748
749 const svalue *fill_value_u8
750 = mgr->get_or_create_cast (unsigned_char_type_node, arg: fill_value_sval);
751
752 const region *sized_dest_reg = mgr->get_sized_region (parent: dest_reg,
753 NULL_TREE,
754 byte_size_sval: num_bytes_sval);
755 model->fill_region (reg: sized_dest_reg, sval: fill_value_u8, ctxt: cd.get_ctxt ());
756
757 cd.maybe_set_lhs (result: dest_sval);
758}
759
760/* A subclass of pending_diagnostic for complaining about 'putenv'
761 called on an auto var. */
762
763class putenv_of_auto_var
764: public pending_diagnostic_subclass<putenv_of_auto_var>
765{
766public:
767 putenv_of_auto_var (tree fndecl, const region *reg)
768 : m_fndecl (fndecl), m_reg (reg),
769 m_var_decl (reg->get_base_region ()->maybe_get_decl ())
770 {
771 }
772
773 const char *get_kind () const final override
774 {
775 return "putenv_of_auto_var";
776 }
777
778 bool operator== (const putenv_of_auto_var &other) const
779 {
780 return (m_fndecl == other.m_fndecl
781 && m_reg == other.m_reg
782 && same_tree_p (t1: m_var_decl, t2: other.m_var_decl));
783 }
784
785 int get_controlling_option () const final override
786 {
787 return OPT_Wanalyzer_putenv_of_auto_var;
788 }
789
790 bool emit (diagnostic_emission_context &ctxt) final override
791 {
792 auto_diagnostic_group d;
793
794 /* SEI CERT C Coding Standard: "POS34-C. Do not call putenv() with a
795 pointer to an automatic variable as the argument". */
796 diagnostic_metadata::precanned_rule
797 rule ("POS34-C", "https://wiki.sei.cmu.edu/confluence/x/6NYxBQ");
798 ctxt.add_rule (r: rule);
799
800 bool warned;
801 if (m_var_decl)
802 warned = ctxt.warn ("%qE on a pointer to automatic variable %qE",
803 m_fndecl, m_var_decl);
804 else
805 warned = ctxt.warn ("%qE on a pointer to an on-stack buffer",
806 m_fndecl);
807 if (warned)
808 {
809 if (m_var_decl)
810 inform (DECL_SOURCE_LOCATION (m_var_decl),
811 "%qE declared on stack here", m_var_decl);
812 inform (ctxt.get_location (), "perhaps use %qs rather than %qE",
813 "setenv", m_fndecl);
814 }
815
816 return warned;
817 }
818
819 label_text describe_final_event (const evdesc::final_event &ev) final override
820 {
821 if (m_var_decl)
822 return ev.formatted_print (fmt: "%qE on a pointer to automatic variable %qE",
823 m_fndecl, m_var_decl);
824 else
825 return ev.formatted_print (fmt: "%qE on a pointer to an on-stack buffer",
826 m_fndecl);
827 }
828
829 void mark_interesting_stuff (interesting_t *interest) final override
830 {
831 if (!m_var_decl)
832 interest->add_region_creation (reg: m_reg->get_base_region ());
833 }
834
835private:
836 tree m_fndecl; // non-NULL
837 const region *m_reg; // non-NULL
838 tree m_var_decl; // could be NULL
839};
840
841/* Handler for calls to "putenv".
842
843 In theory we could try to model the state of the environment variables
844 for the process; for now we merely complain about putenv of regions
845 on the stack. */
846
847class kf_putenv : public known_function
848{
849public:
850 bool matches_call_types_p (const call_details &cd) const final override
851 {
852 return (cd.num_args () == 1 && cd.arg_is_pointer_p (idx: 0));
853 }
854
855 void impl_call_pre (const call_details &cd) const final override
856 {
857 tree fndecl = cd.get_fndecl_for_call ();
858 gcc_assert (fndecl);
859 region_model_context *ctxt = cd.get_ctxt ();
860 region_model *model = cd.get_model ();
861 model->check_for_null_terminated_string_arg (cd, idx: 0);
862 const svalue *ptr_sval = cd.get_arg_svalue (idx: 0);
863 const region *reg
864 = model->deref_rvalue (ptr_sval, ptr_tree: cd.get_arg_tree (idx: 0), ctxt);
865 model->get_store ()->mark_as_escaped (base_reg: reg);
866 enum memory_space mem_space = reg->get_memory_space ();
867 switch (mem_space)
868 {
869 default:
870 gcc_unreachable ();
871 case MEMSPACE_UNKNOWN:
872 case MEMSPACE_CODE:
873 case MEMSPACE_GLOBALS:
874 case MEMSPACE_HEAP:
875 case MEMSPACE_READONLY_DATA:
876 break;
877 case MEMSPACE_STACK:
878 if (ctxt)
879 ctxt->warn (d: make_unique<putenv_of_auto_var> (args&: fndecl, args&: reg));
880 break;
881 }
882 cd.set_any_lhs_with_defaults ();
883 }
884};
885
886/* Handler for "realloc":
887
888 void *realloc(void *ptr, size_t size);
889
890 realloc(3) is awkward, since it has various different outcomes
891 that are best modelled as separate exploded nodes/edges.
892
893 We first check for sm-state, in
894 malloc_state_machine::on_realloc_call, so that we
895 can complain about issues such as realloc of a non-heap
896 pointer, and terminate the path for such cases (and issue
897 the complaints at the call's exploded node).
898
899 Assuming that these checks pass, we split the path here into
900 three special cases (and terminate the "standard" path):
901 (A) failure, returning NULL
902 (B) success, growing the buffer in-place without moving it
903 (C) success, allocating a new buffer, copying the content
904 of the old buffer to it, and freeing the old buffer.
905
906 Each of these has a custom_edge_info subclass, which updates
907 the region_model and sm-state of the destination state. */
908
909class kf_realloc : public builtin_known_function
910{
911public:
912 bool matches_call_types_p (const call_details &cd) const final override
913 {
914 return (cd.num_args () == 2
915 && cd.arg_is_pointer_p (idx: 0)
916 && cd.arg_is_size_p (idx: 1));
917 }
918
919 enum built_in_function builtin_code () const final override
920 {
921 return BUILT_IN_REALLOC;
922 }
923
924 void impl_call_post (const call_details &cd) const final override;
925};
926
927void
928kf_realloc::impl_call_post (const call_details &cd) const
929{
930 /* Three custom subclasses of custom_edge_info, for handling the various
931 outcomes of "realloc". */
932
933 /* Concrete custom_edge_info: a realloc call that fails, returning NULL. */
934 class failure : public failed_call_info
935 {
936 public:
937 failure (const call_details &cd)
938 : failed_call_info (cd)
939 {
940 }
941
942 bool update_model (region_model *model,
943 const exploded_edge *,
944 region_model_context *ctxt) const final override
945 {
946 /* Return NULL; everything else is unchanged. */
947 const call_details cd (get_call_details (model, ctxt));
948 region_model_manager *mgr = cd.get_manager ();
949 if (cd.get_lhs_type ())
950 {
951 const svalue *zero
952 = mgr->get_or_create_int_cst (type: cd.get_lhs_type (), cst: 0);
953 model->set_value (lhs_reg: cd.get_lhs_region (),
954 rhs_sval: zero,
955 ctxt: cd.get_ctxt ());
956 }
957 return true;
958 }
959 };
960
961 /* Concrete custom_edge_info: a realloc call that succeeds, growing
962 the existing buffer without moving it. */
963 class success_no_move : public call_info
964 {
965 public:
966 success_no_move (const call_details &cd)
967 : call_info (cd)
968 {
969 }
970
971 label_text get_desc (bool can_colorize) const final override
972 {
973 return make_label_text (can_colorize,
974 fmt: "when %qE succeeds, without moving buffer",
975 get_fndecl ());
976 }
977
978 bool update_model (region_model *model,
979 const exploded_edge *,
980 region_model_context *ctxt) const final override
981 {
982 /* Update size of buffer and return the ptr unchanged. */
983 const call_details cd (get_call_details (model, ctxt));
984 region_model_manager *mgr = cd.get_manager ();
985 const svalue *ptr_sval = cd.get_arg_svalue (idx: 0);
986 const svalue *size_sval = cd.get_arg_svalue (idx: 1);
987
988 /* We can only grow in place with a non-NULL pointer. */
989 {
990 const svalue *null_ptr
991 = mgr->get_or_create_int_cst (type: ptr_sval->get_type (), cst: 0);
992 if (!model->add_constraint (lhs: ptr_sval, op: NE_EXPR, rhs: null_ptr,
993 ctxt: cd.get_ctxt ()))
994 return false;
995 }
996
997 if (const region *buffer_reg = model->deref_rvalue (ptr_sval, NULL_TREE,
998 ctxt))
999 if (compat_types_p (src_type: size_sval->get_type (), size_type_node))
1000 model->set_dynamic_extents (reg: buffer_reg, size_in_bytes: size_sval, ctxt);
1001 if (cd.get_lhs_region ())
1002 {
1003 model->set_value (lhs_reg: cd.get_lhs_region (), rhs_sval: ptr_sval, ctxt: cd.get_ctxt ());
1004 const svalue *zero
1005 = mgr->get_or_create_int_cst (type: cd.get_lhs_type (), cst: 0);
1006 return model->add_constraint (lhs: ptr_sval, op: NE_EXPR, rhs: zero, ctxt);
1007 }
1008 else
1009 return true;
1010 }
1011 };
1012
1013 /* Concrete custom_edge_info: a realloc call that succeeds, freeing
1014 the existing buffer and moving the content to a freshly allocated
1015 buffer. */
1016 class success_with_move : public call_info
1017 {
1018 public:
1019 success_with_move (const call_details &cd)
1020 : call_info (cd)
1021 {
1022 }
1023
1024 label_text get_desc (bool can_colorize) const final override
1025 {
1026 return make_label_text (can_colorize,
1027 fmt: "when %qE succeeds, moving buffer",
1028 get_fndecl ());
1029 }
1030 bool update_model (region_model *model,
1031 const exploded_edge *,
1032 region_model_context *ctxt) const final override
1033 {
1034 const call_details cd (get_call_details (model, ctxt));
1035 region_model_manager *mgr = cd.get_manager ();
1036 const svalue *old_ptr_sval = cd.get_arg_svalue (idx: 0);
1037 const svalue *new_size_sval = cd.get_arg_svalue (idx: 1);
1038
1039 /* Create the new region. */
1040 const region *new_reg
1041 = model->get_or_create_region_for_heap_alloc (size_in_bytes: new_size_sval, ctxt);
1042 const svalue *new_ptr_sval
1043 = mgr->get_ptr_svalue (ptr_type: cd.get_lhs_type (), pointee: new_reg);
1044 if (!model->add_constraint (lhs: new_ptr_sval, op: NE_EXPR, rhs: old_ptr_sval,
1045 ctxt: cd.get_ctxt ()))
1046 return false;
1047
1048 if (cd.get_lhs_type ())
1049 cd.maybe_set_lhs (result: new_ptr_sval);
1050
1051 if (const region *freed_reg = model->deref_rvalue (ptr_sval: old_ptr_sval,
1052 NULL_TREE, ctxt))
1053 {
1054 /* Copy the data. */
1055 const svalue *old_size_sval = model->get_dynamic_extents (reg: freed_reg);
1056 if (old_size_sval)
1057 {
1058 const svalue *copied_size_sval
1059 = get_copied_size (model, old_size_sval, new_size_sval);
1060 const region *copied_old_reg
1061 = mgr->get_sized_region (parent: freed_reg, NULL, byte_size_sval: copied_size_sval);
1062 const svalue *buffer_content_sval
1063 = model->get_store_value (reg: copied_old_reg, ctxt: cd.get_ctxt ());
1064 const region *copied_new_reg
1065 = mgr->get_sized_region (parent: new_reg, NULL, byte_size_sval: copied_size_sval);
1066 model->set_value (lhs_reg: copied_new_reg, rhs_sval: buffer_content_sval,
1067 ctxt: cd.get_ctxt ());
1068 }
1069 else
1070 {
1071 /* We don't know how big the old region was;
1072 mark the new region as having been touched to avoid uninit
1073 issues. */
1074 model->mark_region_as_unknown (reg: new_reg, uncertainty: cd.get_uncertainty ());
1075 }
1076
1077 /* Free the old region, so that pointers to the old buffer become
1078 invalid. */
1079
1080 /* If the ptr points to an underlying heap region, delete it,
1081 poisoning pointers. */
1082 model->unbind_region_and_descendents (reg: freed_reg, pkind: POISON_KIND_FREED);
1083 model->unset_dynamic_extents (reg: freed_reg);
1084 }
1085
1086 /* Update the sm-state: mark the old_ptr_sval as "freed",
1087 and the new_ptr_sval as "nonnull". */
1088 model->on_realloc_with_move (cd, old_ptr_sval, new_ptr_sval);
1089
1090 if (cd.get_lhs_type ())
1091 {
1092 const svalue *zero
1093 = mgr->get_or_create_int_cst (type: cd.get_lhs_type (), cst: 0);
1094 return model->add_constraint (lhs: new_ptr_sval, op: NE_EXPR, rhs: zero,
1095 ctxt: cd.get_ctxt ());
1096 }
1097 else
1098 return true;
1099 }
1100
1101 private:
1102 /* Return the lesser of OLD_SIZE_SVAL and NEW_SIZE_SVAL.
1103 If unknown, OLD_SIZE_SVAL is returned. */
1104 const svalue *get_copied_size (region_model *model,
1105 const svalue *old_size_sval,
1106 const svalue *new_size_sval) const
1107 {
1108 tristate res
1109 = model->eval_condition (lhs: old_size_sval, op: GT_EXPR, rhs: new_size_sval);
1110 switch (res.get_value ())
1111 {
1112 case tristate::TS_TRUE:
1113 return new_size_sval;
1114 case tristate::TS_FALSE:
1115 case tristate::TS_UNKNOWN:
1116 return old_size_sval;
1117 default:
1118 gcc_unreachable ();
1119 }
1120 }
1121 };
1122
1123 /* Body of kf_realloc::impl_call_post. */
1124
1125 if (cd.get_ctxt ())
1126 {
1127 cd.get_ctxt ()->bifurcate (info: make_unique<failure> (args: cd));
1128 cd.get_ctxt ()->bifurcate (info: make_unique<success_no_move> (args: cd));
1129 cd.get_ctxt ()->bifurcate (info: make_unique<success_with_move> (args: cd));
1130 cd.get_ctxt ()->terminate_path ();
1131 }
1132}
1133
1134/* Handler for "strchr" and "__builtin_strchr". */
1135
1136class kf_strchr : public builtin_known_function
1137{
1138public:
1139 bool matches_call_types_p (const call_details &cd) const final override
1140 {
1141 return (cd.num_args () == 2 && cd.arg_is_pointer_p (idx: 0));
1142 }
1143 void impl_call_pre (const call_details &cd) const final override
1144 {
1145 cd.check_for_null_terminated_string_arg (arg_idx: 0);
1146 }
1147
1148 enum built_in_function builtin_code () const final override
1149 {
1150 return BUILT_IN_STRCHR;
1151 }
1152 void impl_call_post (const call_details &cd) const final override;
1153};
1154
1155void
1156kf_strchr::impl_call_post (const call_details &cd) const
1157{
1158 class strchr_call_info : public call_info
1159 {
1160 public:
1161 strchr_call_info (const call_details &cd, bool found)
1162 : call_info (cd), m_found (found)
1163 {
1164 }
1165
1166 label_text get_desc (bool can_colorize) const final override
1167 {
1168 if (m_found)
1169 return make_label_text (can_colorize,
1170 fmt: "when %qE returns non-NULL",
1171 get_fndecl ());
1172 else
1173 return make_label_text (can_colorize,
1174 fmt: "when %qE returns NULL",
1175 get_fndecl ());
1176 }
1177
1178 bool update_model (region_model *model,
1179 const exploded_edge *,
1180 region_model_context *ctxt) const final override
1181 {
1182 const call_details cd (get_call_details (model, ctxt));
1183 if (tree lhs_type = cd.get_lhs_type ())
1184 {
1185 region_model_manager *mgr = model->get_manager ();
1186 const svalue *result;
1187 if (m_found)
1188 {
1189 const svalue *str_sval = cd.get_arg_svalue (idx: 0);
1190 const region *str_reg
1191 = model->deref_rvalue (ptr_sval: str_sval, ptr_tree: cd.get_arg_tree (idx: 0),
1192 ctxt: cd.get_ctxt ());
1193 /* We want str_sval + OFFSET for some unknown OFFSET.
1194 Use a conjured_svalue to represent the offset,
1195 using the str_reg as the id of the conjured_svalue. */
1196 const svalue *offset
1197 = mgr->get_or_create_conjured_svalue (size_type_node,
1198 stmt: cd.get_call_stmt (),
1199 id_reg: str_reg,
1200 p: conjured_purge (model,
1201 ctxt));
1202 result = mgr->get_or_create_binop (type: lhs_type, op: POINTER_PLUS_EXPR,
1203 arg0: str_sval, arg1: offset);
1204 }
1205 else
1206 result = mgr->get_or_create_int_cst (type: lhs_type, cst: 0);
1207 cd.maybe_set_lhs (result);
1208 }
1209 return true;
1210 }
1211 private:
1212 bool m_found;
1213 };
1214
1215 /* Body of kf_strchr::impl_call_post. */
1216 if (cd.get_ctxt ())
1217 {
1218 cd.get_ctxt ()->bifurcate (info: make_unique<strchr_call_info> (args: cd, args: false));
1219 cd.get_ctxt ()->bifurcate (info: make_unique<strchr_call_info> (args: cd, args: true));
1220 cd.get_ctxt ()->terminate_path ();
1221 }
1222}
1223
1224/* Handler for "sprintf".
1225 int sprintf(char *str, const char *format, ...);
1226*/
1227
1228class kf_sprintf : public builtin_known_function
1229{
1230public:
1231 bool matches_call_types_p (const call_details &cd) const final override
1232 {
1233 return (cd.num_args () >= 2
1234 && cd.arg_is_pointer_p (idx: 0)
1235 && cd.arg_is_pointer_p (idx: 1));
1236 }
1237
1238 enum built_in_function builtin_code () const final override
1239 {
1240 return BUILT_IN_SPRINTF;
1241 }
1242
1243 void impl_call_pre (const call_details &cd) const final override
1244 {
1245 /* For now, merely assume that the destination buffer gets set to a
1246 new svalue. */
1247 region_model *model = cd.get_model ();
1248 region_model_context *ctxt = cd.get_ctxt ();
1249 const svalue *dst_ptr = cd.get_arg_svalue (idx: 0);
1250 const region *dst_reg
1251 = model->deref_rvalue (ptr_sval: dst_ptr, ptr_tree: cd.get_arg_tree (idx: 0), ctxt);
1252 const svalue *content = cd.get_or_create_conjured_svalue (dst_reg);
1253 model->set_value (lhs_reg: dst_reg, rhs_sval: content, ctxt);
1254 cd.set_any_lhs_with_defaults ();
1255 }
1256};
1257
1258/* Handler for "__builtin_stack_restore". */
1259
1260class kf_stack_restore : public pure_known_function_with_default_return
1261{
1262public:
1263 bool matches_call_types_p (const call_details &) const final override
1264 {
1265 return true;
1266 }
1267
1268 /* Currently a no-op. */
1269};
1270
1271/* Handler for "__builtin_stack_save". */
1272
1273class kf_stack_save : public pure_known_function_with_default_return
1274{
1275public:
1276 bool matches_call_types_p (const call_details &) const final override
1277 {
1278 return true;
1279 }
1280
1281 /* Currently a no-op. */
1282};
1283
1284/* Handler for "strcat" and "__builtin_strcat_chk". */
1285
1286class kf_strcat : public builtin_known_function
1287{
1288public:
1289 kf_strcat (unsigned int num_args, bool chk_variant)
1290 : m_num_args (num_args),
1291 m_chk_variant (chk_variant) {}
1292 bool matches_call_types_p (const call_details &cd) const final override
1293 {
1294 return (cd.num_args () == m_num_args
1295 && cd.arg_is_pointer_p (idx: 0)
1296 && cd.arg_is_pointer_p (idx: 1));
1297 }
1298
1299 enum built_in_function builtin_code () const final override
1300 {
1301 return m_chk_variant ? BUILT_IN_STRCAT_CHK : BUILT_IN_STRCAT;
1302 }
1303
1304 void impl_call_pre (const call_details &cd) const final override
1305 {
1306 region_model *model = cd.get_model ();
1307 region_model_manager *mgr = cd.get_manager ();
1308
1309 const svalue *dest_sval = cd.get_arg_svalue (idx: 0);
1310 const region *dest_reg = model->deref_rvalue (ptr_sval: dest_sval, ptr_tree: cd.get_arg_tree (idx: 0),
1311 ctxt: cd.get_ctxt ());
1312
1313 const svalue *dst_strlen_sval
1314 = cd.check_for_null_terminated_string_arg (arg_idx: 0, include_terminator: false, out_sval: nullptr);
1315 if (!dst_strlen_sval)
1316 {
1317 if (cd.get_ctxt ())
1318 cd.get_ctxt ()->terminate_path ();
1319 return;
1320 }
1321
1322 const svalue *bytes_to_copy;
1323 const svalue *num_src_bytes_read_sval
1324 = cd.check_for_null_terminated_string_arg (arg_idx: 1, include_terminator: true, out_sval: &bytes_to_copy);
1325 if (!num_src_bytes_read_sval)
1326 {
1327 if (cd.get_ctxt ())
1328 cd.get_ctxt ()->terminate_path ();
1329 return;
1330 }
1331
1332 cd.maybe_set_lhs (result: dest_sval);
1333 cd.complain_about_overlap (arg_idx_a: 0, arg_idx_b: 1, num_bytes_read_sval: num_src_bytes_read_sval);
1334
1335 const region *offset_reg
1336 = mgr->get_offset_region (parent: dest_reg, NULL_TREE, byte_offset: dst_strlen_sval);
1337 model->write_bytes (dest_reg: offset_reg,
1338 num_bytes_sval: num_src_bytes_read_sval,
1339 sval: bytes_to_copy,
1340 ctxt: cd.get_ctxt ());
1341 }
1342
1343private:
1344 unsigned int m_num_args;
1345 const bool m_chk_variant;
1346};
1347
1348/* Handler for "strcpy" and "__builtin_strcpy_chk". */
1349
1350class kf_strcpy : public builtin_known_function
1351{
1352public:
1353 kf_strcpy (unsigned int num_args, bool chk_variant)
1354 : m_num_args (num_args),
1355 m_chk_variant (chk_variant) {}
1356 bool matches_call_types_p (const call_details &cd) const final override
1357 {
1358 return (cd.num_args () == m_num_args
1359 && cd.arg_is_pointer_p (idx: 0)
1360 && cd.arg_is_pointer_p (idx: 1));
1361 }
1362 enum built_in_function builtin_code () const final override
1363 {
1364 return m_chk_variant ? BUILT_IN_STRCPY_CHK : BUILT_IN_STRCPY;
1365 }
1366 void impl_call_pre (const call_details &cd) const final override;
1367
1368private:
1369 unsigned int m_num_args;
1370 const bool m_chk_variant;
1371};
1372
1373void
1374kf_strcpy::impl_call_pre (const call_details &cd) const
1375{
1376 region_model *model = cd.get_model ();
1377 region_model_context *ctxt = cd.get_ctxt ();
1378
1379 const svalue *dest_sval = cd.get_arg_svalue (idx: 0);
1380 const region *dest_reg = model->deref_rvalue (ptr_sval: dest_sval, ptr_tree: cd.get_arg_tree (idx: 0),
1381 ctxt);
1382 /* strcpy returns the initial param. */
1383 cd.maybe_set_lhs (result: dest_sval);
1384
1385 const svalue *bytes_to_copy;
1386 if (const svalue *num_bytes_read_sval
1387 = cd.check_for_null_terminated_string_arg (arg_idx: 1, include_terminator: true, out_sval: &bytes_to_copy))
1388 {
1389 cd.complain_about_overlap (arg_idx_a: 0, arg_idx_b: 1, num_bytes_read_sval);
1390 model->write_bytes (dest_reg, num_bytes_sval: num_bytes_read_sval, sval: bytes_to_copy, ctxt);
1391 }
1392 else
1393 {
1394 if (cd.get_ctxt ())
1395 cd.get_ctxt ()->terminate_path ();
1396 }
1397}
1398
1399/* Handler for "strdup" and "__builtin_strdup". */
1400
1401class kf_strdup : public builtin_known_function
1402{
1403public:
1404 bool matches_call_types_p (const call_details &cd) const final override
1405 {
1406 return (cd.num_args () == 1 && cd.arg_is_pointer_p (idx: 0));
1407 }
1408 enum built_in_function builtin_code () const final override
1409 {
1410 return BUILT_IN_STRDUP;
1411 }
1412 void impl_call_pre (const call_details &cd) const final override
1413 {
1414 region_model *model = cd.get_model ();
1415 region_model_context *ctxt = cd.get_ctxt ();
1416 region_model_manager *mgr = cd.get_manager ();
1417 const svalue *bytes_to_copy;
1418 if (const svalue *num_bytes_read_sval
1419 = cd.check_for_null_terminated_string_arg (arg_idx: 0, include_terminator: true, out_sval: &bytes_to_copy))
1420 {
1421 const region *new_reg
1422 = model->get_or_create_region_for_heap_alloc (size_in_bytes: num_bytes_read_sval,
1423 ctxt);
1424 model->write_bytes (dest_reg: new_reg, num_bytes_sval: num_bytes_read_sval, sval: bytes_to_copy, ctxt);
1425 if (cd.get_lhs_type ())
1426 {
1427 const svalue *ptr_sval
1428 = mgr->get_ptr_svalue (ptr_type: cd.get_lhs_type (), pointee: new_reg);
1429 cd.maybe_set_lhs (result: ptr_sval);
1430 }
1431 }
1432 else
1433 {
1434 if (ctxt)
1435 ctxt->terminate_path ();
1436 }
1437 }
1438};
1439
1440/* Handler for "strlen" and for "__analyzer_get_strlen". */
1441
1442class kf_strlen : public builtin_known_function
1443{
1444public:
1445 bool matches_call_types_p (const call_details &cd) const final override
1446 {
1447 return (cd.num_args () == 1 && cd.arg_is_pointer_p (idx: 0));
1448 }
1449 enum built_in_function builtin_code () const final override
1450 {
1451 return BUILT_IN_STRLEN;
1452 }
1453
1454 void impl_call_pre (const call_details &cd) const final override
1455 {
1456 if (const svalue *strlen_sval
1457 = cd.check_for_null_terminated_string_arg (arg_idx: 0, include_terminator: false, out_sval: nullptr))
1458 if (strlen_sval->get_kind () != SK_UNKNOWN)
1459 {
1460 cd.maybe_set_lhs (result: strlen_sval);
1461 return;
1462 }
1463
1464 /* Use a conjured svalue. */
1465 cd.set_any_lhs_with_defaults ();
1466 }
1467};
1468
1469/* Factory function, so that kf-analyzer.cc can use this class. */
1470
1471std::unique_ptr<known_function>
1472make_kf_strlen ()
1473{
1474 return make_unique<kf_strlen> ();
1475}
1476
1477/* Handler for "strncpy" and "__builtin_strncpy".
1478 See e.g. https://en.cppreference.com/w/c/string/byte/strncpy
1479
1480 extern char *strncpy (char *dst, const char *src, size_t count);
1481
1482 Handle this by splitting into two outcomes:
1483 (a) truncated read from "src" of "count" bytes,
1484 writing "count" bytes to "dst"
1485 (b) read from "src" of up to (and including) the null terminator,
1486 where the number of bytes read < "count" bytes,
1487 writing those bytes to "dst", and zero-filling the rest,
1488 up to "count". */
1489
1490class kf_strncpy : public builtin_known_function
1491{
1492public:
1493 bool matches_call_types_p (const call_details &cd) const final override
1494 {
1495 return (cd.num_args () == 3
1496 && cd.arg_is_pointer_p (idx: 0)
1497 && cd.arg_is_pointer_p (idx: 1)
1498 && cd.arg_is_integral_p (idx: 2));
1499 }
1500 enum built_in_function builtin_code () const final override
1501 {
1502 return BUILT_IN_STRNCPY;
1503 }
1504 void impl_call_post (const call_details &cd) const final override;
1505};
1506
1507void
1508kf_strncpy::impl_call_post (const call_details &cd) const
1509{
1510 class strncpy_call_info : public call_info
1511 {
1512 public:
1513 strncpy_call_info (const call_details &cd,
1514 const svalue *num_bytes_with_terminator_sval,
1515 bool truncated_read)
1516 : call_info (cd),
1517 m_num_bytes_with_terminator_sval (num_bytes_with_terminator_sval),
1518 m_truncated_read (truncated_read)
1519 {
1520 }
1521
1522 label_text get_desc (bool can_colorize) const final override
1523 {
1524 if (m_truncated_read)
1525 return make_label_text (can_colorize,
1526 fmt: "when %qE truncates the source string",
1527 get_fndecl ());
1528 else
1529 return make_label_text (can_colorize,
1530 fmt: "when %qE copies the full source string",
1531 get_fndecl ());
1532 }
1533
1534 bool update_model (region_model *model,
1535 const exploded_edge *,
1536 region_model_context *ctxt) const final override
1537 {
1538 const call_details cd (get_call_details (model, ctxt));
1539
1540 const svalue *dest_sval = cd.get_arg_svalue (idx: 0);
1541 const region *dest_reg
1542 = model->deref_rvalue (ptr_sval: dest_sval, ptr_tree: cd.get_arg_tree (idx: 0), ctxt);
1543
1544 const svalue *src_sval = cd.get_arg_svalue (idx: 1);
1545 const region *src_reg
1546 = model->deref_rvalue (ptr_sval: src_sval, ptr_tree: cd.get_arg_tree (idx: 1), ctxt);
1547
1548 const svalue *count_sval = cd.get_arg_svalue (idx: 2);
1549
1550 /* strncpy returns the initial param. */
1551 cd.maybe_set_lhs (result: dest_sval);
1552
1553 const svalue *num_bytes_read_sval;
1554 if (m_truncated_read)
1555 {
1556 /* Truncated read. */
1557 num_bytes_read_sval = count_sval;
1558
1559 if (m_num_bytes_with_terminator_sval)
1560 {
1561 /* The terminator is after the limit. */
1562 if (!model->add_constraint (lhs: m_num_bytes_with_terminator_sval,
1563 op: GT_EXPR,
1564 rhs: count_sval,
1565 ctxt))
1566 return false;
1567 }
1568 else
1569 {
1570 /* We don't know where the terminator is, or if there is one.
1571 In theory we know that the first COUNT bytes are non-zero,
1572 but we don't have a way to record that constraint. */
1573 }
1574 }
1575 else
1576 {
1577 /* Full read of the src string before reaching the limit,
1578 so there must be a terminator and it must be at or before
1579 the limit. */
1580 if (m_num_bytes_with_terminator_sval)
1581 {
1582 if (!model->add_constraint (lhs: m_num_bytes_with_terminator_sval,
1583 op: LE_EXPR,
1584 rhs: count_sval,
1585 ctxt))
1586 return false;
1587 num_bytes_read_sval = m_num_bytes_with_terminator_sval;
1588
1589 /* First, zero-fill the dest buffer.
1590 We don't need to do this for the truncation case, as
1591 this fully populates the dest buffer. */
1592 const region *sized_dest_reg
1593 = model->get_manager ()->get_sized_region (parent: dest_reg,
1594 NULL_TREE,
1595 byte_size_sval: count_sval);
1596 model->zero_fill_region (reg: sized_dest_reg, ctxt);
1597 }
1598 else
1599 {
1600 /* Don't analyze this case; the other case will
1601 assume a "truncated" read up to the limit. */
1602 return false;
1603 }
1604 }
1605
1606 gcc_assert (num_bytes_read_sval);
1607
1608 const svalue *bytes_to_copy
1609 = model->read_bytes (src_reg,
1610 src_ptr_expr: cd.get_arg_tree (idx: 1),
1611 num_bytes_sval: num_bytes_read_sval,
1612 ctxt);
1613 cd.complain_about_overlap (arg_idx_a: 0, arg_idx_b: 1, num_bytes_read_sval);
1614 model->write_bytes (dest_reg,
1615 num_bytes_sval: num_bytes_read_sval,
1616 sval: bytes_to_copy,
1617 ctxt);
1618
1619 return true;
1620 }
1621 private:
1622 /* (strlen + 1) of the source string if it has a terminator,
1623 or NULL for the case where UB would happen before
1624 finding any terminator. */
1625 const svalue *m_num_bytes_with_terminator_sval;
1626
1627 /* true: if this is the outcome where the limit was reached before
1628 the null terminator
1629 false: if the null terminator was reached before the limit. */
1630 bool m_truncated_read;
1631 };
1632
1633 /* Body of kf_strncpy::impl_call_post. */
1634 if (cd.get_ctxt ())
1635 {
1636 /* First, scan for a null terminator as if there were no limit,
1637 with a null ctxt so no errors are reported. */
1638 const region_model *model = cd.get_model ();
1639 const svalue *ptr_arg_sval = cd.get_arg_svalue (idx: 1);
1640 const region *buf_reg
1641 = model->deref_rvalue (ptr_sval: ptr_arg_sval, ptr_tree: cd.get_arg_tree (idx: 1), ctxt: nullptr);
1642 const svalue *num_bytes_with_terminator_sval
1643 = model->scan_for_null_terminator (reg: buf_reg,
1644 expr: cd.get_arg_tree (idx: 1),
1645 out_sval: nullptr,
1646 ctxt: nullptr);
1647 cd.get_ctxt ()->bifurcate
1648 (info: make_unique<strncpy_call_info> (args: cd, args&: num_bytes_with_terminator_sval,
1649 args: false));
1650 cd.get_ctxt ()->bifurcate
1651 (info: make_unique<strncpy_call_info> (args: cd, args&: num_bytes_with_terminator_sval,
1652 args: true));
1653 cd.get_ctxt ()->terminate_path ();
1654 }
1655};
1656
1657/* Handler for "strndup" and "__builtin_strndup". */
1658
1659class kf_strndup : public builtin_known_function
1660{
1661public:
1662 bool matches_call_types_p (const call_details &cd) const final override
1663 {
1664 return (cd.num_args () == 2 && cd.arg_is_pointer_p (idx: 0));
1665 }
1666 enum built_in_function builtin_code () const final override
1667 {
1668 return BUILT_IN_STRNDUP;
1669 }
1670 void impl_call_pre (const call_details &cd) const final override
1671 {
1672 region_model *model = cd.get_model ();
1673 region_model_manager *mgr = cd.get_manager ();
1674 /* Ideally we'd get the size here, and simulate copying the bytes. */
1675 const region *new_reg
1676 = model->get_or_create_region_for_heap_alloc (NULL, ctxt: cd.get_ctxt ());
1677 model->mark_region_as_unknown (reg: new_reg, NULL);
1678 if (cd.get_lhs_type ())
1679 {
1680 const svalue *ptr_sval
1681 = mgr->get_ptr_svalue (ptr_type: cd.get_lhs_type (), pointee: new_reg);
1682 cd.maybe_set_lhs (result: ptr_sval);
1683 }
1684 }
1685};
1686
1687/* Handler for "strstr" and "__builtin_strstr".
1688 extern char *strstr (const char* str, const char* substr);
1689 See e.g. https://en.cppreference.com/w/c/string/byte/strstr */
1690
1691class kf_strstr : public builtin_known_function
1692{
1693public:
1694 bool matches_call_types_p (const call_details &cd) const final override
1695 {
1696 return (cd.num_args () == 2
1697 && cd.arg_is_pointer_p (idx: 0)
1698 && cd.arg_is_pointer_p (idx: 1));
1699 }
1700 enum built_in_function builtin_code () const final override
1701 {
1702 return BUILT_IN_STRSTR;
1703 }
1704 void impl_call_pre (const call_details &cd) const final override
1705 {
1706 cd.check_for_null_terminated_string_arg (arg_idx: 0);
1707 cd.check_for_null_terminated_string_arg (arg_idx: 1);
1708 }
1709 void impl_call_post (const call_details &cd) const final override;
1710};
1711
1712void
1713kf_strstr::impl_call_post (const call_details &cd) const
1714{
1715 class strstr_call_info : public call_info
1716 {
1717 public:
1718 strstr_call_info (const call_details &cd, bool found)
1719 : call_info (cd), m_found (found)
1720 {
1721 }
1722
1723 label_text get_desc (bool can_colorize) const final override
1724 {
1725 if (m_found)
1726 return make_label_text (can_colorize,
1727 fmt: "when %qE returns non-NULL",
1728 get_fndecl ());
1729 else
1730 return make_label_text (can_colorize,
1731 fmt: "when %qE returns NULL",
1732 get_fndecl ());
1733 }
1734
1735 bool update_model (region_model *model,
1736 const exploded_edge *,
1737 region_model_context *ctxt) const final override
1738 {
1739 const call_details cd (get_call_details (model, ctxt));
1740 if (tree lhs_type = cd.get_lhs_type ())
1741 {
1742 region_model_manager *mgr = model->get_manager ();
1743 const svalue *result;
1744 if (m_found)
1745 {
1746 const svalue *str_sval = cd.get_arg_svalue (idx: 0);
1747 const region *str_reg
1748 = model->deref_rvalue (ptr_sval: str_sval, ptr_tree: cd.get_arg_tree (idx: 0),
1749 ctxt: cd.get_ctxt ());
1750 /* We want str_sval + OFFSET for some unknown OFFSET.
1751 Use a conjured_svalue to represent the offset,
1752 using the str_reg as the id of the conjured_svalue. */
1753 const svalue *offset
1754 = mgr->get_or_create_conjured_svalue (size_type_node,
1755 stmt: cd.get_call_stmt (),
1756 id_reg: str_reg,
1757 p: conjured_purge (model,
1758 ctxt));
1759 result = mgr->get_or_create_binop (type: lhs_type, op: POINTER_PLUS_EXPR,
1760 arg0: str_sval, arg1: offset);
1761 }
1762 else
1763 result = mgr->get_or_create_int_cst (type: lhs_type, cst: 0);
1764 cd.maybe_set_lhs (result);
1765 }
1766 return true;
1767 }
1768 private:
1769 bool m_found;
1770 };
1771
1772 /* Body of kf_strstr::impl_call_post. */
1773 if (cd.get_ctxt ())
1774 {
1775 cd.get_ctxt ()->bifurcate (info: make_unique<strstr_call_info> (args: cd, args: false));
1776 cd.get_ctxt ()->bifurcate (info: make_unique<strstr_call_info> (args: cd, args: true));
1777 cd.get_ctxt ()->terminate_path ();
1778 }
1779}
1780
1781/* Handle calls to "strtok".
1782 See e.g.
1783 https://en.cppreference.com/w/c/string/byte/strtok
1784 https://man7.org/linux/man-pages/man3/strtok.3.html */
1785
1786class kf_strtok : public known_function
1787{
1788public:
1789 class undefined_behavior : public undefined_function_behavior
1790 {
1791 public:
1792 undefined_behavior (const call_details &cd)
1793 : undefined_function_behavior (cd)
1794 {
1795 }
1796 int get_controlling_option () const final override
1797 {
1798 return OPT_Wanalyzer_undefined_behavior_strtok;
1799 }
1800
1801 bool emit (diagnostic_emission_context &ctxt) final override
1802 {
1803 /* CWE-476: NULL Pointer Dereference. */
1804 ctxt.add_cwe (cwe: 476);
1805 if (ctxt.warn ("calling %qD for first time with NULL as argument 1"
1806 " has undefined behavior",
1807 get_callee_fndecl ()))
1808 {
1809 inform (ctxt.get_location (),
1810 "some implementations of %qD may crash on such input",
1811 get_callee_fndecl ());
1812 return true;
1813 }
1814 return false;
1815 }
1816
1817 label_text describe_final_event (const evdesc::final_event &ev)
1818 final override
1819 {
1820 return ev.formatted_print
1821 (fmt: "calling %qD for first time with NULL as argument 1"
1822 " has undefined behavior",
1823 get_callee_fndecl ());
1824 }
1825 };
1826
1827 /* An outcome of a "strtok" call.
1828 We have a four-way bifurcation of the analysis via the
1829 4 combinations of two flags:
1830 - m_nonnull_str covers whether the "str" param was null or non-null
1831 - m_found covers whether the result is null or non-null
1832 */
1833 class strtok_call_info : public call_info
1834 {
1835 public:
1836 strtok_call_info (const call_details &cd,
1837 const private_region &private_reg,
1838 bool nonnull_str,
1839 bool found)
1840 : call_info (cd),
1841 m_private_reg (private_reg),
1842 m_nonnull_str (nonnull_str),
1843 m_found (found)
1844 {
1845 }
1846
1847 label_text get_desc (bool can_colorize) const final override
1848 {
1849 if (m_nonnull_str)
1850 {
1851 if (m_found)
1852 return make_label_text
1853 (can_colorize,
1854 fmt: "when %qE on non-NULL string returns non-NULL",
1855 get_fndecl ());
1856 else
1857 return make_label_text
1858 (can_colorize,
1859 fmt: "when %qE on non-NULL string returns NULL",
1860 get_fndecl ());
1861 }
1862 else
1863 {
1864 if (m_found)
1865 return make_label_text
1866 (can_colorize,
1867 fmt: "when %qE with NULL string (using prior) returns non-NULL",
1868 get_fndecl ());
1869 else
1870 return make_label_text
1871 (can_colorize,
1872 fmt: "when %qE with NULL string (using prior) returns NULL",
1873 get_fndecl ());
1874 }
1875 }
1876
1877 bool update_model (region_model *model,
1878 const exploded_edge *,
1879 region_model_context *ctxt) const final override
1880 {
1881 region_model_manager *mgr = model->get_manager ();
1882 const call_details cd (get_call_details (model, ctxt));
1883 const svalue *str_sval = cd.get_arg_svalue (idx: 0);
1884 /* const svalue *delim_sval = cd.get_arg_svalue (1); */
1885
1886 cd.check_for_null_terminated_string_arg (arg_idx: 1);
1887 /* We check that either arg 0 or the private region is null
1888 terminated below. */
1889
1890 const svalue *null_ptr_sval
1891 = mgr->get_or_create_null_ptr (pointer_type: cd.get_arg_type (idx: 0));;
1892 if (!model->add_constraint (lhs: str_sval,
1893 op: m_nonnull_str ? NE_EXPR : EQ_EXPR,
1894 rhs: null_ptr_sval,
1895 ctxt: cd.get_ctxt ()))
1896 return false;
1897
1898 if (m_nonnull_str)
1899 {
1900 /* Update internal buffer. */
1901 model->set_value (lhs_reg: &m_private_reg,
1902 rhs_sval: mgr->get_or_create_unmergeable (arg: str_sval),
1903 ctxt);
1904 }
1905 else
1906 {
1907 /* Read from internal buffer. */
1908 str_sval = model->get_store_value (reg: &m_private_reg, ctxt);
1909
1910 /* The initial value of the private region is NULL when we're
1911 on a path from main. */
1912 if (const initial_svalue *initial_sval
1913 = str_sval->dyn_cast_initial_svalue ())
1914 if (initial_sval->get_region () == &m_private_reg
1915 && model->called_from_main_p ())
1916 {
1917 /* Implementations of strtok do not necessarily check for NULL
1918 here, and may crash; see PR analyzer/107573.
1919 Warn for this, if we were definitely passed NULL. */
1920 if (cd.get_arg_svalue (idx: 0)->all_zeroes_p ())
1921 {
1922 if (ctxt)
1923 ctxt->warn (d: ::make_unique<undefined_behavior> (args: cd));
1924 }
1925
1926 /* Assume that "str" was actually non-null; terminate
1927 this path. */
1928 return false;
1929 }
1930
1931 /* Now assume str_sval is non-null. */
1932 if (!model->add_constraint (lhs: str_sval,
1933 op: NE_EXPR,
1934 rhs: null_ptr_sval,
1935 ctxt: cd.get_ctxt ()))
1936 return false;
1937 }
1938
1939 const region *buf_reg = model->deref_rvalue (ptr_sval: str_sval, NULL_TREE, ctxt);
1940 model->scan_for_null_terminator (reg: buf_reg,
1941 NULL_TREE,
1942 out_sval: nullptr,
1943 ctxt);
1944
1945 if (m_found)
1946 {
1947 const region *str_reg
1948 = model->deref_rvalue (ptr_sval: str_sval, ptr_tree: cd.get_arg_tree (idx: 0),
1949 ctxt: cd.get_ctxt ());
1950 /* We want to figure out the start and nul terminator
1951 for the token.
1952 For each, we want str_sval + OFFSET for some unknown OFFSET.
1953 Use a conjured_svalue to represent the offset,
1954 using the str_reg as the id of the conjured_svalue. */
1955 const svalue *start_offset
1956 = mgr->get_or_create_conjured_svalue (size_type_node,
1957 stmt: cd.get_call_stmt (),
1958 id_reg: str_reg,
1959 p: conjured_purge (model,
1960 ctxt),
1961 idx: 0);
1962 const svalue *nul_offset
1963 = mgr->get_or_create_conjured_svalue (size_type_node,
1964 stmt: cd.get_call_stmt (),
1965 id_reg: str_reg,
1966 p: conjured_purge (model,
1967 ctxt),
1968 idx: 1);
1969
1970 tree char_ptr_type = build_pointer_type (char_type_node);
1971 const svalue *result
1972 = mgr->get_or_create_binop (type: char_ptr_type, op: POINTER_PLUS_EXPR,
1973 arg0: str_sval, arg1: start_offset);
1974 cd.maybe_set_lhs (result);
1975
1976 /* nul_offset + 1; the offset to use for the next call. */
1977 const svalue *next_offset
1978 = mgr->get_or_create_binop (size_type_node, op: PLUS_EXPR,
1979 arg0: nul_offset,
1980 arg1: mgr->get_or_create_int_cst
1981 (char_type_node, cst: 1));
1982
1983 /* Write '\0' to str_sval[nul_offset]. */
1984 const svalue *ptr_to_term
1985 = mgr->get_or_create_binop (type: char_ptr_type, op: POINTER_PLUS_EXPR,
1986 arg0: str_sval, arg1: nul_offset);
1987 const region *terminator_reg
1988 = model->deref_rvalue (ptr_sval: ptr_to_term, NULL_TREE, ctxt: cd.get_ctxt ());
1989 model->set_value (lhs_reg: terminator_reg,
1990 rhs_sval: mgr->get_or_create_unmergeable
1991 (arg: mgr->get_or_create_int_cst (char_type_node,
1992 cst: 0)),
1993 ctxt: cd.get_ctxt ());
1994
1995 /* Update saved ptr to be at [nul_offset + 1]. */
1996 const svalue *ptr_to_next
1997 = mgr->get_or_create_binop (type: cd.get_lhs_type (), op: POINTER_PLUS_EXPR,
1998 arg0: str_sval, arg1: next_offset);
1999 model->set_value (lhs_reg: &m_private_reg, rhs_sval: ptr_to_next, ctxt);
2000 }
2001 else
2002 if (tree lhs_type = cd.get_lhs_type ())
2003 {
2004 const svalue *result
2005 = mgr->get_or_create_int_cst (type: lhs_type, cst: 0);
2006 cd.maybe_set_lhs (result);
2007 }
2008 return true;
2009 }
2010 private:
2011 const private_region &m_private_reg;
2012 bool m_nonnull_str;
2013 bool m_found;
2014 }; // class strtok_call_info
2015
2016 kf_strtok (region_model_manager &mgr)
2017 : m_private_reg (mgr.alloc_symbol_id (),
2018 mgr.get_root_region (),
2019 get_region_type (),
2020 "strtok buffer")
2021 {
2022 }
2023
2024 bool matches_call_types_p (const call_details &cd) const final override
2025 {
2026 return (cd.num_args () == 2
2027 && POINTER_TYPE_P (cd.get_arg_type (0))
2028 && POINTER_TYPE_P (cd.get_arg_type (1)));
2029 }
2030
2031 void impl_call_post (const call_details &cd) const final override
2032 {
2033 if (cd.get_ctxt ())
2034 {
2035 /* Four-way bifurcation, based on whether:
2036 - the str is non-null
2037 - the result is non-null
2038 Typically the str is either null or non-null at a particular site,
2039 so hopefully this will generally just lead to two out-edges. */
2040 cd.get_ctxt ()->bifurcate
2041 (info: make_unique<strtok_call_info> (args: cd, args: m_private_reg, args: false, args: false));
2042 cd.get_ctxt ()->bifurcate
2043 (info: make_unique<strtok_call_info> (args: cd, args: m_private_reg, args: false, args: true));
2044 cd.get_ctxt ()->bifurcate
2045 (info: make_unique<strtok_call_info> (args: cd, args: m_private_reg, args: true, args: false));
2046 cd.get_ctxt ()->bifurcate
2047 (info: make_unique<strtok_call_info> (args: cd, args: m_private_reg, args: true, args: true));
2048 cd.get_ctxt ()->terminate_path ();
2049 }
2050 }
2051
2052private:
2053 static tree get_region_type ()
2054 {
2055 return build_pointer_type (char_type_node);
2056 }
2057 const private_region m_private_reg;
2058};
2059
2060class kf_ubsan_bounds : public internal_known_function
2061{
2062 /* Empty. */
2063};
2064
2065/* Handle calls to functions referenced by
2066 __attribute__((malloc(FOO))). */
2067
2068void
2069region_model::impl_deallocation_call (const call_details &cd)
2070{
2071 kf_free kf;
2072 kf.impl_call_post (cd);
2073}
2074
2075static void
2076register_atomic_builtins (known_function_manager &kfm)
2077{
2078 kfm.add (name: BUILT_IN_ATOMIC_EXCHANGE, kf: make_unique<kf_atomic_exchange> ());
2079 kfm.add (name: BUILT_IN_ATOMIC_EXCHANGE_N, kf: make_unique<kf_atomic_exchange_n> ());
2080 kfm.add (name: BUILT_IN_ATOMIC_EXCHANGE_1, kf: make_unique<kf_atomic_exchange_n> ());
2081 kfm.add (name: BUILT_IN_ATOMIC_EXCHANGE_2, kf: make_unique<kf_atomic_exchange_n> ());
2082 kfm.add (name: BUILT_IN_ATOMIC_EXCHANGE_4, kf: make_unique<kf_atomic_exchange_n> ());
2083 kfm.add (name: BUILT_IN_ATOMIC_EXCHANGE_8, kf: make_unique<kf_atomic_exchange_n> ());
2084 kfm.add (name: BUILT_IN_ATOMIC_EXCHANGE_16, kf: make_unique<kf_atomic_exchange_n> ());
2085 kfm.add (name: BUILT_IN_ATOMIC_LOAD, kf: make_unique<kf_atomic_load> ());
2086 kfm.add (name: BUILT_IN_ATOMIC_LOAD_N, kf: make_unique<kf_atomic_load_n> ());
2087 kfm.add (name: BUILT_IN_ATOMIC_LOAD_1, kf: make_unique<kf_atomic_load_n> ());
2088 kfm.add (name: BUILT_IN_ATOMIC_LOAD_2, kf: make_unique<kf_atomic_load_n> ());
2089 kfm.add (name: BUILT_IN_ATOMIC_LOAD_4, kf: make_unique<kf_atomic_load_n> ());
2090 kfm.add (name: BUILT_IN_ATOMIC_LOAD_8, kf: make_unique<kf_atomic_load_n> ());
2091 kfm.add (name: BUILT_IN_ATOMIC_LOAD_16, kf: make_unique<kf_atomic_load_n> ());
2092 kfm.add (name: BUILT_IN_ATOMIC_STORE, kf: make_unique<kf_atomic_store> ());
2093 kfm.add (name: BUILT_IN_ATOMIC_STORE_N, kf: make_unique<kf_atomic_store_n> ());
2094 kfm.add (name: BUILT_IN_ATOMIC_STORE_1, kf: make_unique<kf_atomic_store_n> ());
2095 kfm.add (name: BUILT_IN_ATOMIC_STORE_2, kf: make_unique<kf_atomic_store_n> ());
2096 kfm.add (name: BUILT_IN_ATOMIC_STORE_4, kf: make_unique<kf_atomic_store_n> ());
2097 kfm.add (name: BUILT_IN_ATOMIC_STORE_8, kf: make_unique<kf_atomic_store_n> ());
2098 kfm.add (name: BUILT_IN_ATOMIC_STORE_16, kf: make_unique<kf_atomic_store_n> ());
2099 kfm.add (name: BUILT_IN_ATOMIC_ADD_FETCH_1,
2100 kf: make_unique<kf_atomic_op_fetch> (args: PLUS_EXPR));
2101 kfm.add (name: BUILT_IN_ATOMIC_ADD_FETCH_2,
2102 kf: make_unique<kf_atomic_op_fetch> (args: PLUS_EXPR));
2103 kfm.add (name: BUILT_IN_ATOMIC_ADD_FETCH_4,
2104 kf: make_unique<kf_atomic_op_fetch> (args: PLUS_EXPR));
2105 kfm.add (name: BUILT_IN_ATOMIC_ADD_FETCH_8,
2106 kf: make_unique<kf_atomic_op_fetch> (args: PLUS_EXPR));
2107 kfm.add (name: BUILT_IN_ATOMIC_ADD_FETCH_16,
2108 kf: make_unique<kf_atomic_op_fetch> (args: PLUS_EXPR));
2109 kfm.add (name: BUILT_IN_ATOMIC_SUB_FETCH_1,
2110 kf: make_unique<kf_atomic_op_fetch> (args: MINUS_EXPR));
2111 kfm.add (name: BUILT_IN_ATOMIC_SUB_FETCH_2,
2112 kf: make_unique<kf_atomic_op_fetch> (args: MINUS_EXPR));
2113 kfm.add (name: BUILT_IN_ATOMIC_SUB_FETCH_4,
2114 kf: make_unique<kf_atomic_op_fetch> (args: MINUS_EXPR));
2115 kfm.add (name: BUILT_IN_ATOMIC_SUB_FETCH_8,
2116 kf: make_unique<kf_atomic_op_fetch> (args: MINUS_EXPR));
2117 kfm.add (name: BUILT_IN_ATOMIC_SUB_FETCH_16,
2118 kf: make_unique<kf_atomic_op_fetch> (args: MINUS_EXPR));
2119 kfm.add (name: BUILT_IN_ATOMIC_AND_FETCH_1,
2120 kf: make_unique<kf_atomic_op_fetch> (args: BIT_AND_EXPR));
2121 kfm.add (name: BUILT_IN_ATOMIC_AND_FETCH_2,
2122 kf: make_unique<kf_atomic_op_fetch> (args: BIT_AND_EXPR));
2123 kfm.add (name: BUILT_IN_ATOMIC_AND_FETCH_4,
2124 kf: make_unique<kf_atomic_op_fetch> (args: BIT_AND_EXPR));
2125 kfm.add (name: BUILT_IN_ATOMIC_AND_FETCH_8,
2126 kf: make_unique<kf_atomic_op_fetch> (args: BIT_AND_EXPR));
2127 kfm.add (name: BUILT_IN_ATOMIC_AND_FETCH_16,
2128 kf: make_unique<kf_atomic_op_fetch> (args: BIT_AND_EXPR));
2129 kfm.add (name: BUILT_IN_ATOMIC_XOR_FETCH_1,
2130 kf: make_unique<kf_atomic_op_fetch> (args: BIT_XOR_EXPR));
2131 kfm.add (name: BUILT_IN_ATOMIC_XOR_FETCH_2,
2132 kf: make_unique<kf_atomic_op_fetch> (args: BIT_XOR_EXPR));
2133 kfm.add (name: BUILT_IN_ATOMIC_XOR_FETCH_4,
2134 kf: make_unique<kf_atomic_op_fetch> (args: BIT_XOR_EXPR));
2135 kfm.add (name: BUILT_IN_ATOMIC_XOR_FETCH_8,
2136 kf: make_unique<kf_atomic_op_fetch> (args: BIT_XOR_EXPR));
2137 kfm.add (name: BUILT_IN_ATOMIC_XOR_FETCH_16,
2138 kf: make_unique<kf_atomic_op_fetch> (args: BIT_XOR_EXPR));
2139 kfm.add (name: BUILT_IN_ATOMIC_OR_FETCH_1,
2140 kf: make_unique<kf_atomic_op_fetch> (args: BIT_IOR_EXPR));
2141 kfm.add (name: BUILT_IN_ATOMIC_OR_FETCH_2,
2142 kf: make_unique<kf_atomic_op_fetch> (args: BIT_IOR_EXPR));
2143 kfm.add (name: BUILT_IN_ATOMIC_OR_FETCH_4,
2144 kf: make_unique<kf_atomic_op_fetch> (args: BIT_IOR_EXPR));
2145 kfm.add (name: BUILT_IN_ATOMIC_OR_FETCH_8,
2146 kf: make_unique<kf_atomic_op_fetch> (args: BIT_IOR_EXPR));
2147 kfm.add (name: BUILT_IN_ATOMIC_OR_FETCH_16,
2148 kf: make_unique<kf_atomic_op_fetch> (args: BIT_IOR_EXPR));
2149 kfm.add (name: BUILT_IN_ATOMIC_FETCH_ADD_1,
2150 kf: make_unique<kf_atomic_fetch_op> (args: PLUS_EXPR));
2151 kfm.add (name: BUILT_IN_ATOMIC_FETCH_ADD_2,
2152 kf: make_unique<kf_atomic_fetch_op> (args: PLUS_EXPR));
2153 kfm.add (name: BUILT_IN_ATOMIC_FETCH_ADD_4,
2154 kf: make_unique<kf_atomic_fetch_op> (args: PLUS_EXPR));
2155 kfm.add (name: BUILT_IN_ATOMIC_FETCH_ADD_8,
2156 kf: make_unique<kf_atomic_fetch_op> (args: PLUS_EXPR));
2157 kfm.add (name: BUILT_IN_ATOMIC_FETCH_ADD_16,
2158 kf: make_unique<kf_atomic_fetch_op> (args: PLUS_EXPR));
2159 kfm.add (name: BUILT_IN_ATOMIC_FETCH_SUB_1,
2160 kf: make_unique<kf_atomic_fetch_op> (args: MINUS_EXPR));
2161 kfm.add (name: BUILT_IN_ATOMIC_FETCH_SUB_2,
2162 kf: make_unique<kf_atomic_fetch_op> (args: MINUS_EXPR));
2163 kfm.add (name: BUILT_IN_ATOMIC_FETCH_SUB_4,
2164 kf: make_unique<kf_atomic_fetch_op> (args: MINUS_EXPR));
2165 kfm.add (name: BUILT_IN_ATOMIC_FETCH_SUB_8,
2166 kf: make_unique<kf_atomic_fetch_op> (args: MINUS_EXPR));
2167 kfm.add (name: BUILT_IN_ATOMIC_FETCH_SUB_16,
2168 kf: make_unique<kf_atomic_fetch_op> (args: MINUS_EXPR));
2169 kfm.add (name: BUILT_IN_ATOMIC_FETCH_AND_1,
2170 kf: make_unique<kf_atomic_fetch_op> (args: BIT_AND_EXPR));
2171 kfm.add (name: BUILT_IN_ATOMIC_FETCH_AND_2,
2172 kf: make_unique<kf_atomic_fetch_op> (args: BIT_AND_EXPR));
2173 kfm.add (name: BUILT_IN_ATOMIC_FETCH_AND_4,
2174 kf: make_unique<kf_atomic_fetch_op> (args: BIT_AND_EXPR));
2175 kfm.add (name: BUILT_IN_ATOMIC_FETCH_AND_8,
2176 kf: make_unique<kf_atomic_fetch_op> (args: BIT_AND_EXPR));
2177 kfm.add (name: BUILT_IN_ATOMIC_FETCH_AND_16,
2178 kf: make_unique<kf_atomic_fetch_op> (args: BIT_AND_EXPR));
2179 kfm.add (name: BUILT_IN_ATOMIC_FETCH_XOR_1,
2180 kf: make_unique<kf_atomic_fetch_op> (args: BIT_XOR_EXPR));
2181 kfm.add (name: BUILT_IN_ATOMIC_FETCH_XOR_2,
2182 kf: make_unique<kf_atomic_fetch_op> (args: BIT_XOR_EXPR));
2183 kfm.add (name: BUILT_IN_ATOMIC_FETCH_XOR_4,
2184 kf: make_unique<kf_atomic_fetch_op> (args: BIT_XOR_EXPR));
2185 kfm.add (name: BUILT_IN_ATOMIC_FETCH_XOR_8,
2186 kf: make_unique<kf_atomic_fetch_op> (args: BIT_XOR_EXPR));
2187 kfm.add (name: BUILT_IN_ATOMIC_FETCH_XOR_16,
2188 kf: make_unique<kf_atomic_fetch_op> (args: BIT_XOR_EXPR));
2189 kfm.add (name: BUILT_IN_ATOMIC_FETCH_OR_1,
2190 kf: make_unique<kf_atomic_fetch_op> (args: BIT_IOR_EXPR));
2191 kfm.add (name: BUILT_IN_ATOMIC_FETCH_OR_2,
2192 kf: make_unique<kf_atomic_fetch_op> (args: BIT_IOR_EXPR));
2193 kfm.add (name: BUILT_IN_ATOMIC_FETCH_OR_4,
2194 kf: make_unique<kf_atomic_fetch_op> (args: BIT_IOR_EXPR));
2195 kfm.add (name: BUILT_IN_ATOMIC_FETCH_OR_8,
2196 kf: make_unique<kf_atomic_fetch_op> (args: BIT_IOR_EXPR));
2197 kfm.add (name: BUILT_IN_ATOMIC_FETCH_OR_16,
2198 kf: make_unique<kf_atomic_fetch_op> (args: BIT_IOR_EXPR));
2199}
2200
2201/* Handle calls to the various __builtin___ubsan_handle_*.
2202 These can return, but continuing after such a return
2203 isn't likely to be interesting to the user of the analyzer.
2204 Hence we terminate the analysis path at one of these calls. */
2205
2206class kf_ubsan_handler : public internal_known_function
2207{
2208 void impl_call_post (const call_details &cd) const final override
2209 {
2210 if (cd.get_ctxt ())
2211 cd.get_ctxt ()->terminate_path ();
2212 }
2213};
2214
2215static void
2216register_sanitizer_builtins (known_function_manager &kfm)
2217{
2218 kfm.add (name: BUILT_IN_UBSAN_HANDLE_NONNULL_ARG,
2219 kf: make_unique<kf_ubsan_handler> ());
2220}
2221
2222/* Populate KFM with instances of known functions supported by the core of the
2223 analyzer (as opposed to plugins). */
2224
2225void
2226register_known_functions (known_function_manager &kfm,
2227 region_model_manager &rmm)
2228{
2229 /* Debugging/test support functions, all with a "__analyzer_" prefix. */
2230 register_known_analyzer_functions (kfm);
2231
2232 /* Internal fns the analyzer has known_functions for. */
2233 {
2234 kfm.add (ifn: IFN_BUILTIN_EXPECT, kf: make_unique<kf_expect> ());
2235 kfm.add (ifn: IFN_UBSAN_BOUNDS, kf: make_unique<kf_ubsan_bounds> ());
2236 }
2237
2238 /* GCC built-ins that do not correspond to a function
2239 in the standard library. */
2240 {
2241 kfm.add (name: BUILT_IN_EXPECT, kf: make_unique<kf_expect> ());
2242 kfm.add (name: BUILT_IN_EXPECT_WITH_PROBABILITY, kf: make_unique<kf_expect> ());
2243 kfm.add (name: BUILT_IN_ALLOCA_WITH_ALIGN, kf: make_unique<kf_alloca> ());
2244 kfm.add (name: BUILT_IN_STACK_RESTORE, kf: make_unique<kf_stack_restore> ());
2245 kfm.add (name: BUILT_IN_STACK_SAVE, kf: make_unique<kf_stack_save> ());
2246
2247 register_atomic_builtins (kfm);
2248 register_sanitizer_builtins (kfm);
2249 register_varargs_builtins (kfm);
2250 }
2251
2252 /* Known builtins and C standard library functions
2253 the analyzer has known functions for. */
2254 {
2255 kfm.add (name: "alloca", kf: make_unique<kf_alloca> ());
2256 kfm.add (name: "__builtin_alloca", kf: make_unique<kf_alloca> ());
2257 kfm.add (name: "calloc", kf: make_unique<kf_calloc> ());
2258 kfm.add (name: "__builtin_calloc", kf: make_unique<kf_calloc> ());
2259 kfm.add (name: "free", kf: make_unique<kf_free> ());
2260 kfm.add (name: "__builtin_free", kf: make_unique<kf_free> ());
2261 kfm.add (name: "malloc", kf: make_unique<kf_malloc> ());
2262 kfm.add (name: "__builtin_malloc", kf: make_unique<kf_malloc> ());
2263 kfm.add (name: "memcpy",
2264 kf: make_unique<kf_memcpy_memmove> (args: kf_memcpy_memmove::KF_MEMCPY));
2265 kfm.add (name: "__builtin_memcpy",
2266 kf: make_unique<kf_memcpy_memmove> (args: kf_memcpy_memmove::KF_MEMCPY));
2267 kfm.add (name: "__memcpy_chk", kf: make_unique<kf_memcpy_memmove>
2268 (args: kf_memcpy_memmove::KF_MEMCPY_CHK));
2269 kfm.add (name: "__builtin___memcpy_chk", kf: make_unique<kf_memcpy_memmove>
2270 (args: kf_memcpy_memmove::KF_MEMCPY_CHK));
2271 kfm.add (name: "memmove",
2272 kf: make_unique<kf_memcpy_memmove> (args: kf_memcpy_memmove::KF_MEMMOVE));
2273 kfm.add (name: "__builtin_memmove",
2274 kf: make_unique<kf_memcpy_memmove> (args: kf_memcpy_memmove::KF_MEMMOVE));
2275 kfm.add (name: "__memmove_chk", kf: make_unique<kf_memcpy_memmove>
2276 (args: kf_memcpy_memmove::KF_MEMMOVE_CHK));
2277 kfm.add (name: "__builtin___memmove_chk", kf: make_unique<kf_memcpy_memmove>
2278 (args: kf_memcpy_memmove::KF_MEMMOVE_CHK));
2279 kfm.add (name: "memset", kf: make_unique<kf_memset> (args: false));
2280 kfm.add (name: "__builtin_memset", kf: make_unique<kf_memset> (args: false));
2281 kfm.add (name: "__memset_chk", kf: make_unique<kf_memset> (args: true));
2282 kfm.add (name: "__builtin___memset_chk", kf: make_unique<kf_memset> (args: true));
2283 kfm.add (name: "realloc", kf: make_unique<kf_realloc> ());
2284 kfm.add (name: "__builtin_realloc", kf: make_unique<kf_realloc> ());
2285 kfm.add (name: "sprintf", kf: make_unique<kf_sprintf> ());
2286 kfm.add (name: "__builtin_sprintf", kf: make_unique<kf_sprintf> ());
2287 kfm.add (name: "strchr", kf: make_unique<kf_strchr> ());
2288 kfm.add (name: "__builtin_strchr", kf: make_unique<kf_strchr> ());
2289 kfm.add (name: "strcpy", kf: make_unique<kf_strcpy> (args: 2, args: false));
2290 kfm.add (name: "__builtin_strcpy", kf: make_unique<kf_strcpy> (args: 2, args: false));
2291 kfm.add (name: "__strcpy_chk", kf: make_unique<kf_strcpy> (args: 3, args: true));
2292 kfm.add (name: "__builtin___strcpy_chk", kf: make_unique<kf_strcpy> (args: 3, args: true));
2293 kfm.add (name: "strcat", kf: make_unique<kf_strcat> (args: 2, args: false));
2294 kfm.add (name: "__builtin_strcat", kf: make_unique<kf_strcat> (args: 2, args: false));
2295 kfm.add (name: "__strcat_chk", kf: make_unique<kf_strcat> (args: 3, args: true));
2296 kfm.add (name: "__builtin___strcat_chk", kf: make_unique<kf_strcat> (args: 3, args: true));
2297 kfm.add (name: "strdup", kf: make_unique<kf_strdup> ());
2298 kfm.add (name: "__builtin_strdup", kf: make_unique<kf_strdup> ());
2299 kfm.add (name: "strncpy", kf: make_unique<kf_strncpy> ());
2300 kfm.add (name: "__builtin_strncpy", kf: make_unique<kf_strncpy> ());
2301 kfm.add (name: "strndup", kf: make_unique<kf_strndup> ());
2302 kfm.add (name: "__builtin_strndup", kf: make_unique<kf_strndup> ());
2303 kfm.add (name: "strlen", kf: make_unique<kf_strlen> ());
2304 kfm.add (name: "__builtin_strlen", kf: make_unique<kf_strlen> ());
2305 kfm.add (name: "strstr", kf: make_unique<kf_strstr> ());
2306 kfm.add (name: "__builtin_strstr", kf: make_unique<kf_strstr> ());
2307
2308 register_atomic_builtins (kfm);
2309 register_varargs_builtins (kfm);
2310 }
2311
2312 /* Known POSIX functions, and some non-standard extensions. */
2313 {
2314 kfm.add (name: "fopen", kf: make_unique<kf_fopen> ());
2315 kfm.add (name: "putenv", kf: make_unique<kf_putenv> ());
2316 kfm.add (name: "strtok", kf: make_unique<kf_strtok> (args&: rmm));
2317
2318 register_known_fd_functions (kfm);
2319 register_known_file_functions (kfm);
2320 }
2321
2322 /* glibc functions. */
2323 {
2324 kfm.add (name: "__errno_location", kf: make_unique<kf_errno_location> ());
2325 kfm.add (name: "error", kf: make_unique<kf_error> (args: 3));
2326 kfm.add (name: "error_at_line", kf: make_unique<kf_error> (args: 5));
2327 }
2328
2329 /* Other implementations of C standard library. */
2330 {
2331 /* According to PR 107807 comment #2, Solaris implements "errno"
2332 like this:
2333 extern int *___errno(void) __attribute__((__const__));
2334 #define errno (*(___errno()))
2335 and macOS like this:
2336 extern int * __error(void);
2337 #define errno (*__error())
2338 and similarly __errno for newlib.
2339 Add these as synonyms for "__errno_location". */
2340 kfm.add (name: "___errno", kf: make_unique<kf_errno_location> ());
2341 kfm.add (name: "__error", kf: make_unique<kf_errno_location> ());
2342 kfm.add (name: "__errno", kf: make_unique<kf_errno_location> ());
2343 }
2344
2345 /* Language-specific support functions. */
2346 register_known_functions_lang_cp (kfm);
2347}
2348
2349} // namespace ana
2350
2351#endif /* #if ENABLE_ANALYZER */
2352

source code of gcc/analyzer/kf.cc