CBMC
may_alias.cpp
Go to the documentation of this file.
1 /*******************************************************************\
2 
3 Module: Solver
4 
5 Author: Daniel Kroening, dkr@amazon.com
6 
7 \*******************************************************************/
8 
11 
12 #include "may_alias.h"
13 
14 #include <util/c_types.h>
15 #include <util/namespace.h>
16 #include <util/pointer_expr.h>
17 #include <util/std_expr.h>
18 #include <util/symbol.h>
19 
20 bool permitted_by_strict_aliasing(const typet &a, const typet &b)
21 {
22  // C99; ISO/IEC 9899:1999 6.5/7
23  if(a == b)
24  return true;
25  else if(a == signed_char_type() || a == unsigned_char_type())
26  return true; // char * can alias anyting
27  else if(b == signed_char_type() || b == unsigned_char_type())
28  return true; // char * can alias anyting
29  else if(
30  (a.id() == ID_unsignedbv || a.id() == ID_signedbv) &&
31  (b.id() == ID_unsignedbv || b.id() == ID_signedbv))
32  {
33  // signed/unsigned of same width can alias
35  }
36  else if(a.id() == ID_empty)
37  return true; // void * can alias any pointer
38  else if(b.id() == ID_empty)
39  return true; // void * can alias any pointer
40  else if(
41  a.id() == ID_pointer && to_pointer_type(a).base_type().id() == ID_empty)
42  return true; // void * can alias any pointer
43  else if(
44  b.id() == ID_pointer && to_pointer_type(b).base_type().id() == ID_empty)
45  return true; // void * can alias any pointer
46  else if(
47  a.id() == ID_pointer && to_pointer_type(a).base_type().id() == ID_pointer &&
48  to_pointer_type(to_pointer_type(a).base_type()).base_type().id() ==
49  ID_empty)
50  return true; // void * can alias any pointer
51  else if(
52  b.id() == ID_pointer && to_pointer_type(b).base_type().id() == ID_pointer &&
53  to_pointer_type(to_pointer_type(b).base_type()).base_type().id() ==
54  ID_empty)
55  return true; // void * can alias any pointer
56  else
57  {
58  return false;
59  }
60 }
61 
62 bool is_object_field_element(const exprt &expr)
63 {
64  if(expr.id() == ID_object_address)
65  return true;
66  else if(expr.id() == ID_element_address)
68  else if(expr.id() == ID_field_address)
70  else
71  return false;
72 }
73 
74 bool prefix_of(const typet &a, const typet &b, const namespacet &ns)
75 {
76  if(a == b)
77  return true;
78 
79  if(a.id() == ID_struct_tag)
80  return prefix_of(ns.follow_tag(to_struct_tag_type(a)), b, ns);
81 
82  if(b.id() == ID_struct_tag)
83  return prefix_of(a, ns.follow_tag(to_struct_tag_type(b)), ns);
84 
85  if(a.id() != ID_struct || b.id() != ID_struct)
86  return false;
87 
88  const auto &a_struct = to_struct_type(a);
89  const auto &b_struct = to_struct_type(b);
90 
91  return a_struct.is_prefix_of(b_struct) || b_struct.is_prefix_of(a_struct);
92 }
93 
94 static std::optional<object_address_exprt> find_object(const exprt &expr)
95 {
96  if(expr.id() == ID_object_address)
97  return to_object_address_expr(expr);
98  else if(expr.id() == ID_field_address)
99  return find_object(to_field_address_expr(expr).base());
100  else if(expr.id() == ID_element_address)
101  return find_object(to_element_address_expr(expr).base());
102  else
103  return {};
104 }
105 
106 // Is 'expr' on the stack and it's address is not taken?
108  const exprt &expr,
109  const std::unordered_set<symbol_exprt, irep_hash> &address_taken,
110  const namespacet &ns)
111 {
112  auto object = find_object(expr);
113  if(object.has_value())
114  {
115  auto symbol_expr = object->object_expr();
116  auto identifier = symbol_expr.get_identifier();
117  if(identifier.starts_with("va_arg::"))
118  return true; // on the stack, and might alias
119  else if(identifier.starts_with("var_args::"))
120  return false; // on the stack -- can take address?
121  else if(identifier.starts_with("va_args::"))
122  return false; // on the stack -- can take address?
123  else if(identifier.starts_with("va_arg_array::"))
124  return false; // on the stack -- can take address?
125  else if(identifier.starts_with("old::"))
126  return true; // on the stack, but can't take address
127  else if(identifier == "return_value")
128  return true; // on the stack, but can't take address
129  const auto &symbol = ns.lookup(symbol_expr);
130  return !symbol.is_static_lifetime &&
131  address_taken.find(symbol_expr) == address_taken.end();
132  }
133  else
134  return false;
135 }
136 
138 {
139  if(
140  src.id() == ID_typecast &&
141  to_typecast_expr(src).op().type().id() == ID_pointer)
142  return drop_pointer_typecasts(to_typecast_expr(src).op());
143  else
144  return src;
145 }
146 
147 std::optional<exprt>
148 same_address(const exprt &a, const exprt &b, const namespacet &ns)
149 {
150  static const auto true_expr = true_exprt();
151  static const auto false_expr = false_exprt();
152 
153  // syntactically the same?
155  return true_expr;
156 
157  // a and b are both object/field/element?
159  {
160  if(a.id() == ID_object_address && b.id() == ID_object_address)
161  {
162  if(
163  to_object_address_expr(a).object_identifier() ==
164  to_object_address_expr(b).object_identifier())
165  {
166  return true_expr; // the same
167  }
168  else
169  return false_expr; // different
170  }
171  else if(a.id() == ID_element_address && b.id() == ID_element_address)
172  {
173  const auto &a_element_address = to_element_address_expr(a);
174  const auto &b_element_address = to_element_address_expr(b);
175 
176  // rec. call
177  auto base_same_address =
178  same_address(a_element_address.base(), b_element_address.base(), ns);
179 
180  CHECK_RETURN(base_same_address.has_value());
181 
182  if(base_same_address->is_false())
183  return false_expr;
184  else
185  {
186  return and_exprt(
187  *base_same_address,
188  equal_exprt(a_element_address.index(), b_element_address.index()));
189  }
190  }
191  else if(a.id() == ID_field_address && b.id() == ID_field_address)
192  {
193  const auto &a_field_address = to_field_address_expr(a);
194  const auto &b_field_address = to_field_address_expr(b);
195 
196  // structs can't alias unless one is a prefix of the other
197  if(!prefix_of(
198  a_field_address.type().base_type(),
199  b_field_address.type().base_type(),
200  ns))
201  {
202  return false_expr;
203  }
204 
205  if(a_field_address.component_name() == b_field_address.component_name())
206  {
207  // rec. call
208  return same_address(a_field_address.base(), b_field_address.base(), ns);
209  }
210  else
211  return false_expr;
212  }
213  else
214  return false_expr;
215  }
216 
217  // don't know
218  return {};
219 }
220 
221 std::optional<exprt> may_alias(
222  const exprt &a,
223  const exprt &b,
224  const std::unordered_set<symbol_exprt, irep_hash> &address_taken,
225  const namespacet &ns)
226 {
227  PRECONDITION(a.type().id() == ID_pointer);
228  PRECONDITION(b.type().id() == ID_pointer);
229 
230  static const auto true_expr = true_exprt();
231  static const auto false_expr = false_exprt();
232 
233  // syntactically the same?
235  return true_expr;
236 
237  // consider the strict aliasing rule
238  const auto &a_base = to_pointer_type(a.type()).base_type();
239  const auto &b_base = to_pointer_type(b.type()).base_type();
240 
241  if(!permitted_by_strict_aliasing(a_base, b_base))
242  {
243  // The object is known to be different, because of strict aliasing.
244  return false_expr;
245  }
246 
247  // a and b the same addresses?
248  auto same_address_opt = same_address(a, b, ns);
249  if(same_address_opt.has_value())
250  return same_address_opt;
251 
252  // is one of them stack-allocated and it's address is not taken?
254  return false_expr; // can't alias
255 
257  return false_expr; // can't alias
258 
259  // we don't know
260  return {};
261 }
std::unordered_set< symbol_exprt, irep_hash > address_taken(const std::vector< exprt > &src)
const bitvector_typet & to_bitvector_type(const typet &type)
Cast a typet to a bitvector_typet.
signedbv_typet signed_char_type()
Definition: c_types.cpp:134
unsignedbv_typet unsigned_char_type()
Definition: c_types.cpp:127
Boolean AND.
Definition: std_expr.h:2120
std::size_t get_width() const
Definition: std_types.h:920
Equality.
Definition: std_expr.h:1361
Base class for all expressions.
Definition: expr.h:56
typet & type()
Return the type of the expression.
Definition: expr.h:84
The Boolean constant false.
Definition: std_expr.h:3064
const irep_idt & id() const
Definition: irep.h:384
const union_typet & follow_tag(const union_tag_typet &) const
Follow type tag of union type.
Definition: namespace.cpp:63
A namespacet is essentially one or two symbol tables bound together, to allow for symbol lookups in t...
Definition: namespace.h:94
bool lookup(const irep_idt &name, const symbolt *&symbol) const override
See documentation for namespace_baset::lookup().
Definition: namespace.cpp:148
const typet & base_type() const
The type of the data what we point to.
Definition: pointer_expr.h:35
The Boolean constant true.
Definition: std_expr.h:3055
The type of an expression, extends irept.
Definition: type.h:29
bool permitted_by_strict_aliasing(const typet &a, const typet &b)
Definition: may_alias.cpp:20
bool prefix_of(const typet &a, const typet &b, const namespacet &ns)
Definition: may_alias.cpp:74
std::optional< exprt > may_alias(const exprt &a, const exprt &b, const std::unordered_set< symbol_exprt, irep_hash > &address_taken, const namespacet &ns)
Definition: may_alias.cpp:221
std::optional< exprt > same_address(const exprt &a, const exprt &b, const namespacet &ns)
Definition: may_alias.cpp:148
static exprt drop_pointer_typecasts(exprt src)
Definition: may_alias.cpp:137
static std::optional< object_address_exprt > find_object(const exprt &expr)
Definition: may_alias.cpp:94
bool is_object_field_element(const exprt &expr)
Definition: may_alias.cpp:62
bool stack_and_not_dirty(const exprt &expr, const std::unordered_set< symbol_exprt, irep_hash > &address_taken, const namespacet &ns)
Definition: may_alias.cpp:107
May Alias.
API to expression classes for Pointers.
const field_address_exprt & to_field_address_expr(const exprt &expr)
Cast an exprt to an field_address_exprt.
Definition: pointer_expr.h:727
const pointer_typet & to_pointer_type(const typet &type)
Cast a typet to a pointer_typet.
Definition: pointer_expr.h:93
const object_address_exprt & to_object_address_expr(const exprt &expr)
Cast an exprt to an object_address_exprt.
Definition: pointer_expr.h:643
const element_address_exprt & to_element_address_expr(const exprt &expr)
Cast an exprt to an element_address_exprt.
Definition: pointer_expr.h:814
#define CHECK_RETURN(CONDITION)
Definition: invariant.h:495
#define PRECONDITION(CONDITION)
Definition: invariant.h:463
API to expression classes.
const typecast_exprt & to_typecast_expr(const exprt &expr)
Cast an exprt to a typecast_exprt.
Definition: std_expr.h:2102
const struct_typet & to_struct_type(const typet &type)
Cast a typet to a struct_typet.
Definition: std_types.h:308
const struct_tag_typet & to_struct_tag_type(const typet &type)
Cast a typet to a struct_tag_typet.
Definition: std_types.h:518
Symbol table entry.