/*******************************************************************\ Module: Restrict function pointers Author: Diffblue Ltd. \*******************************************************************/ #include "restrict_function_pointers.h" #include #include #include #include #include #include #include "goto_model.h" #include "remove_function_pointers.h" #include #include namespace { template void for_each_function_call(GotoFunctionT &&goto_function, Handler handler) { using targett = decltype(goto_function.body.instructions.begin()); for_each_instruction_if( goto_function, [](targett target) { return target->is_function_call(); }, handler); } [[nodiscard]] static bool restrict_function_pointer( message_handlert &message_handler, symbol_tablet &symbol_table, goto_programt &goto_program, const irep_idt &function_id, const function_pointer_restrictionst &restrictions, const goto_programt::targett &location) { PRECONDITION(location->is_function_call()); // for each function call, we check if it is using a symbol we have // restrictions for, and if so branch on its value and insert concrete calls // to the restriction functions // Check if this is calling a function pointer, and if so if it is one // we have a restriction for const auto &original_function = location->call_function(); if(!can_cast_expr(original_function)) return false; // because we run the label function pointer calls transformation pass before // this stage a dereference can only dereference a symbol expression auto const &called_function_pointer = to_dereference_expr(original_function).pointer(); PRECONDITION(can_cast_expr(called_function_pointer)); auto const &pointer_symbol = to_symbol_expr(called_function_pointer); auto const restriction_iterator = restrictions.restrictions.find(pointer_symbol.get_identifier()); if(restriction_iterator == restrictions.restrictions.end()) return false; const namespacet ns(symbol_table); std::unordered_set candidates; for(const auto &candidate : restriction_iterator->second) candidates.insert(ns.lookup(candidate).symbol_expr()); remove_function_pointer( message_handler, symbol_table, goto_program, function_id, location, candidates); return true; } } // namespace invalid_restriction_exceptiont::invalid_restriction_exceptiont( std::string reason, std::string correct_format) : cprover_exception_baset(std::move(reason)), correct_format(std::move(correct_format)) { } std::string invalid_restriction_exceptiont::what() const { std::string res; res += "Invalid restriction"; res += "\nReason: " + reason; if(!correct_format.empty()) { res += "\nFormat: " + correct_format; } return res; } void function_pointer_restrictionst::typecheck_function_pointer_restrictions( const goto_modelt &goto_model, const function_pointer_restrictionst::restrictionst &restrictions) { for(auto const &restriction : restrictions) { auto const function_pointer_sym = goto_model.symbol_table.lookup(restriction.first); if(function_pointer_sym == nullptr) { throw invalid_restriction_exceptiont{id2string(restriction.first) + " not found in the symbol table"}; } auto const &function_pointer_type = function_pointer_sym->type; if(function_pointer_type.id() != ID_pointer) { throw invalid_restriction_exceptiont{"not a function pointer: " + id2string(restriction.first)}; } auto const &function_type = to_pointer_type(function_pointer_type).base_type(); if(function_type.id() != ID_code) { throw invalid_restriction_exceptiont{"not a function pointer: " + id2string(restriction.first)}; } auto const &ns = namespacet{goto_model.symbol_table}; for(auto const &function_pointer_target : restriction.second) { auto const function_pointer_target_sym = goto_model.symbol_table.lookup(function_pointer_target); if(function_pointer_target_sym == nullptr) { throw invalid_restriction_exceptiont{ "symbol not found: " + id2string(function_pointer_target)}; } auto const &function_pointer_target_type = function_pointer_target_sym->type; if(function_pointer_target_type.id() != ID_code) { throw invalid_restriction_exceptiont{ "not a function: " + id2string(function_pointer_target)}; } if(!function_is_type_compatible( true, to_code_type(function_type), to_code_type(function_pointer_target_type), ns)) { throw invalid_restriction_exceptiont{ "type mismatch: `" + id2string(restriction.first) + "' points to `" + type2c(function_type, ns) + "', but restriction `" + id2string(function_pointer_target) + "' has type `" + type2c(function_pointer_target_type, ns) + "'"}; } } } } void restrict_function_pointers( message_handlert &message_handler, goto_modelt &goto_model, const optionst &options) { const auto restrictions = function_pointer_restrictionst::from_options( options, goto_model, message_handler); if(restrictions.restrictions.empty()) return; for(auto &function_item : goto_model.goto_functions.function_map) { goto_functiont &goto_function = function_item.second; bool did_something = false; for_each_function_call(goto_function, [&](const goto_programt::targett it) { did_something |= restrict_function_pointer( message_handler, goto_model.symbol_table, goto_function.body, function_item.first, restrictions, it); }); if(did_something) goto_function.body.update(); } } void parse_function_pointer_restriction_options_from_cmdline( const cmdlinet &cmdline, optionst &options) { if(cmdline.isset(RESTRICT_FUNCTION_POINTER_OPT)) { options.set_option( RESTRICT_FUNCTION_POINTER_OPT, cmdline.get_values(RESTRICT_FUNCTION_POINTER_OPT)); } if(cmdline.isset(RESTRICT_FUNCTION_POINTER_FROM_FILE_OPT)) { options.set_option( RESTRICT_FUNCTION_POINTER_FROM_FILE_OPT, cmdline.get_values(RESTRICT_FUNCTION_POINTER_FROM_FILE_OPT)); } if(cmdline.isset(RESTRICT_FUNCTION_POINTER_BY_NAME_OPT)) { options.set_option( RESTRICT_FUNCTION_POINTER_BY_NAME_OPT, cmdline.get_values(RESTRICT_FUNCTION_POINTER_BY_NAME_OPT)); } } function_pointer_restrictionst::restrictionst function_pointer_restrictionst::merge_function_pointer_restrictions( function_pointer_restrictionst::restrictionst lhs, const function_pointer_restrictionst::restrictionst &rhs) { auto &result = lhs; for(auto const &restriction : rhs) { auto emplace_result = result.emplace(restriction.first, restriction.second); if(!emplace_result.second) { for(auto const &target : restriction.second) { emplace_result.first->second.insert(target); } } } return result; } function_pointer_restrictionst::restrictionst function_pointer_restrictionst::parse_function_pointer_restrictions( const std::list &restriction_opts, const std::string &option, const goto_modelt &goto_model) { auto function_pointer_restrictions = function_pointer_restrictionst::restrictionst{}; for(const std::string &restriction_opt : restriction_opts) { const auto restriction = parse_function_pointer_restriction(restriction_opt, option, goto_model); const bool inserted = function_pointer_restrictions .emplace(restriction.first, restriction.second) .second; if(!inserted) { throw invalid_restriction_exceptiont{ "function pointer restriction for `" + id2string(restriction.first) + "' was specified twice"}; } } return function_pointer_restrictions; } function_pointer_restrictionst::restrictionst function_pointer_restrictionst:: parse_function_pointer_restrictions_from_command_line( const std::list &restriction_opts, const goto_modelt &goto_model) { return parse_function_pointer_restrictions( restriction_opts, "--" RESTRICT_FUNCTION_POINTER_OPT, goto_model); } function_pointer_restrictionst::restrictionst function_pointer_restrictionst::parse_function_pointer_restrictions_from_file( const std::list &filenames, const goto_modelt &goto_model, message_handlert &message_handler) { auto merged_restrictions = function_pointer_restrictionst::restrictionst{}; for(auto const &filename : filenames) { auto const restrictions = read_from_file(filename, goto_model, message_handler); merged_restrictions = merge_function_pointer_restrictions( std::move(merged_restrictions), restrictions.restrictions); } return merged_restrictions; } /// Parse \p candidate to distinguish whether it refers to a function pointer /// symbol directly (as produced by \ref label_function_pointer_call_sites), or /// a source location via its statement label. In the latter case, resolve the /// name to the underlying function pointer symbol. static std::string resolve_pointer_name( const std::string &candidate, const goto_modelt &goto_model) { const auto last_dot = candidate.rfind('.'); if( last_dot == std::string::npos || last_dot + 1 == candidate.size() || isdigit(candidate[last_dot + 1])) { return candidate; } std::string pointer_name = candidate; const auto function_id = pointer_name.substr(0, last_dot); const auto label = pointer_name.substr(last_dot + 1); bool found = false; const auto it = goto_model.goto_functions.function_map.find(function_id); if(it != goto_model.goto_functions.function_map.end()) { std::optional location; for(const auto &instruction : it->second.body.instructions) { if( std::find( instruction.labels.begin(), instruction.labels.end(), label) != instruction.labels.end()) { location = instruction.source_location(); } if( instruction.is_function_call() && instruction.call_function().id() == ID_dereference && location.has_value() && instruction.source_location() == *location) { auto const &called_function_pointer = to_dereference_expr(instruction.call_function()).pointer(); pointer_name = id2string(to_symbol_expr(called_function_pointer).get_identifier()); found = true; break; } } } if(!found) { throw invalid_restriction_exceptiont{ "non-existent pointer name " + pointer_name, "pointers should be identifiers or .