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