File: virtual_call_null_checks.cpp

package info (click to toggle)
cbmc 6.6.0-4
  • links: PTS
  • area: main
  • in suites: forky, sid, trixie
  • size: 153,852 kB
  • sloc: cpp: 386,459; ansic: 114,466; java: 28,405; python: 6,003; yacc: 4,552; makefile: 4,041; lex: 2,487; xml: 2,388; sh: 2,050; perl: 557; pascal: 184; javascript: 163; ada: 36
file content (116 lines) | stat: -rw-r--r-- 3,862 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
/*******************************************************************\

Module: Unit test to check Java virtual calls via a pointer
        yield a correct sequence of not-null assumptions.

Author: Diffblue Limited.

\*******************************************************************/

#include <java-testing-utils/load_java_class.h>

#include <util/expr_iterator.h>

#include <analyses/local_safe_pointers.h>
#include <ansi-c/goto-conversion/goto_convert_functions.h>
#include <testing-utils/message.h>
#include <testing-utils/use_catch.h>

// We're expecting a call "something->field . B.virtualmethod()":
static bool is_expected_virtualmethod_call(
  const goto_programt::instructiont &instruction)
{
  if(instruction.type() != FUNCTION_CALL)
    return false;
  const auto &called_function = instruction.call_function();
  if(!can_cast_expr<class_method_descriptor_exprt>(called_function))
    return false;
  if(called_function.get(ID_identifier) != "java::B.virtualmethod:()V")
    return false;
  if(instruction.call_arguments().size() != 1)
    return false;
  const auto &this_argument = instruction.call_arguments()[0];
  if(this_argument.id() != ID_member)
    return false;
  if(to_member_expr(this_argument).compound().id() != ID_dereference)
    return false;

  return true;
}

SCENARIO(
  "java_bytecode_virtual_call_null_checks",
  "[core][java_bytecode][java_bytecode_instrument]")
{
  GIVEN("A class that makes a virtual call via a pointer")
  {
    symbol_tablet symbol_table = load_java_class(
      "VirtualCallNullChecks", "./java_bytecode/java_bytecode_instrument");

    WHEN("The virtual call is converted")
    {
      namespacet ns(symbol_table);
      goto_functionst goto_functions;
      goto_convert(symbol_table, goto_functions, null_message_handler);

      const auto &main_function =
        goto_functions.function_map.at("java::VirtualCallNullChecks.main:()V");

      THEN("The loaded function should call B.virtualmethod via a pointer")
      {
        // This just checks that the class actually makes the expected call,
        // i.e. that it hasn't been optimised away or similar.
        std::size_t found_virtualmethod_calls =
          std::count_if(
            main_function.body.instructions.begin(),
            main_function.body.instructions.end(),
            is_expected_virtualmethod_call);

        REQUIRE(found_virtualmethod_calls == 1);
      }
      THEN("All pointer usages should be safe")
      {
        // This analysis checks that any usage of a pointer is preceded by an
        // assumption that it is non-null
        // (e.g. assume(x != nullptr); y = x->...)
        local_safe_pointerst safe_pointers;
        safe_pointers(main_function.body);

        for(auto instrit = main_function.body.instructions.begin(),
              instrend = main_function.body.instructions.end();
            instrit != instrend; ++instrit)
        {
          for(auto it = instrit->code().depth_begin(),
                   itend = instrit->code().depth_end();
              it != itend;
              ++it)
          {
            if(it->id() == ID_dereference)
            {
              const auto &deref = to_dereference_expr(*it);
              REQUIRE(
                safe_pointers.is_safe_dereference(deref, instrit));
            }
          }

          if(instrit->has_condition())
          {
            const auto &condition = instrit->condition();

            for(auto it = condition.depth_begin(),
                     itend = condition.depth_end();
                it != itend;
                ++it)
            {
              if(it->id() == ID_dereference)
              {
                const auto &deref = to_dereference_expr(*it);
                REQUIRE(safe_pointers.is_safe_dereference(deref, instrit));
              }
            }
          }
        }
      }
    }
  }
}