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