예제 #1
0
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);
}
예제 #2
0
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);
}
예제 #3
0
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);
}
예제 #4
0
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;
}
예제 #5
0
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);
}
예제 #6
0
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();
  }
}