CBMC
Loading...
Searching...
No Matches
restrict_function_pointers.cpp
Go to the documentation of this file.
1/*******************************************************************\
2
3Module: Restrict function pointers
4
5Author: Diffblue Ltd.
6
7\*******************************************************************/
8
10
11#include <ansi-c/expr2c.h>
12
13#include <json/json_parser.h>
14
15#include <util/cmdline.h>
16#include <util/options.h>
17#include <util/pointer_expr.h>
18#include <util/string_utils.h>
19
20#include "goto_model.h"
22
23#include <algorithm>
24#include <fstream>
25
26namespace
27{
28template <typename Handler, typename GotoFunctionT>
30{
31 using targett = decltype(goto_function.body.instructions.begin());
33 goto_function,
34 [](targett target) { return target->is_function_call(); },
35 handler);
36}
37
39 message_handlert &message_handler,
40 symbol_tablet &symbol_table,
41 goto_programt &goto_program,
42 const irep_idt &function_id,
43 const function_pointer_restrictionst &restrictions,
44 const goto_programt::targett &location)
45{
46 PRECONDITION(location->is_function_call());
47
48 // for each function call, we check if it is using a symbol we have
49 // restrictions for, and if so branch on its value and insert concrete calls
50 // to the restriction functions
51
52 // Check if this is calling a function pointer, and if so if it is one
53 // we have a restriction for
54 const auto &original_function = location->call_function();
55
57 return false;
58
59 // because we run the label function pointer calls transformation pass before
60 // this stage a dereference can only dereference a symbol expression
61 auto const &called_function_pointer =
65 auto const restriction_iterator =
66 restrictions.restrictions.find(pointer_symbol.get_identifier());
67
68 if(restriction_iterator == restrictions.restrictions.end())
69 return false;
70
71 const namespacet ns(symbol_table);
72 std::unordered_set<symbol_exprt, irep_hash> candidates;
73 for(const auto &candidate : restriction_iterator->second)
74 candidates.insert(ns.lookup(candidate).symbol_expr());
75
77 message_handler,
78 symbol_table,
79 goto_program,
80 function_id,
81 location,
83
84 return true;
85}
86} // namespace
87
89 std::string reason,
90 std::string correct_format)
91 : cprover_exception_baset(std::move(reason)),
92 correct_format(std::move(correct_format))
93{
94}
95
97{
98 std::string res;
99
100 res += "Invalid restriction";
101 res += "\nReason: " + reason;
102
103 if(!correct_format.empty())
104 {
105 res += "\nFormat: " + correct_format;
106 }
107
108 return res;
109}
110
112 const goto_modelt &goto_model,
114{
115 for(auto const &restriction : restrictions)
116 {
117 auto const function_pointer_sym =
118 goto_model.symbol_table.lookup(restriction.first);
119 if(function_pointer_sym == nullptr)
120 {
122 " not found in the symbol table"};
123 }
126 {
127 throw invalid_restriction_exceptiont{"not a function pointer: " +
128 id2string(restriction.first)};
129 }
130 auto const &function_type =
132 if(function_type.id() != ID_code)
133 {
134 throw invalid_restriction_exceptiont{"not a function pointer: " +
135 id2string(restriction.first)};
136 }
137 auto const &ns = namespacet{goto_model.symbol_table};
138 for(auto const &function_pointer_target : restriction.second)
139 {
140 auto const function_pointer_target_sym =
142 if(function_pointer_target_sym == nullptr)
143 {
145 "symbol not found: " + id2string(function_pointer_target)};
146 }
147 auto const &function_pointer_target_type =
150 {
152 "not a function: " + id2string(function_pointer_target)};
153 }
154
156 true,
157 to_code_type(function_type),
159 ns))
160 {
162 "type mismatch: `" + id2string(restriction.first) + "' points to `" +
163 type2c(function_type, ns) + "', but restriction `" +
164 id2string(function_pointer_target) + "' has type `" +
166 }
167 }
168 }
169}
170
172 message_handlert &message_handler,
173 goto_modelt &goto_model,
174 const optionst &options)
175{
176 const auto restrictions = function_pointer_restrictionst::from_options(
177 options, goto_model, message_handler);
178 if(restrictions.restrictions.empty())
179 return;
180
181 for(auto &function_item : goto_model.goto_functions.function_map)
182 {
183 goto_functiont &goto_function = function_item.second;
184
185 bool did_something = false;
186 for_each_function_call(goto_function, [&](const goto_programt::targett it) {
188 message_handler,
189 goto_model.symbol_table,
190 goto_function.body,
191 function_item.first,
192 restrictions,
193 it);
194 });
195
196 if(did_something)
197 goto_function.body.update();
198 }
199}
200
226
231{
232 auto &result = lhs;
233
234 for(auto const &restriction : rhs)
235 {
236 auto emplace_result = result.emplace(restriction.first, restriction.second);
237 if(!emplace_result.second)
238 {
239 for(auto const &target : restriction.second)
240 {
241 emplace_result.first->second.insert(target);
242 }
243 }
244 }
245
246 return result;
247}
248
251 const std::list<std::string> &restriction_opts,
252 const std::string &option,
253 const goto_modelt &goto_model)
254{
257
258 for(const std::string &restriction_opt : restriction_opts)
259 {
260 const auto restriction =
262
263 const bool inserted = function_pointer_restrictions
264 .emplace(restriction.first, restriction.second)
265 .second;
266
267 if(!inserted)
268 {
270 "function pointer restriction for `" + id2string(restriction.first) +
271 "' was specified twice"};
272 }
273 }
274
276}
277
286
289 const std::list<std::string> &filenames,
290 const goto_modelt &goto_model,
291 message_handlert &message_handler)
292{
294
295 for(auto const &filename : filenames)
296 {
297 auto const restrictions =
298 read_from_file(filename, goto_model, message_handler);
299
301 std::move(merged_restrictions), restrictions.restrictions);
302 }
303
304 return merged_restrictions;
305}
306
311static std::string resolve_pointer_name(
312 const std::string &candidate,
313 const goto_modelt &goto_model)
314{
315 const auto last_dot = candidate.rfind('.');
316 if(
317 last_dot == std::string::npos || last_dot + 1 == candidate.size() ||
319 {
320 return candidate;
321 }
322
323 std::string pointer_name = candidate;
324
325 const auto function_id = pointer_name.substr(0, last_dot);
326 const auto label = pointer_name.substr(last_dot + 1);
327
328 bool found = false;
329 const auto it = goto_model.goto_functions.function_map.find(function_id);
330 if(it != goto_model.goto_functions.function_map.end())
331 {
332 std::optional<source_locationt> location;
333 for(const auto &instruction : it->second.body.instructions)
334 {
335 if(
336 std::find(
337 instruction.labels.begin(), instruction.labels.end(), label) !=
338 instruction.labels.end())
339 {
340 location = instruction.source_location();
341 }
342
343 if(
344 instruction.is_function_call() &&
345 instruction.call_function().id() == ID_dereference &&
346 location.has_value() && instruction.source_location() == *location)
347 {
348 auto const &called_function_pointer =
349 to_dereference_expr(instruction.call_function()).pointer();
352 found = true;
353 break;
354 }
355 }
356 }
357 if(!found)
358 {
360 "non-existent pointer name " + pointer_name,
361 "pointers should be identifiers or <function_name>.<label>"};
362 }
363
364 return pointer_name;
365}
366
369 const std::string &restriction_opt,
370 const std::string &option,
371 const goto_modelt &goto_model)
372{
373 // the format for restrictions is <pointer_name>/<target[,more_targets]*>
374 // exactly one pointer and at least one target
375 auto const pointer_name_end = restriction_opt.find('/');
376 auto const restriction_format_message =
377 "the format for restrictions is "
378 "<pointer_name>/<target[,more_targets]*>";
379
380 if(pointer_name_end == std::string::npos)
381 {
382 throw invalid_restriction_exceptiont{"couldn't find '/' in `" +
383 restriction_opt + "'",
385 }
386
388 {
390 "couldn't find names of targets after '/' in `" + restriction_opt + "'",
392 }
393
394 if(pointer_name_end == 0)
395 {
397 "couldn't find target name before '/' in `" + restriction_opt + "'"};
398 }
399
401 restriction_opt.substr(0, pointer_name_end), goto_model);
402
403 auto const target_names_substring =
406
407 if(target_name_strings.size() == 1 && target_name_strings[0].empty())
408 {
410 "missing target list for function pointer restriction " + pointer_name,
412 }
413
414 std::unordered_set<irep_idt> target_names;
416
417 for(auto const &target_name : target_names)
418 {
420 {
422 "leading or trailing comma in restrictions for `" + pointer_name + "'",
424 }
425 }
426
427 return std::make_pair(pointer_name, target_names);
428}
429
430std::optional<function_pointer_restrictionst::restrictiont>
432 const goto_functiont &goto_function,
434 const goto_programt::const_targett &location)
435{
436 PRECONDITION(location->is_function_call());
437
438 const exprt &function = location->call_function();
439
441 return {};
442
443 // the function pointer is guaranteed to be a symbol expression, as the
444 // label_function_pointer_call_sites() pass (which must be run before the
445 // function pointer restriction) replaces calls via complex pointer
446 // expressions by calls to a function pointer variable
447 auto const &function_pointer_call_site =
448 to_symbol_expr(to_dereference_expr(function).pointer());
449
450 const goto_programt::const_targett it = std::prev(location);
451
452 INVARIANT(
453 to_symbol_expr(it->assign_lhs()).get_identifier() ==
454 function_pointer_call_site.get_identifier(),
455 "called function pointer must have been assigned at the previous location");
456
457 if(!can_cast_expr<symbol_exprt>(it->assign_rhs()))
458 return {};
459
460 const auto &rhs = to_symbol_expr(it->assign_rhs());
461
462 const auto restriction = by_name_restrictions.find(rhs.get_identifier());
463
465 {
466 return std::optional<function_pointer_restrictionst::restrictiont>(
467 std::make_pair(
468 function_pointer_call_site.get_identifier(), restriction->second));
469 }
470
471 return {};
472}
473
475 const optionst &options,
476 const goto_modelt &goto_model,
477 message_handlert &message_handler)
478{
479 auto const restriction_opts =
481
483 try
484 {
487 restriction_opts, goto_model);
489 goto_model, commandline_restrictions);
490 }
491 catch(const invalid_restriction_exceptiont &e)
492 {
494 static_cast<cprover_exception_baset>(e).what(),
497 }
498
500 try
501 {
502 auto const restriction_file_opts =
505 restriction_file_opts, goto_model, message_handler);
507 }
508 catch(const invalid_restriction_exceptiont &e)
509 {
511 }
512
514 try
515 {
516 auto const restriction_name_opts =
519 restriction_name_opts, goto_model);
521 }
522 catch(const invalid_restriction_exceptiont &e)
523 {
525 static_cast<cprover_exception_baset>(e).what(),
528 }
529
533}
534
536 const jsont &json,
537 const goto_modelt &goto_model)
538{
540
541 if(!json.is_object())
542 {
543 throw deserialization_exceptiont{"top level item is not an object"};
544 }
545
546 for(auto const &restriction : to_json_object(json))
547 {
548 std::string pointer_name =
549 resolve_pointer_name(restriction.first, goto_model);
550 restrictions.emplace(irep_idt{pointer_name}, [&] {
551 if(!restriction.second.is_array())
552 {
553 throw deserialization_exceptiont{"Value of " + restriction.first +
554 " is not an array"};
555 }
556 auto possible_targets = std::unordered_set<irep_idt>{};
557 auto const &array = to_json_array(restriction.second);
558 std::transform(
559 array.begin(),
560 array.end(),
561 std::inserter(possible_targets, possible_targets.end()),
562 [&](const jsont &array_element) {
563 if(!array_element.is_string())
564 {
565 throw deserialization_exceptiont{
566 "Value of " + restriction.first +
567 "contains a non-string array element"};
568 }
569 return irep_idt{to_json_string(array_element).value};
570 });
571 return possible_targets;
572 }());
573 }
574
575 return function_pointer_restrictionst{restrictions};
576}
577
579 const std::string &filename,
580 const goto_modelt &goto_model,
581 message_handlert &message_handler)
582{
583 auto inFile = std::ifstream{filename};
584 jsont json;
585
586 if(parse_json(inFile, filename, message_handler, json))
587 {
589 "failed to read function pointer restrictions from " + filename};
590 }
591
592 return from_json(json, goto_model);
593}
594
596{
600
601 for(auto const &restriction : restrictions)
602 {
603 auto &targets_array =
605 for(auto const &target : restriction.second)
606 {
607 targets_array.push_back(json_stringt{target});
608 }
609 }
610
612}
613
615 const std::string &filename) const
616{
618
619 auto outFile = std::ofstream{filename};
620
621 if(!outFile)
622 {
623 throw system_exceptiont{"cannot open " + filename +
624 " for writing function pointer restrictions"};
625 }
626
628 // Ensure output file ends with a newline character.
629 outFile << '\n';
630}
631
634 const std::list<std::string> &restriction_name_opts,
635 const goto_modelt &goto_model)
636{
641 goto_model);
642
644 for(auto const &goto_function : goto_model.goto_functions.function_map)
645 {
647 goto_function.second, [&](const goto_programt::const_targett it) {
648 const auto restriction = get_by_name_restriction(
649 goto_function.second, by_name_restrictions, it);
650
651 if(restriction)
652 {
653 restrictions.insert(*restriction);
654 }
655 });
656 }
657
658 return restrictions;
659}
virtual void output(const namespacet &ns, const irep_idt &function_id, const goto_programt &goto_program, std::ostream &out) const
Output the abstract states for a single function.
Definition ai.cpp:39
ait supplies three of the four components needed: an abstract interpreter (in this case handling func...
Definition ai.h:562
virtual bool isset(char option) const
Definition cmdline.cpp:30
const std::list< std::string > & get_values(const std::string &option) const
Definition cmdline.cpp:119
Base class for exceptions thrown in the cprover project.
Definition c_errors.h:64
std::string reason
The reason this exception was generated.
Definition c_errors.h:83
Thrown when failing to deserialize a value from some low level format, like JSON or raw bytes.
dstringt has one field, an unsigned integer no which is an index into a static table of strings.
Definition dstring.h:38
Base class for all expressions.
Definition expr.h:56
static function_pointer_restrictionst from_options(const optionst &options, const goto_modelt &goto_model, message_handlert &message_handler)
Parse function pointer restrictions from command line.
static restrictionst parse_function_pointer_restrictions_from_command_line(const std::list< std::string > &restriction_opts, const goto_modelt &goto_model)
static restrictionst get_function_pointer_by_name_restrictions(const std::list< std::string > &restriction_name_opts, const goto_modelt &goto_model)
Get function pointer restrictions from restrictions with named pointers.
static restrictionst parse_function_pointer_restrictions(const std::list< std::string > &restriction_opts, const std::string &option, const goto_modelt &goto_model)
static restrictionst merge_function_pointer_restrictions(restrictionst lhs, const restrictionst &rhs)
static restrictiont parse_function_pointer_restriction(const std::string &restriction_opt, const std::string &option, const goto_modelt &goto_model)
static function_pointer_restrictionst read_from_file(const std::string &filename, const goto_modelt &goto_model, message_handlert &message_handler)
std::unordered_map< irep_idt, std::unordered_set< irep_idt > > restrictionst
static restrictionst parse_function_pointer_restrictions_from_file(const std::list< std::string > &filenames, const goto_modelt &goto_model, message_handlert &message_handler)
void write_to_file(const std::string &filename) const
static void typecheck_function_pointer_restrictions(const goto_modelt &goto_model, const restrictionst &restrictions)
static function_pointer_restrictionst from_json(const jsont &json, const goto_modelt &goto_model)
static std::optional< restrictiont > get_by_name_restriction(const goto_functiont &goto_function, const function_pointer_restrictionst::restrictionst &by_name_restrictions, const goto_programt::const_targett &location)
function_mapt function_map
A goto function, consisting of function body (see body) and parameter identifiers (see parameter_iden...
goto_programt body
symbol_tablet symbol_table
Symbol table.
Definition goto_model.h:31
goto_functionst goto_functions
GOTO functions.
Definition goto_model.h:34
A generic container class for the GOTO intermediate representation of one function.
void update()
Update all indices.
instructionst::iterator targett
instructionst::const_iterator const_targett
Thrown when users pass incorrect command line arguments, for example passing no files to analysis or ...
invalid_restriction_exceptiont(std::string reason, std::string correct_format="")
std::string what() const override
A human readable description of what went wrong.
Definition json.h:27
A namespacet is essentially one or two symbol tables bound together, to allow for symbol lookups in t...
Definition namespace.h:91
void set_option(const std::string &option, const bool value)
Definition options.cpp:28
const value_listt & get_list_option(const std::string &option) const
Definition options.cpp:80
const symbolt * lookup(const irep_idt &name) const
Find a symbol in the symbol table for read-only access.
The symbol table.
Thrown when some external system fails unexpectedly.
int isdigit(int c)
Definition ctype.c:24
std::string type2c(const typet &type, const namespacet &ns, const expr2c_configurationt &configuration)
Definition expr2c.cpp:4200
Symbol Table + CFG.
void for_each_instruction_if(GotoFunctionT &&goto_function, PredicateT predicate, HandlerT handler)
const std::string & id2string(const irep_idt &d)
Definition irep.h:44
json_objectt & to_json_object(jsont &json)
Definition json.h:442
json_arrayt & to_json_array(jsont &json)
Definition json.h:424
bool parse_json(std::istream &in, const std::string &filename, message_handlert &message_handler, jsont &dest)
STL namespace.
Options.
API to expression classes for Pointers.
const pointer_typet & to_pointer_type(const typet &type)
Cast a typet to a pointer_typet.
const dereference_exprt & to_dereference_expr(const exprt &expr)
Cast an exprt to a dereference_exprt.
static void json(json_objectT &result, const irep_idt &property_id, const property_infot &property_info)
void remove_function_pointer(message_handlert &message_handler, symbol_tablet &symbol_table, goto_programt &goto_program, const irep_idt &function_id, goto_programt::targett target, const std::unordered_set< symbol_exprt, irep_hash > &functions)
Replace a call to a dynamic function at location target in the given goto-program by a case-split ove...
bool function_is_type_compatible(bool return_value_used, const code_typet &call_type, const code_typet &function_type, const namespacet &ns)
Returns true iff call_type can be converted to produce a function call of the same type as function_t...
Remove Indirect Function Calls.
static std::string resolve_pointer_name(const std::string &candidate, const goto_modelt &goto_model)
Parse candidate to distinguish whether it refers to a function pointer symbol directly (as produced b...
void parse_function_pointer_restriction_options_from_cmdline(const cmdlinet &cmdline, optionst &options)
void restrict_function_pointers(message_handlert &message_handler, goto_modelt &goto_model, const optionst &options)
Apply function pointer restrictions to a goto_model.
Given goto functions and a list of function parameters or globals that are function pointers with lis...
#define RESTRICT_FUNCTION_POINTER_OPT
#define RESTRICT_FUNCTION_POINTER_FROM_FILE_OPT
#define RESTRICT_FUNCTION_POINTER_BY_NAME_OPT
#define PRECONDITION(CONDITION)
Definition invariant.h:463
#define INVARIANT(CONDITION, REASON)
This macro uses the wrapper function 'invariant_violated_string'.
Definition invariant.h:423
const symbol_exprt & to_symbol_expr(const exprt &expr)
Cast an exprt to a symbol_exprt.
Definition std_expr.h:272
const code_typet & to_code_type(const typet &type)
Cast a typet to a code_typet.
Definition std_types.h:788
void split_string(const std::string &s, char delim, std::vector< std::string > &result, bool strip, bool remove_empty)