void goto_symext::clean_expr( exprt &expr, statet &state, const bool write) { rewrite_union(expr, ns); replace_nondet(expr); dereference(expr, state, write); replace_array_equal(expr); adjust_float_expressions(expr, ns); }
void goto_symext::replace_nondet(exprt &expr) { if(expr.id()==ID_side_effect && expr.get(ID_statement)==ID_nondet) { exprt new_expr(ID_nondet_symbol, expr.type()); new_expr.set(ID_identifier, "symex::nondet"+std::to_string(nondet_count++)); new_expr.add_source_location()=expr.source_location(); expr.swap(new_expr); } else Forall_operands(it, expr) replace_nondet(*it); }
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_other() { const goto_programt::instructiont &instruction = *cur_state->source.pc; expr2tc code2 = instruction.code; if(is_code_expression2t(code2)) { // Represents an expression that gets evaluated, but does not have any // other effect on execution, i.e. doesn't contain a call or assignment. // This can, however, cause the program to fail if it dereferences an // invalid pointer. Therefore, dereference it. const code_expression2t &expr = to_code_expression2t(code2); expr2tc operand = expr.operand; dereference(operand, dereferencet::READ); } else if(is_code_cpp_del_array2t(code2) || is_code_cpp_delete2t(code2)) { expr2tc deref_code(code2); replace_dynamic_allocation(deref_code); replace_nondet(deref_code); dereference(deref_code, dereferencet::READ); symex_cpp_delete(deref_code); } else if(is_code_free2t(code2)) { symex_free(code2); } else if(is_code_printf2t(code2)) { replace_dynamic_allocation(code2); replace_nondet(code2); dereference(code2, dereferencet::READ); symex_printf(expr2tc(), code2); } else if(is_code_decl2t(code2)) { replace_dynamic_allocation(code2); replace_nondet(code2); dereference(code2, dereferencet::READ); const code_decl2t &decl_code = to_code_decl2t(code2); // just do the L2 renaming to preseve locality const irep_idt &identifier = decl_code.value; // Generate dummy symbol as a vehicle for renaming. symbol2tc l1_sym(get_empty_type(), identifier); cur_state->top().level1.get_ident_name(l1_sym); symbol2t &l1_symbol = to_symbol2t(l1_sym); // increase the frame if we have seen this declaration before while(cur_state->top().declaration_history.find( renaming::level2t::name_record(l1_symbol)) != cur_state->top().declaration_history.end()) { unsigned &index = cur_state->variable_instance_nums[identifier]; cur_state->top().level1.rename(l1_sym, ++index); l1_symbol.level1_num = index; } renaming::level2t::name_record tmp_name(l1_symbol); cur_state->top().declaration_history.insert(tmp_name); cur_state->top().local_variables.insert(tmp_name); // seen it before? // it should get a fresh value if(cur_state->level2.current_number(l1_sym) != 0) { // Dummy assignment - blank constant value isn't considered for const // propagation, variable number will be bumped to result in a new free // variable. Invalidates l1_symbol reference? cur_state->level2.make_assignment(l1_sym, expr2tc(), expr2tc()); } } else if(is_code_asm2t(code2)) { // Assembly statement -> do nothing. return; } else throw "goto_symext: unexpected statement: " + get_expr_id(code2); }
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(); } }