AIM For Today: 

- Make the anlalyzer evaluate the vfunc calls by creating enodes and eedges 
  representing the call. 

- Make the analyzer not treat such calls as call to "unknown function"

---
PROGRESS :

- After the analyzer sucessfully found which funcitons to call ( thanks to 
  functions from ipa-devirt.c ), All that was left was it to create enodes and 
  eedges for analyzeing such calls for which I took the same aproach I used to 
  detect and analyzer calls via function pointers. 

- now the analyzer was sucessfully decting and analyzing the calls via 
  functions pointers, but due to some reason it was still not giving out 
  analysis reports for such calls. 

- After a bit of digging, I found out that analyzer was also treating such 
  calls as " call to unknown funcitons " and in case of such calls, analyzer 
  plays safe by resetting all of the state-machine state for the values that 
  are reachable by the function call, to avoid false reports. ( it was also 
  mentioned in a previous discussions 
<https://gcc.gnu.org/pipermail/gcc/2021-April/235335.html> on the list)

- I fixed the porblem by simply updating exploded_node::on_stmt () by setting
  the value of "unknown_side_effects" ( a boolean which is set true when
  analyzer thinks executing a gimple stmt would cause some side effects, and 
  acts conservatively for such cases ) to "false" when gimple call stmt is a 
  polymorphic call. Some thing like this, at the end of the definition of 
  exploded_node::on_stmt (): -

File: {src-dir}/gcc/analyzer/engine.cc
1246:     /* If the statmement is a polymorphic call then assume 
1247:        there are no side effects.  */
1248:     gimple *call_stmt = const_cast<gimple *>(stmt);
1249:     if (gcall *call = dyn_cast<gcall *> (call_stmt))
1250:     {
1251:       function *fun = this->get_function();
1252:       cgraph_edge *e = cgraph_node::get (fun->decl)->get_edge (call);
1253:       if ((e && e->indirect_info) && (e->indirect_info->polymorphic))
1254:         unknown_side_effects = false;
1255:     }
1256: 
1257:   on_stmt_post (stmt, state, unknown_side_effects, &ctxt);
1258: 
1259:   return on_stmt_flags ();

- After this the analyzer was working as expected for virtual function calls on
  all of the examples I created. 

- Let's take example of the following program ( the same program which was
  mentioned in proposal, for which analyzer was giving out a false positive )

File: test.cpp
01: #include <cstdlib>
02: 
03: struct A
04: {
05:     virtual int foo (void) 
06:     {
07:         return 42;
08:     }
09: };
10: 
11: struct B: public A
12: {
13:     int *ptr;
14:     void alloc ()
15:     {
16:         ptr = (int*)malloc(sizeof(int));
17:     }
18:     int foo (void) 
19:     { 
20:         free(ptr);
21:         return 0;
22:     }
23: };
24: 
25: int test()
26: {
27:     struct B b, *bptr=&b;
28:     b.alloc();
29:     bptr->foo();
30:     return bptr->foo();
31: }
32: 
33: int main()
34: {
35:     test();
36: }
37: 
( godbolt link of the above code (https://godbolt.org/z/n17WK4MxG) )

for the same porgram, the analyzer now generates the following warning message :

warning: double-‘free’ of ‘b.B::ptr’ [CWE-415] [-Wanalyzer-double-free]
   20 |         free(ptr);
      |         ~~~~^~~~~
  ‘int test()’: events 1-2
    |
    |   25 | int test()
    |      |     ^~~~
    |      |     |
    |      |     (1) entry to ‘test’
    |......
    |   28 |     b.alloc();
    |      |     ~~~~~~~~~
    |      |            |
    |      |            (2) calling ‘B::alloc’ from ‘test’
    |
    +--> ‘void B::alloc()’: events 3-4
           |
           |   14 |     void alloc ()
           |      |          ^~~~~
           |      |          |
           |      |          (3) entry to ‘B::alloc’
           |   15 |     {
           |   16 |         ptr = (int*)malloc(sizeof(int));
           |      |                     ~~~~~~~~~~~~~~~~~~~
           |      |                           |
           |      |                           (4) allocated here
           |
    <------+
    |
  ‘int test()’: events 5-6
    |
    |   28 |     b.alloc();
    |      |     ~~~~~~~^~
    |      |            |
    |      |            (5) returning to ‘test’ from ‘B::alloc’
    |   29 |     bptr->foo();
    |      |     ~~~~~~~~~~~
    |      |              |
    |      |              (6) calling ‘B::foo’ from ‘test’
    |
    +--> ‘virtual int B::foo()’: events 7-8
           |
           |   18 |         int foo (void)
           |      |             ^~~
           |      |             |
           |      |             (7) entry to ‘B::foo’
           |   19 |     {
           |   20 |         free(ptr);
           |      |         ~~~~~~~~~
           |      |             |
           |      |             (8) first ‘free’ here
           |
    <------+
    |
  ‘int test()’: events 9-10
    |
    |   29 |     bptr->foo();
    |      |     ~~~~~~~~~^~
    |      |              |
    |      |              (9) returning to ‘test’ from ‘B::foo’
    |   30 |     return bptr->foo();
    |      |            ~~~~~~~~~~~
    |      |                     |
    |      |                     (10) calling ‘B::foo’ from ‘test’
    |
    +--> ‘virtual int B::foo()’: events 11-12
           |
           |   18 |         int foo (void)
           |      |             ^~~
           |      |             |
           |      |             (11) entry to ‘B::foo’
           |   19 |     {
           |   20 |         free(ptr);
           |      |         ~~~~~~~~~
           |      |             |
           |      |             (12) second ‘free’ here; first ‘free’ was at (8)
           |

- Sorry no patch for the following changes available right now as there is
  still a bit of cleaning and refining left to do in it ( will post the patch
  with tomorrow's report )

- That being said, I also want to test the working of this patch with a few
 more examples of vfunc calls to check for a possible loophole.

---
STATUS AT THE END OF THE DAY :- 

- Make the anlalyzer evaluate the vfunc calls by creating enodes and eedges
  representing the call. ( done )

- Make the analyzer not treat such calls as call to "unknown function" ( done )

Thank you
- Ankur

Reply via email to