CBMC
remove_exceptions.cpp
Go to the documentation of this file.
1 /*******************************************************************\
2 
3 Module: Remove exception handling
4 
5 Author: Cristina David
6 
7 Date: December 2016
8 
9 \*******************************************************************/
10 
13 
14 #include "remove_exceptions.h"
15 #include "remove_instanceof.h"
16 
17 #ifdef DEBUG
18 #include <iostream>
19 #endif
20 
21 #include <util/c_types.h>
22 #include <util/pointer_expr.h>
23 #include <util/std_code.h>
24 
27 
29 
31 
32 #include "java_expr.h"
33 #include "java_types.h"
34 
82 {
83  typedef std::vector<std::pair<
85  typedef std::vector<catch_handlerst> stack_catcht;
86 
87 public:
88  typedef std::function<bool(const irep_idt &)> function_may_throwt;
89 
91  symbol_table_baset &_symbol_table,
92  const class_hierarchyt *_class_hierarchy,
93  function_may_throwt _function_may_throw,
94  bool _remove_added_instanceof,
95  message_handlert &_message_handler)
96  : symbol_table(_symbol_table),
97  class_hierarchy(_class_hierarchy),
98  function_may_throw(std::move(_function_may_throw)),
99  remove_added_instanceof(_remove_added_instanceof),
100  message_handler(_message_handler)
101  {
103  {
104  INVARIANT(
105  class_hierarchy != nullptr,
106  "remove_exceptions needs a class hierarchy to remove instanceof "
107  "statements (either supply one, or don't use REMOVE_ADDED_INSTANCEOF)");
108  }
109  }
110 
111  void operator()(goto_functionst &goto_functions);
112  void
113  operator()(const irep_idt &function_identifier, goto_programt &goto_program);
114 
115 protected:
121 
123  {
124  DID_NOTHING,
127  };
128 
130 
131  bool function_or_callees_may_throw(const goto_programt &) const;
132 
134  goto_programt &goto_program,
135  const goto_programt::targett &,
136  bool may_catch);
137 
139  const remove_exceptionst::stack_catcht &stack_catch,
140  goto_programt &goto_program,
141  std::size_t &universal_try,
142  std::size_t &universal_catch);
143 
145  const irep_idt &function_identifier,
146  goto_programt &goto_program,
147  const goto_programt::targett &instr_it,
148  const stack_catcht &stack_catch,
149  const std::vector<symbol_exprt> &locals);
150 
151  bool instrument_throw(
152  const irep_idt &function_identifier,
153  goto_programt &goto_program,
154  const goto_programt::targett &,
155  const stack_catcht &,
156  const std::vector<symbol_exprt> &);
157 
159  const irep_idt &function_identifier,
160  goto_programt &goto_program,
161  const goto_programt::targett &,
162  const stack_catcht &,
163  const std::vector<symbol_exprt> &);
164 
166  const irep_idt &function_identifier,
167  goto_programt &goto_program);
168 };
169 
173 {
174  const symbolt *existing_symbol =
176  INVARIANT(
177  existing_symbol != nullptr,
178  "Java frontend should have created @inflight_exception variable");
179  return existing_symbol->symbol_expr();
180 }
181 
188  const goto_programt &goto_program) const
189 {
190  for(const auto &instruction : goto_program.instructions)
191  {
192  if(instruction.is_throw())
193  {
194  return true;
195  }
196 
197  if(instruction.is_function_call())
198  {
199  const exprt &function_expr = instruction.call_function();
201  function_expr.id()==ID_symbol,
202  "identifier expected to be a symbol");
203  const irep_idt &function_name=
204  to_symbol_expr(function_expr).get_identifier();
205  if(function_may_throw(function_name))
206  return true;
207  }
208  }
209 
210  return false;
211 }
212 
224  goto_programt &goto_program,
225  const goto_programt::targett &instr_it,
226  bool may_catch)
227 {
228  PRECONDITION(instr_it->type() == CATCH);
229 
230  if(may_catch)
231  {
232  // retrieve the exception variable
233  const exprt &thrown_exception_local =
234  to_code_landingpad(instr_it->code()).catch_expr();
235 
236  const symbol_exprt thrown_global_symbol=
238  // next we reset the exceptional return to NULL
240 
241  // add the assignment @inflight_exception = NULL
242  goto_program.insert_after(
243  instr_it,
245  code_assignt(thrown_global_symbol, null_voidptr),
246  instr_it->source_location()));
247 
248  // add the assignment exc = @inflight_exception (before the null assignment)
249  goto_program.insert_after(
250  instr_it,
252  code_assignt(
253  thrown_exception_local,
254  typecast_exprt(thrown_global_symbol, thrown_exception_local.type())),
255  instr_it->source_location()));
256  }
257 
258  instr_it->turn_into_skip();
259 }
260 
283  const remove_exceptionst::stack_catcht &stack_catch,
284  goto_programt &goto_program,
285  std::size_t &universal_try,
286  std::size_t &universal_catch)
287 {
288  for(std::size_t i=stack_catch.size(); i>0;)
289  {
290  i--;
291  for(std::size_t j=0; j<stack_catch[i].size(); ++j)
292  {
293  if(stack_catch[i][j].first.empty())
294  {
295  // Record the position of the default behaviour as any further catches
296  // will not capture the throw
297  universal_try=i;
298  universal_catch=j;
299 
300  // Universal handler. Highest on the stack takes
301  // precedence, so overwrite any we've already seen:
302  return stack_catch[i][j].second;
303  }
304  }
305  }
306  // Unless we have a universal exception handler, jump to end of function
307  return goto_program.get_end_function();
308 }
309 
321  const irep_idt &function_identifier,
322  goto_programt &goto_program,
323  const goto_programt::targett &instr_it,
324  const remove_exceptionst::stack_catcht &stack_catch,
325  const std::vector<symbol_exprt> &locals)
326 {
327  // Jump to the universal handler or function end, as appropriate.
328  // This will appear after the GOTO-based dynamic dispatch below
329  goto_programt::targett default_dispatch=goto_program.insert_after(instr_it);
330 
331  // find the symbol corresponding to the caught exceptions
332  symbol_exprt exc_thrown =
334 
335  std::size_t default_try=0;
336  std::size_t default_catch=(!stack_catch.empty()) ? stack_catch[0].size() : 0;
337 
338  goto_programt::targett default_target=
339  find_universal_exception(stack_catch, goto_program,
340  default_try, default_catch);
341 
342  // add GOTOs implementing the dynamic dispatch of the
343  // exception handlers.
344  // The first loop is in forward order because the insertion reverses the order
345  // we note that try1{ try2 {} catch2c {} catch2d {}} catch1a() {} catch1b{}
346  // must catch in the following order: 2c 2d 1a 1b hence the numerical index
347  // is reversed whereas the letter ordering remains the same.
348  for(std::size_t i=default_try; i<stack_catch.size(); i++)
349  {
350  for(std::size_t j=(i==default_try) ? default_catch : stack_catch[i].size();
351  j>0;)
352  {
353  j--;
354  goto_programt::targett new_state_pc=
355  stack_catch[i][j].second;
356  if(!stack_catch[i][j].first.empty())
357  {
358  // use instanceof to check that this is the correct handler
359  struct_tag_typet type(stack_catch[i][j].first);
360 
361  java_instanceof_exprt check(exc_thrown, type);
362 
363  // Normal exception handler, make an instanceof check.
364  goto_programt::targett t_exc = goto_program.insert_after(
365  instr_it,
367  new_state_pc, check, instr_it->source_location()));
368 
370  {
372  function_identifier,
373  t_exc,
374  goto_program,
375  symbol_table,
378  }
379  }
380  }
381  }
382 
383  *default_dispatch = goto_programt::make_goto(
384  default_target, true_exprt(), instr_it->source_location());
385 
386  // add dead instructions
387  for(const auto &local : locals)
388  {
389  goto_program.insert_after(
390  instr_it, goto_programt::make_dead(local, instr_it->source_location()));
391  }
392 }
393 
397  const irep_idt &function_identifier,
398  goto_programt &goto_program,
399  const goto_programt::targett &instr_it,
400  const remove_exceptionst::stack_catcht &stack_catch,
401  const std::vector<symbol_exprt> &locals)
402 {
403  PRECONDITION(instr_it->type() == THROW);
404 
405  const exprt &exc_expr =
407 
409  function_identifier, goto_program, instr_it, stack_catch, locals);
410 
411  // find the symbol where the thrown exception should be stored:
412  symbol_exprt exc_thrown =
414 
415  // now turn the `throw' into an assignment with the appropriate cast
416  *instr_it = goto_programt::make_assignment(
417  exc_thrown,
418  typecast_exprt(exc_expr, exc_thrown.type()),
419  instr_it->source_location());
420 
421  return true;
422 }
423 
428  const irep_idt &function_identifier,
429  goto_programt &goto_program,
430  const goto_programt::targett &instr_it,
431  const stack_catcht &stack_catch,
432  const std::vector<symbol_exprt> &locals)
433 {
434  PRECONDITION(instr_it->type() == FUNCTION_CALL);
435 
436  // save the address of the next instruction
437  goto_programt::targett next_it=instr_it;
438  next_it++;
439 
440  const auto &function = instr_it->call_function();
441 
443  function.id() == ID_symbol, "function call expected to be a symbol");
444  const irep_idt &callee_id = to_symbol_expr(function).get_identifier();
445 
446  if(function_may_throw(callee_id))
447  {
448  equal_exprt no_exception_currently_in_flight(
451 
452  if(symbol_table.lookup_ref(callee_id).type.get_bool(ID_C_must_not_throw))
453  {
454  // Function is annotated must-not-throw, but we can't prove that here.
455  // Insert an ASSUME(@inflight_exception == null):
456  goto_program.insert_after(
457  instr_it,
458  goto_programt::make_assumption(no_exception_currently_in_flight));
459 
461  }
462  else
463  {
465  function_identifier, goto_program, instr_it, stack_catch, locals);
466 
467  // add a null check (so that instanceof can be applied)
468  goto_program.insert_after(
469  instr_it,
471  next_it,
472  no_exception_currently_in_flight,
473  instr_it->source_location()));
474 
476  }
477  }
478 
480 }
481 
486  const irep_idt &function_identifier,
487  goto_programt &goto_program)
488 {
489  stack_catcht stack_catch; // stack of try-catch blocks
490  std::vector<std::vector<symbol_exprt>> stack_locals; // stack of local vars
491  std::vector<symbol_exprt> locals;
492 
493  if(goto_program.empty())
494  return;
495 
496  bool program_or_callees_may_throw =
497  function_or_callees_may_throw(goto_program);
498 
499  bool did_something = false;
500  bool added_goto_instruction = false;
501 
502  Forall_goto_program_instructions(instr_it, goto_program)
503  {
504  if(instr_it->is_decl())
505  {
506  locals.push_back(instr_it->decl_symbol());
507  }
508  // Is it a handler push/pop or catch landing-pad?
509  else if(instr_it->type() == CATCH)
510  {
511  const irep_idt &statement = instr_it->code().get_statement();
512  // Is it an exception landing pad (start of a catch block)?
513  if(statement==ID_exception_landingpad)
514  {
516  goto_program, instr_it, program_or_callees_may_throw);
517  }
518  // Is it a catch handler pop?
519  else if(statement==ID_pop_catch)
520  {
521  // pop the local vars stack
522  if(!stack_locals.empty())
523  {
524  locals=stack_locals.back();
525  stack_locals.pop_back();
526  }
527  // pop from the stack if possible
528  if(!stack_catch.empty())
529  {
530  stack_catch.pop_back();
531  }
532  else
533  {
534 #ifdef DEBUG
535  std::cout << "Remove exceptions: empty stack\n";
536 #endif
537  }
538  }
539  // Is it a catch handler push?
540  else if(statement==ID_push_catch)
541  {
542  stack_locals.push_back(locals);
543  locals.clear();
544 
546  stack_catch.push_back(exception);
547  remove_exceptionst::catch_handlerst &last_exception=
548  stack_catch.back();
549 
550  // copy targets
551  const code_push_catcht::exception_listt &exception_list =
552  to_code_push_catch(instr_it->code()).exception_list();
553 
554  // The target list can be empty if `finish_catch_push_targets` found that
555  // the targets were unreachable (in which case no exception can truly
556  // be thrown at runtime)
557  INVARIANT(
558  instr_it->targets.empty() ||
559  exception_list.size()==instr_it->targets.size(),
560  "`exception_list` should contain current instruction's targets");
561 
562  // Fill the map with the catch type and the target
563  unsigned i=0;
564  for(auto target : instr_it->targets)
565  {
566  last_exception.push_back(
567  std::make_pair(exception_list[i].get_tag(), target));
568  i++;
569  }
570  }
571  else
572  {
573  INVARIANT(
574  false,
575  "CATCH opcode should be one of push-catch, pop-catch, landingpad");
576  }
577 
578  instr_it->turn_into_skip();
579  did_something = true;
580  }
581  else if(instr_it->type() == THROW)
582  {
583  did_something = instrument_throw(
584  function_identifier, goto_program, instr_it, stack_catch, locals);
585  }
586  else if(instr_it->type() == FUNCTION_CALL)
587  {
588  instrumentation_resultt result =
590  function_identifier, goto_program, instr_it, stack_catch, locals);
591  did_something = result != instrumentation_resultt::DID_NOTHING;
592  added_goto_instruction =
594  }
595  }
596 
597  // INITIALIZE_FUNCTION should not contain any exception handling branches for
598  // two reasons: (1) with symex-driven lazy loading it means that code that
599  // references @inflight_exception might be generated before
600  // @inflight_exception is initialized; (2) symex can analyze
601  // INITIALIZE_FUNCTION much faster if it doesn't contain any branching.
602  INVARIANT(
603  function_identifier != INITIALIZE_FUNCTION || !added_goto_instruction,
604  INITIALIZE_FUNCTION " should not contain any exception handling branches");
605 
606  if(did_something)
607  remove_skip(goto_program);
608 }
609 
611 {
612  for(auto &gf_entry : goto_functions.function_map)
613  instrument_exceptions(gf_entry.first, gf_entry.second.body);
614 }
615 
617 operator()(const irep_idt &function_identifier, goto_programt &goto_program)
618 {
619  instrument_exceptions(function_identifier, goto_program);
620 }
621 
624  symbol_table_baset &symbol_table,
625  goto_functionst &goto_functions,
626  message_handlert &message_handler)
627 {
628  const namespacet ns(symbol_table);
629  std::map<irep_idt, std::set<irep_idt>> exceptions_map;
630 
631  uncaught_exceptions(goto_functions, ns, exceptions_map);
632 
633  remove_exceptionst::function_may_throwt function_may_throw =
634  [&exceptions_map](const irep_idt &id) {
635  return !exceptions_map[id].empty();
636  };
637 
639  symbol_table, nullptr, function_may_throw, false, message_handler);
640 
641  remove_exceptions(goto_functions);
642 }
643 
657  const irep_idt &function_identifier,
658  goto_programt &goto_program,
659  symbol_table_baset &symbol_table,
660  message_handlert &message_handler)
661 {
662  remove_exceptionst::function_may_throwt any_function_may_throw =
663  [](const irep_idt &) { return true; };
664 
666  symbol_table, nullptr, any_function_may_throw, false, message_handler);
667 
668  remove_exceptions(function_identifier, goto_program);
669 }
670 
679  goto_modelt &goto_model,
680  message_handlert &message_handler)
681 {
683  goto_model.symbol_table, goto_model.goto_functions, message_handler);
684 }
685 
688  symbol_table_baset &symbol_table,
689  goto_functionst &goto_functions,
690  const class_hierarchyt &class_hierarchy,
691  message_handlert &message_handler)
692 {
693  const namespacet ns(symbol_table);
694  std::map<irep_idt, std::set<irep_idt>> exceptions_map;
695 
696  uncaught_exceptions(goto_functions, ns, exceptions_map);
697 
698  remove_exceptionst::function_may_throwt function_may_throw =
699  [&exceptions_map](const irep_idt &id) {
700  return !exceptions_map[id].empty();
701  };
702 
704  symbol_table, &class_hierarchy, function_may_throw, true, message_handler);
705 
706  remove_exceptions(goto_functions);
707 }
708 
724  const irep_idt &function_identifier,
725  goto_programt &goto_program,
726  symbol_table_baset &symbol_table,
727  const class_hierarchyt &class_hierarchy,
728  message_handlert &message_handler)
729 {
730  remove_exceptionst::function_may_throwt any_function_may_throw =
731  [](const irep_idt &) { return true; };
732 
734  symbol_table,
735  &class_hierarchy,
736  any_function_may_throw,
737  true,
738  message_handler);
739 
740  remove_exceptions(function_identifier, goto_program);
741 }
742 
753  goto_modelt &goto_model,
754  const class_hierarchyt &class_hierarchy,
755  message_handlert &message_handler)
756 {
758  goto_model.symbol_table,
759  goto_model.goto_functions,
760  class_hierarchy,
761  message_handler);
762 }
pointer_typet pointer_type(const typet &subtype)
Definition: c_types.cpp:235
Non-graph-based representation of the class hierarchy.
A goto_instruction_codet representing an assignment in the program.
const exprt & catch_expr() const
Definition: std_code.h:1948
std::vector< exception_list_entryt > exception_listt
Definition: std_code.h:1849
exception_listt & exception_list()
Definition: std_code.h:1860
dstringt has one field, an unsigned integer no which is an index into a static table of strings.
Definition: dstring.h:38
Equality.
Definition: std_expr.h:1361
Base class for all expressions.
Definition: expr.h:56
const source_locationt & source_location() const
Definition: expr.h:231
typet & type()
Return the type of the expression.
Definition: expr.h:84
A collection of goto functions.
function_mapt function_map
symbol_tablet symbol_table
Symbol table.
Definition: goto_model.h:31
goto_functionst goto_functions
GOTO functions.
Definition: goto_model.h:34
A generic container class for the GOTO intermediate representation of one function.
Definition: goto_program.h:73
static instructiont make_assumption(const exprt &g, const source_locationt &l=source_locationt::nil())
Definition: goto_program.h:945
instructionst instructions
The list of instructions in the goto program.
Definition: goto_program.h:622
static instructiont make_dead(const symbol_exprt &symbol, const source_locationt &l=source_locationt::nil())
Definition: goto_program.h:971
instructionst::iterator targett
Definition: goto_program.h:614
static instructiont make_assignment(const code_assignt &_code, const source_locationt &l=source_locationt::nil())
Create an assignment instruction.
targett get_end_function()
Get an instruction iterator pointing to the END_FUNCTION instruction of the goto program.
Definition: goto_program.h:827
targett insert_after(const_targett target)
Insertion after the instruction pointed-to by the given instruction iterator target.
Definition: goto_program.h:708
static instructiont make_goto(targett _target, const source_locationt &l=source_locationt::nil())
bool empty() const
Is the program empty?
Definition: goto_program.h:799
bool get_bool(const irep_idt &name) const
Definition: irep.cpp:57
const irep_idt & id() const
Definition: irep.h:384
A namespacet is essentially one or two symbol tables bound together, to allow for symbol lookups in t...
Definition: namespace.h:94
The null pointer constant.
Definition: pointer_expr.h:909
Lowers high-level exception descriptions into low-level operations suitable for symex and other analy...
const class_hierarchyt * class_hierarchy
std::vector< catch_handlerst > stack_catcht
function_may_throwt function_may_throw
symbol_exprt get_inflight_exception_global()
Create a global named java::@inflight_exception that holds any exception that has been thrown but not...
void add_exception_dispatch_sequence(const irep_idt &function_identifier, goto_programt &goto_program, const goto_programt::targett &instr_it, const stack_catcht &stack_catch, const std::vector< symbol_exprt > &locals)
Emit the code: if (exception instanceof ExnA) then goto handlerA else if (exception instanceof ExnB) ...
message_handlert & message_handler
void instrument_exception_handler(goto_programt &goto_program, const goto_programt::targett &, bool may_catch)
Translates an exception landing-pad into instructions that copy the in-flight exception pointer to a ...
std::vector< std::pair< irep_idt, goto_programt::targett > > catch_handlerst
bool function_or_callees_may_throw(const goto_programt &) const
Checks whether a function may ever experience an exception (whether or not it catches),...
goto_programt::targett find_universal_exception(const remove_exceptionst::stack_catcht &stack_catch, goto_programt &goto_program, std::size_t &universal_try, std::size_t &universal_catch)
Find the innermost universal exception handler for the current program location which may throw (i....
remove_exceptionst(symbol_table_baset &_symbol_table, const class_hierarchyt *_class_hierarchy, function_may_throwt _function_may_throw, bool _remove_added_instanceof, message_handlert &_message_handler)
std::function< bool(const irep_idt &)> function_may_throwt
instrumentation_resultt instrument_function_call(const irep_idt &function_identifier, goto_programt &goto_program, const goto_programt::targett &, const stack_catcht &, const std::vector< symbol_exprt > &)
instruments each function call that may escape exceptions with conditional GOTOS to the corresponding...
bool instrument_throw(const irep_idt &function_identifier, goto_programt &goto_program, const goto_programt::targett &, const stack_catcht &, const std::vector< symbol_exprt > &)
instruments each throw with conditional GOTOS to the corresponding exception handlers
void instrument_exceptions(const irep_idt &function_identifier, goto_programt &goto_program)
instruments throws, function calls that may escape exceptions and exception handlers.
symbol_table_baset & symbol_table
void operator()(goto_functionst &goto_functions)
A struct tag type, i.e., struct_typet with an identifier.
Definition: std_types.h:493
Expression to hold a symbol (variable)
Definition: std_expr.h:131
const irep_idt & get_identifier() const
Definition: std_expr.h:160
The symbol table base class interface.
const symbolt * lookup(const irep_idt &name) const
Find a symbol in the symbol table for read-only access.
const symbolt & lookup_ref(const irep_idt &name) const
Find a symbol in the symbol table for read-only access.
Symbol table entry.
Definition: symbol.h:28
class symbol_exprt symbol_expr() const
Produces a symbol_exprt for a symbol.
Definition: symbol.cpp:121
typet type
Type of symbol.
Definition: symbol.h:31
The Boolean constant true.
Definition: std_expr.h:3055
Semantic type conversion.
Definition: std_expr.h:2068
static exprt get_exception_symbol(const exprt &exor)
Returns the symbol corresponding to an exception.
Symbol Table + CFG.
#define Forall_goto_program_instructions(it, program)
@ FUNCTION_CALL
Definition: goto_program.h:49
@ CATCH
Definition: goto_program.h:51
@ THROW
Definition: goto_program.h:50
Java-specific exprt subclasses.
static irep_idt get_tag(const typet &type)
empty_typet java_void_type()
Definition: java_types.cpp:37
API to expression classes for Pointers.
void remove_exceptions_using_instanceof(symbol_table_baset &symbol_table, goto_functionst &goto_functions, message_handlert &message_handler)
removes throws/CATCH-POP/CATCH-PUSH
void remove_exceptions(symbol_table_baset &symbol_table, goto_functionst &goto_functions, const class_hierarchyt &class_hierarchy, message_handlert &message_handler)
removes throws/CATCH-POP/CATCH-PUSH
Remove function exceptional returns.
#define INFLIGHT_EXCEPTION_VARIABLE_NAME
void remove_instanceof(const irep_idt &function_identifier, goto_programt::targett target, goto_programt &goto_program, symbol_table_baset &symbol_table, const class_hierarchyt &class_hierarchy, message_handlert &message_handler)
Replace an instanceof in the expression or guard of the passed instruction of the given function body...
Remove Instance-of Operators.
void remove_skip(goto_programt &goto_program, goto_programt::targett begin, goto_programt::targett end)
remove unnecessary skip statements
Definition: remove_skip.cpp:87
Program Transformation.
#define DATA_INVARIANT(CONDITION, REASON)
This condition should be used to document that assumptions that are made on goto_functions,...
Definition: invariant.h:534
#define PRECONDITION(CONDITION)
Definition: invariant.h:463
#define INITIALIZE_FUNCTION
static code_landingpadt & to_code_landingpad(codet &code)
Definition: std_code.h:1972
static code_push_catcht & to_code_push_catch(codet &code)
Definition: std_code.h:1883
const symbol_exprt & to_symbol_expr(const exprt &expr)
Cast an exprt to a symbol_exprt.
Definition: std_expr.h:272
void uncaught_exceptions(const goto_functionst &goto_functions, const namespacet &ns, std::map< irep_idt, std::set< irep_idt >> &exceptions_map)
Applies the uncaught exceptions analysis and outputs the result.
Over-approximative uncaught exceptions analysis.
dstringt irep_idt