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