static bool is_skip(goto_programt::instructionst::iterator it) { if (it->is_skip()) return true; if (it->is_goto()) { if (is_constant_bool2t(it->guard) && !to_constant_bool2t(it->guard).constant_value) return true; if (it->targets.size()!=1) return false; goto_programt::instructionst::iterator next_it=it; next_it++; return it->targets.front()==next_it; } if(it->is_other()) { return is_nil_expr(it->code); } return false; }
void goto_inlinet::replace_return( goto_programt &dest, const exprt &lhs, const exprt &constrain) { for(goto_programt::instructionst::iterator it=dest.instructions.begin(); it!=dest.instructions.end(); it++) { if(it->is_return()) { if(lhs.is_not_nil()) { goto_programt tmp; goto_programt::targett assignment=tmp.add_instruction(ASSIGN); const code_return2t &ret = to_code_return2t(it->code); code_assignt code_assign(lhs, migrate_expr_back(ret.operand)); // this may happen if the declared return type at the call site // differs from the defined return type if(code_assign.lhs().type()!= code_assign.rhs().type()) code_assign.rhs().make_typecast(code_assign.lhs().type()); migrate_expr(code_assign, assignment->code); assignment->location=it->location; assignment->local_variables=it->local_variables; assignment->function=it->location.get_function(); assert(constrain.is_nil()); // bp_constrain gumpf reomved dest.insert_swap(it, *assignment); it++; } else if(!is_nil_expr(it->code)) { // Encode evaluation of return expr, so that returns with pointer // derefs in them still get dereferenced, even when the result is // discarded. goto_programt tmp; goto_programt::targett expression=tmp.add_instruction(OTHER); expression->make_other(); expression->location=it->location; expression->function=it->location.get_function(); expression->local_variables=it->local_variables; const code_return2t &ret = to_code_return2t(it->code); expression->code = code_expression2tc(ret.operand); dest.insert_swap(it, *expression); it++; } it->make_goto(--dest.instructions.end()); } } }
void goto_symext::track_new_pointer( const expr2tc &ptr_obj, const type2tc &new_type, const expr2tc &size) { // Also update all the accounting data. // Mark that object as being dynamic, in the __ESBMC_is_dynamic array type2tc sym_type = type2tc(new array_type2t(get_bool_type(), expr2tc(), true)); symbol2tc sym(sym_type, dyn_info_arr_name); index2tc idx(get_bool_type(), sym, ptr_obj); expr2tc truth = gen_true_expr(); symex_assign(code_assign2tc(idx, truth), true); symbol2tc valid_sym(sym_type, valid_ptr_arr_name); index2tc valid_index_expr(get_bool_type(), valid_sym, ptr_obj); truth = gen_true_expr(); symex_assign(code_assign2tc(valid_index_expr, truth), true); symbol2tc dealloc_sym(sym_type, deallocd_arr_name); index2tc dealloc_index_expr(get_bool_type(), dealloc_sym, ptr_obj); expr2tc falseity = gen_false_expr(); symex_assign(code_assign2tc(dealloc_index_expr, falseity), true); type2tc sz_sym_type = type2tc(new array_type2t(pointer_type2(), expr2tc(), true)); symbol2tc sz_sym(sz_sym_type, alloc_size_arr_name); index2tc sz_index_expr(get_bool_type(), sz_sym, ptr_obj); expr2tc object_size_exp; if(is_nil_expr(size)) { try { mp_integer object_size = type_byte_size(new_type); object_size_exp = constant_int2tc(pointer_type2(), object_size.to_ulong()); } catch(array_type2t::dyn_sized_array_excp *e) { object_size_exp = typecast2tc(pointer_type2(), e->size); } } else { object_size_exp = size; } symex_assign(code_assign2tc(sz_index_expr, object_size_exp), true); }
smt_astt array_sym_smt_ast::update( smt_convt *ctx, smt_astt value, unsigned int idx, expr2tc idx_expr) const { const array_type2t array_type = to_array_type(sort->get_tuple_type()); const struct_union_data &data = ctx->get_type_def(array_type.subtype); expr2tc index; if(is_nil_expr(idx_expr)) { index = constant_int2tc(ctx->make_array_domain_type(array_type), BigInt(idx)); } else { index = idx_expr; } std::string name = ctx->mk_fresh_name("tuple_array_update::") + "."; tuple_sym_smt_astt result = new array_sym_smt_ast(ctx, sort, name); // Iterate over all members. They are _all_ indexed and updated. unsigned int i = 0; for(auto const &it : data.members) { type2tc arrtype( new array_type2t(it, array_type.array_size, array_type.size_is_infinite)); // Project and update a field in 'this' smt_astt field = project(ctx, i); smt_astt resval = value->project(ctx, i); smt_astt updated = field->update(ctx, resval, 0, index); // Now equality it into the result object smt_astt res_field = result->project(ctx, i); updated->assign(ctx, res_field); i++; } return result; }
bool goto_symex_statet::constant_propagation(const expr2tc &expr) const { static unsigned int with_counter=0; // Don't permit const propagaion of infinite-size arrays. They're going to // be special modelling arrays that require special handling either at SMT // or some other level, so attempting to optimse them is a Bad Plan (TM). if (is_array_type(expr) && to_array_type(expr->type).size_is_infinite) return false; if (is_nil_expr(expr)) { return true; // It's fine to constant propagate something that's absent. } else if (is_constant_expr(expr)) { return true; } else if (is_symbol2t(expr) && to_symbol2t(expr).thename == "NULL") { // Null is also essentially a constant. return true; } else if (is_address_of2t(expr)) { return constant_propagation_reference(to_address_of2t(expr).ptr_obj); } else if (is_typecast2t(expr)) { return constant_propagation(to_typecast2t(expr).from); } else if (is_add2t(expr)) { forall_operands2(it, idx, expr) if(!constant_propagation(*it)) return false; return true; } else if (is_constant_array_of2t(expr))
expr2tc goto_symext::symex_mem( const bool is_malloc, const expr2tc &lhs, const sideeffect2t &code) { if (is_nil_expr(lhs)) return expr2tc(); // ignore // size type2tc type = code.alloctype; expr2tc size = code.size; bool size_is_one = false; if (is_nil_expr(size)) size_is_one=true; else { cur_state->rename(size); mp_integer i; if (is_constant_int2t(size) && to_constant_int2t(size).as_ulong() == 1) size_is_one = true; } if (is_nil_type(type)) type = char_type2(); else if (is_union_type(type)) { // Filter out creation of instantiated unions. They're now all byte arrays. size_is_one = false; type = char_type2(); } unsigned int &dynamic_counter = get_dynamic_counter(); dynamic_counter++; // value symbolt symbol; symbol.base_name = "dynamic_" + i2string(dynamic_counter) + (size_is_one ? "_value" : "_array"); symbol.name = "symex_dynamic::" + id2string(symbol.base_name); symbol.lvalue = true; typet renamedtype = ns.follow(migrate_type_back(type)); if(size_is_one) symbol.type=renamedtype; else { symbol.type=typet(typet::t_array); symbol.type.subtype()=renamedtype; symbol.type.size(migrate_expr_back(size)); } symbol.type.dynamic(true); symbol.mode="C"; new_context.add(symbol); type2tc new_type; migrate_type(symbol.type, new_type); address_of2tc rhs_addrof(get_empty_type(), expr2tc()); if(size_is_one) { rhs_addrof.get()->type = get_pointer_type(pointer_typet(symbol.type)); rhs_addrof.get()->ptr_obj = symbol2tc(new_type, symbol.name); } else { type2tc subtype; migrate_type(symbol.type.subtype(), subtype); expr2tc sym = symbol2tc(new_type, symbol.name); expr2tc idx_val = zero_ulong; expr2tc idx = index2tc(subtype, sym, idx_val); rhs_addrof.get()->type = get_pointer_type(pointer_typet(symbol.type.subtype())); rhs_addrof.get()->ptr_obj = idx; } expr2tc rhs = rhs_addrof; expr2tc ptr_rhs = rhs; if (!options.get_bool_option("force-malloc-success")) { symbol2tc null_sym(rhs->type, "NULL"); sideeffect2tc choice(get_bool_type(), expr2tc(), expr2tc(), std::vector<expr2tc>(), type2tc(), sideeffect2t::nondet); rhs = if2tc(rhs->type, choice, rhs, null_sym); replace_nondet(rhs); ptr_rhs = rhs; } if (rhs->type != lhs->type) rhs = typecast2tc(lhs->type, rhs); cur_state->rename(rhs); expr2tc rhs_copy(rhs); guardt guard; symex_assign_rec(lhs, rhs, guard); pointer_object2tc ptr_obj(pointer_type2(), ptr_rhs); track_new_pointer(ptr_obj, new_type); dynamic_memory.push_back(allocated_obj(rhs_copy, cur_state->guard, !is_malloc)); return rhs_addrof->ptr_obj; }
void goto_symext::symex_realloc(const expr2tc &lhs, const sideeffect2t &code) { expr2tc src_ptr = code.operand; expr2tc realloc_size = code.size; internal_deref_items.clear(); dereference2tc deref(get_empty_type(), src_ptr); dereference(deref, dereferencet::INTERNAL); // src_ptr is now invalidated. // Free the given pointer. This just uses the pointer object from the pointer // variable that's the argument to realloc. It also leads to pointer validity // checking, and checks that the offset is zero. code_free2tc fr(code.operand); symex_free(fr); // We now have a list of things to work on. Recurse into them, build a result, // and then switch between those results afterwards. // Result list is the address of the reallocated piece of data, and the guard. std::list<std::pair<expr2tc, expr2tc>> result_list; for(auto &item : internal_deref_items) { expr2tc guard = item.guard; cur_state->rename_address(item.object); cur_state->guard.guard_expr(guard); target->renumber(guard, item.object, realloc_size, cur_state->source); type2tc new_ptr = type2tc(new pointer_type2t(item.object->type)); address_of2tc addrof(new_ptr, item.object); result_list.emplace_back(addrof, item.guard); // Bump the realloc-numbering of the object. This ensures that, after // renaming, the address_of we just generated compares differently to // previous address_of's before the realloc. unsigned int cur_num = 0; if(cur_state->realloc_map.find(item.object) != cur_state->realloc_map.end()) { cur_num = cur_state->realloc_map[item.object]; } cur_num++; std::map<expr2tc, unsigned>::value_type v(item.object, cur_num); cur_state->realloc_map.insert(v); } // Rebuild a gigantic if-then-else chain from the result list. expr2tc result; if(result_list.size() == 0) { // Nothing happened; there was nothing, or only null, to point at. // In this case, just return right now and leave the pointer free. The // symex_free that occurred above should trigger a dereference failure. return; } result = expr2tc(); for(auto const &it : result_list) { if(is_nil_expr(result)) result = it.first; else result = if2tc(result->type, it.second, it.first, result); } // Install pointer modelling data into the relevant arrays. pointer_object2tc ptr_obj(pointer_type2(), result); track_new_pointer(ptr_obj, type2tc(), realloc_size); symex_assign(code_assign2tc(lhs, result), true); }
void goto_symext::symex_step(reachability_treet & art) { assert(!cur_state->call_stack.empty()); const goto_programt::instructiont &instruction = *cur_state->source.pc; // depth exceeded? { if (depth_limit != 0 && cur_state->depth > depth_limit) cur_state->guard.add(false_expr); cur_state->depth++; } // actually do instruction switch (instruction.type) { case SKIP: case LOCATION: // really ignore cur_state->source.pc++; break; case END_FUNCTION: symex_end_of_function(); // Potentially skip to run another function ptr target; if not, // continue if (!run_next_function_ptr_target(false)) cur_state->source.pc++; break; case GOTO: { expr2tc tmp(instruction.guard); replace_nondet(tmp); dereference(tmp, false); replace_dynamic_allocation(tmp); symex_goto(tmp); } break; case ASSUME: if (!cur_state->guard.is_false()) { expr2tc tmp = instruction.guard; replace_nondet(tmp); dereference(tmp, false); replace_dynamic_allocation(tmp); cur_state->rename(tmp); do_simplify(tmp); if (!is_true(tmp)) { expr2tc tmp2 = tmp; expr2tc tmp3 = tmp2; cur_state->guard.guard_expr(tmp2); assume(tmp2); // we also add it to the state guard cur_state->guard.add(tmp3); } } cur_state->source.pc++; break; case ASSERT: if (!cur_state->guard.is_false()) { if (!no_assertions || !cur_state->source.pc->location.user_provided() || deadlock_check) { std::string msg = cur_state->source.pc->location.comment().as_string(); if (msg == "") msg = "assertion"; expr2tc tmp = instruction.guard; replace_nondet(tmp); dereference(tmp, false); replace_dynamic_allocation(tmp); claim(tmp, msg); } } cur_state->source.pc++; break; case RETURN: if (!cur_state->guard.is_false()) { expr2tc thecode = instruction.code, assign; if (make_return_assignment(assign, thecode)) { goto_symext::symex_assign(assign); } symex_return(); } cur_state->source.pc++; break; case ASSIGN: if (!cur_state->guard.is_false()) { code_assign2tc deref_code = instruction.code; // XXX jmorse -- this is not fully symbolic. if (thrown_obj_map.find(cur_state->source.pc) != thrown_obj_map.end()) { symbol2tc thrown_obj = thrown_obj_map[cur_state->source.pc]; if (is_pointer_type(deref_code.get()->target.get()->type) && !is_pointer_type(thrown_obj.get()->type)) { expr2tc new_thrown_obj(new address_of2t(thrown_obj.get()->type, thrown_obj)); deref_code.get()->source = new_thrown_obj; } else deref_code.get()->source = thrown_obj; thrown_obj_map.erase(cur_state->source.pc); } replace_nondet(deref_code); code_assign2t &assign = to_code_assign2t(deref_code); dereference(assign.target, true); dereference(assign.source, false); replace_dynamic_allocation(deref_code); symex_assign(deref_code); } cur_state->source.pc++; break; case FUNCTION_CALL: { expr2tc deref_code = instruction.code; replace_nondet(deref_code); code_function_call2t &call = to_code_function_call2t(deref_code); if (!is_nil_expr(call.ret)) { dereference(call.ret, true); } replace_dynamic_allocation(deref_code); for (std::vector<expr2tc>::iterator it = call.operands.begin(); it != call.operands.end(); it++) if (!is_nil_expr(*it)) dereference(*it, false); // Always run intrinsics, whether guard is false or not. This is due to the // unfortunate circumstance where a thread starts with false guard due to // decision taken in another thread in this trace. In that case the // terminate intrinsic _has_ to run, or we explode. if (is_symbol2t(call.function)) { const irep_idt &id = to_symbol2t(call.function).thename; if (has_prefix(id.as_string(), "c::__ESBMC")) { cur_state->source.pc++; std::string name = id.as_string().substr(3); run_intrinsic(call, art, name); return; } else if (has_prefix(id.as_string(), "cpp::__ESBMC")) { cur_state->source.pc++; std::string name = id.as_string().substr(5); name = name.substr(0, name.find("(")); run_intrinsic(call, art, name); return; } } // Don't run a function call if the guard is false. if (!cur_state->guard.is_false()) { symex_function_call(deref_code); } else { cur_state->source.pc++; } } break; case OTHER: if (!cur_state->guard.is_false()) { symex_other(); } cur_state->source.pc++; break; case CATCH: symex_catch(); break; case THROW: if (!cur_state->guard.is_false()) { if(symex_throw()) cur_state->source.pc++; } else { cur_state->source.pc++; } break; case THROW_DECL: symex_throw_decl(); cur_state->source.pc++; break; case THROW_DECL_END: // When we reach THROW_DECL_END, we must clear any throw_decl if(stack_catch.size()) { // Get to the correct try (always the last one) goto_symex_statet::exceptiont* except=&stack_catch.top(); except->has_throw_decl=false; except->throw_list_set.clear(); } cur_state->source.pc++; break; default: std::cerr << "GOTO instruction type " << instruction.type; std::cerr << " not handled in goto_symext::symex_step" << std::endl; abort(); } }