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, false, true); address_of2tc addrof(code.operand->type, tmp); pointer_offset2tc ptr_offs(pointer_type2(), addrof); equality2tc eq(ptr_offs, zero_ulong); claim(eq, "Operand of free must have zero pointer offset"); // Clear the alloc bit, and set the deallocated bit. guardt guard; type2tc sym_type = type2tc(new array_type2t(get_bool_type(), expr2tc(), true)); pointer_object2tc ptr_obj(pointer_type2(), code.operand); symbol2tc dealloc_sym(sym_type, deallocd_arr_name); index2tc dealloc_index_expr(get_bool_type(), dealloc_sym, ptr_obj); expr2tc truth = true_expr; symex_assign_rec(dealloc_index_expr, truth, guard); symbol2tc valid_sym(sym_type, valid_ptr_arr_name); index2tc valid_index_expr(get_bool_type(), valid_sym, ptr_obj); expr2tc falsity = false_expr; symex_assign_rec(valid_index_expr, falsity, guard); }
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); }
expr2tc abstract_domain_baset::get_return_lhs(locationt to) const { // get predecessor of "to" to--; if(to->is_end_function()) return expr2tc(); // must be the function call assert(to->is_function_call()); const code_function_call2t &code = to_code_function_call2t(to->code); return code.ret; }
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, false, true); // 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, false, false, true); // 'internal' dereference for (const auto &item : internal_deref_items) { guardt g = cur_state->guard; g.add(item.guard); expr2tc offset = item.offset; expr2tc eq = equality2tc(offset, zero_ulong); g.guard_expr(eq); claim(eq, "Operand of free must have zero pointer offset"); } // Clear the alloc bit, and set the deallocated bit. guardt guard; type2tc sym_type = type2tc(new array_type2t(get_bool_type(), expr2tc(), true)); pointer_object2tc ptr_obj(pointer_type2(), code.operand); symbol2tc dealloc_sym(sym_type, deallocd_arr_name); index2tc dealloc_index_expr(get_bool_type(), dealloc_sym, ptr_obj); expr2tc truth = true_expr; symex_assign_rec(dealloc_index_expr, truth, guard); symbol2tc valid_sym(sym_type, valid_ptr_arr_name); index2tc valid_index_expr(get_bool_type(), valid_sym, ptr_obj); expr2tc falsity = false_expr; symex_assign_rec(valid_index_expr, falsity, guard); }
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_inlinet::expand_function_call( goto_programt &dest, goto_programt::targett &target, const exprt &lhs, const exprt &function, const exprt::operandst &arguments, const exprt &constrain, bool full) { // look it up if(function.id()!="symbol") { err_location(function); throw "function_call expects symbol as function operand, " "but got `"+function.id_string()+"'"; } const irep_idt &identifier=function.identifier(); // see if we are already expanding it if(recursion_set.find(identifier)!=recursion_set.end()) { if(!full) { target++; return; // simply ignore, we don't do full inlining, it's ok } // it's really recursive. Uh. Buh. Give up. err_location(function); warning("recursion is ignored"); target->make_skip(); target++; return; } goto_functionst::function_mapt::iterator m_it= goto_functions.function_map.find(identifier); if(m_it==goto_functions.function_map.end()) { err_location(function); str << "failed to find function `" << identifier << "'"; throw 0; } goto_functiont &f=m_it->second; // see if we need to inline this if(!full) { if(!f.body_available || (!f.is_inlined() && f.body.instructions.size() > smallfunc_limit)) { target++; return; } } if(f.body_available) { inlined_funcs.insert(identifier.as_string()); for (std::set<std::string>::const_iterator it2 = f.inlined_funcs.begin(); it2 != f.inlined_funcs.end(); it2++) { inlined_funcs.insert(*it2); } recursion_sett::iterator recursion_it= recursion_set.insert(identifier).first; goto_programt tmp2; tmp2.copy_from(f.body); assert(tmp2.instructions.back().is_end_function()); tmp2.instructions.back().type=LOCATION; replace_return(tmp2, lhs, constrain); goto_programt tmp; parameter_assignments(tmp2.instructions.front().location, f.type, arguments, tmp); tmp.destructive_append(tmp2); // set local variables Forall_goto_program_instructions(it, tmp) it->local_variables.insert(target->local_variables.begin(), target->local_variables.end()); if(f.type.hide()) { const locationt &new_location=function.find_location(); Forall_goto_program_instructions(it, tmp) { if(new_location.is_not_nil()) { // can't just copy, e.g., due to comments field it->location.id(""); // not NIL it->location.set_file(new_location.get_file()); it->location.set_line(new_location.get_line()); it->location.set_column(new_location.get_column()); it->location.set_function(new_location.get_function()); } } } // do this recursively goto_inline_rec(tmp, full); // set up location instruction for function call target->type=LOCATION; target->code = expr2tc(); goto_programt::targett next_target(target); next_target++; dest.instructions.splice(next_target, tmp.instructions); target=next_target; recursion_set.erase(recursion_it); }
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_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); }