void goto_checkt::invalidate(const exprt &lhs) { if(lhs.id()==ID_index) invalidate(to_index_expr(lhs).array()); else if(lhs.id()==ID_member) invalidate(to_member_expr(lhs).struct_op()); else if(lhs.id()==ID_symbol) { // clear all assertions about 'symbol' find_symbols_sett find_symbols_set; find_symbols_set.insert(to_symbol_expr(lhs).get_identifier()); for(assertionst::iterator it=assertions.begin(); it!=assertions.end(); ) // no it++ { assertionst::iterator next=it; next++; if(has_symbol(*it, find_symbols_set) || has_dereference(*it)) assertions.erase(it); it=next; } } else { // give up, clear all assertions.clear(); } }
static bool has_dereference(const exprt &expr) { if (expr.id() == "dereference") return true; else if (expr.id() == "index" && expr.op0().type().id() == "pointer") // This is an index of a pointer, which is a dereference return true; else if (expr.operands().size() > 0 && expr.op0().is_not_nil()) // Recurse through all subsequent source objects, which are always operand // zero. return has_dereference(expr.op0()); else return false; }
void goto_checkt::bounds_check(const exprt &expr, const guardt &guard) { if (options.get_bool_option("no-bounds-check")) return; if (expr.id() != "index") return; if (expr.operands().size() != 2) throw "index takes two operands"; // Don't bounds check the initial index of argv in the "main" function; it's // always correct, and just adds needless claims. In the past a "no bounds // check" attribute in old irep handled this. if (expr.op0().id_string() == "symbol" && expr.op0().identifier() == "c::argv'" && expr.op1().id_string() == "symbol" && expr.op1().identifier() == "c::argc'") return; if (expr.op0().id_string() == "symbol" && expr.op0().identifier() == "c::envp'" && expr.op1().id_string() == "symbol" && expr.op1().identifier() == "c::envp_size'") return; typet array_type = ns.follow(expr.op0().type()); if (array_type.id() == "pointer") return; // done by the pointer code else if (array_type.id() == "incomplete_array") { std::cerr << expr.pretty() << std::endl; throw "index got incomplete array"; } else if (!array_type.is_array()) throw "bounds check expected array type, got " + array_type.id_string(); // Otherwise, if there's a dereference in the array source, this bounds check // should be performed by the symex-time dereferencing code, as the base thing // being accessed may be anything. if (has_dereference(expr.op0())) return; std::string name = "array bounds violated: " + array_name(expr.op0()); const exprt &index = expr.op1(); if (index.type().id() != "unsignedbv") { // we undo typecasts to signedbv if (index.id() == "typecast" && index.operands().size() == 1 && index.op0().type().id() == "unsignedbv") { // ok } else { mp_integer i; if (!to_integer(index, i) && i >= 0) { // ok } else { exprt zero = gen_zero(index.type()); if (zero.is_nil()) throw "no zero constant of index type " + index.type().to_string(); exprt inequality(">=", bool_typet()); inequality.copy_to_operands(index, zero); add_guarded_claim(inequality, name + " lower bound", "array bounds", expr.find_location(), guard); } } } { if (array_type.size_irep().is_nil()) throw "index array operand of wrong type"; const exprt &size = (const exprt &) array_type.size_irep(); if (size.id() != "infinity") { exprt inequality("<", bool_typet()); inequality.copy_to_operands(index, size); // typecast size if (inequality.op1().type() != inequality.op0().type()) inequality.op1().make_typecast(inequality.op0().type()); add_guarded_claim(inequality, name + " upper bound", "array bounds", expr.find_location(), guard); } } }