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 28 : undefined_function_behavior (const call_details &cd)
41 28 : : m_call_stmt (cd.get_call_stmt ()),
42 28 : m_callee_fndecl (cd.get_fndecl_for_call ())
43 : {
44 28 : gcc_assert (m_callee_fndecl);
45 28 : }
46 :
47 250 : const char *get_kind () const final override
48 : {
49 250 : return "undefined_behavior";
50 : }
51 :
52 28 : bool operator== (const undefined_function_behavior &other) const
53 : {
54 28 : return (&m_call_stmt == &other.m_call_stmt
55 28 : && m_callee_fndecl == other.m_callee_fndecl);
56 : }
57 :
58 28 : bool terminate_path_p () const final override { return true; }
59 :
60 76 : 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 2270 : pure_known_function_with_default_return::
71 : impl_call_pre (const call_details &cd) const
72 : {
73 2270 : cd.set_any_lhs_with_defaults ();
74 2270 : }
75 :
76 : /* Implementations of specific functions. */
77 :
78 : /* Handler for "alloca". */
79 :
80 10269 : class kf_alloca : public builtin_known_function
81 : {
82 : public:
83 1326 : bool matches_call_types_p (const call_details &cd) const final override
84 : {
85 1326 : 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 527 : kf_alloca::impl_call_pre (const call_details &cd) const
96 : {
97 527 : const svalue *size_sval = cd.get_arg_svalue (0);
98 :
99 527 : region_model *model = cd.get_model ();
100 527 : region_model_manager *mgr = cd.get_manager ();
101 :
102 527 : const region *new_reg
103 527 : = model->create_region_for_alloca (size_sval, cd.get_ctxt ());
104 527 : const svalue *ptr_sval
105 527 : = mgr->get_ptr_svalue (cd.get_lhs_type (), new_reg);
106 527 : cd.maybe_set_lhs (ptr_sval);
107 527 : }
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 3423 : 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 20538 : 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 39 : void impl_call_pre (const call_details &cd) const final override
172 : {
173 39 : const svalue *ptr_sval = cd.get_arg_svalue (0);
174 39 : tree ptr_tree = cd.get_arg_tree (0);
175 39 : const svalue *set_sval = cd.get_arg_svalue (1);
176 : /* Ignore the memorder param. */
177 :
178 39 : region_model *model = cd.get_model ();
179 39 : region_model_context *ctxt = cd.get_ctxt ();
180 :
181 39 : const region *dst_region = model->deref_rvalue (ptr_sval, ptr_tree, ctxt);
182 39 : const svalue *ret_sval = model->get_store_value (dst_region, ctxt);
183 39 : model->set_value (dst_region, set_sval, ctxt);
184 39 : cd.maybe_set_lhs (ret_sval);
185 39 : }
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 85575 : 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 85575 : 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 3423 : 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 3423 : 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 20538 : 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 20538 : 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 10269 : class kf_expect : public internal_known_function
409 : {
410 : public:
411 63 : void impl_call_pre (const call_details &cd) const final override
412 : {
413 : /* __builtin_expect's return value is its initial argument. */
414 63 : const svalue *sval = cd.get_arg_svalue (0);
415 63 : cd.maybe_set_lhs (sval);
416 63 : }
417 : };
418 :
419 : /* Handler for "calloc". */
420 :
421 10269 : class kf_calloc : public builtin_known_function
422 : {
423 : public:
424 1576 : bool matches_call_types_p (const call_details &cd) const final override
425 : {
426 1576 : return (cd.num_args () == 2
427 1576 : && cd.arg_is_size_p (0)
428 3152 : && 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 454 : kf_calloc::impl_call_pre (const call_details &cd) const
440 : {
441 454 : region_model *model = cd.get_model ();
442 454 : region_model_manager *mgr = cd.get_manager ();
443 454 : const svalue *nmemb_sval = cd.get_arg_svalue (0);
444 454 : const svalue *size_sval = cd.get_arg_svalue (1);
445 : /* TODO: check for overflow here? */
446 454 : const svalue *prod_sval
447 454 : = mgr->get_or_create_binop (size_type_node, MULT_EXPR,
448 : nmemb_sval, size_sval);
449 454 : const region *new_reg
450 454 : = model->get_or_create_region_for_heap_alloc (prod_sval, cd.get_ctxt ());
451 454 : const region *sized_reg
452 454 : = mgr->get_sized_region (new_reg, NULL_TREE, prod_sval);
453 454 : model->zero_fill_region (sized_reg, cd.get_ctxt ());
454 454 : if (cd.get_lhs_type ())
455 : {
456 454 : const svalue *ptr_sval
457 454 : = mgr->get_ptr_svalue (cd.get_lhs_type (), new_reg);
458 454 : cd.maybe_set_lhs (ptr_sval);
459 : }
460 454 : }
461 :
462 : /* Handler for glibc's "__errno_location". */
463 :
464 17115 : class kf_errno_location : public known_function
465 : {
466 : public:
467 3288 : bool matches_call_types_p (const call_details &cd) const final override
468 : {
469 3288 : return cd.num_args () == 0;
470 : }
471 :
472 519 : void impl_call_pre (const call_details &cd) const final override
473 : {
474 519 : if (cd.get_lhs_region ())
475 : {
476 519 : region_model_manager *mgr = cd.get_manager ();
477 519 : const region *errno_reg = mgr->get_errno_region ();
478 519 : const svalue *errno_ptr = mgr->get_ptr_svalue (cd.get_lhs_type (),
479 : errno_reg);
480 519 : cd.maybe_set_lhs (errno_ptr);
481 : }
482 519 : }
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 13692 : kf_error (unsigned min_args) : m_min_args (min_args) {}
493 :
494 467 : bool matches_call_types_p (const call_details &cd) const final override
495 : {
496 467 : return (cd.num_args () >= m_min_args
497 467 : && 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 44 : 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 44 : tree status = cd.get_arg_tree (0);
513 44 : region_model_context *ctxt = cd.get_ctxt ();
514 44 : region_model *model = cd.get_model ();
515 44 : 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 44 : const int fmt_arg_idx = (m_min_args == 3) ? 2 : 4;
521 44 : model->check_for_null_terminated_string_arg (cd, fmt_arg_idx);
522 44 : }
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 3423 : class kf_fopen : public known_function
531 : {
532 : public:
533 2889 : bool matches_call_types_p (const call_details &cd) const final override
534 : {
535 2889 : return (cd.num_args () == 2
536 2889 : && cd.arg_is_pointer_p (0)
537 5778 : && cd.arg_is_pointer_p (1));
538 : }
539 :
540 780 : void impl_call_pre (const call_details &cd) const final override
541 : {
542 780 : cd.check_for_null_terminated_string_arg (0);
543 780 : cd.check_for_null_terminated_string_arg (1);
544 780 : 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 780 : }
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 10269 : class kf_free : public builtin_known_function
567 : {
568 : public:
569 68291 : bool matches_call_types_p (const call_details &cd) const final override
570 : {
571 68291 : return (cd.num_args () == 1 && cd.arg_is_pointer_p (0));
572 : }
573 65790 : enum built_in_function builtin_code () const final override
574 : {
575 65790 : return BUILT_IN_FREE;
576 : }
577 : void impl_call_post (const call_details &cd) const final override;
578 : };
579 :
580 : void
581 12695 : kf_free::impl_call_post (const call_details &cd) const
582 : {
583 12695 : const svalue *ptr_sval = cd.get_arg_svalue (0);
584 12695 : 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 10232 : region_model *model = cd.get_model ();
589 10232 : model->unbind_region_and_descendents (freed_reg, poison_kind::freed);
590 10232 : model->unset_dynamic_extents (freed_reg);
591 : }
592 12695 : }
593 :
594 : /* Handle the on_call_pre part of "malloc". */
595 :
596 10269 : class kf_malloc : public builtin_known_function
597 : {
598 : public:
599 45057 : bool matches_call_types_p (const call_details &cd) const final override
600 : {
601 45057 : return (cd.num_args () == 1
602 45057 : && cd.arg_is_size_p (0));
603 : }
604 36228 : enum built_in_function builtin_code () const final override
605 : {
606 36228 : return BUILT_IN_MALLOC;
607 : }
608 : void impl_call_pre (const call_details &cd) const final override;
609 : };
610 :
611 : void
612 10444 : kf_malloc::impl_call_pre (const call_details &cd) const
613 : {
614 10444 : region_model *model = cd.get_model ();
615 10444 : region_model_manager *mgr = cd.get_manager ();
616 10444 : const svalue *size_sval = cd.get_arg_svalue (0);
617 10444 : const region *new_reg
618 10444 : = model->get_or_create_region_for_heap_alloc (size_sval, cd.get_ctxt ());
619 10444 : if (cd.get_lhs_type ())
620 : {
621 10444 : const svalue *ptr_sval
622 10444 : = mgr->get_ptr_svalue (cd.get_lhs_type (), new_reg);
623 10444 : cd.maybe_set_lhs (ptr_sval);
624 : }
625 10444 : }
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 34230 : kf_memcpy_memmove (enum kf_memcpy_memmove_variant variant)
641 34230 : : m_variant (variant) {};
642 3270 : bool matches_call_types_p (const call_details &cd) const final override
643 : {
644 3270 : return (cd.num_args () == 3
645 3270 : && cd.arg_is_pointer_p (0)
646 3270 : && cd.arg_is_pointer_p (1)
647 6540 : && 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 564 : kf_memcpy_memmove::impl_call_pre (const call_details &cd) const
672 : {
673 564 : const svalue *dest_ptr_sval = cd.get_arg_svalue (0);
674 564 : const svalue *src_ptr_sval = cd.get_arg_svalue (1);
675 564 : const svalue *num_bytes_sval = cd.get_arg_svalue (2);
676 :
677 564 : region_model *model = cd.get_model ();
678 :
679 564 : const region *dest_reg
680 564 : = model->deref_rvalue (dest_ptr_sval, cd.get_arg_tree (0), cd.get_ctxt ());
681 564 : const region *src_reg
682 564 : = model->deref_rvalue (src_ptr_sval, cd.get_arg_tree (1), cd.get_ctxt ());
683 :
684 564 : cd.maybe_set_lhs (dest_ptr_sval);
685 : /* Check for overlap. */
686 564 : switch (m_variant)
687 : {
688 543 : case KF_MEMCPY:
689 543 : case KF_MEMCPY_CHK:
690 543 : cd.complain_about_overlap (0, 1, num_bytes_sval);
691 543 : 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 564 : model->copy_bytes (dest_reg,
702 : src_reg, cd.get_arg_tree (1),
703 : num_bytes_sval,
704 : cd.get_ctxt ());
705 564 : }
706 :
707 : /* Handler for "memset" and "__builtin_memset". */
708 :
709 : class kf_memset : public builtin_known_function
710 : {
711 : public:
712 17115 : kf_memset (bool chk_variant) : m_chk_variant (chk_variant) {}
713 3675 : bool matches_call_types_p (const call_details &cd) const final override
714 : {
715 3675 : return (cd.num_args () == 3 && cd.arg_is_pointer_p (0));
716 : }
717 3416 : enum built_in_function builtin_code () const final override
718 : {
719 3416 : 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 742 : kf_memset::impl_call_pre (const call_details &cd) const
728 : {
729 742 : const svalue *dest_sval = cd.get_arg_svalue (0);
730 742 : const svalue *fill_value_sval = cd.get_arg_svalue (1);
731 742 : const svalue *num_bytes_sval = cd.get_arg_svalue (2);
732 :
733 742 : region_model *model = cd.get_model ();
734 742 : region_model_manager *mgr = cd.get_manager ();
735 :
736 742 : const region *dest_reg
737 742 : = model->deref_rvalue (dest_sval, cd.get_arg_tree (0), cd.get_ctxt ());
738 :
739 742 : const svalue *fill_value_u8
740 742 : = mgr->get_or_create_cast (unsigned_char_type_node, fill_value_sval);
741 :
742 742 : const region *sized_dest_reg = mgr->get_sized_region (dest_reg,
743 : NULL_TREE,
744 : num_bytes_sval);
745 742 : model->fill_region (sized_dest_reg, fill_value_u8, cd.get_ctxt ());
746 :
747 742 : cd.maybe_set_lhs (dest_sval);
748 742 : }
749 :
750 : /* A subclass of pending_diagnostic for complaining about functions on the
751 : 'mktemp' family called on a string literal. */
752 :
753 : class mktemp_of_string_literal : public undefined_function_behavior
754 : {
755 : public:
756 20 : mktemp_of_string_literal (const call_details &cd)
757 20 : : undefined_function_behavior (cd)
758 : {
759 : }
760 :
761 : int
762 40 : get_controlling_option () const final override
763 : {
764 40 : return OPT_Wanalyzer_mktemp_of_string_literal;
765 : }
766 :
767 : bool
768 20 : emit (diagnostic_emission_context &ctxt) final override
769 : {
770 20 : auto_diagnostic_group d;
771 :
772 : /* SEI CERT C Coding Standard: "STR30-C. Do not attempt to modify string
773 : literals. */
774 20 : diagnostics::metadata::precanned_rule rule (
775 20 : "STR30-C", "https://wiki.sei.cmu.edu/confluence/x/VtYxBQ");
776 20 : ctxt.add_rule (rule);
777 :
778 20 : bool warned = ctxt.warn ("%qE on a string literal", get_callee_fndecl ());
779 20 : if (warned)
780 20 : inform (ctxt.get_location (),
781 : "use a writable character array as the template argument,"
782 : " e.g. %<char tmpl[] = \"/tmp/fooXXXXXX\"%>");
783 40 : return warned;
784 20 : }
785 :
786 : bool
787 40 : describe_final_event (pretty_printer &pp,
788 : const evdesc::final_event &) final override
789 : {
790 40 : pp_printf (&pp, "%qE on a string literal", get_callee_fndecl ());
791 40 : return true;
792 : }
793 : };
794 :
795 : /* A subclass of pending_diagnostic for complaining about functions in the
796 : 'mktemp' family called with a template that does not contain the expected
797 : "XXXXXX" placeholder. */
798 :
799 : class mktemp_missing_placeholder
800 : : public pending_diagnostic_subclass<mktemp_missing_placeholder>
801 : {
802 : public:
803 34 : mktemp_missing_placeholder (const call_details &cd, size_t trailing_len)
804 68 : : m_call_stmt (cd.get_call_stmt ()), m_fndecl (cd.get_fndecl_for_call ()),
805 34 : m_trailing_len (trailing_len)
806 : {
807 34 : gcc_assert (m_fndecl);
808 34 : }
809 :
810 : const char *
811 1007 : get_kind () const final override
812 : {
813 1007 : return "mktemp_missing_placeholder";
814 : }
815 :
816 : bool
817 34 : operator== (const mktemp_missing_placeholder &other) const
818 : {
819 34 : return &m_call_stmt == &other.m_call_stmt;
820 : }
821 :
822 : int
823 68 : get_controlling_option () const final override
824 : {
825 68 : return OPT_Wanalyzer_mktemp_missing_placeholder;
826 : }
827 :
828 : bool
829 34 : emit (diagnostic_emission_context &ctxt) final override
830 : {
831 34 : if (m_trailing_len == 0)
832 24 : return ctxt.warn ("%qE template string does not end with %qs", m_fndecl,
833 24 : "XXXXXX");
834 : else
835 10 : return ctxt.warn ("%qE template string does not contain %qs"
836 : " before a %zu-character suffix",
837 10 : m_fndecl, "XXXXXX", m_trailing_len);
838 : }
839 :
840 : bool
841 68 : describe_final_event (pretty_printer &pp,
842 : const evdesc::final_event &) final override
843 : {
844 68 : if (m_trailing_len == 0)
845 48 : pp_printf (&pp, "%qE template string does not end with %qs", m_fndecl,
846 : "XXXXXX");
847 : else
848 20 : pp_printf (&pp,
849 : "%qE template string does not contain %qs"
850 : " before a %zu-character suffix",
851 : m_fndecl, "XXXXXX", m_trailing_len);
852 68 : return true;
853 : }
854 :
855 : private:
856 : const gimple &m_call_stmt;
857 : tree m_fndecl; // non-NULL
858 : size_t m_trailing_len;
859 : };
860 :
861 : /* A subclass of pending_diagnostic for complaining about 'mkostemp'
862 : or 'mkostemps' called with flags that are already included
863 : internally (O_CREAT, O_EXCL, O_RDWR). */
864 :
865 : class mkostemp_redundant_flags
866 : : public pending_diagnostic_subclass<mkostemp_redundant_flags>
867 : {
868 : public:
869 8 : mkostemp_redundant_flags (const call_details &cd)
870 8 : : m_call_stmt (cd.get_call_stmt ()), m_fndecl (cd.get_fndecl_for_call ())
871 : {
872 8 : gcc_assert (m_fndecl);
873 8 : }
874 :
875 : const char *
876 484 : get_kind () const final override
877 : {
878 484 : return "mkostemp_redundant_flags";
879 : }
880 :
881 : bool
882 8 : operator== (const mkostemp_redundant_flags &other) const
883 : {
884 8 : return &m_call_stmt == &other.m_call_stmt;
885 : }
886 :
887 : int
888 16 : get_controlling_option () const final override
889 : {
890 16 : return OPT_Wanalyzer_mkostemp_redundant_flags;
891 : }
892 :
893 : bool
894 8 : emit (diagnostic_emission_context &ctxt) final override
895 : {
896 8 : return ctxt.warn (
897 : "%qE flags argument should not include %<O_RDWR%>, %<O_CREAT%>,"
898 : " or %<O_EXCL%> as these are already implied",
899 8 : m_fndecl);
900 : }
901 :
902 : bool
903 16 : describe_final_event (pretty_printer &pp,
904 : const evdesc::final_event &) final override
905 : {
906 16 : pp_printf (&pp,
907 : "%qE flags argument should not include %<O_RDWR%>, %<O_CREAT%>,"
908 : " or %<O_EXCL%> as these are already implied",
909 : m_fndecl);
910 16 : return true;
911 : }
912 :
913 : private:
914 : const gimple &m_call_stmt;
915 : tree m_fndecl; // non-NULL
916 : };
917 :
918 : class kf_mktemp_family : public known_function
919 : {
920 : public:
921 : /* Describes how the mktemp-family function signals success or failure
922 : through its return value. */
923 : enum class outcome
924 : {
925 : /* Returns fd on success, -1 on failure (mkstemp, mkostemp, etc.). */
926 : fd,
927 :
928 : /* Returns pointer on success, NULL on failure (mkdtemp). */
929 : null_ptr,
930 :
931 : /* Returns template pointer; first byte \0 on failure (mktemp). */
932 : modif_tmpl
933 : };
934 :
935 : protected:
936 : /* suffixlen_arg_idx is the index of the suffixlen argument, or -1
937 : if there is none (trailing_len is implicitly 0). */
938 20538 : kf_mktemp_family (outcome oc, int suffixlen_arg_idx)
939 20538 : : m_outcome (oc), m_suffixlen_arg_idx (suffixlen_arg_idx)
940 : {
941 : }
942 :
943 : class failure;
944 : class success;
945 :
946 : static void check_for_string_literal_arg (const call_details &cd);
947 :
948 : /* Check whether the flags argument at FLAGS_ARG_IDX contains any of
949 : O_RDWR, O_CREAT, or O_EXCL, which are already included internally
950 : by mkostemp/mkostemps. */
951 : static void check_flags (const call_details &cd, unsigned int flags_arg_idx);
952 :
953 : HOST_WIDE_INT get_trailing_len (const call_details &cd,
954 : tristate &valid) const;
955 :
956 : void impl_call_post (const call_details &cd) const final override;
957 :
958 : private:
959 : outcome m_outcome;
960 : int m_suffixlen_arg_idx;
961 :
962 : static const int PLACEHOLDER_LEN = 6;
963 :
964 : /* Return true if the placeholder is "XXXXXX", false if it definitely isn't,
965 : or unknown if we can't determine. */
966 : static tristate check_placeholder (const call_details &cd,
967 : size_t trailing_len,
968 : const svalue *ptr_sval,
969 : const svalue *strlen_sval);
970 : };
971 :
972 : void
973 122 : kf_mktemp_family::check_for_string_literal_arg (const call_details &cd)
974 : {
975 122 : region_model_context *ctxt = cd.get_ctxt ();
976 122 : gcc_assert (ctxt);
977 122 : cd.get_model ()->check_for_null_terminated_string_arg (cd, 0, false,
978 : nullptr);
979 122 : if (cd.get_arg_string_literal (0))
980 : {
981 20 : ctxt->warn (std::make_unique<mktemp_of_string_literal> (cd));
982 20 : ctxt->terminate_path ();
983 : }
984 122 : }
985 :
986 : void
987 49 : kf_mktemp_family::check_flags (const call_details &cd,
988 : unsigned int flags_arg_idx)
989 : {
990 49 : region_model_context *ctxt = cd.get_ctxt ();
991 49 : gcc_assert (ctxt);
992 :
993 49 : const svalue *flags_sval = cd.get_arg_svalue (flags_arg_idx);
994 49 : const constant_svalue *cst = flags_sval->dyn_cast_constant_svalue ();
995 49 : if (!cst)
996 : return;
997 :
998 39 : unsigned HOST_WIDE_INT flags = TREE_INT_CST_LOW (cst->get_constant ());
999 :
1000 : /* Check whether any of the implicit flags are redundantly specified. */
1001 39 : unsigned HOST_WIDE_INT implicit_flags = 0;
1002 156 : for (const char *name : { "O_RDWR", "O_CREAT", "O_EXCL" })
1003 117 : if (tree cst_tree = get_stashed_constant_by_name (name))
1004 117 : implicit_flags |= TREE_INT_CST_LOW (cst_tree);
1005 :
1006 39 : if (flags & implicit_flags)
1007 8 : ctxt->warn (std::make_unique<mkostemp_redundant_flags> (cd));
1008 : }
1009 :
1010 : /* Extract the trailing length from the suffixlen argument.
1011 :
1012 : Returns the trailing length on success, or -1 if the suffixlen is
1013 : not a compile-time constant. Sets VALID to TS_FALSE if the
1014 : suffixlen is a negative constant (always invalid). */
1015 :
1016 : HOST_WIDE_INT
1017 102 : kf_mktemp_family::get_trailing_len (const call_details &cd,
1018 : tristate &valid) const
1019 : {
1020 102 : if (m_suffixlen_arg_idx < 0)
1021 : return 0;
1022 :
1023 36 : const svalue *suffixlen_sval = cd.get_arg_svalue (m_suffixlen_arg_idx);
1024 36 : const constant_svalue *cst = suffixlen_sval->dyn_cast_constant_svalue ();
1025 36 : if (!cst)
1026 : return -1;
1027 :
1028 : /* TODO: Negative suffixlen is always wrong and potentially OOB, maybe add a
1029 : warning in the future? */
1030 26 : if (tree_int_cst_sgn (cst->get_constant ()) < 0)
1031 : {
1032 0 : valid = tristate::TS_FALSE;
1033 0 : return -1;
1034 : }
1035 :
1036 26 : return TREE_INT_CST_LOW (cst->get_constant ());
1037 : }
1038 :
1039 : /* Model the failure outcome of a mktemp-family function call.
1040 :
1041 : Sets the return value according to the function's convention (fd == -1, NULL
1042 : pointer, or '\0' in template[0]) and sets errno. */
1043 :
1044 : class kf_mktemp_family::failure : public failed_call_info
1045 : {
1046 : public:
1047 102 : failure (const call_details &cd, outcome oc)
1048 102 : : failed_call_info (cd), m_outcome (oc)
1049 : {
1050 : }
1051 :
1052 : bool
1053 102 : update_model (region_model *model, const exploded_edge *,
1054 : region_model_context *ctxt) const final override
1055 : {
1056 102 : const call_details cd (get_call_details (model, ctxt));
1057 :
1058 102 : switch (m_outcome)
1059 : {
1060 73 : case outcome::fd:
1061 73 : model->update_for_int_cst_return (cd, -1, true);
1062 73 : break;
1063 14 : case outcome::null_ptr:
1064 14 : model->update_for_null_return (cd, true);
1065 14 : break;
1066 15 : case outcome::modif_tmpl:
1067 15 : {
1068 15 : const svalue *first_arg_svalue = cd.get_arg_svalue (0);
1069 :
1070 : /* Return the same pointer that was passed in. */
1071 15 : cd.maybe_set_lhs (first_arg_svalue);
1072 :
1073 15 : region_model_manager *mgr = cd.get_manager ();
1074 15 : const region *template_reg = model->deref_rvalue (
1075 : first_arg_svalue, cd.get_arg_tree (0), ctxt);
1076 :
1077 : /* mktemp may have modified the X positions before failing;
1078 : invalidate the old buffer contents. */
1079 15 : model->mark_region_as_unknown (template_reg, nullptr);
1080 :
1081 : /* Then: template[0] = '\0'. */
1082 15 : const svalue *nul = mgr->get_or_create_int_cst (char_type_node, 0);
1083 15 : model->set_value (template_reg, nul, ctxt);
1084 15 : break;
1085 : }
1086 0 : default:
1087 0 : gcc_unreachable ();
1088 : }
1089 :
1090 102 : model->set_errno (cd);
1091 102 : return true;
1092 : }
1093 :
1094 : private:
1095 : outcome m_outcome;
1096 : };
1097 :
1098 : /* Model the success outcome of a mktemp-family function call.
1099 :
1100 : For fd-returning functions, conjures a non-negative fd and marks it valid.
1101 : For pointer-returning functions, returns the template pointer and marks the
1102 : template region as modified. */
1103 :
1104 : class kf_mktemp_family::success : public success_call_info
1105 : {
1106 : public:
1107 68 : success (const call_details &cd, outcome oc)
1108 68 : : success_call_info (cd), m_outcome (oc)
1109 : {
1110 : }
1111 :
1112 : bool
1113 170 : update_model (region_model *model, const exploded_edge *,
1114 : region_model_context *ctxt) const final override
1115 : {
1116 170 : const call_details cd (get_call_details (model, ctxt));
1117 :
1118 170 : switch (m_outcome)
1119 : {
1120 152 : case outcome::fd:
1121 152 : {
1122 152 : region_model_manager *mgr = cd.get_manager ();
1123 152 : const region *lhs_reg = cd.get_lhs_region ();
1124 : /* conjured_svalue needs a non-null id_reg. When there is
1125 : no LHS, use an unknown symbolic region; we still conjure
1126 : and mark the fd so the leak checker can see it. */
1127 152 : const region *id_reg
1128 152 : = lhs_reg ? lhs_reg
1129 116 : : mgr->get_unknown_symbolic_region (integer_type_node);
1130 :
1131 152 : tree lhs_type = cd.get_lhs_type ();
1132 152 : if (!lhs_type)
1133 116 : lhs_type = integer_type_node;
1134 :
1135 152 : conjured_purge p (model, ctxt);
1136 152 : const svalue *fd_sval = mgr->get_or_create_conjured_svalue (
1137 152 : lhs_type, &cd.get_call_stmt (), id_reg, p);
1138 152 : const svalue *zero = mgr->get_or_create_int_cst (lhs_type, 0);
1139 :
1140 152 : if (!model->add_constraint (fd_sval, GE_EXPR, zero, ctxt))
1141 0 : return false;
1142 :
1143 152 : if (lhs_reg)
1144 36 : model->set_value (lhs_reg, fd_sval, ctxt);
1145 :
1146 : /* TODO: transition to an unchecked state so that
1147 : use-without-check can be detected. */
1148 152 : model->mark_as_valid_fd (fd_sval, ctxt);
1149 152 : break;
1150 : }
1151 11 : case outcome::null_ptr:
1152 11 : {
1153 11 : region_model_manager *mgr = cd.get_manager ();
1154 11 : const svalue *first_arg_svalue = cd.get_arg_svalue (0);
1155 11 : cd.maybe_set_lhs (first_arg_svalue);
1156 :
1157 : /* On success, mkdtemp returns non-NULL. */
1158 11 : const svalue *null_ptr
1159 11 : = mgr->get_or_create_int_cst (first_arg_svalue->get_type (), 0);
1160 11 : if (!model->add_constraint (first_arg_svalue, NE_EXPR, null_ptr,
1161 : ctxt))
1162 : return false;
1163 :
1164 10 : const region *template_reg = model->deref_rvalue (
1165 : first_arg_svalue, cd.get_arg_tree (0), ctxt);
1166 10 : model->mark_region_as_unknown (template_reg, nullptr);
1167 10 : break;
1168 : }
1169 7 : case outcome::modif_tmpl:
1170 7 : {
1171 7 : const svalue *first_arg_svalue = cd.get_arg_svalue (0);
1172 7 : cd.maybe_set_lhs (first_arg_svalue);
1173 7 : const region *template_reg = model->deref_rvalue (
1174 : first_arg_svalue, cd.get_arg_tree (0), ctxt);
1175 7 : model->mark_region_as_unknown (template_reg, nullptr);
1176 7 : break;
1177 : }
1178 0 : default:
1179 0 : gcc_unreachable ();
1180 : }
1181 :
1182 : return true;
1183 : }
1184 :
1185 : private:
1186 : outcome m_outcome;
1187 : };
1188 :
1189 : /* Bifurcate into success and failure paths. The template placeholder is
1190 : validated when possible. If definitely invalid, only the failure path is
1191 : explored. */
1192 :
1193 : void
1194 102 : kf_mktemp_family::impl_call_post (const call_details &cd) const
1195 : {
1196 102 : if (!cd.get_ctxt ())
1197 0 : return;
1198 :
1199 102 : tristate valid = tristate::TS_UNKNOWN;
1200 102 : HOST_WIDE_INT trailing_len = get_trailing_len (cd, valid);
1201 :
1202 : /* Determine whether the template placeholder is valid. */
1203 102 : const svalue *strlen_sval = nullptr;
1204 102 : const svalue *ptr_sval = nullptr;
1205 102 : if (trailing_len >= 0 && !valid.is_false ())
1206 : {
1207 92 : ptr_sval = cd.get_arg_svalue (0);
1208 92 : strlen_sval = cd.get_model ()->check_for_null_terminated_string_arg (
1209 : cd, 0, false, nullptr);
1210 : }
1211 :
1212 92 : if (strlen_sval)
1213 : {
1214 92 : valid = check_placeholder (cd, trailing_len, ptr_sval, strlen_sval);
1215 92 : if (valid.is_false ())
1216 68 : cd.get_ctxt ()->warn (
1217 34 : std::make_unique<mktemp_missing_placeholder> (cd, trailing_len));
1218 : }
1219 :
1220 : /* Failure is always possible (bad template or runtime failure). */
1221 102 : cd.get_ctxt ()->bifurcate (std::make_unique<failure> (cd, m_outcome));
1222 : /* Success is only possible if the template is not definitely invalid. */
1223 102 : if (!valid.is_false ())
1224 68 : cd.get_ctxt ()->bifurcate (std::make_unique<success> (cd, m_outcome));
1225 102 : cd.get_ctxt ()->terminate_path ();
1226 : }
1227 :
1228 : tristate
1229 92 : kf_mktemp_family::check_placeholder (const call_details &cd,
1230 : size_t trailing_len,
1231 : const svalue *ptr_sval,
1232 : const svalue *strlen_sval)
1233 : {
1234 92 : region_model *model = cd.get_model ();
1235 :
1236 92 : const constant_svalue *len_cst = strlen_sval->dyn_cast_constant_svalue ();
1237 92 : if (!len_cst)
1238 26 : return tristate::TS_UNKNOWN;
1239 :
1240 66 : byte_offset_t len = TREE_INT_CST_LOW (len_cst->get_constant ());
1241 66 : if (len < PLACEHOLDER_LEN + trailing_len)
1242 16 : return tristate::TS_FALSE;
1243 :
1244 50 : tree arg_tree = cd.get_arg_tree (0);
1245 50 : const region *reg = model->deref_rvalue (ptr_sval, arg_tree, cd.get_ctxt ());
1246 :
1247 : /* Find the byte offset of the pointed-to region. */
1248 50 : region_offset reg_offset = reg->get_offset (cd.get_manager ());
1249 50 : if (reg_offset.symbolic_p ())
1250 0 : return tristate::TS_UNKNOWN;
1251 50 : byte_offset_t ptr_byte_offset;
1252 50 : if (!reg_offset.get_concrete_byte_offset (&ptr_byte_offset))
1253 0 : return tristate::TS_UNKNOWN;
1254 :
1255 50 : const region *base_reg = reg->get_base_region ();
1256 50 : const svalue *base_sval = model->get_store_value (base_reg, cd.get_ctxt ());
1257 :
1258 50 : const constant_svalue *cst_sval = base_sval->dyn_cast_constant_svalue ();
1259 50 : if (!cst_sval)
1260 0 : return tristate::TS_UNKNOWN;
1261 :
1262 50 : tree cst = cst_sval->get_constant ();
1263 50 : if (TREE_CODE (cst) != STRING_CST)
1264 0 : return tristate::TS_UNKNOWN;
1265 :
1266 50 : HOST_WIDE_INT str_len = len.to_shwi ();
1267 50 : HOST_WIDE_INT start = ptr_byte_offset.to_shwi ();
1268 :
1269 : /* Ensure we can read up to and including the NUL terminator at position
1270 : [start + str_len - trailing_len] within the STRING_CST. */
1271 50 : HOST_WIDE_INT range = start + str_len - trailing_len + 1;
1272 50 : if (range > TREE_STRING_LENGTH (cst))
1273 0 : return tristate::TS_UNKNOWN;
1274 :
1275 50 : if (memcmp (TREE_STRING_POINTER (cst) + start + str_len - trailing_len
1276 50 : - PLACEHOLDER_LEN,
1277 : "XXXXXX", PLACEHOLDER_LEN)
1278 : != 0)
1279 18 : return tristate::TS_FALSE;
1280 :
1281 32 : return tristate::TS_TRUE;
1282 : }
1283 :
1284 : /* Handler for calls to "mkdtemp", "mkstemp", and "mktemp", which all
1285 : take a single char * template argument.
1286 :
1287 : The template must not be a string constant, and its last six
1288 : characters must be "XXXXXX". */
1289 :
1290 : class kf_mktemp_simple : public kf_mktemp_family
1291 : {
1292 : public:
1293 10269 : kf_mktemp_simple (outcome oc) : kf_mktemp_family (oc, -1) {}
1294 :
1295 : bool
1296 369 : matches_call_types_p (const call_details &cd) const final override
1297 : {
1298 369 : return (cd.num_args () == 1 && cd.arg_is_pointer_p (0));
1299 : }
1300 :
1301 : void
1302 54 : impl_call_pre (const call_details &cd) const final override
1303 : {
1304 54 : if (cd.get_ctxt ())
1305 54 : check_for_string_literal_arg (cd);
1306 54 : }
1307 : };
1308 :
1309 : /* Handler for calls to "mkostemp":
1310 :
1311 : int mkostemp(char *template, int flags);
1312 :
1313 : The template must not be a string constant, and its last six
1314 : characters must be "XXXXXX". Warns when flags contains O_RDWR,
1315 : O_CREAT, or O_EXCL, which are already included internally. */
1316 :
1317 : class kf_mkostemp : public kf_mktemp_family
1318 : {
1319 : public:
1320 3423 : kf_mkostemp () : kf_mktemp_family (outcome::fd, -1) {}
1321 :
1322 : bool
1323 165 : matches_call_types_p (const call_details &cd) const final override
1324 : {
1325 330 : return (cd.num_args () == 2 && cd.arg_is_pointer_p (0)
1326 330 : && cd.arg_is_integral_p (1));
1327 : }
1328 :
1329 : void
1330 24 : impl_call_pre (const call_details &cd) const final override
1331 : {
1332 24 : if (cd.get_ctxt ())
1333 : {
1334 24 : check_for_string_literal_arg (cd);
1335 24 : check_flags (cd, 1);
1336 : }
1337 24 : }
1338 : };
1339 :
1340 : /* Handler for calls to "mkostemps":
1341 :
1342 : int mkostemps(char *template, int suffixlen, int flags);
1343 :
1344 : The template must not be a string constant, and must contain
1345 : "XXXXXX" before a suffixlen-character suffix. Warns when flags
1346 : contains O_RDWR, O_CREAT, or O_EXCL, which are already included
1347 : internally. */
1348 :
1349 : class kf_mkostemps : public kf_mktemp_family
1350 : {
1351 : public:
1352 3423 : kf_mkostemps () : kf_mktemp_family (outcome::fd, 1) {}
1353 :
1354 : bool
1355 171 : matches_call_types_p (const call_details &cd) const final override
1356 : {
1357 342 : return (cd.num_args () == 3 && cd.arg_is_pointer_p (0)
1358 342 : && cd.arg_is_integral_p (1) && cd.arg_is_integral_p (2));
1359 : }
1360 :
1361 : void
1362 25 : impl_call_pre (const call_details &cd) const final override
1363 : {
1364 25 : if (cd.get_ctxt ())
1365 : {
1366 25 : check_for_string_literal_arg (cd);
1367 25 : check_flags (cd, 2);
1368 : }
1369 25 : }
1370 : };
1371 :
1372 : /* Handler for calls to "mkstemps":
1373 :
1374 : int mkstemps(char *template, int suffixlen);
1375 :
1376 : The template must not be a string constant, and must contain
1377 : "XXXXXX" before a suffixlen-character suffix. */
1378 :
1379 : class kf_mkstemps : public kf_mktemp_family
1380 : {
1381 : public:
1382 3423 : kf_mkstemps () : kf_mktemp_family (outcome::fd, 1) {}
1383 :
1384 : bool
1385 129 : matches_call_types_p (const call_details &cd) const final override
1386 : {
1387 258 : return (cd.num_args () == 2 && cd.arg_is_pointer_p (0)
1388 258 : && cd.arg_is_integral_p (1));
1389 : }
1390 :
1391 : void
1392 19 : impl_call_pre (const call_details &cd) const final override
1393 : {
1394 19 : if (cd.get_ctxt ())
1395 19 : check_for_string_literal_arg (cd);
1396 19 : }
1397 : };
1398 :
1399 : /* A subclass of pending_diagnostic for complaining about 'putenv'
1400 : called on an auto var. */
1401 :
1402 : class putenv_of_auto_var
1403 : : public pending_diagnostic_subclass<putenv_of_auto_var>
1404 : {
1405 : public:
1406 7 : putenv_of_auto_var (tree fndecl, const region *reg)
1407 7 : : m_fndecl (fndecl), m_reg (reg),
1408 7 : m_var_decl (reg->get_base_region ()->maybe_get_decl ())
1409 : {
1410 7 : }
1411 :
1412 106 : const char *get_kind () const final override
1413 : {
1414 106 : return "putenv_of_auto_var";
1415 : }
1416 :
1417 7 : bool operator== (const putenv_of_auto_var &other) const
1418 : {
1419 7 : return (m_fndecl == other.m_fndecl
1420 7 : && m_reg == other.m_reg
1421 14 : && same_tree_p (m_var_decl, other.m_var_decl));
1422 : }
1423 :
1424 14 : int get_controlling_option () const final override
1425 : {
1426 14 : return OPT_Wanalyzer_putenv_of_auto_var;
1427 : }
1428 :
1429 7 : bool emit (diagnostic_emission_context &ctxt) final override
1430 : {
1431 7 : auto_diagnostic_group d;
1432 :
1433 : /* SEI CERT C Coding Standard: "POS34-C. Do not call putenv() with a
1434 : pointer to an automatic variable as the argument". */
1435 7 : diagnostics::metadata::precanned_rule
1436 7 : rule ("POS34-C", "https://wiki.sei.cmu.edu/confluence/x/6NYxBQ");
1437 7 : ctxt.add_rule (rule);
1438 :
1439 7 : bool warned;
1440 7 : if (m_var_decl)
1441 6 : warned = ctxt.warn ("%qE on a pointer to automatic variable %qE",
1442 : m_fndecl, m_var_decl);
1443 : else
1444 1 : warned = ctxt.warn ("%qE on a pointer to an on-stack buffer",
1445 : m_fndecl);
1446 7 : if (warned)
1447 : {
1448 7 : if (m_var_decl)
1449 6 : inform (DECL_SOURCE_LOCATION (m_var_decl),
1450 : "%qE declared on stack here", m_var_decl);
1451 7 : inform (ctxt.get_location (), "perhaps use %qs rather than %qE",
1452 : "setenv", m_fndecl);
1453 : }
1454 :
1455 14 : return warned;
1456 7 : }
1457 :
1458 : bool
1459 14 : describe_final_event (pretty_printer &pp,
1460 : const evdesc::final_event &) final override
1461 : {
1462 14 : if (m_var_decl)
1463 12 : pp_printf (&pp,
1464 : "%qE on a pointer to automatic variable %qE",
1465 : m_fndecl, m_var_decl);
1466 : else
1467 2 : pp_printf (&pp,
1468 : "%qE on a pointer to an on-stack buffer",
1469 : m_fndecl);
1470 14 : return true;
1471 : }
1472 :
1473 14 : void mark_interesting_stuff (interesting_t *interest) final override
1474 : {
1475 14 : if (!m_var_decl)
1476 2 : interest->add_region_creation (m_reg->get_base_region ());
1477 14 : }
1478 :
1479 : private:
1480 : tree m_fndecl; // non-NULL
1481 : const region *m_reg; // non-NULL
1482 : tree m_var_decl; // could be NULL
1483 : };
1484 :
1485 20538 : class kf_atoi_family: public known_function
1486 : {
1487 : public:
1488 41 : bool matches_call_types_p (const call_details &cd) const final override
1489 : {
1490 41 : return (cd.num_args () == 1 && cd.arg_is_pointer_p (0));
1491 : }
1492 :
1493 6 : void impl_call_pre (const call_details &cd) const final override
1494 : {
1495 : /* atoi expects a valid, null-terminated string. */
1496 6 : cd.check_for_null_terminated_string_arg (0, false, nullptr);
1497 :
1498 : /* atoi returns an integer, but we don't know what it is statically.
1499 : Tell the analyzer to assume it returns a generic, unknown value. */
1500 6 : cd.set_any_lhs_with_defaults ();
1501 6 : }
1502 : };
1503 :
1504 : /* Handler for calls to "getenv".
1505 : char *getenv (const char *name);
1506 :
1507 : Returns either NULL (if the environment variable is not found),
1508 : or a pointer to the value string. */
1509 :
1510 3423 : class kf_getenv : public known_function
1511 : {
1512 : public:
1513 63 : bool matches_call_types_p (const call_details &cd) const final override
1514 : {
1515 63 : return (cd.num_args () == 1 && cd.arg_is_pointer_p (0));
1516 : }
1517 :
1518 9 : void impl_call_pre (const call_details &cd) const final override
1519 : {
1520 9 : cd.check_for_null_terminated_string_arg (0);
1521 9 : }
1522 :
1523 : void impl_call_post (const call_details &cd) const final override;
1524 : };
1525 :
1526 : void
1527 9 : kf_getenv::impl_call_post (const call_details &cd) const
1528 : {
1529 0 : class getenv_call_info : public call_info
1530 : {
1531 : public:
1532 18 : getenv_call_info (const call_details &cd, bool found)
1533 18 : : call_info (cd), m_found (found)
1534 : {
1535 : }
1536 :
1537 74 : void print_desc (pretty_printer &pp) const final override
1538 : {
1539 74 : if (m_found)
1540 70 : pp_printf (&pp,
1541 : "when %qE returns non-NULL",
1542 : get_fndecl ());
1543 : else
1544 4 : pp_printf (&pp,
1545 : "when %qE returns NULL",
1546 : get_fndecl ());
1547 74 : }
1548 :
1549 509 : bool update_model (region_model *model,
1550 : const exploded_edge *,
1551 : region_model_context *ctxt) const final override
1552 : {
1553 509 : const call_details cd (get_call_details (model, ctxt));
1554 509 : if (tree lhs_type = cd.get_lhs_type ())
1555 : {
1556 507 : region_model_manager *mgr = model->get_manager ();
1557 507 : const svalue *result;
1558 507 : if (m_found)
1559 : {
1560 : /* Return a conjured non-NULL pointer. */
1561 269 : result
1562 269 : = mgr->get_or_create_conjured_svalue (lhs_type,
1563 269 : &cd.get_call_stmt (),
1564 : cd.get_lhs_region (),
1565 269 : conjured_purge (model,
1566 269 : ctxt));
1567 269 : const svalue *null_ptr
1568 269 : = mgr->get_or_create_int_cst (lhs_type, 0);
1569 269 : model->add_constraint (result, NE_EXPR, null_ptr, ctxt);
1570 : }
1571 : else
1572 238 : result = mgr->get_or_create_int_cst (lhs_type, 0);
1573 507 : cd.maybe_set_lhs (result);
1574 : }
1575 509 : return true;
1576 : }
1577 : private:
1578 : bool m_found;
1579 : };
1580 :
1581 : /* Body of kf_getenv::impl_call_post. */
1582 9 : if (cd.get_ctxt ())
1583 : {
1584 9 : cd.get_ctxt ()->bifurcate
1585 9 : (std::make_unique<getenv_call_info> (cd, false));
1586 9 : cd.get_ctxt ()->bifurcate
1587 9 : (std::make_unique<getenv_call_info> (cd, true));
1588 9 : cd.get_ctxt ()->terminate_path ();
1589 : }
1590 9 : }
1591 :
1592 : /* Handler for calls to "putenv".
1593 :
1594 : In theory we could try to model the state of the environment variables
1595 : for the process; for now we merely complain about putenv of regions
1596 : on the stack. */
1597 :
1598 3423 : class kf_putenv : public known_function
1599 : {
1600 : public:
1601 118 : bool matches_call_types_p (const call_details &cd) const final override
1602 : {
1603 118 : return (cd.num_args () == 1 && cd.arg_is_pointer_p (0));
1604 : }
1605 :
1606 17 : void impl_call_pre (const call_details &cd) const final override
1607 : {
1608 17 : tree fndecl = cd.get_fndecl_for_call ();
1609 17 : gcc_assert (fndecl);
1610 17 : region_model_context *ctxt = cd.get_ctxt ();
1611 17 : region_model *model = cd.get_model ();
1612 17 : model->check_for_null_terminated_string_arg (cd, 0);
1613 17 : const svalue *ptr_sval = cd.get_arg_svalue (0);
1614 17 : const region *reg
1615 17 : = model->deref_rvalue (ptr_sval, cd.get_arg_tree (0), ctxt);
1616 17 : store_manager *store_mgr = model->get_manager ()->get_store_manager ();
1617 17 : model->get_store ()->mark_as_escaped (*store_mgr, reg->get_base_region ());
1618 17 : enum memory_space mem_space = reg->get_memory_space ();
1619 17 : switch (mem_space)
1620 : {
1621 0 : default:
1622 0 : gcc_unreachable ();
1623 : case MEMSPACE_UNKNOWN:
1624 : case MEMSPACE_CODE:
1625 : case MEMSPACE_GLOBALS:
1626 : case MEMSPACE_HEAP:
1627 : case MEMSPACE_READONLY_DATA:
1628 : break;
1629 7 : case MEMSPACE_STACK:
1630 7 : if (ctxt)
1631 7 : ctxt->warn (std::make_unique<putenv_of_auto_var> (fndecl, reg));
1632 : break;
1633 : }
1634 17 : cd.set_any_lhs_with_defaults ();
1635 17 : }
1636 : };
1637 :
1638 : /* Handler for "realloc":
1639 :
1640 : void *realloc(void *ptr, size_t size);
1641 :
1642 : realloc(3) is awkward, since it has various different outcomes
1643 : that are best modelled as separate exploded nodes/edges.
1644 :
1645 : We first check for sm-state, in
1646 : malloc_state_machine::on_realloc_call, so that we
1647 : can complain about issues such as realloc of a non-heap
1648 : pointer, and terminate the path for such cases (and issue
1649 : the complaints at the call's exploded node).
1650 :
1651 : Assuming that these checks pass, we split the path here into
1652 : three special cases (and terminate the "standard" path):
1653 : (A) failure, returning NULL
1654 : (B) success, growing the buffer in-place without moving it
1655 : (C) success, allocating a new buffer, copying the content
1656 : of the old buffer to it, and freeing the old buffer.
1657 :
1658 : Each of these has a custom_edge_info subclass, which updates
1659 : the region_model and sm-state of the destination state. */
1660 :
1661 10269 : class kf_realloc : public builtin_known_function
1662 : {
1663 : public:
1664 1980 : bool matches_call_types_p (const call_details &cd) const final override
1665 : {
1666 1980 : return (cd.num_args () == 2
1667 1980 : && cd.arg_is_pointer_p (0)
1668 3960 : && cd.arg_is_size_p (1));
1669 : }
1670 :
1671 2004 : enum built_in_function builtin_code () const final override
1672 : {
1673 2004 : return BUILT_IN_REALLOC;
1674 : }
1675 :
1676 : void impl_call_post (const call_details &cd) const final override;
1677 : };
1678 :
1679 : void
1680 310 : kf_realloc::impl_call_post (const call_details &cd) const
1681 : {
1682 : /* Three custom subclasses of custom_edge_info, for handling the various
1683 : outcomes of "realloc". */
1684 :
1685 : /* Concrete custom_edge_info: a realloc call that fails, returning NULL. */
1686 0 : class failure : public failed_call_info
1687 : {
1688 : public:
1689 310 : failure (const call_details &cd)
1690 310 : : failed_call_info (cd)
1691 : {
1692 : }
1693 :
1694 374 : bool update_model (region_model *model,
1695 : const exploded_edge *,
1696 : region_model_context *ctxt) const final override
1697 : {
1698 : /* Return NULL; everything else is unchanged. */
1699 374 : const call_details cd (get_call_details (model, ctxt));
1700 374 : region_model_manager *mgr = cd.get_manager ();
1701 374 : if (cd.get_lhs_type ())
1702 : {
1703 365 : const svalue *zero
1704 365 : = mgr->get_or_create_int_cst (cd.get_lhs_type (), 0);
1705 365 : model->set_value (cd.get_lhs_region (),
1706 : zero,
1707 : cd.get_ctxt ());
1708 : }
1709 374 : return true;
1710 : }
1711 : };
1712 :
1713 : /* Concrete custom_edge_info: a realloc call that succeeds, growing
1714 : the existing buffer without moving it. */
1715 0 : class success_no_move : public call_info
1716 : {
1717 : public:
1718 310 : success_no_move (const call_details &cd)
1719 310 : : call_info (cd)
1720 : {
1721 : }
1722 :
1723 8 : void print_desc (pretty_printer &pp) const final override
1724 : {
1725 8 : pp_printf (&pp,
1726 : "when %qE succeeds, without moving buffer",
1727 : get_fndecl ());
1728 8 : }
1729 :
1730 343 : bool update_model (region_model *model,
1731 : const exploded_edge *,
1732 : region_model_context *ctxt) const final override
1733 : {
1734 : /* Update size of buffer and return the ptr unchanged. */
1735 343 : const call_details cd (get_call_details (model, ctxt));
1736 343 : region_model_manager *mgr = cd.get_manager ();
1737 343 : const svalue *ptr_sval = cd.get_arg_svalue (0);
1738 343 : const svalue *size_sval = cd.get_arg_svalue (1);
1739 :
1740 : /* We can only grow in place with a non-NULL pointer. */
1741 343 : {
1742 343 : const svalue *null_ptr
1743 343 : = mgr->get_or_create_int_cst (ptr_sval->get_type (), 0);
1744 343 : if (!model->add_constraint (ptr_sval, NE_EXPR, null_ptr,
1745 : cd.get_ctxt ()))
1746 : return false;
1747 : }
1748 :
1749 337 : if (const region *buffer_reg = model->deref_rvalue (ptr_sval, NULL_TREE,
1750 : ctxt))
1751 337 : if (compat_types_p (size_sval->get_type (), size_type_node))
1752 337 : model->set_dynamic_extents (buffer_reg, size_sval, ctxt);
1753 337 : if (cd.get_lhs_region ())
1754 : {
1755 328 : model->set_value (cd.get_lhs_region (), ptr_sval, cd.get_ctxt ());
1756 328 : const svalue *zero
1757 328 : = mgr->get_or_create_int_cst (cd.get_lhs_type (), 0);
1758 328 : return model->add_constraint (ptr_sval, NE_EXPR, zero, ctxt);
1759 : }
1760 : else
1761 : return true;
1762 : }
1763 : };
1764 :
1765 : /* Concrete custom_edge_info: a realloc call that succeeds, freeing
1766 : the existing buffer and moving the content to a freshly allocated
1767 : buffer. */
1768 0 : class success_with_move : public call_info
1769 : {
1770 : public:
1771 310 : success_with_move (const call_details &cd)
1772 310 : : call_info (cd)
1773 : {
1774 : }
1775 :
1776 72 : void print_desc (pretty_printer &pp) const final override
1777 : {
1778 72 : pp_printf (&pp,
1779 : "when %qE succeeds, moving buffer",
1780 : get_fndecl ());
1781 72 : }
1782 430 : bool update_model (region_model *model,
1783 : const exploded_edge *,
1784 : region_model_context *ctxt) const final override
1785 : {
1786 430 : const call_details cd (get_call_details (model, ctxt));
1787 430 : region_model_manager *mgr = cd.get_manager ();
1788 430 : const svalue *old_ptr_sval = cd.get_arg_svalue (0);
1789 430 : const svalue *new_size_sval = cd.get_arg_svalue (1);
1790 :
1791 : /* Create the new region. */
1792 430 : const region *new_reg
1793 430 : = model->get_or_create_region_for_heap_alloc (new_size_sval, ctxt);
1794 430 : const svalue *new_ptr_sval
1795 430 : = mgr->get_ptr_svalue (cd.get_lhs_type (), new_reg);
1796 430 : if (!model->add_constraint (new_ptr_sval, NE_EXPR, old_ptr_sval,
1797 : cd.get_ctxt ()))
1798 : return false;
1799 :
1800 430 : if (cd.get_lhs_type ())
1801 421 : cd.maybe_set_lhs (new_ptr_sval);
1802 :
1803 430 : if (const region *freed_reg = model->deref_rvalue (old_ptr_sval,
1804 : NULL_TREE, ctxt))
1805 : {
1806 : /* Copy the data. */
1807 430 : const svalue *old_size_sval = model->get_dynamic_extents (freed_reg);
1808 430 : if (old_size_sval)
1809 : {
1810 187 : const svalue *copied_size_sval
1811 187 : = get_copied_size (model, old_size_sval, new_size_sval);
1812 187 : const region *copied_old_reg
1813 187 : = mgr->get_sized_region (freed_reg, nullptr, copied_size_sval);
1814 187 : const svalue *buffer_content_sval
1815 187 : = model->get_store_value (copied_old_reg, cd.get_ctxt ());
1816 187 : const region *copied_new_reg
1817 187 : = mgr->get_sized_region (new_reg, nullptr, copied_size_sval);
1818 187 : model->set_value (copied_new_reg, buffer_content_sval,
1819 : cd.get_ctxt ());
1820 : }
1821 : else
1822 : {
1823 : /* We don't know how big the old region was;
1824 : mark the new region as having been touched to avoid uninit
1825 : issues. */
1826 243 : model->mark_region_as_unknown (new_reg, cd.get_uncertainty ());
1827 : }
1828 :
1829 : /* Free the old region, so that pointers to the old buffer become
1830 : invalid. */
1831 :
1832 : /* If the ptr points to an underlying heap region, delete it,
1833 : poisoning pointers. */
1834 430 : model->unbind_region_and_descendents (freed_reg, poison_kind::freed);
1835 430 : model->unset_dynamic_extents (freed_reg);
1836 : }
1837 :
1838 : /* Update the sm-state: mark the old_ptr_sval as "freed",
1839 : and the new_ptr_sval as "nonnull". */
1840 430 : model->on_realloc_with_move (cd, old_ptr_sval, new_ptr_sval);
1841 :
1842 430 : if (cd.get_lhs_type ())
1843 : {
1844 421 : const svalue *zero
1845 421 : = mgr->get_or_create_int_cst (cd.get_lhs_type (), 0);
1846 421 : return model->add_constraint (new_ptr_sval, NE_EXPR, zero,
1847 421 : cd.get_ctxt ());
1848 : }
1849 : else
1850 : return true;
1851 : }
1852 :
1853 : private:
1854 : /* Return the lesser of OLD_SIZE_SVAL and NEW_SIZE_SVAL.
1855 : If unknown, OLD_SIZE_SVAL is returned. */
1856 187 : const svalue *get_copied_size (region_model *model,
1857 : const svalue *old_size_sval,
1858 : const svalue *new_size_sval) const
1859 : {
1860 187 : tristate res
1861 187 : = model->eval_condition (old_size_sval, GT_EXPR, new_size_sval);
1862 187 : switch (res.get_value ())
1863 : {
1864 : case tristate::TS_TRUE:
1865 : return new_size_sval;
1866 143 : case tristate::TS_FALSE:
1867 143 : case tristate::TS_UNKNOWN:
1868 143 : return old_size_sval;
1869 0 : default:
1870 0 : gcc_unreachable ();
1871 : }
1872 : }
1873 : };
1874 :
1875 : /* Body of kf_realloc::impl_call_post. */
1876 :
1877 310 : if (cd.get_ctxt ())
1878 : {
1879 310 : cd.get_ctxt ()->bifurcate (std::make_unique<failure> (cd));
1880 310 : cd.get_ctxt ()->bifurcate (std::make_unique<success_no_move> (cd));
1881 310 : cd.get_ctxt ()->bifurcate (std::make_unique<success_with_move> (cd));
1882 310 : cd.get_ctxt ()->terminate_path ();
1883 : }
1884 310 : }
1885 :
1886 : /* Handler for "strchr" and "__builtin_strchr". */
1887 :
1888 6846 : class kf_strchr : public builtin_known_function
1889 : {
1890 : public:
1891 153 : bool matches_call_types_p (const call_details &cd) const final override
1892 : {
1893 153 : return (cd.num_args () == 2 && cd.arg_is_pointer_p (0));
1894 : }
1895 22 : void impl_call_pre (const call_details &cd) const final override
1896 : {
1897 22 : cd.check_for_null_terminated_string_arg (0);
1898 22 : }
1899 :
1900 176 : enum built_in_function builtin_code () const final override
1901 : {
1902 176 : return BUILT_IN_STRCHR;
1903 : }
1904 : void impl_call_post (const call_details &cd) const final override;
1905 : };
1906 :
1907 : void
1908 21 : kf_strchr::impl_call_post (const call_details &cd) const
1909 : {
1910 0 : class strchr_call_info : public call_info
1911 : {
1912 : public:
1913 42 : strchr_call_info (const call_details &cd, bool found)
1914 42 : : call_info (cd), m_found (found)
1915 : {
1916 : }
1917 :
1918 38 : void print_desc (pretty_printer &pp) const final override
1919 : {
1920 38 : if (m_found)
1921 18 : pp_printf (&pp,
1922 : "when %qE returns non-NULL",
1923 : get_fndecl ());
1924 : else
1925 20 : pp_printf (&pp,
1926 : "when %qE returns NULL",
1927 : get_fndecl ());
1928 38 : }
1929 :
1930 84 : bool update_model (region_model *model,
1931 : const exploded_edge *,
1932 : region_model_context *ctxt) const final override
1933 : {
1934 84 : const call_details cd (get_call_details (model, ctxt));
1935 84 : if (tree lhs_type = cd.get_lhs_type ())
1936 : {
1937 82 : region_model_manager *mgr = model->get_manager ();
1938 82 : const svalue *result;
1939 82 : if (m_found)
1940 : {
1941 40 : const svalue *str_sval = cd.get_arg_svalue (0);
1942 40 : const region *str_reg
1943 40 : = model->deref_rvalue (str_sval, cd.get_arg_tree (0),
1944 : cd.get_ctxt ());
1945 : /* We want str_sval + OFFSET for some unknown OFFSET.
1946 : Use a conjured_svalue to represent the offset,
1947 : using the str_reg as the id of the conjured_svalue. */
1948 40 : const svalue *offset
1949 40 : = mgr->get_or_create_conjured_svalue (size_type_node,
1950 40 : &cd.get_call_stmt (),
1951 : str_reg,
1952 40 : conjured_purge (model,
1953 40 : ctxt));
1954 40 : result = mgr->get_or_create_binop (lhs_type, POINTER_PLUS_EXPR,
1955 : str_sval, offset);
1956 : }
1957 : else
1958 42 : result = mgr->get_or_create_int_cst (lhs_type, 0);
1959 82 : cd.maybe_set_lhs (result);
1960 : }
1961 84 : return true;
1962 : }
1963 : private:
1964 : bool m_found;
1965 : };
1966 :
1967 : /* Body of kf_strchr::impl_call_post. */
1968 21 : if (cd.get_ctxt ())
1969 : {
1970 21 : cd.get_ctxt ()->bifurcate (std::make_unique<strchr_call_info> (cd, false));
1971 21 : cd.get_ctxt ()->bifurcate (std::make_unique<strchr_call_info> (cd, true));
1972 21 : cd.get_ctxt ()->terminate_path ();
1973 : }
1974 21 : }
1975 :
1976 : /* Handler for "sprintf".
1977 : int sprintf(char *str, const char *format, ...);
1978 : */
1979 :
1980 6846 : class kf_sprintf : public builtin_known_function
1981 : {
1982 : public:
1983 3223 : bool matches_call_types_p (const call_details &cd) const final override
1984 : {
1985 3223 : return (cd.num_args () >= 2
1986 3223 : && cd.arg_is_pointer_p (0)
1987 6446 : && cd.arg_is_pointer_p (1));
1988 : }
1989 :
1990 640 : enum built_in_function builtin_code () const final override
1991 : {
1992 640 : return BUILT_IN_SPRINTF;
1993 : }
1994 :
1995 1416 : void impl_call_pre (const call_details &cd) const final override
1996 : {
1997 : /* For now, merely assume that the destination buffer gets set to a
1998 : new svalue. */
1999 1416 : region_model *model = cd.get_model ();
2000 1416 : region_model_context *ctxt = cd.get_ctxt ();
2001 1416 : const svalue *dst_ptr = cd.get_arg_svalue (0);
2002 1416 : const region *dst_reg
2003 1416 : = model->deref_rvalue (dst_ptr, cd.get_arg_tree (0), ctxt);
2004 : /* Restrict the region we consider to be affected to the valid capacity
2005 : so that we don't trigger buffer overflow false positives. */
2006 1416 : const svalue *capacity = model->get_capacity (dst_reg);
2007 1416 : dst_reg = model->get_manager ()->get_sized_region (dst_reg,
2008 : NULL_TREE,
2009 : capacity);
2010 1416 : const svalue *content = cd.get_or_create_conjured_svalue (dst_reg);
2011 1416 : model->set_value (dst_reg, content, ctxt);
2012 1416 : cd.set_any_lhs_with_defaults ();
2013 1416 : }
2014 : };
2015 :
2016 : /* Handler for "__builtin_stack_restore". */
2017 :
2018 3423 : class kf_stack_restore : public pure_known_function_with_default_return
2019 : {
2020 : public:
2021 0 : bool matches_call_types_p (const call_details &) const final override
2022 : {
2023 0 : return true;
2024 : }
2025 :
2026 : /* Currently a no-op. */
2027 : };
2028 :
2029 : /* Handler for "__builtin_stack_save". */
2030 :
2031 3423 : class kf_stack_save : public pure_known_function_with_default_return
2032 : {
2033 : public:
2034 0 : bool matches_call_types_p (const call_details &) const final override
2035 : {
2036 0 : return true;
2037 : }
2038 :
2039 : /* Currently a no-op. */
2040 : };
2041 :
2042 : /* Handler for "__builtin_eh_pointer". */
2043 :
2044 3423 : class kf_eh_pointer : public builtin_known_function
2045 : {
2046 : public:
2047 0 : bool matches_call_types_p (const call_details &) const final override
2048 : {
2049 0 : return true;
2050 : }
2051 :
2052 992 : enum built_in_function builtin_code () const final override
2053 : {
2054 992 : return BUILT_IN_EH_POINTER;
2055 : }
2056 :
2057 271 : void impl_call_pre (const call_details &cd) const final override
2058 : {
2059 271 : cd.set_any_lhs_with_defaults ();
2060 271 : }
2061 : };
2062 :
2063 : /* Handler for "strcat" and "__builtin_strcat_chk". */
2064 :
2065 : class kf_strcat : public builtin_known_function
2066 : {
2067 : public:
2068 17115 : kf_strcat (unsigned int num_args, bool chk_variant)
2069 17115 : : m_num_args (num_args),
2070 17115 : m_chk_variant (chk_variant) {}
2071 395 : bool matches_call_types_p (const call_details &cd) const final override
2072 : {
2073 395 : return (cd.num_args () == m_num_args
2074 395 : && cd.arg_is_pointer_p (0)
2075 790 : && cd.arg_is_pointer_p (1));
2076 : }
2077 :
2078 456 : enum built_in_function builtin_code () const final override
2079 : {
2080 456 : return m_chk_variant ? BUILT_IN_STRCAT_CHK : BUILT_IN_STRCAT;
2081 : }
2082 :
2083 57 : void impl_call_pre (const call_details &cd) const final override
2084 : {
2085 57 : region_model *model = cd.get_model ();
2086 57 : region_model_manager *mgr = cd.get_manager ();
2087 :
2088 57 : const svalue *dest_sval = cd.get_arg_svalue (0);
2089 57 : const region *dest_reg = model->deref_rvalue (dest_sval, cd.get_arg_tree (0),
2090 : cd.get_ctxt ());
2091 :
2092 57 : const svalue *dst_strlen_sval
2093 57 : = cd.check_for_null_terminated_string_arg (0, false, nullptr);
2094 57 : if (!dst_strlen_sval)
2095 : {
2096 2 : if (cd.get_ctxt ())
2097 2 : cd.get_ctxt ()->terminate_path ();
2098 4 : return;
2099 : }
2100 :
2101 55 : const svalue *bytes_to_copy;
2102 55 : const svalue *num_src_bytes_read_sval
2103 55 : = cd.check_for_null_terminated_string_arg (1, true, &bytes_to_copy);
2104 55 : if (!num_src_bytes_read_sval)
2105 : {
2106 2 : if (cd.get_ctxt ())
2107 2 : cd.get_ctxt ()->terminate_path ();
2108 2 : return;
2109 : }
2110 :
2111 53 : cd.maybe_set_lhs (dest_sval);
2112 53 : cd.complain_about_overlap (0, 1, num_src_bytes_read_sval);
2113 :
2114 53 : const region *offset_reg
2115 53 : = mgr->get_offset_region (dest_reg, NULL_TREE, dst_strlen_sval);
2116 53 : model->write_bytes (offset_reg,
2117 : num_src_bytes_read_sval,
2118 : bytes_to_copy,
2119 : cd.get_ctxt ());
2120 : }
2121 :
2122 : private:
2123 : unsigned int m_num_args;
2124 : const bool m_chk_variant;
2125 : };
2126 :
2127 : /* Handler for "strcpy" and "__builtin_strcpy_chk". */
2128 :
2129 : class kf_strcpy : public builtin_known_function
2130 : {
2131 : public:
2132 17115 : kf_strcpy (unsigned int num_args, bool chk_variant)
2133 17115 : : m_num_args (num_args),
2134 17115 : m_chk_variant (chk_variant) {}
2135 2053 : bool matches_call_types_p (const call_details &cd) const final override
2136 : {
2137 2053 : return (cd.num_args () == m_num_args
2138 2053 : && cd.arg_is_pointer_p (0)
2139 4106 : && cd.arg_is_pointer_p (1));
2140 : }
2141 656 : enum built_in_function builtin_code () const final override
2142 : {
2143 656 : return m_chk_variant ? BUILT_IN_STRCPY_CHK : BUILT_IN_STRCPY;
2144 : }
2145 : void impl_call_pre (const call_details &cd) const final override;
2146 :
2147 : private:
2148 : unsigned int m_num_args;
2149 : const bool m_chk_variant;
2150 : };
2151 :
2152 : void
2153 823 : kf_strcpy::impl_call_pre (const call_details &cd) const
2154 : {
2155 823 : region_model *model = cd.get_model ();
2156 823 : region_model_context *ctxt = cd.get_ctxt ();
2157 :
2158 823 : const svalue *dest_sval = cd.get_arg_svalue (0);
2159 823 : const region *dest_reg = model->deref_rvalue (dest_sval, cd.get_arg_tree (0),
2160 : ctxt);
2161 : /* strcpy returns the initial param. */
2162 823 : cd.maybe_set_lhs (dest_sval);
2163 :
2164 823 : const svalue *bytes_to_copy = nullptr;
2165 1646 : if (const svalue *num_bytes_read_sval
2166 823 : = cd.check_for_null_terminated_string_arg (1, true, &bytes_to_copy))
2167 : {
2168 820 : gcc_assert (bytes_to_copy);
2169 820 : cd.complain_about_overlap (0, 1, num_bytes_read_sval);
2170 820 : model->write_bytes (dest_reg, num_bytes_read_sval, bytes_to_copy, ctxt);
2171 : }
2172 : else
2173 : {
2174 3 : if (cd.get_ctxt ())
2175 3 : cd.get_ctxt ()->terminate_path ();
2176 : }
2177 823 : }
2178 :
2179 : /* Handler for "strdup" and "__builtin_strdup". */
2180 :
2181 6846 : class kf_strdup : public builtin_known_function
2182 : {
2183 : public:
2184 1456 : bool matches_call_types_p (const call_details &cd) const final override
2185 : {
2186 1456 : return (cd.num_args () == 1 && cd.arg_is_pointer_p (0));
2187 : }
2188 1326 : enum built_in_function builtin_code () const final override
2189 : {
2190 1326 : return BUILT_IN_STRDUP;
2191 : }
2192 287 : void impl_call_pre (const call_details &cd) const final override
2193 : {
2194 287 : region_model *model = cd.get_model ();
2195 287 : region_model_context *ctxt = cd.get_ctxt ();
2196 287 : region_model_manager *mgr = cd.get_manager ();
2197 287 : const svalue *bytes_to_copy;
2198 574 : if (const svalue *num_bytes_read_sval
2199 287 : = cd.check_for_null_terminated_string_arg (0, true, &bytes_to_copy))
2200 : {
2201 285 : const region *new_reg
2202 285 : = model->get_or_create_region_for_heap_alloc (num_bytes_read_sval,
2203 : ctxt);
2204 285 : model->write_bytes (new_reg, num_bytes_read_sval, bytes_to_copy, ctxt);
2205 285 : if (cd.get_lhs_type ())
2206 : {
2207 285 : const svalue *ptr_sval
2208 285 : = mgr->get_ptr_svalue (cd.get_lhs_type (), new_reg);
2209 285 : cd.maybe_set_lhs (ptr_sval);
2210 : }
2211 : }
2212 : else
2213 : {
2214 2 : if (ctxt)
2215 2 : ctxt->terminate_path ();
2216 : }
2217 287 : }
2218 : };
2219 :
2220 : /* Handler for "strlen" and for "__analyzer_get_strlen". */
2221 :
2222 13692 : class kf_strlen : public builtin_known_function
2223 : {
2224 : public:
2225 10484 : bool matches_call_types_p (const call_details &cd) const final override
2226 : {
2227 10484 : return (cd.num_args () == 1 && cd.arg_is_pointer_p (0));
2228 : }
2229 6816 : enum built_in_function builtin_code () const final override
2230 : {
2231 6816 : return BUILT_IN_STRLEN;
2232 : }
2233 :
2234 3116 : void impl_call_pre (const call_details &cd) const final override
2235 : {
2236 6232 : if (const svalue *strlen_sval
2237 3116 : = cd.check_for_null_terminated_string_arg (0, false, nullptr))
2238 3098 : if (strlen_sval->get_kind () != SK_UNKNOWN)
2239 : {
2240 2388 : cd.maybe_set_lhs (strlen_sval);
2241 2388 : return;
2242 : }
2243 :
2244 : /* Use a conjured svalue. */
2245 728 : cd.set_any_lhs_with_defaults ();
2246 : }
2247 : };
2248 :
2249 : /* Factory function, so that kf-analyzer.cc can use this class. */
2250 :
2251 : std::unique_ptr<known_function>
2252 3423 : make_kf_strlen ()
2253 : {
2254 3423 : return std::make_unique<kf_strlen> ();
2255 : }
2256 :
2257 : /* Handler for "strncpy" and "__builtin_strncpy".
2258 : See e.g. https://en.cppreference.com/w/c/string/byte/strncpy
2259 :
2260 : extern char *strncpy (char *dst, const char *src, size_t count);
2261 :
2262 : Handle this by splitting into two outcomes:
2263 : (a) truncated read from "src" of "count" bytes,
2264 : writing "count" bytes to "dst"
2265 : (b) read from "src" of up to (and including) the null terminator,
2266 : where the number of bytes read < "count" bytes,
2267 : writing those bytes to "dst", and zero-filling the rest,
2268 : up to "count". */
2269 :
2270 10269 : class kf_strncpy : public builtin_known_function
2271 : {
2272 : public:
2273 1225 : bool matches_call_types_p (const call_details &cd) const final override
2274 : {
2275 1225 : return (cd.num_args () == 3
2276 1225 : && cd.arg_is_pointer_p (0)
2277 1225 : && cd.arg_is_pointer_p (1)
2278 2450 : && cd.arg_is_integral_p (2));
2279 : }
2280 1400 : enum built_in_function builtin_code () const final override
2281 : {
2282 1400 : return BUILT_IN_STRNCPY;
2283 : }
2284 : void impl_call_post (const call_details &cd) const final override;
2285 : };
2286 :
2287 : void
2288 175 : kf_strncpy::impl_call_post (const call_details &cd) const
2289 : {
2290 0 : class strncpy_call_info : public call_info
2291 : {
2292 : public:
2293 350 : strncpy_call_info (const call_details &cd,
2294 : const svalue *num_bytes_with_terminator_sval,
2295 : bool truncated_read)
2296 350 : : call_info (cd),
2297 350 : m_num_bytes_with_terminator_sval (num_bytes_with_terminator_sval),
2298 350 : m_truncated_read (truncated_read)
2299 : {
2300 : }
2301 :
2302 76 : void print_desc (pretty_printer &pp) const final override
2303 : {
2304 76 : if (m_truncated_read)
2305 4 : pp_printf (&pp,
2306 : "when %qE truncates the source string",
2307 : get_fndecl ());
2308 : else
2309 72 : pp_printf (&pp,
2310 : "when %qE copies the full source string",
2311 : get_fndecl ());
2312 76 : }
2313 :
2314 520 : bool update_model (region_model *model,
2315 : const exploded_edge *,
2316 : region_model_context *ctxt) const final override
2317 : {
2318 520 : const call_details cd (get_call_details (model, ctxt));
2319 :
2320 520 : const svalue *dest_sval = cd.get_arg_svalue (0);
2321 520 : const region *dest_reg
2322 520 : = model->deref_rvalue (dest_sval, cd.get_arg_tree (0), ctxt);
2323 :
2324 520 : const svalue *src_sval = cd.get_arg_svalue (1);
2325 520 : const region *src_reg
2326 520 : = model->deref_rvalue (src_sval, cd.get_arg_tree (1), ctxt);
2327 :
2328 520 : const svalue *count_sval = cd.get_arg_svalue (2);
2329 :
2330 : /* strncpy returns the initial param. */
2331 520 : cd.maybe_set_lhs (dest_sval);
2332 :
2333 520 : const svalue *num_bytes_read_sval;
2334 520 : if (m_truncated_read)
2335 : {
2336 : /* Truncated read. */
2337 243 : num_bytes_read_sval = count_sval;
2338 :
2339 243 : if (m_num_bytes_with_terminator_sval)
2340 : {
2341 : /* The terminator is after the limit. */
2342 213 : if (!model->add_constraint (m_num_bytes_with_terminator_sval,
2343 : GT_EXPR,
2344 : count_sval,
2345 : ctxt))
2346 : return false;
2347 : }
2348 : else
2349 : {
2350 : /* We don't know where the terminator is, or if there is one.
2351 : In theory we know that the first COUNT bytes are non-zero,
2352 : but we don't have a way to record that constraint. */
2353 : }
2354 : }
2355 : else
2356 : {
2357 : /* Full read of the src string before reaching the limit,
2358 : so there must be a terminator and it must be at or before
2359 : the limit. */
2360 277 : if (m_num_bytes_with_terminator_sval)
2361 : {
2362 247 : if (!model->add_constraint (m_num_bytes_with_terminator_sval,
2363 : LE_EXPR,
2364 : count_sval,
2365 : ctxt))
2366 : return false;
2367 243 : num_bytes_read_sval = m_num_bytes_with_terminator_sval;
2368 :
2369 : /* First, zero-fill the dest buffer.
2370 : We don't need to do this for the truncation case, as
2371 : this fully populates the dest buffer. */
2372 243 : const region *sized_dest_reg
2373 243 : = model->get_manager ()->get_sized_region (dest_reg,
2374 : NULL_TREE,
2375 : count_sval);
2376 243 : model->zero_fill_region (sized_dest_reg, ctxt);
2377 : }
2378 : else
2379 : {
2380 : /* Don't analyze this case; the other case will
2381 : assume a "truncated" read up to the limit. */
2382 : return false;
2383 : }
2384 : }
2385 :
2386 472 : gcc_assert (num_bytes_read_sval);
2387 :
2388 472 : const svalue *bytes_to_copy
2389 472 : = model->read_bytes (src_reg,
2390 : cd.get_arg_tree (1),
2391 : num_bytes_read_sval,
2392 : ctxt);
2393 472 : cd.complain_about_overlap (0, 1, num_bytes_read_sval);
2394 472 : model->write_bytes (dest_reg,
2395 : num_bytes_read_sval,
2396 : bytes_to_copy,
2397 : ctxt);
2398 :
2399 472 : return true;
2400 : }
2401 : private:
2402 : /* (strlen + 1) of the source string if it has a terminator,
2403 : or nullptr for the case where UB would happen before
2404 : finding any terminator. */
2405 : const svalue *m_num_bytes_with_terminator_sval;
2406 :
2407 : /* true: if this is the outcome where the limit was reached before
2408 : the null terminator
2409 : false: if the null terminator was reached before the limit. */
2410 : bool m_truncated_read;
2411 : };
2412 :
2413 : /* Body of kf_strncpy::impl_call_post. */
2414 175 : if (cd.get_ctxt ())
2415 : {
2416 : /* First, scan for a null terminator as if there were no limit,
2417 : with a null ctxt so no errors are reported. */
2418 175 : const region_model *model = cd.get_model ();
2419 175 : const svalue *ptr_arg_sval = cd.get_arg_svalue (1);
2420 175 : const region *buf_reg
2421 175 : = model->deref_rvalue (ptr_arg_sval, cd.get_arg_tree (1), nullptr);
2422 175 : const svalue *num_bytes_with_terminator_sval
2423 175 : = model->scan_for_null_terminator (buf_reg,
2424 : cd.get_arg_tree (1),
2425 : nullptr,
2426 175 : nullptr);
2427 175 : cd.get_ctxt ()->bifurcate
2428 175 : (std::make_unique<strncpy_call_info>
2429 175 : (cd, num_bytes_with_terminator_sval,
2430 175 : false));
2431 175 : cd.get_ctxt ()->bifurcate
2432 175 : (std::make_unique<strncpy_call_info>
2433 175 : (cd, num_bytes_with_terminator_sval,
2434 175 : true));
2435 175 : cd.get_ctxt ()->terminate_path ();
2436 : }
2437 175 : };
2438 :
2439 : /* Handler for "strndup" and "__builtin_strndup". */
2440 :
2441 6846 : class kf_strndup : public builtin_known_function
2442 : {
2443 : public:
2444 124 : bool matches_call_types_p (const call_details &cd) const final override
2445 : {
2446 124 : return (cd.num_args () == 2 && cd.arg_is_pointer_p (0));
2447 : }
2448 84 : enum built_in_function builtin_code () const final override
2449 : {
2450 84 : return BUILT_IN_STRNDUP;
2451 : }
2452 34 : void impl_call_pre (const call_details &cd) const final override
2453 : {
2454 34 : region_model *model = cd.get_model ();
2455 34 : region_model_manager *mgr = cd.get_manager ();
2456 : /* Ideally we'd get the size here, and simulate copying the bytes. */
2457 34 : const region *new_reg
2458 34 : = model->get_or_create_region_for_heap_alloc (nullptr, cd.get_ctxt ());
2459 34 : model->mark_region_as_unknown (new_reg, nullptr);
2460 34 : if (cd.get_lhs_type ())
2461 : {
2462 34 : const svalue *ptr_sval
2463 34 : = mgr->get_ptr_svalue (cd.get_lhs_type (), new_reg);
2464 34 : cd.maybe_set_lhs (ptr_sval);
2465 : }
2466 34 : }
2467 : };
2468 :
2469 : /* Handler for "strstr" and "__builtin_strstr".
2470 : extern char *strstr (const char* str, const char* substr);
2471 : See e.g. https://en.cppreference.com/w/c/string/byte/strstr */
2472 :
2473 6846 : class kf_strstr : public builtin_known_function
2474 : {
2475 : public:
2476 279 : bool matches_call_types_p (const call_details &cd) const final override
2477 : {
2478 279 : return (cd.num_args () == 2
2479 279 : && cd.arg_is_pointer_p (0)
2480 558 : && cd.arg_is_pointer_p (1));
2481 : }
2482 328 : enum built_in_function builtin_code () const final override
2483 : {
2484 328 : return BUILT_IN_STRSTR;
2485 : }
2486 41 : void impl_call_pre (const call_details &cd) const final override
2487 : {
2488 41 : cd.check_for_null_terminated_string_arg (0);
2489 41 : cd.check_for_null_terminated_string_arg (1);
2490 41 : }
2491 : void impl_call_post (const call_details &cd) const final override;
2492 : };
2493 :
2494 : void
2495 33 : kf_strstr::impl_call_post (const call_details &cd) const
2496 : {
2497 0 : class strstr_call_info : public call_info
2498 : {
2499 : public:
2500 66 : strstr_call_info (const call_details &cd, bool found)
2501 66 : : call_info (cd), m_found (found)
2502 : {
2503 : }
2504 :
2505 8 : void print_desc (pretty_printer &pp) const final override
2506 : {
2507 8 : if (m_found)
2508 0 : pp_printf (&pp,
2509 : "when %qE returns non-NULL",
2510 : get_fndecl ());
2511 : else
2512 8 : pp_printf (&pp,
2513 : "when %qE returns NULL",
2514 : get_fndecl ());
2515 8 : }
2516 :
2517 106 : bool update_model (region_model *model,
2518 : const exploded_edge *,
2519 : region_model_context *ctxt) const final override
2520 : {
2521 106 : const call_details cd (get_call_details (model, ctxt));
2522 106 : if (tree lhs_type = cd.get_lhs_type ())
2523 : {
2524 106 : region_model_manager *mgr = model->get_manager ();
2525 106 : const svalue *result;
2526 106 : if (m_found)
2527 : {
2528 33 : const svalue *str_sval = cd.get_arg_svalue (0);
2529 33 : const region *str_reg
2530 33 : = model->deref_rvalue (str_sval, cd.get_arg_tree (0),
2531 : cd.get_ctxt ());
2532 : /* We want str_sval + OFFSET for some unknown OFFSET.
2533 : Use a conjured_svalue to represent the offset,
2534 : using the str_reg as the id of the conjured_svalue. */
2535 33 : const svalue *offset
2536 33 : = mgr->get_or_create_conjured_svalue (size_type_node,
2537 33 : &cd.get_call_stmt (),
2538 : str_reg,
2539 33 : conjured_purge (model,
2540 33 : ctxt));
2541 33 : result = mgr->get_or_create_binop (lhs_type, POINTER_PLUS_EXPR,
2542 : str_sval, offset);
2543 : }
2544 : else
2545 73 : result = mgr->get_or_create_int_cst (lhs_type, 0);
2546 106 : cd.maybe_set_lhs (result);
2547 : }
2548 106 : return true;
2549 : }
2550 : private:
2551 : bool m_found;
2552 : };
2553 :
2554 : /* Body of kf_strstr::impl_call_post. */
2555 33 : if (cd.get_ctxt ())
2556 : {
2557 33 : cd.get_ctxt ()->bifurcate (std::make_unique<strstr_call_info> (cd, false));
2558 33 : cd.get_ctxt ()->bifurcate (std::make_unique<strstr_call_info> (cd, true));
2559 33 : cd.get_ctxt ()->terminate_path ();
2560 : }
2561 33 : }
2562 :
2563 : /* Handle calls to "strtok".
2564 : See e.g.
2565 : https://en.cppreference.com/w/c/string/byte/strtok
2566 : https://man7.org/linux/man-pages/man3/strtok.3.html */
2567 :
2568 : class kf_strtok : public known_function
2569 : {
2570 : public:
2571 : class undefined_behavior : public undefined_function_behavior
2572 : {
2573 : public:
2574 8 : undefined_behavior (const call_details &cd)
2575 8 : : undefined_function_behavior (cd)
2576 : {
2577 : }
2578 12 : int get_controlling_option () const final override
2579 : {
2580 12 : return OPT_Wanalyzer_undefined_behavior_strtok;
2581 : }
2582 :
2583 4 : bool emit (diagnostic_emission_context &ctxt) final override
2584 : {
2585 : /* CWE-476: NULL Pointer Dereference. */
2586 4 : ctxt.add_cwe (476);
2587 4 : if (ctxt.warn ("calling %qD for first time with NULL as argument 1"
2588 : " has undefined behavior",
2589 : get_callee_fndecl ()))
2590 : {
2591 4 : inform (ctxt.get_location (),
2592 : "some implementations of %qD may crash on such input",
2593 : get_callee_fndecl ());
2594 4 : return true;
2595 : }
2596 : return false;
2597 : }
2598 :
2599 : bool
2600 8 : describe_final_event (pretty_printer &pp,
2601 : const evdesc::final_event &) final override
2602 : {
2603 8 : pp_printf (&pp,
2604 : "calling %qD for first time with NULL as argument 1"
2605 : " has undefined behavior",
2606 : get_callee_fndecl ());
2607 8 : return true;
2608 : }
2609 : };
2610 :
2611 : /* An outcome of a "strtok" call.
2612 : We have a four-way bifurcation of the analysis via the
2613 : 4 combinations of two flags:
2614 : - m_nonnull_str covers whether the "str" param was null or non-null
2615 : - m_found covers whether the result is null or non-null
2616 : */
2617 : class strtok_call_info : public call_info
2618 : {
2619 : public:
2620 512 : strtok_call_info (const call_details &cd,
2621 : const private_region &private_reg,
2622 : bool nonnull_str,
2623 : bool found)
2624 512 : : call_info (cd),
2625 512 : m_private_reg (private_reg),
2626 512 : m_nonnull_str (nonnull_str),
2627 512 : m_found (found)
2628 : {
2629 : }
2630 :
2631 0 : void print_desc (pretty_printer &pp) const final override
2632 : {
2633 0 : if (m_nonnull_str)
2634 : {
2635 0 : if (m_found)
2636 0 : pp_printf (&pp,
2637 : "when %qE on non-NULL string returns non-NULL",
2638 : get_fndecl ());
2639 : else
2640 0 : pp_printf (&pp,
2641 : "when %qE on non-NULL string returns NULL",
2642 : get_fndecl ());
2643 : }
2644 : else
2645 : {
2646 0 : if (m_found)
2647 0 : pp_printf (&pp,
2648 : "when %qE with NULL string (using prior) returns"
2649 : " non-NULL",
2650 : get_fndecl ());
2651 : else
2652 0 : pp_printf (&pp,
2653 : "when %qE with NULL string (using prior) returns NULL",
2654 : get_fndecl ());
2655 : }
2656 0 : }
2657 :
2658 528 : bool update_model (region_model *model,
2659 : const exploded_edge *,
2660 : region_model_context *ctxt) const final override
2661 : {
2662 528 : region_model_manager *mgr = model->get_manager ();
2663 528 : const call_details cd (get_call_details (model, ctxt));
2664 528 : const svalue *str_sval = cd.get_arg_svalue (0);
2665 : /* const svalue *delim_sval = cd.get_arg_svalue (1); */
2666 :
2667 528 : cd.check_for_null_terminated_string_arg (1);
2668 : /* We check that either arg 0 or the private region is null
2669 : terminated below. */
2670 :
2671 528 : const svalue *null_ptr_sval
2672 528 : = mgr->get_or_create_null_ptr (cd.get_arg_type (0));;
2673 800 : if (!model->add_constraint (str_sval,
2674 528 : m_nonnull_str ? NE_EXPR : EQ_EXPR,
2675 : null_ptr_sval,
2676 : cd.get_ctxt ()))
2677 : return false;
2678 :
2679 312 : if (m_nonnull_str)
2680 : {
2681 : /* Update internal buffer. */
2682 72 : model->set_value (&m_private_reg,
2683 : mgr->get_or_create_unmergeable (str_sval),
2684 : ctxt);
2685 : }
2686 : else
2687 : {
2688 : /* Read from internal buffer. */
2689 240 : str_sval = model->get_store_value (&m_private_reg, ctxt);
2690 :
2691 : /* The initial value of the private region is NULL when we're
2692 : on a path from main. */
2693 480 : if (const initial_svalue *initial_sval
2694 240 : = str_sval->dyn_cast_initial_svalue ())
2695 56 : if (initial_sval->get_region () == &m_private_reg
2696 56 : && model->called_from_main_p ())
2697 : {
2698 : /* Implementations of strtok do not necessarily check for NULL
2699 : here, and may crash; see PR analyzer/107573.
2700 : Warn for this, if we were definitely passed NULL. */
2701 16 : if (cd.get_arg_svalue (0)->all_zeroes_p ())
2702 : {
2703 8 : if (ctxt)
2704 8 : ctxt->warn (::std::make_unique<undefined_behavior> (cd));
2705 : }
2706 :
2707 : /* Assume that "str" was actually non-null; terminate
2708 : this path. */
2709 16 : return false;
2710 : }
2711 :
2712 : /* Now assume str_sval is non-null. */
2713 224 : if (!model->add_constraint (str_sval,
2714 : NE_EXPR,
2715 : null_ptr_sval,
2716 : cd.get_ctxt ()))
2717 : return false;
2718 : }
2719 :
2720 296 : const region *buf_reg = model->deref_rvalue (str_sval, NULL_TREE, ctxt);
2721 296 : model->scan_for_null_terminator (buf_reg,
2722 : NULL_TREE,
2723 : nullptr,
2724 : ctxt);
2725 :
2726 296 : if (m_found)
2727 : {
2728 148 : const region *str_reg
2729 148 : = model->deref_rvalue (str_sval, cd.get_arg_tree (0),
2730 : cd.get_ctxt ());
2731 : /* We want to figure out the start and nul terminator
2732 : for the token.
2733 : For each, we want str_sval + OFFSET for some unknown OFFSET.
2734 : Use a conjured_svalue to represent the offset,
2735 : using the str_reg as the id of the conjured_svalue. */
2736 148 : const svalue *start_offset
2737 148 : = mgr->get_or_create_conjured_svalue (size_type_node,
2738 148 : &cd.get_call_stmt (),
2739 : str_reg,
2740 148 : conjured_purge (model,
2741 148 : ctxt),
2742 : 0);
2743 148 : const svalue *nul_offset
2744 148 : = mgr->get_or_create_conjured_svalue (size_type_node,
2745 148 : &cd.get_call_stmt (),
2746 : str_reg,
2747 148 : conjured_purge (model,
2748 148 : ctxt),
2749 : 1);
2750 :
2751 148 : tree char_ptr_type = build_pointer_type (char_type_node);
2752 148 : const svalue *result
2753 148 : = mgr->get_or_create_binop (char_ptr_type, POINTER_PLUS_EXPR,
2754 : str_sval, start_offset);
2755 148 : cd.maybe_set_lhs (result);
2756 :
2757 : /* nul_offset + 1; the offset to use for the next call. */
2758 148 : const svalue *next_offset
2759 148 : = mgr->get_or_create_binop (size_type_node, PLUS_EXPR,
2760 : nul_offset,
2761 : mgr->get_or_create_int_cst
2762 148 : (char_type_node, 1));
2763 :
2764 : /* Write '\0' to str_sval[nul_offset]. */
2765 148 : const svalue *ptr_to_term
2766 148 : = mgr->get_or_create_binop (char_ptr_type, POINTER_PLUS_EXPR,
2767 : str_sval, nul_offset);
2768 148 : const region *terminator_reg
2769 148 : = model->deref_rvalue (ptr_to_term, NULL_TREE, cd.get_ctxt ());
2770 148 : model->set_value (terminator_reg,
2771 : mgr->get_or_create_unmergeable
2772 : (mgr->get_or_create_int_cst (char_type_node,
2773 148 : 0)),
2774 : cd.get_ctxt ());
2775 :
2776 : /* Update saved ptr to be at [nul_offset + 1]. */
2777 148 : const svalue *ptr_to_next
2778 148 : = mgr->get_or_create_binop (cd.get_lhs_type (), POINTER_PLUS_EXPR,
2779 : str_sval, next_offset);
2780 148 : model->set_value (&m_private_reg, ptr_to_next, ctxt);
2781 : }
2782 : else
2783 148 : if (tree lhs_type = cd.get_lhs_type ())
2784 : {
2785 140 : const svalue *result
2786 140 : = mgr->get_or_create_int_cst (lhs_type, 0);
2787 140 : cd.maybe_set_lhs (result);
2788 : }
2789 : return true;
2790 : }
2791 : private:
2792 : const private_region &m_private_reg;
2793 : bool m_nonnull_str;
2794 : bool m_found;
2795 : }; // class strtok_call_info
2796 :
2797 6846 : kf_strtok (region_model_manager &mgr)
2798 6846 : : m_private_reg (mgr.alloc_symbol_id (),
2799 6846 : mgr.get_root_region (),
2800 : get_region_type (),
2801 6846 : "strtok buffer")
2802 : {
2803 6846 : }
2804 :
2805 896 : bool matches_call_types_p (const call_details &cd) const final override
2806 : {
2807 896 : return (cd.num_args () == 2
2808 896 : && POINTER_TYPE_P (cd.get_arg_type (0))
2809 1792 : && POINTER_TYPE_P (cd.get_arg_type (1)));
2810 : }
2811 :
2812 128 : void impl_call_post (const call_details &cd) const final override
2813 : {
2814 128 : if (cd.get_ctxt ())
2815 : {
2816 : /* Four-way bifurcation, based on whether:
2817 : - the str is non-null
2818 : - the result is non-null
2819 : Typically the str is either null or non-null at a particular site,
2820 : so hopefully this will generally just lead to two out-edges. */
2821 128 : cd.get_ctxt ()->bifurcate
2822 128 : (std::make_unique<strtok_call_info> (cd, m_private_reg, false, false));
2823 128 : cd.get_ctxt ()->bifurcate
2824 128 : (std::make_unique<strtok_call_info> (cd, m_private_reg, false, true));
2825 128 : cd.get_ctxt ()->bifurcate
2826 128 : (std::make_unique<strtok_call_info> (cd, m_private_reg, true, false));
2827 128 : cd.get_ctxt ()->bifurcate
2828 128 : (std::make_unique<strtok_call_info> (cd, m_private_reg, true, true));
2829 128 : cd.get_ctxt ()->terminate_path ();
2830 : }
2831 128 : }
2832 :
2833 : private:
2834 6846 : static tree get_region_type ()
2835 : {
2836 6846 : return build_pointer_type (char_type_node);
2837 : }
2838 : const private_region m_private_reg;
2839 : };
2840 :
2841 : /* Handle calls to functions referenced by
2842 : __attribute__((malloc(FOO))). */
2843 :
2844 : void
2845 487 : region_model::impl_deallocation_call (const call_details &cd)
2846 : {
2847 487 : kf_free kf;
2848 487 : kf.impl_call_post (cd);
2849 487 : }
2850 :
2851 : /* Handler for "strcasecmp" */
2852 :
2853 10269 : class kf_strcasecmp : public builtin_known_function
2854 : {
2855 : public:
2856 : bool
2857 0 : matches_call_types_p (const call_details &cd) const final override
2858 : {
2859 0 : return (cd.num_args () == 2
2860 0 : && cd.arg_is_pointer_p (0)
2861 0 : && cd.arg_is_pointer_p (1));
2862 : }
2863 : void
2864 7 : impl_call_pre (const call_details &cd) const final override
2865 : {
2866 7 : cd.check_for_null_terminated_string_arg (0);
2867 7 : cd.check_for_null_terminated_string_arg (1);
2868 7 : }
2869 :
2870 : enum built_in_function
2871 56 : builtin_code () const final override
2872 : {
2873 56 : return BUILT_IN_STRCASECMP;
2874 : }
2875 :
2876 : void impl_call_post (const call_details &cd) const final override;
2877 : };
2878 :
2879 : void
2880 7 : kf_strcasecmp::impl_call_post (const call_details &cd) const
2881 : {
2882 7 : if (cd.get_lhs_type ())
2883 : {
2884 2 : const svalue *result_val
2885 2 : = cd.get_or_create_conjured_svalue (cd.get_lhs_region ());
2886 2 : cd.maybe_set_lhs (result_val);
2887 : }
2888 7 : }
2889 :
2890 : static void
2891 3423 : register_atomic_builtins (known_function_manager &kfm)
2892 : {
2893 3423 : kfm.add (BUILT_IN_ATOMIC_EXCHANGE, std::make_unique<kf_atomic_exchange> ());
2894 3423 : kfm.add (BUILT_IN_ATOMIC_EXCHANGE_N, std::make_unique<kf_atomic_exchange_n> ());
2895 3423 : kfm.add (BUILT_IN_ATOMIC_EXCHANGE_1, std::make_unique<kf_atomic_exchange_n> ());
2896 3423 : kfm.add (BUILT_IN_ATOMIC_EXCHANGE_2, std::make_unique<kf_atomic_exchange_n> ());
2897 3423 : kfm.add (BUILT_IN_ATOMIC_EXCHANGE_4, std::make_unique<kf_atomic_exchange_n> ());
2898 3423 : kfm.add (BUILT_IN_ATOMIC_EXCHANGE_8, std::make_unique<kf_atomic_exchange_n> ());
2899 3423 : kfm.add (BUILT_IN_ATOMIC_EXCHANGE_16, std::make_unique<kf_atomic_exchange_n> ());
2900 3423 : kfm.add (BUILT_IN_ATOMIC_LOAD, std::make_unique<kf_atomic_load> ());
2901 3423 : kfm.add (BUILT_IN_ATOMIC_LOAD_N, std::make_unique<kf_atomic_load_n> ());
2902 3423 : kfm.add (BUILT_IN_ATOMIC_LOAD_1, std::make_unique<kf_atomic_load_n> ());
2903 3423 : kfm.add (BUILT_IN_ATOMIC_LOAD_2, std::make_unique<kf_atomic_load_n> ());
2904 3423 : kfm.add (BUILT_IN_ATOMIC_LOAD_4, std::make_unique<kf_atomic_load_n> ());
2905 3423 : kfm.add (BUILT_IN_ATOMIC_LOAD_8, std::make_unique<kf_atomic_load_n> ());
2906 3423 : kfm.add (BUILT_IN_ATOMIC_LOAD_16, std::make_unique<kf_atomic_load_n> ());
2907 3423 : kfm.add (BUILT_IN_ATOMIC_STORE, std::make_unique<kf_atomic_store> ());
2908 3423 : kfm.add (BUILT_IN_ATOMIC_STORE_N, std::make_unique<kf_atomic_store_n> ());
2909 3423 : kfm.add (BUILT_IN_ATOMIC_STORE_1, std::make_unique<kf_atomic_store_n> ());
2910 3423 : kfm.add (BUILT_IN_ATOMIC_STORE_2, std::make_unique<kf_atomic_store_n> ());
2911 3423 : kfm.add (BUILT_IN_ATOMIC_STORE_4, std::make_unique<kf_atomic_store_n> ());
2912 3423 : kfm.add (BUILT_IN_ATOMIC_STORE_8, std::make_unique<kf_atomic_store_n> ());
2913 3423 : kfm.add (BUILT_IN_ATOMIC_STORE_16, std::make_unique<kf_atomic_store_n> ());
2914 3423 : kfm.add (BUILT_IN_ATOMIC_ADD_FETCH_1,
2915 3423 : std::make_unique<kf_atomic_op_fetch> (PLUS_EXPR));
2916 3423 : kfm.add (BUILT_IN_ATOMIC_ADD_FETCH_2,
2917 3423 : std::make_unique<kf_atomic_op_fetch> (PLUS_EXPR));
2918 3423 : kfm.add (BUILT_IN_ATOMIC_ADD_FETCH_4,
2919 3423 : std::make_unique<kf_atomic_op_fetch> (PLUS_EXPR));
2920 3423 : kfm.add (BUILT_IN_ATOMIC_ADD_FETCH_8,
2921 3423 : std::make_unique<kf_atomic_op_fetch> (PLUS_EXPR));
2922 3423 : kfm.add (BUILT_IN_ATOMIC_ADD_FETCH_16,
2923 3423 : std::make_unique<kf_atomic_op_fetch> (PLUS_EXPR));
2924 3423 : kfm.add (BUILT_IN_ATOMIC_SUB_FETCH_1,
2925 3423 : std::make_unique<kf_atomic_op_fetch> (MINUS_EXPR));
2926 3423 : kfm.add (BUILT_IN_ATOMIC_SUB_FETCH_2,
2927 3423 : std::make_unique<kf_atomic_op_fetch> (MINUS_EXPR));
2928 3423 : kfm.add (BUILT_IN_ATOMIC_SUB_FETCH_4,
2929 3423 : std::make_unique<kf_atomic_op_fetch> (MINUS_EXPR));
2930 3423 : kfm.add (BUILT_IN_ATOMIC_SUB_FETCH_8,
2931 3423 : std::make_unique<kf_atomic_op_fetch> (MINUS_EXPR));
2932 3423 : kfm.add (BUILT_IN_ATOMIC_SUB_FETCH_16,
2933 3423 : std::make_unique<kf_atomic_op_fetch> (MINUS_EXPR));
2934 3423 : kfm.add (BUILT_IN_ATOMIC_AND_FETCH_1,
2935 3423 : std::make_unique<kf_atomic_op_fetch> (BIT_AND_EXPR));
2936 3423 : kfm.add (BUILT_IN_ATOMIC_AND_FETCH_2,
2937 3423 : std::make_unique<kf_atomic_op_fetch> (BIT_AND_EXPR));
2938 3423 : kfm.add (BUILT_IN_ATOMIC_AND_FETCH_4,
2939 3423 : std::make_unique<kf_atomic_op_fetch> (BIT_AND_EXPR));
2940 3423 : kfm.add (BUILT_IN_ATOMIC_AND_FETCH_8,
2941 3423 : std::make_unique<kf_atomic_op_fetch> (BIT_AND_EXPR));
2942 3423 : kfm.add (BUILT_IN_ATOMIC_AND_FETCH_16,
2943 3423 : std::make_unique<kf_atomic_op_fetch> (BIT_AND_EXPR));
2944 3423 : kfm.add (BUILT_IN_ATOMIC_XOR_FETCH_1,
2945 3423 : std::make_unique<kf_atomic_op_fetch> (BIT_XOR_EXPR));
2946 3423 : kfm.add (BUILT_IN_ATOMIC_XOR_FETCH_2,
2947 3423 : std::make_unique<kf_atomic_op_fetch> (BIT_XOR_EXPR));
2948 3423 : kfm.add (BUILT_IN_ATOMIC_XOR_FETCH_4,
2949 3423 : std::make_unique<kf_atomic_op_fetch> (BIT_XOR_EXPR));
2950 3423 : kfm.add (BUILT_IN_ATOMIC_XOR_FETCH_8,
2951 3423 : std::make_unique<kf_atomic_op_fetch> (BIT_XOR_EXPR));
2952 3423 : kfm.add (BUILT_IN_ATOMIC_XOR_FETCH_16,
2953 3423 : std::make_unique<kf_atomic_op_fetch> (BIT_XOR_EXPR));
2954 3423 : kfm.add (BUILT_IN_ATOMIC_OR_FETCH_1,
2955 3423 : std::make_unique<kf_atomic_op_fetch> (BIT_IOR_EXPR));
2956 3423 : kfm.add (BUILT_IN_ATOMIC_OR_FETCH_2,
2957 3423 : std::make_unique<kf_atomic_op_fetch> (BIT_IOR_EXPR));
2958 3423 : kfm.add (BUILT_IN_ATOMIC_OR_FETCH_4,
2959 3423 : std::make_unique<kf_atomic_op_fetch> (BIT_IOR_EXPR));
2960 3423 : kfm.add (BUILT_IN_ATOMIC_OR_FETCH_8,
2961 3423 : std::make_unique<kf_atomic_op_fetch> (BIT_IOR_EXPR));
2962 3423 : kfm.add (BUILT_IN_ATOMIC_OR_FETCH_16,
2963 3423 : std::make_unique<kf_atomic_op_fetch> (BIT_IOR_EXPR));
2964 3423 : kfm.add (BUILT_IN_ATOMIC_FETCH_ADD_1,
2965 3423 : std::make_unique<kf_atomic_fetch_op> (PLUS_EXPR));
2966 3423 : kfm.add (BUILT_IN_ATOMIC_FETCH_ADD_2,
2967 3423 : std::make_unique<kf_atomic_fetch_op> (PLUS_EXPR));
2968 3423 : kfm.add (BUILT_IN_ATOMIC_FETCH_ADD_4,
2969 3423 : std::make_unique<kf_atomic_fetch_op> (PLUS_EXPR));
2970 3423 : kfm.add (BUILT_IN_ATOMIC_FETCH_ADD_8,
2971 3423 : std::make_unique<kf_atomic_fetch_op> (PLUS_EXPR));
2972 3423 : kfm.add (BUILT_IN_ATOMIC_FETCH_ADD_16,
2973 3423 : std::make_unique<kf_atomic_fetch_op> (PLUS_EXPR));
2974 3423 : kfm.add (BUILT_IN_ATOMIC_FETCH_SUB_1,
2975 3423 : std::make_unique<kf_atomic_fetch_op> (MINUS_EXPR));
2976 3423 : kfm.add (BUILT_IN_ATOMIC_FETCH_SUB_2,
2977 3423 : std::make_unique<kf_atomic_fetch_op> (MINUS_EXPR));
2978 3423 : kfm.add (BUILT_IN_ATOMIC_FETCH_SUB_4,
2979 3423 : std::make_unique<kf_atomic_fetch_op> (MINUS_EXPR));
2980 3423 : kfm.add (BUILT_IN_ATOMIC_FETCH_SUB_8,
2981 3423 : std::make_unique<kf_atomic_fetch_op> (MINUS_EXPR));
2982 3423 : kfm.add (BUILT_IN_ATOMIC_FETCH_SUB_16,
2983 3423 : std::make_unique<kf_atomic_fetch_op> (MINUS_EXPR));
2984 3423 : kfm.add (BUILT_IN_ATOMIC_FETCH_AND_1,
2985 3423 : std::make_unique<kf_atomic_fetch_op> (BIT_AND_EXPR));
2986 3423 : kfm.add (BUILT_IN_ATOMIC_FETCH_AND_2,
2987 3423 : std::make_unique<kf_atomic_fetch_op> (BIT_AND_EXPR));
2988 3423 : kfm.add (BUILT_IN_ATOMIC_FETCH_AND_4,
2989 3423 : std::make_unique<kf_atomic_fetch_op> (BIT_AND_EXPR));
2990 3423 : kfm.add (BUILT_IN_ATOMIC_FETCH_AND_8,
2991 3423 : std::make_unique<kf_atomic_fetch_op> (BIT_AND_EXPR));
2992 3423 : kfm.add (BUILT_IN_ATOMIC_FETCH_AND_16,
2993 3423 : std::make_unique<kf_atomic_fetch_op> (BIT_AND_EXPR));
2994 3423 : kfm.add (BUILT_IN_ATOMIC_FETCH_XOR_1,
2995 3423 : std::make_unique<kf_atomic_fetch_op> (BIT_XOR_EXPR));
2996 3423 : kfm.add (BUILT_IN_ATOMIC_FETCH_XOR_2,
2997 3423 : std::make_unique<kf_atomic_fetch_op> (BIT_XOR_EXPR));
2998 3423 : kfm.add (BUILT_IN_ATOMIC_FETCH_XOR_4,
2999 3423 : std::make_unique<kf_atomic_fetch_op> (BIT_XOR_EXPR));
3000 3423 : kfm.add (BUILT_IN_ATOMIC_FETCH_XOR_8,
3001 3423 : std::make_unique<kf_atomic_fetch_op> (BIT_XOR_EXPR));
3002 3423 : kfm.add (BUILT_IN_ATOMIC_FETCH_XOR_16,
3003 3423 : std::make_unique<kf_atomic_fetch_op> (BIT_XOR_EXPR));
3004 3423 : kfm.add (BUILT_IN_ATOMIC_FETCH_OR_1,
3005 3423 : std::make_unique<kf_atomic_fetch_op> (BIT_IOR_EXPR));
3006 3423 : kfm.add (BUILT_IN_ATOMIC_FETCH_OR_2,
3007 3423 : std::make_unique<kf_atomic_fetch_op> (BIT_IOR_EXPR));
3008 3423 : kfm.add (BUILT_IN_ATOMIC_FETCH_OR_4,
3009 3423 : std::make_unique<kf_atomic_fetch_op> (BIT_IOR_EXPR));
3010 3423 : kfm.add (BUILT_IN_ATOMIC_FETCH_OR_8,
3011 3423 : std::make_unique<kf_atomic_fetch_op> (BIT_IOR_EXPR));
3012 3423 : kfm.add (BUILT_IN_ATOMIC_FETCH_OR_16,
3013 3423 : std::make_unique<kf_atomic_fetch_op> (BIT_IOR_EXPR));
3014 3423 : }
3015 :
3016 : /* Handle calls to the various IFN_UBSAN_* with no return value.
3017 : For now, treat these as no-ops. */
3018 :
3019 10269 : class kf_ubsan_noop : public internal_known_function
3020 : {
3021 : };
3022 :
3023 : /* Handle calls to the various __builtin___ubsan_handle_*.
3024 : These can return, but continuing after such a return
3025 : isn't likely to be interesting to the user of the analyzer.
3026 : Hence we terminate the analysis path at one of these calls. */
3027 :
3028 3423 : class kf_ubsan_handler : public internal_known_function
3029 : {
3030 12 : void impl_call_post (const call_details &cd) const final override
3031 : {
3032 12 : if (cd.get_ctxt ())
3033 12 : cd.get_ctxt ()->terminate_path ();
3034 12 : }
3035 : };
3036 :
3037 : static void
3038 3423 : register_sanitizer_builtins (known_function_manager &kfm)
3039 : {
3040 : /* Handle calls to the various IFN_UBSAN_* with no return value.
3041 : For now, treat these as no-ops. */
3042 3423 : kfm.add (IFN_UBSAN_NULL,
3043 3423 : std::make_unique<kf_ubsan_noop> ());
3044 3423 : kfm.add (IFN_UBSAN_BOUNDS,
3045 3423 : std::make_unique<kf_ubsan_noop> ());
3046 3423 : kfm.add (IFN_UBSAN_PTR,
3047 3423 : std::make_unique<kf_ubsan_noop> ());
3048 :
3049 3423 : kfm.add (BUILT_IN_UBSAN_HANDLE_NONNULL_ARG,
3050 3423 : std::make_unique<kf_ubsan_handler> ());
3051 3423 : }
3052 :
3053 : /* Populate KFM with instances of known functions supported by the core of the
3054 : analyzer (as opposed to plugins). */
3055 :
3056 : void
3057 3423 : register_known_functions (known_function_manager &kfm,
3058 : region_model_manager &rmm)
3059 : {
3060 : /* Debugging/test support functions, all with a "__analyzer_" prefix. */
3061 3423 : register_known_analyzer_functions (kfm);
3062 :
3063 : /* Internal fns the analyzer has known_functions for. */
3064 3423 : {
3065 3423 : kfm.add (IFN_BUILTIN_EXPECT, std::make_unique<kf_expect> ());
3066 : }
3067 :
3068 : /* GCC built-ins that do not correspond to a function
3069 : in the standard library. */
3070 3423 : {
3071 3423 : kfm.add (BUILT_IN_EXPECT, std::make_unique<kf_expect> ());
3072 3423 : kfm.add (BUILT_IN_EXPECT_WITH_PROBABILITY, std::make_unique<kf_expect> ());
3073 3423 : kfm.add (BUILT_IN_ALLOCA_WITH_ALIGN, std::make_unique<kf_alloca> ());
3074 3423 : kfm.add (BUILT_IN_STACK_RESTORE, std::make_unique<kf_stack_restore> ());
3075 3423 : kfm.add (BUILT_IN_STACK_SAVE, std::make_unique<kf_stack_save> ());
3076 :
3077 3423 : kfm.add (BUILT_IN_EH_POINTER, std::make_unique<kf_eh_pointer> ());
3078 :
3079 3423 : kfm.add(BUILT_IN_STRCASECMP, std::make_unique<kf_strcasecmp>());
3080 :
3081 3423 : register_atomic_builtins (kfm);
3082 3423 : register_sanitizer_builtins (kfm);
3083 3423 : register_varargs_builtins (kfm);
3084 : }
3085 :
3086 : /* Known builtins and C standard library functions
3087 : the analyzer has known functions for. */
3088 3423 : {
3089 3423 : kfm.add ("atoi", std::make_unique<kf_atoi_family> ());
3090 3423 : kfm.add ("atol", std::make_unique<kf_atoi_family> ());
3091 3423 : kfm.add ("atoll", std::make_unique<kf_atoi_family> ());
3092 :
3093 3423 : kfm.add ("alloca", std::make_unique<kf_alloca> ());
3094 3423 : kfm.add ("__builtin_alloca", std::make_unique<kf_alloca> ());
3095 3423 : kfm.add ("calloc", std::make_unique<kf_calloc> ());
3096 3423 : kfm.add ("__builtin_calloc", std::make_unique<kf_calloc> ());
3097 3423 : kfm.add ("free", std::make_unique<kf_free> ());
3098 3423 : kfm.add ("__builtin_free", std::make_unique<kf_free> ());
3099 3423 : kfm.add ("malloc", std::make_unique<kf_malloc> ());
3100 3423 : kfm.add ("__builtin_malloc", std::make_unique<kf_malloc> ());
3101 3423 : kfm.add ("memcpy",
3102 3423 : std::make_unique<kf_memcpy_memmove> (kf_memcpy_memmove::KF_MEMCPY));
3103 3423 : kfm.add ("__builtin_memcpy",
3104 3423 : std::make_unique<kf_memcpy_memmove> (kf_memcpy_memmove::KF_MEMCPY));
3105 3423 : kfm.add ("__memcpy_chk", std::make_unique<kf_memcpy_memmove>
3106 3423 : (kf_memcpy_memmove::KF_MEMCPY_CHK));
3107 3423 : kfm.add ("__builtin___memcpy_chk", std::make_unique<kf_memcpy_memmove>
3108 3423 : (kf_memcpy_memmove::KF_MEMCPY_CHK));
3109 3423 : kfm.add ("memmove",
3110 3423 : std::make_unique<kf_memcpy_memmove> (kf_memcpy_memmove::KF_MEMMOVE));
3111 3423 : kfm.add ("__builtin_memmove",
3112 3423 : std::make_unique<kf_memcpy_memmove> (kf_memcpy_memmove::KF_MEMMOVE));
3113 3423 : kfm.add ("__memmove_chk", std::make_unique<kf_memcpy_memmove>
3114 3423 : (kf_memcpy_memmove::KF_MEMMOVE_CHK));
3115 3423 : kfm.add ("__builtin___memmove_chk", std::make_unique<kf_memcpy_memmove>
3116 3423 : (kf_memcpy_memmove::KF_MEMMOVE_CHK));
3117 3423 : kfm.add ("memset", std::make_unique<kf_memset> (false));
3118 3423 : kfm.add ("__builtin_memset", std::make_unique<kf_memset> (false));
3119 3423 : kfm.add ("__memset_chk", std::make_unique<kf_memset> (true));
3120 3423 : kfm.add ("__builtin___memset_chk", std::make_unique<kf_memset> (true));
3121 3423 : kfm.add ("realloc", std::make_unique<kf_realloc> ());
3122 3423 : kfm.add ("__builtin_realloc", std::make_unique<kf_realloc> ());
3123 3423 : kfm.add ("sprintf", std::make_unique<kf_sprintf> ());
3124 3423 : kfm.add ("__builtin_sprintf", std::make_unique<kf_sprintf> ());
3125 3423 : kfm.add ("strchr", std::make_unique<kf_strchr> ());
3126 3423 : kfm.add ("__builtin_strchr", std::make_unique<kf_strchr> ());
3127 3423 : kfm.add ("strcpy", std::make_unique<kf_strcpy> (2, false));
3128 3423 : kfm.add ("__builtin_strcpy", std::make_unique<kf_strcpy> (2, false));
3129 3423 : kfm.add ("__strcpy_chk", std::make_unique<kf_strcpy> (3, true));
3130 3423 : kfm.add ("__builtin___strcpy_chk", std::make_unique<kf_strcpy> (3, true));
3131 3423 : kfm.add ("strcat", std::make_unique<kf_strcat> (2, false));
3132 3423 : kfm.add ("__builtin_strcat", std::make_unique<kf_strcat> (2, false));
3133 3423 : kfm.add ("__strcat_chk", std::make_unique<kf_strcat> (3, true));
3134 3423 : kfm.add ("__builtin___strcat_chk", std::make_unique<kf_strcat> (3, true));
3135 3423 : kfm.add ("strdup", std::make_unique<kf_strdup> ());
3136 3423 : kfm.add ("__builtin_strdup", std::make_unique<kf_strdup> ());
3137 3423 : kfm.add ("strncpy", std::make_unique<kf_strncpy> ());
3138 3423 : kfm.add ("__builtin_strncpy", std::make_unique<kf_strncpy> ());
3139 3423 : kfm.add ("strndup", std::make_unique<kf_strndup> ());
3140 3423 : kfm.add ("__builtin_strndup", std::make_unique<kf_strndup> ());
3141 3423 : kfm.add ("strlen", std::make_unique<kf_strlen> ());
3142 3423 : kfm.add ("__builtin_strlen", std::make_unique<kf_strlen> ());
3143 3423 : kfm.add ("strstr", std::make_unique<kf_strstr> ());
3144 3423 : kfm.add ("__builtin_strstr", std::make_unique<kf_strstr> ());
3145 3423 : kfm.add("strcasecmp", std::make_unique<kf_strcasecmp>());
3146 3423 : kfm.add("__builtin_strcasecmp", std::make_unique<kf_strcasecmp>());
3147 : }
3148 :
3149 : /* Known POSIX functions, and some non-standard extensions. */
3150 3423 : {
3151 3423 : kfm.add ("fopen", std::make_unique<kf_fopen> ());
3152 3423 : kfm.add ("getenv", std::make_unique<kf_getenv> ());
3153 3423 : kfm.add ("mkdtemp", std::make_unique<kf_mktemp_simple> (
3154 3423 : kf_mktemp_family::outcome::null_ptr));
3155 3423 : kfm.add ("mkostemp", std::make_unique<kf_mkostemp> ());
3156 3423 : kfm.add ("mkostemps", std::make_unique<kf_mkostemps> ());
3157 3423 : kfm.add ("mkstemps", std::make_unique<kf_mkstemps> ());
3158 3423 : kfm.add ("mkstemp", std::make_unique<kf_mktemp_simple> (
3159 3423 : kf_mktemp_family::outcome::fd));
3160 : /* TODO: Report mktemp as deprecated per MSC24-C
3161 : (https://wiki.sei.cmu.edu/confluence/x/hNYxBQ). */
3162 3423 : kfm.add ("mktemp", std::make_unique<kf_mktemp_simple> (
3163 3423 : kf_mktemp_family::outcome::modif_tmpl));
3164 3423 : kfm.add ("putenv", std::make_unique<kf_putenv> ());
3165 3423 : kfm.add ("strtok", std::make_unique<kf_strtok> (rmm));
3166 :
3167 3423 : register_known_fd_functions (kfm);
3168 3423 : register_known_file_functions (kfm);
3169 : }
3170 :
3171 : /* glibc functions. */
3172 3423 : {
3173 3423 : kfm.add ("__errno_location", std::make_unique<kf_errno_location> ());
3174 3423 : kfm.add ("error", std::make_unique<kf_error> (3));
3175 3423 : kfm.add ("error_at_line", std::make_unique<kf_error> (5));
3176 : /* Variants of "error" and "error_at_line" seen by the
3177 : analyzer at -O0 (PR analyzer/115724). */
3178 3423 : kfm.add ("__error_alias", std::make_unique<kf_error> (3));
3179 3423 : kfm.add ("__error_at_line_alias", std::make_unique<kf_error> (5));
3180 : }
3181 :
3182 : /* Other implementations of C standard library. */
3183 3423 : {
3184 : /* According to PR 107807 comment #2, Solaris implements "errno"
3185 : like this:
3186 : extern int *___errno(void) __attribute__((__const__));
3187 : #define errno (*(___errno()))
3188 : and macOS like this:
3189 : extern int * __error(void);
3190 : #define errno (*__error())
3191 : and similarly __errno for newlib.
3192 : Add these as synonyms for "__errno_location". */
3193 3423 : kfm.add ("___errno", std::make_unique<kf_errno_location> ());
3194 3423 : kfm.add ("__error", std::make_unique<kf_errno_location> ());
3195 3423 : kfm.add ("__errno", std::make_unique<kf_errno_location> ());
3196 3423 : kfm.add ("__get_errno_ptr", std::make_unique<kf_errno_location> ());
3197 : }
3198 :
3199 : /* Language-specific support functions. */
3200 3423 : register_known_functions_lang_cp (kfm);
3201 :
3202 : /* Some C++ implementations use the std:: copies of these functions
3203 : from <cstdlib> etc for the C spellings of these headers (e.g. <stdlib.h>),
3204 : so we must match against these too. */
3205 3423 : {
3206 3423 : kfm.add_std_ns ("atoi", std::make_unique<kf_atoi_family> ());
3207 3423 : kfm.add_std_ns ("atol", std::make_unique<kf_atoi_family> ());
3208 3423 : kfm.add_std_ns ("atoll", std::make_unique<kf_atoi_family> ());
3209 :
3210 3423 : kfm.add_std_ns ("malloc", std::make_unique<kf_malloc> ());
3211 3423 : kfm.add_std_ns ("free", std::make_unique<kf_free> ());
3212 3423 : kfm.add_std_ns ("realloc", std::make_unique<kf_realloc> ());
3213 3423 : kfm.add_std_ns ("calloc", std::make_unique<kf_calloc> ());
3214 3423 : kfm.add_std_ns
3215 3423 : ("memcpy",
3216 3423 : std::make_unique<kf_memcpy_memmove> (kf_memcpy_memmove::KF_MEMCPY));
3217 3423 : kfm.add_std_ns
3218 3423 : ("memmove",
3219 3423 : std::make_unique<kf_memcpy_memmove> (kf_memcpy_memmove::KF_MEMMOVE));
3220 3423 : kfm.add_std_ns ("memset", std::make_unique<kf_memset> (false));
3221 3423 : kfm.add_std_ns ("strcat", std::make_unique<kf_strcat> (2, false));
3222 3423 : kfm.add_std_ns ("strcpy", std::make_unique<kf_strcpy> (2, false));
3223 3423 : kfm.add_std_ns ("strlen", std::make_unique<kf_strlen> ());
3224 3423 : kfm.add_std_ns ("strncpy", std::make_unique<kf_strncpy> ());
3225 3423 : kfm.add_std_ns ("strtok", std::make_unique<kf_strtok> (rmm));
3226 : }
3227 3423 : }
3228 :
3229 : } // namespace ana
3230 :
3231 : #endif /* #if ENABLE_ANALYZER */
|