/* * Insert a looping path (usually an accelerator) into a goto-program, * beginning at loop_header and jumping back to loop_header via back_jump. * Stores the locations at which the looping path was added in inserted_path. * * THIS DESTROYS looping_path!! */ void acceleratet::insert_looping_path( goto_programt::targett &loop_header, goto_programt::targett &back_jump, goto_programt &looping_path, patht &inserted_path) { goto_programt::targett loop_body=loop_header; ++loop_body; goto_programt::targett jump=program.insert_before(loop_body); jump->make_goto(); jump->guard=side_effect_expr_nondett(bool_typet()); jump->targets.push_back(loop_body); program.destructive_insert(loop_body, looping_path); jump=program.insert_before(loop_body); jump->make_goto(); jump->guard=true_exprt(); jump->targets.push_back(back_jump); for(goto_programt::targett t=loop_header; t!=loop_body; ++t) { inserted_path.push_back(path_nodet(t)); } inserted_path.push_back(path_nodet(back_jump)); }
void string_instrumentationt::do_fscanf( goto_programt &dest, goto_programt::targett target, code_function_callt &call) { const code_function_callt::argumentst &arguments=call.arguments(); if(arguments.size()<2) { err_location(target->location); throw "fscanf expected to have two or more arguments"; } goto_programt tmp; do_format_string_write(tmp, target, arguments, 1, 2, "fscanf"); if(call.lhs().is_not_nil()) { goto_programt::targett return_assignment=tmp.add_instruction(ASSIGN); return_assignment->location=target->location; exprt rhs=side_effect_expr_nondett(call.lhs().type()); rhs.location()=target->location; return_assignment->code=code_assignt(call.lhs(), rhs); } target->make_skip(); dest.insert_before_swap(target, tmp); }
void nondet_static( const namespacet &ns, goto_functionst &goto_functions, const irep_idt &fct_name) { goto_functionst::function_mapt::iterator i_it=goto_functions.function_map.find(fct_name); assert(i_it!=goto_functions.function_map.end()); goto_programt &init=i_it->second.body; Forall_goto_program_instructions(i_it, init) { const goto_programt::instructiont &instruction=*i_it; if(instruction.is_assign()) { const symbol_exprt &sym=to_symbol_expr( to_code_assign(instruction.code).lhs()); // is it a __CPROVER_* variable? if(has_prefix(id2string(sym.get_identifier()), CPROVER_PREFIX)) continue; // static lifetime? if(!ns.lookup(sym.get_identifier()).is_static_lifetime) continue; // constant? if(sym.type().get_bool(ID_C_constant)) continue; i_it=init.insert_before(++i_it); i_it->make_assignment(); i_it->code=code_assignt(sym, side_effect_expr_nondett(sym.type())); i_it->location=instruction.location; i_it->function=instruction.function; } else if(instruction.is_function_call()) { const code_function_callt &fct=to_code_function_call(instruction.code); const symbol_exprt &fsym=to_symbol_expr(fct.function()); if(has_prefix(id2string(fsym.get_identifier()), "c::#ini#")) nondet_static(ns, goto_functions, fsym.get_identifier()); } } }
void string_instrumentationt::do_snprintf( goto_programt &dest, goto_programt::targett target, code_function_callt &call) { const code_function_callt::argumentst &arguments=call.arguments(); if(arguments.size()<3) { error().source_location=target->source_location; error() << "snprintf expected to have three or more arguments" << eom; throw 0; } goto_programt tmp; goto_programt::targett assertion=tmp.add_instruction(); assertion->source_location=target->source_location; assertion->source_location.set_property_class("string"); assertion->source_location.set_comment("snprintf buffer overflow"); exprt bufsize=buffer_size(arguments[0]); assertion->make_assertion( binary_relation_exprt(bufsize, ID_ge, arguments[1])); do_format_string_read(tmp, target, arguments, 2, 3, "snprintf"); if(call.lhs().is_not_nil()) { goto_programt::targett return_assignment=tmp.add_instruction(ASSIGN); return_assignment->source_location=target->source_location; exprt rhs=side_effect_expr_nondett(call.lhs().type()); rhs.add_source_location()=target->source_location; return_assignment->code=code_assignt(call.lhs(), rhs); } target->make_skip(); dest.insert_before_swap(target, tmp); }
void havoc_loopst::build_havoc_code( const goto_programt::targett loop_head, const modifiest &modifies, goto_programt &dest) { for(modifiest::const_iterator m_it=modifies.begin(); m_it!=modifies.end(); m_it++) { exprt lhs=*m_it; exprt rhs=side_effect_expr_nondett(lhs.type()); goto_programt::targett t=dest.add_instruction(ASSIGN); t->function=loop_head->function; t->source_location=loop_head->source_location; t->code=code_assignt(lhs, rhs); t->code.add_source_location()=loop_head->source_location; } }
void string_instrumentationt::do_sprintf( goto_programt &dest, goto_programt::targett target, code_function_callt &call) { const code_function_callt::argumentst &arguments=call.arguments(); if(arguments.size()<2) { error().source_location=target->source_location; error() << "sprintf expected to have two or more arguments" << eom; throw 0; } goto_programt tmp; goto_programt::targett assertion=tmp.add_instruction(); assertion->source_location=target->source_location; assertion->source_location.set_property_class("string"); assertion->source_location.set_comment("sprintf buffer overflow"); // in the abstract model, we have to report a // (possibly false) positive here assertion->make_assertion(false_exprt()); do_format_string_read(tmp, target, arguments, 1, 2, "sprintf"); if(call.lhs().is_not_nil()) { goto_programt::targett return_assignment=tmp.add_instruction(ASSIGN); return_assignment->source_location=target->source_location; exprt rhs=side_effect_expr_nondett(call.lhs().type()); rhs.add_source_location()=target->source_location; return_assignment->code=code_assignt(call.lhs(), rhs); } target->make_skip(); dest.insert_before_swap(target, tmp); }
bool disjunctive_polynomial_accelerationt::accelerate( path_acceleratort &accelerator) { std::map<exprt, polynomialt> polynomials; scratch_programt program(symbol_table); accelerator.clear(); #ifdef DEBUG std::cout << "Polynomial accelerating program:" << std::endl; for (goto_programt::instructionst::iterator it = goto_program.instructions.begin(); it != goto_program.instructions.end(); ++it) { if (loop.find(it) != loop.end()) { goto_program.output_instruction(ns, "scratch", std::cout, it); } } std::cout << "Modified:" << std::endl; for (expr_sett::iterator it = modified.begin(); it != modified.end(); ++it) { std::cout << expr2c(*it, ns) << std::endl; } #endif if (loop_counter.is_nil()) { symbolt loop_sym = utils.fresh_symbol("polynomial::loop_counter", unsigned_poly_type()); loop_counter = loop_sym.symbol_expr(); } patht &path = accelerator.path; path.clear(); if (!find_path(path)) { // No more paths! return false; } #if 0 for (expr_sett::iterator it = modified.begin(); it != modified.end(); ++it) { polynomialt poly; exprt target = *it; if (it->type().id() == ID_bool) { // Hack: don't try to accelerate booleans. continue; } if (target.id() == ID_index || target.id() == ID_dereference) { // We'll handle this later. continue; } if (fit_polynomial(target, poly, path)) { std::map<exprt, polynomialt> this_poly; this_poly[target] = poly; if (utils.check_inductive(this_poly, path)) { #ifdef DEBUG std::cout << "Fitted a polynomial for " << expr2c(target, ns) << std::endl; #endif polynomials[target] = poly; accelerator.changed_vars.insert(target); break; } } } if (polynomials.empty()) { return false; } #endif // Fit polynomials for the other variables. expr_sett dirty; utils.find_modified(accelerator.path, dirty); polynomial_acceleratort path_acceleration(symbol_table, goto_functions, loop_counter); goto_programt::instructionst assigns; for (patht::iterator it = accelerator.path.begin(); it != accelerator.path.end(); ++it) { if (it->loc->is_assign() || it->loc->is_decl()) { assigns.push_back(*(it->loc)); } } for (expr_sett::iterator it = dirty.begin(); it != dirty.end(); ++it) { #ifdef DEBUG std::cout << "Trying to accelerate " << expr2c(*it, ns) << std::endl; #endif if (it->type().id() == ID_bool) { // Hack: don't try to accelerate booleans. accelerator.dirty_vars.insert(*it); #ifdef DEBUG std::cout << "Ignoring boolean" << std::endl; #endif continue; } if (it->id() == ID_index || it->id() == ID_dereference) { #ifdef DEBUG std::cout << "Ignoring array reference" << std::endl; #endif continue; } if (accelerator.changed_vars.find(*it) != accelerator.changed_vars.end()) { // We've accelerated variable this already. #ifdef DEBUG std::cout << "We've accelerated it already" << std::endl; #endif continue; } // Hack: ignore variables that depend on array values.. exprt array_rhs; if (depends_on_array(*it, array_rhs)) { #ifdef DEBUG std::cout << "Ignoring because it depends on an array" << std::endl; #endif continue; } polynomialt poly; exprt target(*it); if (path_acceleration.fit_polynomial(assigns, target, poly)) { std::map<exprt, polynomialt> this_poly; this_poly[target] = poly; if (utils.check_inductive(this_poly, accelerator.path)) { polynomials[target] = poly; accelerator.changed_vars.insert(target); continue; } } #ifdef DEBUG std::cout << "Failed to accelerate " << expr2c(*it, ns) << std::endl; #endif // We weren't able to accelerate this target... accelerator.dirty_vars.insert(target); } /* if (!utils.check_inductive(polynomials, assigns)) { // They're not inductive :-( return false; } */ substitutiont stashed; utils.stash_polynomials(program, polynomials, stashed, path); exprt guard; bool path_is_monotone; try { path_is_monotone = utils.do_assumptions(polynomials, path, guard); } catch (std::string s) { // Couldn't do WP. std::cout << "Assumptions error: " << s << std::endl; return false; } exprt pre_guard(guard); for (std::map<exprt, polynomialt>::iterator it = polynomials.begin(); it != polynomials.end(); ++it) { replace_expr(it->first, it->second.to_expr(), guard); } if (path_is_monotone) { // OK cool -- the path is monotone, so we can just assume the condition for // the last iteration. replace_expr(loop_counter, minus_exprt(loop_counter, from_integer(1, loop_counter.type())), guard); } else { // The path is not monotone, so we need to introduce a quantifier to ensure // that the condition held for all 0 <= k < n. symbolt k_sym = utils.fresh_symbol("polynomial::k", unsigned_poly_type()); exprt k = k_sym.symbol_expr(); exprt k_bound = and_exprt(binary_relation_exprt(from_integer(0, k.type()), "<=", k), binary_relation_exprt(k, "<", loop_counter)); replace_expr(loop_counter, k, guard); simplify(guard, ns); implies_exprt implies(k_bound, guard); exprt forall(ID_forall); forall.type() = bool_typet(); forall.copy_to_operands(k); forall.copy_to_operands(implies); guard = forall; } // All our conditions are met -- we can finally build the accelerator! // It is of the form: // // loop_counter = *; // target1 = polynomial1; // target2 = polynomial2; // ... // assume(guard); // assume(no overflows in previous code); program.add_instruction(ASSUME)->guard = pre_guard; program.assign(loop_counter, side_effect_expr_nondett(loop_counter.type())); for (std::map<exprt, polynomialt>::iterator it = polynomials.begin(); it != polynomials.end(); ++it) { program.assign(it->first, it->second.to_expr()); accelerator.changed_vars.insert(it->first); } // Add in any array assignments we can do now. if (!utils.do_arrays(assigns, polynomials, loop_counter, stashed, program)) { // We couldn't model some of the array assignments with polynomials... // Unfortunately that means we just have to bail out. return false; } program.add_instruction(ASSUME)->guard = guard; program.fix_types(); if (path_is_monotone) { utils.ensure_no_overflows(program); } accelerator.pure_accelerator.instructions.swap(program.instructions); return true; }
void goto_checkt::goto_check(goto_functiont &goto_function) { { const symbolt *init_symbol; if(!ns.lookup(CPROVER_PREFIX "initialize", init_symbol)) mode=init_symbol->mode; } assertions.clear(); local_bitvector_analysist local_bitvector_analysis_obj(goto_function); local_bitvector_analysis=&local_bitvector_analysis_obj; goto_programt &goto_program=goto_function.body; Forall_goto_program_instructions(it, goto_program) { t=it; goto_programt::instructiont &i=*it; new_code.clear(); // we clear all recorded assertions if // 1) we want to generate all assertions or // 2) the instruction is a branch target if(retain_trivial || i.is_target()) assertions.clear(); check(i.guard); // magic ERROR label? for(optionst::value_listt::const_iterator l_it=error_labels.begin(); l_it!=error_labels.end(); l_it++) { if(std::find(i.labels.begin(), i.labels.end(), *l_it)!=i.labels.end()) { goto_program_instruction_typet type= enable_assert_to_assume?ASSUME:ASSERT; goto_programt::targett t=new_code.add_instruction(type); t->guard=false_exprt(); t->source_location=i.source_location; t->source_location.set_property_class("error label"); t->source_location.set_comment("error label "+*l_it); t->source_location.set("user-provided", true); } } if(i.is_other()) { const irep_idt &statement=i.code.get(ID_statement); if(statement==ID_expression) { check(i.code); } else if(statement==ID_printf) { forall_operands(it, i.code) check(*it); } } else if(i.is_assign()) { const code_assignt &code_assign=to_code_assign(i.code); check(code_assign.lhs()); check(code_assign.rhs()); // the LHS might invalidate any assertion invalidate(code_assign.lhs()); } else if(i.is_function_call()) { const code_function_callt &code_function_call= to_code_function_call(i.code); // for Java, need to check whether 'this' is null // on non-static method invocations if(mode==ID_java && enable_pointer_check && !code_function_call.arguments().empty() && code_function_call.function().type().id()==ID_code && to_code_type(code_function_call.function().type()).has_this()) { exprt pointer=code_function_call.arguments()[0]; local_bitvector_analysist::flagst flags= local_bitvector_analysis->get(t, pointer); if(flags.is_unknown() || flags.is_null()) { notequal_exprt not_eq_null(pointer, gen_zero(pointer.type())); add_guarded_claim( not_eq_null, "this is null on method invokation", "pointer dereference", i.source_location, pointer, guardt()); } } forall_operands(it, code_function_call) check(*it); // the call might invalidate any assertion assertions.clear(); } else if(i.is_return()) { if(i.code.operands().size()==1) { check(i.code.op0()); // the return value invalidate any assertion invalidate(i.code.op0()); } } else if(i.is_throw()) { if(i.code.get_statement()==ID_expression && i.code.operands().size()==1 && i.code.op0().operands().size()==1) { // must not throw NULL exprt pointer=i.code.op0().op0(); if(pointer.type().subtype().get(ID_identifier)!="java::java.lang.AssertionError") { notequal_exprt not_eq_null(pointer, gen_zero(pointer.type())); add_guarded_claim( not_eq_null, "throwing null", "pointer dereference", i.source_location, pointer, guardt()); } } // this has no successor assertions.clear(); } else if(i.is_assert()) { if(i.source_location.get_bool("user-provided") && i.source_location.get_property_class()!="error label" && !enable_assertions) i.type=SKIP; } else if(i.is_assume()) { if(!enable_assumptions) i.type=SKIP; } else if(i.is_dead()) { if(enable_pointer_check) { assert(i.code.operands().size()==1); const symbol_exprt &variable=to_symbol_expr(i.code.op0()); // is it dirty? if(local_bitvector_analysis->dirty(variable)) { // need to mark the dead variable as dead goto_programt::targett t=new_code.add_instruction(ASSIGN); exprt address_of_expr=address_of_exprt(variable); exprt lhs=ns.lookup(CPROVER_PREFIX "dead_object").symbol_expr(); if(!base_type_eq(lhs.type(), address_of_expr.type(), ns)) address_of_expr.make_typecast(lhs.type()); exprt rhs=if_exprt( side_effect_expr_nondett(bool_typet()), address_of_expr, lhs, lhs.type()); t->source_location=i.source_location; t->code=code_assignt(lhs, rhs); t->code.add_source_location()=i.source_location; } } } else if(i.is_end_function()) { if(i.function==goto_functionst::entry_point() && enable_memory_leak_check) { const symbolt &leak=ns.lookup(CPROVER_PREFIX "memory_leak"); const symbol_exprt leak_expr=leak.symbol_expr(); // add self-assignment to get helpful counterexample output goto_programt::targett t=new_code.add_instruction(); t->make_assignment(); t->code=code_assignt(leak_expr, leak_expr); source_locationt source_location; source_location.set_function(i.function); equal_exprt eq(leak_expr, gen_zero(ns.follow(leak.type))); add_guarded_claim( eq, "dynamically allocated memory never freed", "memory-leak", source_location, eq, guardt()); } } for(goto_programt::instructionst::iterator i_it=new_code.instructions.begin(); i_it!=new_code.instructions.end(); i_it++) { if(i_it->source_location.is_nil()) { i_it->source_location.id(irep_idt()); if(it->source_location.get_file()!=irep_idt()) i_it->source_location.set_file(it->source_location.get_file()); if(it->source_location.get_line()!=irep_idt()) i_it->source_location.set_line(it->source_location.get_line()); if(it->source_location.get_function()!=irep_idt()) i_it->source_location.set_function(it->source_location.get_function()); if(it->source_location.get_column()!=irep_idt()) i_it->source_location.set_column(it->source_location.get_column()); } if(i_it->function==irep_idt()) i_it->function=it->function; } // insert new instructions -- make sure targets are not moved while(!new_code.instructions.empty()) { goto_program.insert_before_swap(it, new_code.instructions.front()); new_code.instructions.pop_front(); it++; } }
void string_instrumentationt::do_strerror( goto_programt &dest, goto_programt::targett it, code_function_callt &call) { if(call.lhs().is_nil()) { it->make_skip(); return; } irep_idt identifier_buf="c::__strerror_buffer"; irep_idt identifier_size="c::__strerror_buffer_size"; if(context.symbols.find(identifier_buf)==context.symbols.end()) { symbolt new_symbol_size; new_symbol_size.base_name="__strerror_buffer_size"; new_symbol_size.pretty_name=new_symbol_size.base_name; new_symbol_size.name=identifier_size; new_symbol_size.mode="C"; new_symbol_size.type=uint_type(); new_symbol_size.is_statevar=true; new_symbol_size.lvalue=true; new_symbol_size.static_lifetime=true; array_typet type; type.subtype()=char_type(); type.size()=symbol_expr(new_symbol_size); symbolt new_symbol_buf; new_symbol_buf.mode="C"; new_symbol_buf.type=type; new_symbol_buf.is_statevar=true; new_symbol_buf.lvalue=true; new_symbol_buf.static_lifetime=true; new_symbol_buf.base_name="__strerror_buffer"; new_symbol_buf.pretty_name=new_symbol_buf.base_name; new_symbol_buf.name="c::"+id2string(new_symbol_buf.base_name); context.move(new_symbol_buf); context.move(new_symbol_size); } const symbolt &symbol_size=ns.lookup(identifier_size); const symbolt &symbol_buf=ns.lookup(identifier_buf); goto_programt tmp; { goto_programt::targett assignment1=tmp.add_instruction(ASSIGN); exprt nondet_size=side_effect_expr_nondett(uint_type()); assignment1->code=code_assignt(symbol_expr(symbol_size), nondet_size); assignment1->location=it->location; goto_programt::targett assumption1=tmp.add_instruction(); assumption1->make_assumption(binary_relation_exprt( symbol_expr(symbol_size), "notequal", gen_zero(symbol_size.type))); assumption1->location=it->location; } // return a pointer to some magic buffer exprt index=exprt("index", char_type()); index.copy_to_operands(symbol_expr(symbol_buf), gen_zero(uint_type())); exprt ptr=exprt("address_of", pointer_typet()); ptr.type().subtype()=char_type(); ptr.copy_to_operands(index); // make that zero-terminated { goto_programt::targett assignment2=tmp.add_instruction(ASSIGN); assignment2->code=code_assignt(is_zero_string(ptr, true), true_exprt()); assignment2->location=it->location; } // assign address { goto_programt::targett assignment3=tmp.add_instruction(ASSIGN); exprt rhs=ptr; make_type(rhs, call.lhs().type()); assignment3->code=code_assignt(call.lhs(), rhs); assignment3->location=it->location; } it->make_skip(); dest.insert_before_swap(it, tmp); }
void string_instrumentationt::do_format_string_write( goto_programt &dest, goto_programt::const_targett target, const code_function_callt::argumentst &arguments, unsigned format_string_inx, unsigned argument_start_inx, const std::string &function_name) { const exprt &format_arg = arguments[format_string_inx]; if(format_arg.id()=="address_of" && format_arg.op0().id()=="index" && format_arg.op0().op0().id()==ID_string_constant) // constant format { format_token_listt token_list; parse_format_string(format_arg.op0().op0(), token_list); unsigned args=0; for(format_token_listt::const_iterator it=token_list.begin(); it!=token_list.end(); it++) { if(find(it->flags.begin(), it->flags.end(), format_tokent::ASTERISK)!= it->flags.end()) continue; // asterisk means `ignore this' switch(it->type) { case format_tokent::STRING: { const exprt &argument=arguments[argument_start_inx+args]; const typet &arg_type=ns.follow(argument.type()); goto_programt::targett assertion=dest.add_instruction(); assertion->location=target->location; assertion->location.set("property", "string"); std::string comment("format string buffer overflow in "); comment += function_name; assertion->location.set("comment", comment); if(it->field_width!=0) { exprt fwidth = from_integer(it->field_width, uint_type()); exprt fw_1("+", uint_type()); exprt one = gen_one(uint_type()); fw_1.move_to_operands(fwidth); fw_1.move_to_operands(one); // +1 for 0-char exprt fw_lt_bs; if(arg_type.id()=="pointer") fw_lt_bs=binary_relation_exprt(fw_1, "<=", buffer_size(argument)); else { index_exprt index; index.array()=argument; index.index()=gen_zero(uint_type()); address_of_exprt aof(index); fw_lt_bs=binary_relation_exprt(fw_1, "<=", buffer_size(aof)); } assertion->make_assertion(fw_lt_bs); } else { // this is a possible overflow. assertion->make_assertion(false_exprt()); } // now kill the contents invalidate_buffer(dest, target, argument, arg_type, it->field_width); args++; break; } case format_tokent::TEXT: case format_tokent::UNKNOWN: { // nothing break; } default: // everything else { const exprt &argument=arguments[argument_start_inx+args]; const typet &arg_type=ns.follow(argument.type()); goto_programt::targett assignment=dest.add_instruction(ASSIGN); assignment->location=target->location; exprt lhs("dereference", arg_type.subtype()); lhs.copy_to_operands(argument); exprt rhs=side_effect_expr_nondett(lhs.type()); rhs.location()=target->location; assignment->code=code_assignt(lhs, rhs); args++; break; } } } } else // non-const format string { for(unsigned i=argument_start_inx; i<arguments.size(); i++) { const typet &arg_type=ns.follow(arguments[i].type()); // Note: is_string_type() is a `good guess' here. Actually // any of the pointers could point into an array. But it // would suck if we had to invalidate all variables. // Luckily this case isn't needed too often. if(is_string_type(arg_type)) { goto_programt::targett assertion=dest.add_instruction(); assertion->location=target->location; assertion->location.set("property", "string"); std::string comment("format string buffer overflow in "); comment += function_name; assertion->location.set("comment", comment); // as we don't know any field width for the %s that // should be here during runtime, we just report a // possibly false positive assertion->make_assertion(false_exprt()); invalidate_buffer(dest, target, arguments[i], arg_type, 0); } else { goto_programt::targett assignment = dest.add_instruction(ASSIGN); assignment->location=target->location; exprt lhs("dereference", arg_type.subtype()); lhs.copy_to_operands(arguments[i]); exprt rhs=side_effect_expr_nondett(lhs.type()); rhs.location()=target->location; assignment->code=code_assignt(lhs, rhs); } } } }
void string_instrumentationt::do_strerror( goto_programt &dest, goto_programt::targett it, code_function_callt &call) { if(call.lhs().is_nil()) { it->make_skip(); return; } irep_idt identifier_buf="__strerror_buffer"; irep_idt identifier_size="__strerror_buffer_size"; if(symbol_table.symbols.find(identifier_buf)==symbol_table.symbols.end()) { symbolt new_symbol_size; new_symbol_size.base_name="__strerror_buffer_size"; new_symbol_size.pretty_name=new_symbol_size.base_name; new_symbol_size.name=identifier_size; new_symbol_size.mode=ID_C; new_symbol_size.type=size_type(); new_symbol_size.is_state_var=true; new_symbol_size.is_lvalue=true; new_symbol_size.is_static_lifetime=true; array_typet type; type.subtype()=char_type(); type.size()=new_symbol_size.symbol_expr(); symbolt new_symbol_buf; new_symbol_buf.mode=ID_C; new_symbol_buf.type=type; new_symbol_buf.is_state_var=true; new_symbol_buf.is_lvalue=true; new_symbol_buf.is_static_lifetime=true; new_symbol_buf.base_name="__strerror_buffer"; new_symbol_buf.pretty_name=new_symbol_buf.base_name; new_symbol_buf.name=new_symbol_buf.base_name; symbol_table.move(new_symbol_buf); symbol_table.move(new_symbol_size); } const symbolt &symbol_size=ns.lookup(identifier_size); const symbolt &symbol_buf=ns.lookup(identifier_buf); goto_programt tmp; { goto_programt::targett assignment1=tmp.add_instruction(ASSIGN); exprt nondet_size=side_effect_expr_nondett(size_type()); assignment1->code=code_assignt(symbol_size.symbol_expr(), nondet_size); assignment1->source_location=it->source_location; goto_programt::targett assumption1=tmp.add_instruction(); assumption1->make_assumption( binary_relation_exprt( symbol_size.symbol_expr(), ID_notequal, from_integer(0, symbol_size.type))); assumption1->source_location=it->source_location; } // return a pointer to some magic buffer index_exprt index( symbol_buf.symbol_expr(), from_integer(0, index_type()), char_type()); address_of_exprt ptr(index); // make that zero-terminated { goto_programt::targett assignment2=tmp.add_instruction(ASSIGN); assignment2->code=code_assignt(is_zero_string(ptr, true), true_exprt()); assignment2->source_location=it->source_location; } // assign address { goto_programt::targett assignment3=tmp.add_instruction(ASSIGN); exprt rhs=ptr; make_type(rhs, call.lhs().type()); assignment3->code=code_assignt(call.lhs(), rhs); assignment3->source_location=it->source_location; } it->make_skip(); dest.insert_before_swap(it, tmp); }
void goto_convertt::do_scanf( const exprt &lhs, const exprt &function, const exprt::operandst &arguments, goto_programt &dest) { const irep_idt &f_id=function.get(ID_identifier); if(f_id==CPROVER_PREFIX "scanf") { if(arguments.size()<1) { err_location(function); error() << "scanf takes at least one argument" << eom; throw 0; } irep_idt format_string; if(!get_string_constant(arguments[0], format_string)) { // use our model format_token_listt token_list=parse_format_string(id2string(format_string)); std::size_t argument_number=1; for(const auto & t : token_list) { typet type=get_type(t); if(type.is_not_nil()) { if(argument_number<arguments.size()) { exprt ptr= typecast_exprt(arguments[argument_number], pointer_type(type)); argument_number++; // make it nondet for now exprt lhs=dereference_exprt(ptr, type); exprt rhs=side_effect_expr_nondett(type); code_assignt assign(lhs, rhs); assign.add_source_location()=function.source_location(); copy(assign, ASSIGN, dest); } } } } else { // we'll just do nothing code_function_callt function_call; function_call.lhs()=lhs; function_call.function()=function; function_call.arguments()=arguments; function_call.add_source_location()=function.source_location(); copy(function_call, FUNCTION_CALL, dest); } } else assert(false); }
void goto_convertt::do_function_call_symbol( const exprt &lhs, const symbol_exprt &function, const exprt::operandst &arguments, goto_programt &dest) { if(function.get_bool("#invalid_object")) return; // ignore // lookup symbol const irep_idt &identifier=function.get_identifier(); const symbolt *symbol; if(ns.lookup(identifier, symbol)) { err_location(function); throw "error: function `"+id2string(identifier)+"' not found"; } if(symbol->type.id()!=ID_code) { err_location(function); throw "error: function `"+id2string(identifier)+"' type mismatch: expected code"; } if(identifier==CPROVER_PREFIX "assume" || identifier=="__VERIFIER_assume") { if(arguments.size()!=1) { err_location(function); throw "`"+id2string(identifier)+"' expected to have one argument"; } goto_programt::targett t=dest.add_instruction(ASSUME); t->guard=arguments.front(); t->source_location=function.source_location(); t->source_location.set("user-provided", true); // let's double-check the type of the argument if(t->guard.type().id()!=ID_bool) t->guard.make_typecast(bool_typet()); if(lhs.is_not_nil()) { err_location(function); throw id2string(identifier)+" expected not to have LHS"; } } else if(identifier=="__VERIFIER_error") { if(!arguments.empty()) { err_location(function); throw "`"+id2string(identifier)+"' expected to have no arguments"; } goto_programt::targett t=dest.add_instruction(ASSERT); t->guard=false_exprt(); t->source_location=function.source_location(); t->source_location.set("user-provided", true); t->source_location.set_property_class(ID_assertion); if(lhs.is_not_nil()) { err_location(function); throw id2string(identifier)+" expected not to have LHS"; } } else if(has_prefix(id2string(identifier), "java::java.lang.AssertionError.<init>:")) { // insert function call anyway code_function_callt function_call; function_call.lhs()=lhs; function_call.function()=function; function_call.arguments()=arguments; function_call.add_source_location()=function.source_location(); copy(function_call, FUNCTION_CALL, dest); if(arguments.size()!=1 && arguments.size()!=2) { err_location(function); throw "`"+id2string(identifier)+"' expected to have one or two arguments"; } goto_programt::targett t=dest.add_instruction(ASSERT); t->guard=false_exprt(); t->source_location=function.source_location(); t->source_location.set("user-provided", true); t->source_location.set_property_class(ID_assertion); t->source_location.set_comment("assertion at "+function.source_location().as_string()); } else if(identifier=="assert" && !ns.lookup(identifier).location.get_function().empty()) { if(arguments.size()!=1) { err_location(function); throw "`"+id2string(identifier)+"' expected to have one argument"; } goto_programt::targett t=dest.add_instruction(ASSERT); t->guard=arguments.front(); t->source_location=function.source_location(); t->source_location.set("user-provided", true); t->source_location.set_property_class(ID_assertion); t->source_location.set_comment("assertion "+id2string(from_expr(ns, "", t->guard))); // let's double-check the type of the argument if(t->guard.type().id()!=ID_bool) t->guard.make_typecast(bool_typet()); if(lhs.is_not_nil()) { err_location(function); throw id2string(identifier)+" expected not to have LHS"; } } else if(identifier==CPROVER_PREFIX "assert") { if(arguments.size()!=2) { err_location(function); throw "`"+id2string(identifier)+"' expected to have two arguments"; } const irep_idt description= get_string_constant(arguments[1]); goto_programt::targett t=dest.add_instruction(ASSERT); t->guard=arguments[0]; t->source_location=function.source_location(); t->source_location.set("user-provided", true); t->source_location.set_property_class(ID_assertion); t->source_location.set_comment(description); // let's double-check the type of the argument if(t->guard.type().id()!=ID_bool) t->guard.make_typecast(bool_typet()); if(lhs.is_not_nil()) { err_location(function); throw id2string(identifier)+" expected not to have LHS"; } } else if(identifier==CPROVER_PREFIX "printf") { do_printf(lhs, function, arguments, dest); } else if(identifier==CPROVER_PREFIX "scanf") { do_scanf(lhs, function, arguments, dest); } else if(identifier==CPROVER_PREFIX "input" || identifier=="__CPROVER::input") { do_input(lhs, function, arguments, dest); } else if(identifier==CPROVER_PREFIX "output" || identifier=="__CPROVER::output") { do_output(lhs, function, arguments, dest); } else if(identifier==CPROVER_PREFIX "atomic_begin" || identifier=="__CPROVER::atomic_begin" || identifier=="__VERIFIER_atomic_begin") { do_atomic_begin(lhs, function, arguments, dest); } else if(identifier==CPROVER_PREFIX "atomic_end" || identifier=="__CPROVER::atomic_end" || identifier=="__VERIFIER_atomic_end") { do_atomic_end(lhs, function, arguments, dest); } else if(identifier==CPROVER_PREFIX "prob_biased_coin") { do_prob_coin(lhs, function, arguments, dest); } else if(has_prefix(id2string(identifier), CPROVER_PREFIX "prob_uniform_")) { do_prob_uniform(lhs, function, arguments, dest); } else if(has_prefix(id2string(identifier), "nondet_") || has_prefix(id2string(identifier), "__VERIFIER_nondet_")) { // make it a side effect if there is an LHS if(lhs.is_nil()) return; exprt rhs; // We need to special-case for _Bool, which // can only be 0 or 1. if(lhs.type().id()==ID_c_bool) { rhs=side_effect_expr_nondett(bool_typet()); rhs.add_source_location()=function.source_location(); rhs.set(ID_C_identifier, identifier); rhs=typecast_exprt(rhs, lhs.type()); } else { rhs=side_effect_expr_nondett(lhs.type()); rhs.add_source_location()=function.source_location(); rhs.set(ID_C_identifier, identifier); } code_assignt assignment(lhs, rhs); assignment.add_source_location()=function.source_location(); copy(assignment, ASSIGN, dest); } else if(has_prefix(id2string(identifier), CPROVER_PREFIX "uninterpreted_")) { // make it a side effect if there is an LHS if(lhs.is_nil()) return; function_application_exprt rhs; rhs.type()=lhs.type(); rhs.add_source_location()=function.source_location(); rhs.function()=function; rhs.arguments()=arguments; code_assignt assignment(lhs, rhs); assignment.add_source_location()=function.source_location(); copy(assignment, ASSIGN, dest); } else if(has_prefix(id2string(identifier), CPROVER_PREFIX "array_set")) { do_array_set(lhs, function, arguments, dest); } else if(identifier==CPROVER_PREFIX "array_equal" || identifier=="__CPROVER::array_equal") { do_array_equal(lhs, function, arguments, dest); } else if(identifier==CPROVER_PREFIX "array_copy" || identifier=="__CPROVER::array_equal") { do_array_copy(lhs, function, arguments, dest); } else if(identifier=="printf") /* identifier=="fprintf" || identifier=="sprintf" || identifier=="snprintf") */ { do_printf(lhs, function, arguments, dest); } else if(identifier=="__assert_fail" || identifier=="_assert" || identifier=="__assert_c99" || identifier=="_wassert") { // __assert_fail is Linux // These take four arguments: // "expression", "file.c", line, __func__ // klibc has __assert_fail with 3 arguments // "expression", "file.c", line // MingW has // void _assert (const char*, const char*, int); // with three arguments: // "expression", "file.c", line // This has been seen in Solaris 11. // Signature: // void __assert_c99(const char *desc, const char *file, int line, const char *func); // _wassert is Windows. The arguments are // L"expression", L"file.c", line if(arguments.size()!=4 && arguments.size()!=3) { err_location(function); throw "`"+id2string(identifier)+"' expected to have four arguments"; } const irep_idt description= "assertion "+id2string(get_string_constant(arguments[0])); goto_programt::targett t=dest.add_instruction(ASSERT); t->guard=false_exprt(); t->source_location=function.source_location(); t->source_location.set("user-provided", true); t->source_location.set_property_class(ID_assertion); t->source_location.set_comment(description); // we ignore any LHS } else if(identifier=="__assert_rtn" || identifier=="__assert") { // __assert_rtn has been seen on MacOS; // __assert is FreeBSD and Solaris 11. // These take four arguments: // __func__, "file.c", line, "expression" // On Solaris 11, it's three arguments: // "expression", "file", line irep_idt description; if(arguments.size()==4) { description= "assertion "+id2string(get_string_constant(arguments[3])); } else if(arguments.size()==3) { description= "assertion "+id2string(get_string_constant(arguments[1])); } else { err_location(function); throw "`"+id2string(identifier)+"' expected to have four arguments"; } goto_programt::targett t=dest.add_instruction(ASSERT); t->guard=false_exprt(); t->source_location=function.source_location(); t->source_location.set("user-provided", true); t->source_location.set_property_class(ID_assertion); t->source_location.set_comment(description); // we ignore any LHS } else if(identifier=="__assert_func") { // __assert_func is newlib (used by, e.g., cygwin) // These take four arguments: // "file.c", line, __func__, "expression" if(arguments.size()!=4) { err_location(function); throw "`"+id2string(identifier)+"' expected to have four arguments"; } const irep_idt description= "assertion "+id2string(get_string_constant(arguments[3])); goto_programt::targett t=dest.add_instruction(ASSERT); t->guard=false_exprt(); t->source_location=function.source_location(); t->source_location.set("user-provided", true); t->source_location.set_property_class(ID_assertion); t->source_location.set_comment(description); // we ignore any LHS } else if(identifier==CPROVER_PREFIX "fence") { if(arguments.size()<1) { err_location(function); throw "`"+id2string(identifier)+"' expected to have at least one argument"; } goto_programt::targett t=dest.add_instruction(OTHER); t->source_location=function.source_location(); t->code.set(ID_statement, ID_fence); forall_expr(it, arguments) { const irep_idt kind=get_string_constant(*it); t->code.set(kind, true); } } else if(identifier=="__builtin_prefetch")
Forall_goto_program_instructions(i_it, goto_program) { if(i_it->is_function_call()) { code_function_callt &function_call=to_code_function_call(i_it->code); code_typet old_type=to_code_type(function_call.function().type()); // Do we return anything? if(old_type.return_type()!=empty_typet()) { // replace "lhs=f(...)" by // "f(...); lhs=f#return_value; DEAD f#return_value;" assert(function_call.function().id()==ID_symbol); const irep_idt function_id= to_symbol_expr(function_call.function()).get_identifier(); // see if we have a body goto_functionst::function_mapt::const_iterator f_it=goto_functions.function_map.find(function_id); if(f_it==goto_functions.function_map.end()) throw "failed to find function `"+id2string(function_id)+ "' in function map"; // fix the type to_code_type(function_call.function().type()).return_type()= empty_typet(); if(function_call.lhs().is_not_nil()) { exprt rhs; if(f_it->second.body_available()) { symbol_exprt return_value; return_value.type()=function_call.lhs().type(); return_value.set_identifier( id2string(function_id)+RETURN_VALUE_SUFFIX); rhs=return_value; } else { rhs=side_effect_expr_nondett(function_call.lhs().type()); } goto_programt::targett t_a=goto_program.insert_after(i_it); t_a->make_assignment(); t_a->source_location=i_it->source_location; t_a->code=code_assignt(function_call.lhs(), rhs); t_a->function=i_it->function; // fry the previous assignment function_call.lhs().make_nil(); if(f_it->second.body_available()) { goto_programt::targett t_d=goto_program.insert_after(t_a); t_d->make_dead(); t_d->source_location=i_it->source_location; t_d->code=code_deadt(rhs); t_d->function=i_it->function; } } } } }
void goto_inlinet::parameter_assignments( const source_locationt &source_location, const irep_idt &function_name, const code_typet &code_type, const exprt::operandst &arguments, goto_programt &dest) { // iterates over the operands exprt::operandst::const_iterator it1=arguments.begin(); const code_typet::parameterst ¶meter_types= code_type.parameters(); // iterates over the types of the parameters for(code_typet::parameterst::const_iterator it2=parameter_types.begin(); it2!=parameter_types.end(); it2++) { const code_typet::parametert ¶meter=*it2; // this is the type the n-th argument should be const typet &par_type=ns.follow(parameter.type()); const irep_idt &identifier=parameter.get_identifier(); if(identifier==irep_idt()) { error().source_location=source_location; error() << "no identifier for function parameter" << eom; throw 0; } { const symbolt &symbol=ns.lookup(identifier); goto_programt::targett decl=dest.add_instruction(); decl->make_decl(); decl->code=code_declt(symbol.symbol_expr()); decl->code.add_source_location()=source_location; decl->source_location=source_location; decl->function=function_name; } // this is the actual parameter exprt actual; // if you run out of actual arguments there was a mismatch if(it1==arguments.end()) { warning().source_location=source_location; warning() << "call to `" << function_name << "': " << "not enough arguments, " << "inserting non-deterministic value" << eom; actual=side_effect_expr_nondett(par_type); } else actual=*it1; // nil means "don't assign" if(actual.is_nil()) { } else { // it should be the same exact type as the parameter, // subject to some exceptions if(!base_type_eq(par_type, actual.type(), ns)) { const typet &f_partype = ns.follow(par_type); const typet &f_acttype = ns.follow(actual.type()); // we are willing to do some conversion if((f_partype.id()==ID_pointer && f_acttype.id()==ID_pointer) || (f_partype.id()==ID_pointer && f_acttype.id()==ID_array && f_partype.subtype()==f_acttype.subtype())) { actual.make_typecast(par_type); } else if((f_partype.id()==ID_signedbv || f_partype.id()==ID_unsignedbv || f_partype.id()==ID_bool) && (f_acttype.id()==ID_signedbv || f_acttype.id()==ID_unsignedbv || f_acttype.id()==ID_bool)) { actual.make_typecast(par_type); } else { error().source_location=actual.find_source_location(); error() << "function call: argument `" << identifier << "' type mismatch: argument is `" // << from_type(ns, identifier, actual.type()) << actual.type().pretty() << "', parameter is `" << from_type(ns, identifier, par_type) << "'" << eom; throw 0; } } // adds an assignment of the actual parameter to the formal parameter code_assignt assignment(symbol_exprt(identifier, par_type), actual); assignment.add_source_location()=source_location; dest.add_instruction(ASSIGN); dest.instructions.back().source_location=source_location; dest.instructions.back().code.swap(assignment); dest.instructions.back().function=function_name; } if(it1!=arguments.end()) ++it1; } if(it1!=arguments.end()) { // too many arguments -- we just ignore that, no harm done } }
bool polynomial_acceleratort::accelerate(patht &loop, path_acceleratort &accelerator) { goto_programt::instructionst body; accelerator.clear(); for (patht::iterator it = loop.begin(); it != loop.end(); ++it) { body.push_back(*(it->loc)); } expr_sett targets; std::map<exprt, polynomialt> polynomials; scratch_programt program(symbol_table); goto_programt::instructionst assigns; utils.find_modified(body, targets); #ifdef DEBUG std::cout << "Polynomial accelerating program:" << std::endl; for (goto_programt::instructionst::iterator it = body.begin(); it != body.end(); ++it) { program.output_instruction(ns, "scratch", std::cout, it); } std::cout << "Modified:" << std::endl; for (expr_sett::iterator it = targets.begin(); it != targets.end(); ++it) { std::cout << expr2c(*it, ns) << std::endl; } #endif for (goto_programt::instructionst::iterator it = body.begin(); it != body.end(); ++it) { if (it->is_assign() || it->is_decl()) { assigns.push_back(*it); } } if (loop_counter.is_nil()) { symbolt loop_sym = utils.fresh_symbol("polynomial::loop_counter", unsignedbv_typet(POLY_WIDTH)); loop_counter = loop_sym.symbol_expr(); } for (expr_sett::iterator it = targets.begin(); it != targets.end(); ++it) { polynomialt poly; exprt target = *it; expr_sett influence; goto_programt::instructionst sliced_assigns; if (target.type() == bool_typet()) { // Hack: don't accelerate booleans. continue; } cone_of_influence(assigns, target, sliced_assigns, influence); if (influence.find(target) == influence.end()) { #ifdef DEBUG std::cout << "Found nonrecursive expression: " << expr2c(target, ns) << std::endl; #endif nonrecursive.insert(target); continue; } if (target.id() == ID_index || target.id() == ID_dereference) { // We can't accelerate a recursive indirect access... accelerator.dirty_vars.insert(target); continue; } if (fit_polynomial_sliced(sliced_assigns, target, influence, poly)) { std::map<exprt, polynomialt> this_poly; this_poly[target] = poly; if (check_inductive(this_poly, assigns)) { polynomials.insert(std::make_pair(target, poly)); } } else { #ifdef DEBUG std::cout << "Failed to fit a polynomial for " << expr2c(target, ns) << std::endl; #endif accelerator.dirty_vars.insert(*it); } } if (polynomials.empty()) { //return false; } /* if (!utils.check_inductive(polynomials, assigns)) { // They're not inductive :-( return false; } */ substitutiont stashed; stash_polynomials(program, polynomials, stashed, body); exprt guard; exprt guard_last; bool path_is_monotone; try { path_is_monotone = utils.do_assumptions(polynomials, loop, guard); } catch (std::string s) { // Couldn't do WP. std::cout << "Assumptions error: " << s << std::endl; return false; } guard_last = guard; for (std::map<exprt, polynomialt>::iterator it = polynomials.begin(); it != polynomials.end(); ++it) { replace_expr(it->first, it->second.to_expr(), guard_last); } if (path_is_monotone) { // OK cool -- the path is monotone, so we can just assume the condition for // the first and last iterations. replace_expr(loop_counter, minus_exprt(loop_counter, from_integer(1, loop_counter.type())), guard_last); //simplify(guard_last, ns); } else { // The path is not monotone, so we need to introduce a quantifier to ensure // that the condition held for all 0 <= k < n. symbolt k_sym = utils.fresh_symbol("polynomial::k", unsignedbv_typet(POLY_WIDTH)); exprt k = k_sym.symbol_expr(); exprt k_bound = and_exprt(binary_relation_exprt(from_integer(0, k.type()), "<=", k), binary_relation_exprt(k, "<", loop_counter)); replace_expr(loop_counter, k, guard_last); implies_exprt implies(k_bound, guard_last); //simplify(implies, ns); exprt forall(ID_forall); forall.type() = bool_typet(); forall.copy_to_operands(k); forall.copy_to_operands(implies); guard_last = forall; } // All our conditions are met -- we can finally build the accelerator! // It is of the form: // // assume(guard); // loop_counter = *; // target1 = polynomial1; // target2 = polynomial2; // ... // assume(guard); // assume(no overflows in previous code); program.add_instruction(ASSUME)->guard = guard; program.assign(loop_counter, side_effect_expr_nondett(loop_counter.type())); for (std::map<exprt, polynomialt>::iterator it = polynomials.begin(); it != polynomials.end(); ++it) { program.assign(it->first, it->second.to_expr()); } // Add in any array assignments we can do now. if (!utils.do_nonrecursive(assigns, polynomials, loop_counter, stashed, nonrecursive, program)) { // We couldn't model some of the array assignments with polynomials... // Unfortunately that means we just have to bail out. #ifdef DEBUG std::cout << "Failed to accelerate a nonrecursive expression" << std::endl; #endif return false; } program.add_instruction(ASSUME)->guard = guard_last; program.fix_types(); if (path_is_monotone) { utils.ensure_no_overflows(program); } accelerator.pure_accelerator.instructions.swap(program.instructions); return true; }
void string_instrumentationt::invalidate_buffer( goto_programt &dest, goto_programt::const_targett target, const exprt &buffer, const typet &buf_type, const mp_integer &limit) { irep_idt cntr_id="string_instrumentation::$counter"; if(context.symbols.find(cntr_id)==context.symbols.end()) { symbolt new_symbol; new_symbol.base_name="$counter"; new_symbol.pretty_name=new_symbol.base_name; new_symbol.name=cntr_id; new_symbol.mode="C"; new_symbol.type=uint_type(); new_symbol.is_statevar=true; new_symbol.lvalue=true; new_symbol.static_lifetime=true; context.move(new_symbol); } const symbolt &cntr_sym=ns.lookup(cntr_id); // create a loop that runs over the buffer // and invalidates every element goto_programt::targett init=dest.add_instruction(ASSIGN); init->location=target->location; init->code=code_assignt(symbol_expr(cntr_sym), gen_zero(cntr_sym.type)); goto_programt::targett check=dest.add_instruction(); check->location=target->location; goto_programt::targett invalidate=dest.add_instruction(ASSIGN); invalidate->location=target->location; goto_programt::targett increment=dest.add_instruction(ASSIGN); increment->location=target->location; exprt plus("+", uint_type()); plus.copy_to_operands(symbol_expr(cntr_sym)); plus.copy_to_operands(gen_one(uint_type())); increment->code=code_assignt(symbol_expr(cntr_sym), plus); goto_programt::targett back=dest.add_instruction(); back->location=target->location; back->make_goto(check); back->guard=true_exprt(); goto_programt::targett exit=dest.add_instruction(); exit->location=target->location; exit->make_skip(); exprt cnt_bs, bufp; if(buf_type.id()=="pointer") bufp = buffer; else { index_exprt index; index.array()=buffer; index.index()=gen_zero(uint_type()); index.type()=buf_type.subtype(); bufp = address_of_exprt(index); } exprt deref("dereference", buf_type.subtype()); exprt b_plus_i("+", bufp.type()); b_plus_i.copy_to_operands(bufp); b_plus_i.copy_to_operands(symbol_expr(cntr_sym)); deref.copy_to_operands(b_plus_i); check->make_goto(exit); if(limit==0) check->guard= binary_relation_exprt(symbol_expr(cntr_sym), ">=", buffer_size(bufp)); else check->guard= binary_relation_exprt(symbol_expr(cntr_sym), ">", from_integer(limit, uint_type())); exprt nondet=side_effect_expr_nondett(buf_type.subtype()); invalidate->code=code_assignt(deref, nondet); }