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); }
void goto_symext::symex_free(const expr2tc &expr) { const code_free2t &code = to_code_free2t(expr); // Trigger 'free'-mode dereference of this pointer. Should generate various // dereference failure callbacks. expr2tc tmp = code.operand; dereference(tmp, dereferencet::FREE); // Don't rely on the output of dereference in free mode; instead fetch all // the internal dereference state for pointed at objects, and creates claims // that if pointed at, their offset is zero. internal_deref_items.clear(); tmp = code.operand; // Create temporary, dummy, dereference tmp = dereference2tc(get_uint8_type(), tmp); dereference(tmp, dereferencet::INTERNAL); // Only add assertions to check pointer offset if pointer check is enabled if(!options.get_bool_option("no-pointer-check")) { for(auto const &item : internal_deref_items) { guardt g = cur_state->guard; g.add(item.guard); expr2tc offset = item.offset; expr2tc eq = equality2tc(offset, gen_ulong(0)); g.guard_expr(eq); claim(eq, "Operand of free must have zero pointer offset"); } } // Clear the alloc bit, and set the deallocated bit. type2tc sym_type = type2tc(new array_type2t(get_bool_type(), expr2tc(), true)); expr2tc ptr_obj = pointer_object2tc(pointer_type2(), code.operand); dereference(ptr_obj, dereferencet::READ); symbol2tc dealloc_sym(sym_type, deallocd_arr_name); index2tc dealloc_index_expr(get_bool_type(), dealloc_sym, ptr_obj); expr2tc truth = gen_true_expr(); symex_assign(code_assign2tc(dealloc_index_expr, truth), true); symbol2tc valid_sym(sym_type, valid_ptr_arr_name); index2tc valid_index_expr(get_bool_type(), valid_sym, ptr_obj); expr2tc falsity = gen_false_expr(); symex_assign(code_assign2tc(valid_index_expr, falsity), true); }
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); }
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 = std::string("symex_dynamic::") + (!is_malloc ? "alloca::" : "") + 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->type = get_pointer_type(pointer_typet(symbol.type)); rhs_addrof->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 = gen_ulong(0); expr2tc idx = index2tc(subtype, sym, idx_val); rhs_addrof->type = get_pointer_type(pointer_typet(symbol.type.subtype())); rhs_addrof->ptr_obj = idx; } expr2tc rhs = rhs_addrof; expr2tc ptr_rhs = rhs; guardt alloc_guard = cur_state->guard; 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); replace_nondet(choice); rhs = if2tc(rhs->type, choice, rhs, null_sym); alloc_guard.add(choice); ptr_rhs = rhs; } if(rhs->type != lhs->type) rhs = typecast2tc(lhs->type, rhs); cur_state->rename(rhs); expr2tc rhs_copy(rhs); symex_assign(code_assign2tc(lhs, rhs), true); pointer_object2tc ptr_obj(pointer_type2(), ptr_rhs); track_new_pointer(ptr_obj, new_type); dynamic_memory.emplace_back( rhs_copy, alloc_guard, !is_malloc, symbol.base_name.as_string()); return rhs_addrof->ptr_obj; }
void goto_symext::symex_other( const goto_functionst &goto_functions, statet &state) { const goto_programt::instructiont &instruction=*state.source.pc; const codet &code=to_code(instruction.code); const irep_idt &statement=code.get_statement(); if(statement==ID_expression) { // ignore } else if(statement==ID_cpp_delete || statement=="cpp_delete[]") { codet clean_code=code; clean_expr(clean_code, state, false); symex_cpp_delete(state, clean_code); } else if(statement==ID_free) { // ignore } else if(statement==ID_printf) { codet clean_code=code; clean_expr(clean_code, state, false); symex_printf(state, nil_exprt(), clean_code); } else if(statement==ID_input) { codet clean_code(code); clean_expr(clean_code, state, false); symex_input(state, clean_code); } else if(statement==ID_output) { codet clean_code(code); clean_expr(clean_code, state, false); symex_output(state, clean_code); } else if(statement==ID_decl) { assert(false); // see symex_decl.cpp } else if(statement==ID_nondet) { // like skip } else if(statement==ID_asm) { // we ignore this for now } else if(statement==ID_array_copy) { assert(code.operands().size()==2); codet clean_code(code); // we need to add dereferencing for both operands dereference_exprt d0, d1; d0.op0()=code.op0(); d0.type()=code.op0().type().subtype(); d1.op0()=code.op1(); d1.type()=code.op1().type().subtype(); clean_code.op0()=d0; clean_code.op1()=d1; clean_expr(clean_code.op0(), state, true); clean_expr(clean_code.op1(), state, false); process_array_expr(clean_code.op0()); clean_expr(clean_code.op0(), state, true); process_array_expr(clean_code.op1()); clean_expr(clean_code.op1(), state, false); if(!base_type_eq(clean_code.op0().type(), clean_code.op1().type(), ns)) { byte_extract_exprt be(byte_extract_id()); be.type()=clean_code.op0().type(); be.op()=clean_code.op1(); be.offset()=from_integer(0, index_type()); clean_code.op1()=be; } code_assignt assignment; assignment.lhs()=clean_code.op0(); assignment.rhs()=clean_code.op1(); symex_assign(state, assignment); } else if(statement==ID_array_set) { assert(code.operands().size()==2); codet clean_code(code); // we need to add dereferencing for the first operand dereference_exprt d0; d0.op0()=code.op0(); d0.type()=code.op0().type().subtype(); clean_code.op0()=d0; clean_expr(clean_code.op0(), state, true); clean_expr(clean_code.op1(), state, false); process_array_expr(clean_code.op0()); clean_expr(clean_code.op0(), state, true); const typet &op0_type=ns.follow(clean_code.op0().type()); if(op0_type.id()!=ID_array) throw "array_set expects array operand"; const array_typet &array_type= to_array_type(op0_type); if(!base_type_eq(array_type.subtype(), clean_code.op1().type(), ns)) clean_code.op1().make_typecast(array_type.subtype()); code_assignt assignment; assignment.lhs()=clean_code.op0(); assignment.rhs()=array_of_exprt(clean_code.op1(), array_type); symex_assign(state, assignment); } else if(statement==ID_user_specified_predicate || statement==ID_user_specified_parameter_predicates || statement==ID_user_specified_return_predicates) { // like skip } else if(statement==ID_fence) { target.memory_barrier(state.guard.as_expr(), state.source); } else throw "unexpected statement: "+id2string(statement); }
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(); } }