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