]>
Commit | Line | Data |
---|---|---|
808f4dfe | 1 | /* Handling for the known behavior of various specific functions. |
7adcbafe | 2 | Copyright (C) 2020-2022 Free Software Foundation, Inc. |
808f4dfe DM |
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 | #include "system.h" | |
23 | #include "coretypes.h" | |
24 | #include "tree.h" | |
25 | #include "function.h" | |
26 | #include "basic-block.h" | |
27 | #include "gimple.h" | |
28 | #include "gimple-iterator.h" | |
29 | #include "diagnostic-core.h" | |
30 | #include "graphviz.h" | |
31 | #include "options.h" | |
32 | #include "cgraph.h" | |
33 | #include "tree-dfa.h" | |
34 | #include "stringpool.h" | |
35 | #include "convert.h" | |
36 | #include "target.h" | |
37 | #include "fold-const.h" | |
38 | #include "tree-pretty-print.h" | |
39 | #include "diagnostic-color.h" | |
40 | #include "diagnostic-metadata.h" | |
41 | #include "tristate.h" | |
42 | #include "bitmap.h" | |
43 | #include "selftest.h" | |
44 | #include "function.h" | |
809192e7 | 45 | #include "json.h" |
808f4dfe DM |
46 | #include "analyzer/analyzer.h" |
47 | #include "analyzer/analyzer-logging.h" | |
48 | #include "ordered-hash-map.h" | |
49 | #include "options.h" | |
50 | #include "cgraph.h" | |
51 | #include "cfg.h" | |
52 | #include "digraph.h" | |
53 | #include "analyzer/supergraph.h" | |
54 | #include "sbitmap.h" | |
55 | #include "analyzer/call-string.h" | |
56 | #include "analyzer/program-point.h" | |
57 | #include "analyzer/store.h" | |
58 | #include "analyzer/region-model.h" | |
eafa9d96 | 59 | #include "analyzer/call-info.h" |
808f4dfe DM |
60 | #include "gimple-pretty-print.h" |
61 | ||
62 | #if ENABLE_ANALYZER | |
63 | ||
64 | namespace ana { | |
65 | ||
66 | /* class call_details. */ | |
67 | ||
68 | /* call_details's ctor. */ | |
69 | ||
70 | call_details::call_details (const gcall *call, region_model *model, | |
71 | region_model_context *ctxt) | |
72 | : m_call (call), m_model (model), m_ctxt (ctxt), | |
73 | m_lhs_type (NULL_TREE), m_lhs_region (NULL) | |
74 | { | |
75 | m_lhs_type = NULL_TREE; | |
76 | if (tree lhs = gimple_call_lhs (call)) | |
77 | { | |
78 | m_lhs_region = model->get_lvalue (lhs, ctxt); | |
79 | m_lhs_type = TREE_TYPE (lhs); | |
80 | } | |
81 | } | |
82 | ||
3a66c289 DM |
83 | /* Get any uncertainty_t associated with the region_model_context. */ |
84 | ||
85 | uncertainty_t * | |
86 | call_details::get_uncertainty () const | |
87 | { | |
33255ad3 DM |
88 | if (m_ctxt) |
89 | return m_ctxt->get_uncertainty (); | |
90 | else | |
91 | return NULL; | |
3a66c289 DM |
92 | } |
93 | ||
808f4dfe DM |
94 | /* If the callsite has a left-hand-side region, set it to RESULT |
95 | and return true. | |
96 | Otherwise do nothing and return false. */ | |
97 | ||
98 | bool | |
99 | call_details::maybe_set_lhs (const svalue *result) const | |
100 | { | |
101 | gcc_assert (result); | |
102 | if (m_lhs_region) | |
103 | { | |
104 | m_model->set_value (m_lhs_region, result, m_ctxt); | |
105 | return true; | |
106 | } | |
107 | else | |
108 | return false; | |
109 | } | |
110 | ||
5ee4ba03 DM |
111 | /* Return the number of arguments used by the call statement. */ |
112 | ||
113 | unsigned | |
114 | call_details::num_args () const | |
115 | { | |
116 | return gimple_call_num_args (m_call); | |
117 | } | |
118 | ||
808f4dfe DM |
119 | /* Get argument IDX at the callsite as a tree. */ |
120 | ||
121 | tree | |
122 | call_details::get_arg_tree (unsigned idx) const | |
123 | { | |
124 | return gimple_call_arg (m_call, idx); | |
125 | } | |
126 | ||
1e19ecd7 DM |
127 | /* Get the type of argument IDX. */ |
128 | ||
129 | tree | |
130 | call_details::get_arg_type (unsigned idx) const | |
131 | { | |
132 | return TREE_TYPE (gimple_call_arg (m_call, idx)); | |
133 | } | |
134 | ||
808f4dfe DM |
135 | /* Get argument IDX at the callsite as an svalue. */ |
136 | ||
137 | const svalue * | |
138 | call_details::get_arg_svalue (unsigned idx) const | |
139 | { | |
140 | tree arg = get_arg_tree (idx); | |
141 | return m_model->get_rvalue (arg, m_ctxt); | |
142 | } | |
143 | ||
9ea10c48 DM |
144 | /* Attempt to get the string literal for argument IDX, or return NULL |
145 | otherwise. | |
146 | For use when implementing "__analyzer_*" functions that take | |
147 | string literals. */ | |
148 | ||
149 | const char * | |
150 | call_details::get_arg_string_literal (unsigned idx) const | |
151 | { | |
152 | const svalue *str_arg = get_arg_svalue (idx); | |
153 | if (const region *pointee = str_arg->maybe_get_region ()) | |
154 | if (const string_region *string_reg = pointee->dyn_cast_string_region ()) | |
155 | { | |
156 | tree string_cst = string_reg->get_string_cst (); | |
157 | return TREE_STRING_POINTER (string_cst); | |
158 | } | |
159 | return NULL; | |
160 | } | |
161 | ||
eafa9d96 DM |
162 | /* Attempt to get the fndecl used at this call, if known, or NULL_TREE |
163 | otherwise. */ | |
164 | ||
165 | tree | |
166 | call_details::get_fndecl_for_call () const | |
167 | { | |
168 | return m_model->get_fndecl_for_call (m_call, m_ctxt); | |
169 | } | |
170 | ||
808f4dfe DM |
171 | /* Dump a multiline representation of this call to PP. */ |
172 | ||
173 | void | |
174 | call_details::dump_to_pp (pretty_printer *pp, bool simple) const | |
175 | { | |
176 | pp_string (pp, "gcall: "); | |
177 | pp_gimple_stmt_1 (pp, m_call, 0 /* spc */, TDF_NONE /* flags */); | |
178 | pp_newline (pp); | |
179 | pp_string (pp, "return region: "); | |
180 | if (m_lhs_region) | |
181 | m_lhs_region->dump_to_pp (pp, simple); | |
182 | else | |
183 | pp_string (pp, "NULL"); | |
184 | pp_newline (pp); | |
185 | for (unsigned i = 0; i < gimple_call_num_args (m_call); i++) | |
186 | { | |
187 | const svalue *arg_sval = get_arg_svalue (i); | |
188 | pp_printf (pp, "arg %i: ", i); | |
189 | arg_sval->dump_to_pp (pp, simple); | |
190 | pp_newline (pp); | |
191 | } | |
192 | } | |
193 | ||
194 | /* Dump a multiline representation of this call to stderr. */ | |
195 | ||
196 | DEBUG_FUNCTION void | |
197 | call_details::dump (bool simple) const | |
198 | { | |
199 | pretty_printer pp; | |
200 | pp_format_decoder (&pp) = default_tree_printer; | |
201 | pp_show_color (&pp) = pp_show_color (global_dc->printer); | |
202 | pp.buffer->stream = stderr; | |
203 | dump_to_pp (&pp, simple); | |
204 | pp_flush (&pp); | |
205 | } | |
206 | ||
33255ad3 DM |
207 | /* Get a conjured_svalue for this call for REG. */ |
208 | ||
209 | const svalue * | |
210 | call_details::get_or_create_conjured_svalue (const region *reg) const | |
211 | { | |
212 | region_model_manager *mgr = m_model->get_manager (); | |
213 | return mgr->get_or_create_conjured_svalue (reg->get_type (), m_call, reg); | |
214 | } | |
215 | ||
808f4dfe DM |
216 | /* Implementations of specific functions. */ |
217 | ||
218 | /* Handle the on_call_pre part of "alloca". */ | |
219 | ||
b5081130 | 220 | void |
808f4dfe DM |
221 | region_model::impl_call_alloca (const call_details &cd) |
222 | { | |
223 | const svalue *size_sval = cd.get_arg_svalue (0); | |
b9365b93 | 224 | const region *new_reg = create_region_for_alloca (size_sval, cd.get_ctxt ()); |
808f4dfe DM |
225 | const svalue *ptr_sval |
226 | = m_mgr->get_ptr_svalue (cd.get_lhs_type (), new_reg); | |
227 | cd.maybe_set_lhs (ptr_sval); | |
808f4dfe DM |
228 | } |
229 | ||
230 | /* Handle a call to "__analyzer_describe". | |
231 | ||
232 | Emit a warning describing the 2nd argument (which can be of any | |
233 | type), at the given verbosity level. This is for use when | |
234 | debugging, and may be of use in DejaGnu tests. */ | |
235 | ||
236 | void | |
237 | region_model::impl_call_analyzer_describe (const gcall *call, | |
238 | region_model_context *ctxt) | |
239 | { | |
240 | tree t_verbosity = gimple_call_arg (call, 0); | |
241 | tree t_val = gimple_call_arg (call, 1); | |
242 | const svalue *sval = get_rvalue (t_val, ctxt); | |
243 | bool simple = zerop (t_verbosity); | |
244 | label_text desc = sval->get_desc (simple); | |
245 | warning_at (call->location, 0, "svalue: %qs", desc.m_buffer); | |
246 | } | |
247 | ||
9a2c9579 DM |
248 | /* Handle a call to "__analyzer_dump_capacity". |
249 | ||
250 | Emit a warning describing the capacity of the base region of | |
251 | the region pointed to by the 1st argument. | |
252 | This is for use when debugging, and may be of use in DejaGnu tests. */ | |
253 | ||
254 | void | |
255 | region_model::impl_call_analyzer_dump_capacity (const gcall *call, | |
256 | region_model_context *ctxt) | |
257 | { | |
258 | tree t_ptr = gimple_call_arg (call, 0); | |
259 | const svalue *sval_ptr = get_rvalue (t_ptr, ctxt); | |
260 | const region *reg = deref_rvalue (sval_ptr, t_ptr, ctxt); | |
261 | const region *base_reg = reg->get_base_region (); | |
262 | const svalue *capacity = get_capacity (base_reg); | |
263 | label_text desc = capacity->get_desc (true); | |
264 | warning_at (call->location, 0, "capacity: %qs", desc.m_buffer); | |
265 | } | |
266 | ||
808f4dfe DM |
267 | /* Handle a call to "__analyzer_eval" by evaluating the input |
268 | and dumping as a dummy warning, so that test cases can use | |
269 | dg-warning to validate the result (and so unexpected warnings will | |
270 | lead to DejaGnu failures). | |
271 | Broken out as a subroutine to make it easier to put a breakpoint on it | |
272 | - though typically this doesn't help, as we have an SSA name as the arg, | |
273 | and what's more interesting is usually the def stmt for that name. */ | |
274 | ||
275 | void | |
276 | region_model::impl_call_analyzer_eval (const gcall *call, | |
277 | region_model_context *ctxt) | |
278 | { | |
279 | tree t_arg = gimple_call_arg (call, 0); | |
280 | tristate t = eval_condition (t_arg, NE_EXPR, integer_zero_node, ctxt); | |
281 | warning_at (call->location, 0, "%s", t.as_string ()); | |
282 | } | |
283 | ||
284 | /* Handle the on_call_pre part of "__builtin_expect" etc. */ | |
285 | ||
b5081130 | 286 | void |
808f4dfe DM |
287 | region_model::impl_call_builtin_expect (const call_details &cd) |
288 | { | |
289 | /* __builtin_expect's return value is its initial argument. */ | |
290 | const svalue *sval = cd.get_arg_svalue (0); | |
291 | cd.maybe_set_lhs (sval); | |
808f4dfe DM |
292 | } |
293 | ||
294 | /* Handle the on_call_pre part of "calloc". */ | |
295 | ||
b5081130 | 296 | void |
808f4dfe DM |
297 | region_model::impl_call_calloc (const call_details &cd) |
298 | { | |
299 | const svalue *nmemb_sval = cd.get_arg_svalue (0); | |
300 | const svalue *size_sval = cd.get_arg_svalue (1); | |
301 | /* TODO: check for overflow here? */ | |
302 | const svalue *prod_sval | |
303 | = m_mgr->get_or_create_binop (size_type_node, MULT_EXPR, | |
304 | nmemb_sval, size_sval); | |
b9365b93 DM |
305 | const region *new_reg |
306 | = create_region_for_heap_alloc (prod_sval, cd.get_ctxt ()); | |
808f4dfe DM |
307 | zero_fill_region (new_reg); |
308 | if (cd.get_lhs_type ()) | |
309 | { | |
310 | const svalue *ptr_sval | |
311 | = m_mgr->get_ptr_svalue (cd.get_lhs_type (), new_reg); | |
312 | cd.maybe_set_lhs (ptr_sval); | |
313 | } | |
808f4dfe DM |
314 | } |
315 | ||
5ee4ba03 DM |
316 | /* Handle the on_call_pre part of "error" and "error_at_line" from |
317 | GNU's non-standard <error.h>. | |
318 | MIN_ARGS identifies the minimum number of expected arguments | |
319 | to be consistent with such a call (3 and 5 respectively). | |
320 | Return true if handling it as one of these functions. | |
321 | Write true to *OUT_TERMINATE_PATH if this execution path should be | |
322 | terminated (e.g. the function call terminates the process). */ | |
323 | ||
324 | bool | |
325 | region_model::impl_call_error (const call_details &cd, unsigned min_args, | |
326 | bool *out_terminate_path) | |
327 | { | |
328 | /* Bail if not enough args. */ | |
329 | if (cd.num_args () < min_args) | |
330 | return false; | |
331 | ||
332 | /* Initial argument ought to be of type "int". */ | |
333 | if (cd.get_arg_type (0) != integer_type_node) | |
334 | return false; | |
335 | ||
336 | /* The process exits if status != 0, so it only continues | |
337 | for the case where status == 0. | |
338 | Add that constraint, or terminate this analysis path. */ | |
339 | tree status = cd.get_arg_tree (0); | |
340 | if (!add_constraint (status, EQ_EXPR, integer_zero_node, cd.get_ctxt ())) | |
341 | *out_terminate_path = true; | |
342 | ||
343 | return true; | |
344 | } | |
345 | ||
33255ad3 DM |
346 | /* Handle the on_call_pre part of "fgets" and "fgets_unlocked". */ |
347 | ||
348 | void | |
349 | region_model::impl_call_fgets (const call_details &cd) | |
350 | { | |
351 | /* Ideally we would bifurcate state here between the | |
352 | error vs no error cases. */ | |
353 | const svalue *ptr_sval = cd.get_arg_svalue (0); | |
5932dd35 | 354 | if (const region *reg = ptr_sval->maybe_get_region ()) |
33255ad3 | 355 | { |
33255ad3 DM |
356 | const region *base_reg = reg->get_base_region (); |
357 | const svalue *new_sval = cd.get_or_create_conjured_svalue (base_reg); | |
358 | purge_state_involving (new_sval, cd.get_ctxt ()); | |
359 | set_value (base_reg, new_sval, cd.get_ctxt ()); | |
360 | } | |
361 | } | |
362 | ||
363 | /* Handle the on_call_pre part of "fread". */ | |
364 | ||
365 | void | |
366 | region_model::impl_call_fread (const call_details &cd) | |
367 | { | |
368 | const svalue *ptr_sval = cd.get_arg_svalue (0); | |
5932dd35 | 369 | if (const region *reg = ptr_sval->maybe_get_region ()) |
33255ad3 | 370 | { |
33255ad3 DM |
371 | const region *base_reg = reg->get_base_region (); |
372 | const svalue *new_sval = cd.get_or_create_conjured_svalue (base_reg); | |
373 | purge_state_involving (new_sval, cd.get_ctxt ()); | |
374 | set_value (base_reg, new_sval, cd.get_ctxt ()); | |
375 | } | |
376 | } | |
377 | ||
808f4dfe DM |
378 | /* Handle the on_call_post part of "free", after sm-handling. |
379 | ||
380 | If the ptr points to an underlying heap region, delete the region, | |
381 | poisoning pointers to it and regions within it. | |
382 | ||
383 | We delay this until after sm-state has been updated so that the | |
384 | sm-handling can transition all of the various casts of the pointer | |
385 | to a "freed" state *before* we delete the related region here. | |
386 | ||
387 | This has to be done here so that the sm-handling can use the fact | |
388 | that they point to the same region to establish that they are equal | |
389 | (in region_model::eval_condition_without_cm), and thus transition | |
390 | all pointers to the region to the "freed" state together, regardless | |
391 | of casts. */ | |
392 | ||
393 | void | |
394 | region_model::impl_call_free (const call_details &cd) | |
395 | { | |
396 | const svalue *ptr_sval = cd.get_arg_svalue (0); | |
5932dd35 | 397 | if (const region *freed_reg = ptr_sval->maybe_get_region ()) |
808f4dfe DM |
398 | { |
399 | /* If the ptr points to an underlying heap region, delete it, | |
400 | poisoning pointers. */ | |
808f4dfe | 401 | unbind_region_and_descendents (freed_reg, POISON_KIND_FREED); |
9a2c9579 | 402 | m_dynamic_extents.remove (freed_reg); |
808f4dfe DM |
403 | } |
404 | } | |
405 | ||
406 | /* Handle the on_call_pre part of "malloc". */ | |
407 | ||
b5081130 | 408 | void |
808f4dfe DM |
409 | region_model::impl_call_malloc (const call_details &cd) |
410 | { | |
411 | const svalue *size_sval = cd.get_arg_svalue (0); | |
b9365b93 DM |
412 | const region *new_reg |
413 | = create_region_for_heap_alloc (size_sval, cd.get_ctxt ()); | |
808f4dfe DM |
414 | if (cd.get_lhs_type ()) |
415 | { | |
416 | const svalue *ptr_sval | |
417 | = m_mgr->get_ptr_svalue (cd.get_lhs_type (), new_reg); | |
418 | cd.maybe_set_lhs (ptr_sval); | |
419 | } | |
808f4dfe DM |
420 | } |
421 | ||
b7028f06 DM |
422 | /* Handle the on_call_pre part of "memcpy" and "__builtin_memcpy". */ |
423 | ||
424 | void | |
425 | region_model::impl_call_memcpy (const call_details &cd) | |
426 | { | |
427 | const svalue *dest_sval = cd.get_arg_svalue (0); | |
428 | const svalue *num_bytes_sval = cd.get_arg_svalue (2); | |
429 | ||
430 | const region *dest_reg = deref_rvalue (dest_sval, cd.get_arg_tree (0), | |
431 | cd.get_ctxt ()); | |
432 | ||
433 | cd.maybe_set_lhs (dest_sval); | |
434 | ||
435 | if (tree num_bytes = num_bytes_sval->maybe_get_constant ()) | |
436 | { | |
437 | /* "memcpy" of zero size is a no-op. */ | |
438 | if (zerop (num_bytes)) | |
439 | return; | |
440 | } | |
441 | ||
9faf8348 | 442 | check_region_for_write (dest_reg, cd.get_ctxt ()); |
3175d40f | 443 | |
b7028f06 | 444 | /* Otherwise, mark region's contents as unknown. */ |
3a66c289 | 445 | mark_region_as_unknown (dest_reg, cd.get_uncertainty ()); |
b7028f06 DM |
446 | } |
447 | ||
808f4dfe DM |
448 | /* Handle the on_call_pre part of "memset" and "__builtin_memset". */ |
449 | ||
b5081130 | 450 | void |
808f4dfe DM |
451 | region_model::impl_call_memset (const call_details &cd) |
452 | { | |
453 | const svalue *dest_sval = cd.get_arg_svalue (0); | |
454 | const svalue *fill_value_sval = cd.get_arg_svalue (1); | |
455 | const svalue *num_bytes_sval = cd.get_arg_svalue (2); | |
456 | ||
457 | const region *dest_reg = deref_rvalue (dest_sval, cd.get_arg_tree (0), | |
458 | cd.get_ctxt ()); | |
459 | ||
e61ffa20 DM |
460 | const svalue *fill_value_u8 |
461 | = m_mgr->get_or_create_cast (unsigned_char_type_node, fill_value_sval); | |
462 | ||
463 | const region *sized_dest_reg = m_mgr->get_sized_region (dest_reg, | |
464 | NULL_TREE, | |
465 | num_bytes_sval); | |
9faf8348 | 466 | check_region_for_write (sized_dest_reg, cd.get_ctxt ()); |
e61ffa20 | 467 | fill_region (sized_dest_reg, fill_value_u8); |
808f4dfe DM |
468 | } |
469 | ||
1690a839 DM |
470 | /* Handle the on_call_pre part of "operator new". */ |
471 | ||
b5081130 | 472 | void |
1690a839 DM |
473 | region_model::impl_call_operator_new (const call_details &cd) |
474 | { | |
475 | const svalue *size_sval = cd.get_arg_svalue (0); | |
b9365b93 DM |
476 | const region *new_reg |
477 | = create_region_for_heap_alloc (size_sval, cd.get_ctxt ()); | |
1690a839 DM |
478 | if (cd.get_lhs_type ()) |
479 | { | |
480 | const svalue *ptr_sval | |
481 | = m_mgr->get_ptr_svalue (cd.get_lhs_type (), new_reg); | |
482 | cd.maybe_set_lhs (ptr_sval); | |
483 | } | |
1690a839 DM |
484 | } |
485 | ||
486 | /* Handle the on_call_pre part of "operator delete", which comes in | |
487 | both sized and unsized variants (2 arguments and 1 argument | |
488 | respectively). */ | |
489 | ||
b5081130 | 490 | void |
1690a839 DM |
491 | region_model::impl_call_operator_delete (const call_details &cd) |
492 | { | |
493 | const svalue *ptr_sval = cd.get_arg_svalue (0); | |
5932dd35 | 494 | if (const region *freed_reg = ptr_sval->maybe_get_region ()) |
1690a839 DM |
495 | { |
496 | /* If the ptr points to an underlying heap region, delete it, | |
497 | poisoning pointers. */ | |
1690a839 DM |
498 | unbind_region_and_descendents (freed_reg, POISON_KIND_FREED); |
499 | } | |
1690a839 DM |
500 | } |
501 | ||
eafa9d96 DM |
502 | /* Handle the on_call_post part of "realloc": |
503 | ||
504 | void *realloc(void *ptr, size_t size); | |
505 | ||
506 | realloc(3) is awkward, since it has various different outcomes | |
507 | that are best modelled as separate exploded nodes/edges. | |
508 | ||
509 | We first check for sm-state, in | |
510 | malloc_state_machine::on_realloc_call, so that we | |
511 | can complain about issues such as realloc of a non-heap | |
512 | pointer, and terminate the path for such cases (and issue | |
513 | the complaints at the call's exploded node). | |
514 | ||
515 | Assuming that these checks pass, we split the path here into | |
516 | three special cases (and terminate the "standard" path): | |
517 | (A) failure, returning NULL | |
518 | (B) success, growing the buffer in-place without moving it | |
519 | (C) success, allocating a new buffer, copying the content | |
520 | of the old buffer to it, and freeing the old buffer. | |
521 | ||
522 | Each of these has a custom_edge_info subclass, which updates | |
523 | the region_model and sm-state of the destination state. */ | |
a6baafca DM |
524 | |
525 | void | |
eafa9d96 | 526 | region_model::impl_call_realloc (const call_details &cd) |
a6baafca | 527 | { |
eafa9d96 DM |
528 | /* Three custom subclasses of custom_edge_info, for handling the various |
529 | outcomes of "realloc". */ | |
530 | ||
531 | /* Concrete custom_edge_info: a realloc call that fails, returning NULL. */ | |
532 | class failure : public failed_call_info | |
533 | { | |
534 | public: | |
535 | failure (const call_details &cd) | |
536 | : failed_call_info (cd) | |
537 | { | |
538 | } | |
539 | ||
540 | bool update_model (region_model *model, | |
541 | const exploded_edge *, | |
542 | region_model_context *ctxt) const FINAL OVERRIDE | |
543 | { | |
544 | /* Return NULL; everything else is unchanged. */ | |
545 | const call_details cd (get_call_details (model, ctxt)); | |
e66b9f67 DM |
546 | if (cd.get_lhs_type ()) |
547 | { | |
548 | const svalue *zero | |
549 | = model->m_mgr->get_or_create_int_cst (cd.get_lhs_type (), 0); | |
550 | model->set_value (cd.get_lhs_region (), | |
551 | zero, | |
552 | cd.get_ctxt ()); | |
553 | } | |
eafa9d96 DM |
554 | return true; |
555 | } | |
556 | }; | |
557 | ||
558 | /* Concrete custom_edge_info: a realloc call that succeeds, growing | |
559 | the existing buffer without moving it. */ | |
560 | class success_no_move : public call_info | |
561 | { | |
562 | public: | |
563 | success_no_move (const call_details &cd) | |
564 | : call_info (cd) | |
565 | { | |
566 | } | |
567 | ||
568 | label_text get_desc (bool can_colorize) const FINAL OVERRIDE | |
569 | { | |
570 | return make_label_text (can_colorize, | |
571 | "when %qE succeeds, without moving buffer", | |
572 | get_fndecl ()); | |
573 | } | |
574 | ||
575 | bool update_model (region_model *model, | |
576 | const exploded_edge *, | |
577 | region_model_context *ctxt) const FINAL OVERRIDE | |
578 | { | |
579 | /* Update size of buffer and return the ptr unchanged. */ | |
580 | const call_details cd (get_call_details (model, ctxt)); | |
581 | const svalue *ptr_sval = cd.get_arg_svalue (0); | |
582 | const svalue *size_sval = cd.get_arg_svalue (1); | |
583 | if (const region *buffer_reg = ptr_sval->maybe_get_region ()) | |
e66b9f67 | 584 | if (compat_types_p (size_sval->get_type (), size_type_node)) |
b9365b93 | 585 | model->set_dynamic_extents (buffer_reg, size_sval, ctxt); |
e66b9f67 DM |
586 | if (cd.get_lhs_region ()) |
587 | { | |
588 | model->set_value (cd.get_lhs_region (), ptr_sval, cd.get_ctxt ()); | |
589 | const svalue *zero | |
590 | = model->m_mgr->get_or_create_int_cst (cd.get_lhs_type (), 0); | |
591 | return model->add_constraint (ptr_sval, NE_EXPR, zero, cd.get_ctxt ()); | |
592 | } | |
593 | else | |
594 | return true; | |
eafa9d96 DM |
595 | } |
596 | }; | |
597 | ||
598 | /* Concrete custom_edge_info: a realloc call that succeeds, freeing | |
599 | the existing buffer and moving the content to a freshly allocated | |
600 | buffer. */ | |
601 | class success_with_move : public call_info | |
602 | { | |
603 | public: | |
604 | success_with_move (const call_details &cd) | |
605 | : call_info (cd) | |
606 | { | |
607 | } | |
608 | ||
609 | label_text get_desc (bool can_colorize) const FINAL OVERRIDE | |
610 | { | |
611 | return make_label_text (can_colorize, | |
612 | "when %qE succeeds, moving buffer", | |
613 | get_fndecl ()); | |
614 | } | |
615 | bool update_model (region_model *model, | |
616 | const exploded_edge *, | |
617 | region_model_context *ctxt) const FINAL OVERRIDE | |
618 | { | |
619 | const call_details cd (get_call_details (model, ctxt)); | |
620 | const svalue *old_ptr_sval = cd.get_arg_svalue (0); | |
621 | const svalue *new_size_sval = cd.get_arg_svalue (1); | |
622 | ||
623 | /* Create the new region. */ | |
624 | const region *new_reg | |
b9365b93 | 625 | = model->create_region_for_heap_alloc (new_size_sval, ctxt); |
eafa9d96 DM |
626 | const svalue *new_ptr_sval |
627 | = model->m_mgr->get_ptr_svalue (cd.get_lhs_type (), new_reg); | |
628 | if (cd.get_lhs_type ()) | |
629 | cd.maybe_set_lhs (new_ptr_sval); | |
630 | ||
631 | if (const region *freed_reg = old_ptr_sval->maybe_get_region ()) | |
632 | { | |
633 | /* Copy the data. */ | |
634 | const svalue *old_size_sval = model->get_dynamic_extents (freed_reg); | |
635 | if (old_size_sval) | |
636 | { | |
637 | const region *sized_old_reg | |
638 | = model->m_mgr->get_sized_region (freed_reg, NULL, | |
639 | old_size_sval); | |
640 | const svalue *buffer_content_sval | |
641 | = model->get_store_value (sized_old_reg, cd.get_ctxt ()); | |
642 | model->set_value (new_reg, buffer_content_sval, cd.get_ctxt ()); | |
643 | } | |
644 | ||
645 | /* Free the old region, so that pointers to the old buffer become | |
646 | invalid. */ | |
647 | ||
648 | /* If the ptr points to an underlying heap region, delete it, | |
649 | poisoning pointers. */ | |
650 | model->unbind_region_and_descendents (freed_reg, POISON_KIND_FREED); | |
651 | model->m_dynamic_extents.remove (freed_reg); | |
652 | } | |
653 | ||
654 | /* Update the sm-state: mark the old_ptr_sval as "freed", | |
655 | and the new_ptr_sval as "nonnull". */ | |
656 | model->on_realloc_with_move (cd, old_ptr_sval, new_ptr_sval); | |
657 | ||
e66b9f67 DM |
658 | if (cd.get_lhs_type ()) |
659 | { | |
660 | const svalue *zero | |
661 | = model->m_mgr->get_or_create_int_cst (cd.get_lhs_type (), 0); | |
662 | return model->add_constraint (new_ptr_sval, NE_EXPR, zero, | |
663 | cd.get_ctxt ()); | |
664 | } | |
665 | else | |
666 | return true; | |
eafa9d96 DM |
667 | } |
668 | }; | |
669 | ||
670 | /* Body of region_model::impl_call_realloc. */ | |
671 | ||
672 | if (cd.get_ctxt ()) | |
673 | { | |
674 | cd.get_ctxt ()->bifurcate (new failure (cd)); | |
675 | cd.get_ctxt ()->bifurcate (new success_no_move (cd)); | |
676 | cd.get_ctxt ()->bifurcate (new success_with_move (cd)); | |
677 | cd.get_ctxt ()->terminate_path (); | |
678 | } | |
a6baafca DM |
679 | } |
680 | ||
111fd515 DM |
681 | /* Handle the on_call_pre part of "strchr" and "__builtin_strchr". */ |
682 | ||
683 | void | |
684 | region_model::impl_call_strchr (const call_details &cd) | |
685 | { | |
686 | class strchr_call_info : public call_info | |
687 | { | |
688 | public: | |
689 | strchr_call_info (const call_details &cd, bool found) | |
690 | : call_info (cd), m_found (found) | |
691 | { | |
692 | } | |
693 | ||
694 | label_text get_desc (bool can_colorize) const FINAL OVERRIDE | |
695 | { | |
696 | if (m_found) | |
697 | return make_label_text (can_colorize, | |
698 | "when %qE returns non-NULL", | |
699 | get_fndecl ()); | |
700 | else | |
701 | return make_label_text (can_colorize, | |
702 | "when %qE returns NULL", | |
703 | get_fndecl ()); | |
704 | } | |
705 | ||
706 | bool update_model (region_model *model, | |
707 | const exploded_edge *, | |
708 | region_model_context *ctxt) const FINAL OVERRIDE | |
709 | { | |
710 | const call_details cd (get_call_details (model, ctxt)); | |
711 | if (tree lhs_type = cd.get_lhs_type ()) | |
712 | { | |
713 | region_model_manager *mgr = model->get_manager (); | |
714 | const svalue *result; | |
715 | if (m_found) | |
716 | { | |
717 | const svalue *str_sval = cd.get_arg_svalue (0); | |
718 | const region *str_reg | |
719 | = model->deref_rvalue (str_sval, cd.get_arg_tree (0), | |
720 | cd.get_ctxt ()); | |
721 | /* We want str_sval + OFFSET for some unknown OFFSET. | |
722 | Use a conjured_svalue to represent the offset, | |
723 | using the str_reg as the id of the conjured_svalue. */ | |
724 | const svalue *offset | |
725 | = mgr->get_or_create_conjured_svalue (size_type_node, | |
726 | cd.get_call_stmt (), | |
727 | str_reg); | |
728 | result = mgr->get_or_create_binop (lhs_type, POINTER_PLUS_EXPR, | |
729 | str_sval, offset); | |
730 | } | |
731 | else | |
732 | result = mgr->get_or_create_int_cst (lhs_type, 0); | |
733 | cd.maybe_set_lhs (result); | |
734 | } | |
735 | return true; | |
736 | } | |
737 | private: | |
738 | bool m_found; | |
739 | }; | |
740 | ||
741 | /* Bifurcate state, creating a "not found" out-edge. */ | |
742 | if (cd.get_ctxt ()) | |
743 | cd.get_ctxt ()->bifurcate (new strchr_call_info (cd, false)); | |
744 | ||
745 | /* The "unbifurcated" state is the "found" case. */ | |
746 | strchr_call_info found (cd, true); | |
747 | found.update_model (this, NULL, cd.get_ctxt ()); | |
748 | } | |
749 | ||
b7028f06 DM |
750 | /* Handle the on_call_pre part of "strcpy" and "__builtin_strcpy_chk". */ |
751 | ||
752 | void | |
753 | region_model::impl_call_strcpy (const call_details &cd) | |
754 | { | |
755 | const svalue *dest_sval = cd.get_arg_svalue (0); | |
756 | const region *dest_reg = deref_rvalue (dest_sval, cd.get_arg_tree (0), | |
757 | cd.get_ctxt ()); | |
758 | ||
759 | cd.maybe_set_lhs (dest_sval); | |
760 | ||
9faf8348 | 761 | check_region_for_write (dest_reg, cd.get_ctxt ()); |
3175d40f | 762 | |
b7028f06 | 763 | /* For now, just mark region's contents as unknown. */ |
3a66c289 | 764 | mark_region_as_unknown (dest_reg, cd.get_uncertainty ()); |
b7028f06 DM |
765 | } |
766 | ||
b5081130 | 767 | /* Handle the on_call_pre part of "strlen". */ |
808f4dfe | 768 | |
b5081130 | 769 | void |
808f4dfe DM |
770 | region_model::impl_call_strlen (const call_details &cd) |
771 | { | |
772 | region_model_context *ctxt = cd.get_ctxt (); | |
773 | const svalue *arg_sval = cd.get_arg_svalue (0); | |
774 | const region *buf_reg = deref_rvalue (arg_sval, cd.get_arg_tree (0), ctxt); | |
775 | if (const string_region *str_reg | |
776 | = buf_reg->dyn_cast_string_region ()) | |
777 | { | |
778 | tree str_cst = str_reg->get_string_cst (); | |
779 | /* TREE_STRING_LENGTH is sizeof, not strlen. */ | |
780 | int sizeof_cst = TREE_STRING_LENGTH (str_cst); | |
781 | int strlen_cst = sizeof_cst - 1; | |
782 | if (cd.get_lhs_type ()) | |
783 | { | |
784 | tree t_cst = build_int_cst (cd.get_lhs_type (), strlen_cst); | |
785 | const svalue *result_sval | |
786 | = m_mgr->get_or_create_constant_svalue (t_cst); | |
787 | cd.maybe_set_lhs (result_sval); | |
b5081130 | 788 | return; |
808f4dfe DM |
789 | } |
790 | } | |
b5081130 | 791 | /* Otherwise a conjured value. */ |
808f4dfe DM |
792 | } |
793 | ||
c7e276b8 DM |
794 | /* Handle calls to functions referenced by |
795 | __attribute__((malloc(FOO))). */ | |
796 | ||
797 | void | |
798 | region_model::impl_deallocation_call (const call_details &cd) | |
799 | { | |
800 | impl_call_free (cd); | |
801 | } | |
802 | ||
808f4dfe DM |
803 | } // namespace ana |
804 | ||
805 | #endif /* #if ENABLE_ANALYZER */ |